diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ec48c438267b72d2c2b8d2341175cbd6022cbf4..6dbdf0c93e763863e1ef7cff6c52181d141ecace 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -537,45 +537,6 @@ set(ZIG_STAGE2_SOURCES src/Value.zig src/Zcu.zig src/Zcu/PerThread.zig - src/arch/aarch64/CodeGen.zig - src/arch/aarch64/Emit.zig - src/arch/aarch64/Mir.zig - src/arch/aarch64/abi.zig - src/arch/aarch64/bits.zig - src/arch/arm/CodeGen.zig - src/arch/arm/Emit.zig - src/arch/arm/Mir.zig - src/arch/arm/abi.zig - src/arch/arm/bits.zig - src/arch/powerpc/CodeGen.zig - src/arch/riscv64/abi.zig - src/arch/riscv64/bits.zig - src/arch/riscv64/CodeGen.zig - src/arch/riscv64/Emit.zig - src/arch/riscv64/encoding.zig - src/arch/riscv64/Lower.zig - src/arch/riscv64/Mir.zig - src/arch/riscv64/mnem.zig - src/arch/sparc64/CodeGen.zig - src/arch/sparc64/Emit.zig - src/arch/sparc64/Mir.zig - src/arch/sparc64/abi.zig - src/arch/sparc64/bits.zig - src/arch/wasm/CodeGen.zig - src/arch/wasm/Emit.zig - src/arch/wasm/Mir.zig - src/arch/wasm/abi.zig - src/arch/x86/bits.zig - src/arch/x86_64/CodeGen.zig - src/arch/x86_64/Disassembler.zig - src/arch/x86_64/Emit.zig - src/arch/x86_64/Encoding.zig - src/arch/x86_64/Lower.zig - src/arch/x86_64/Mir.zig - src/arch/x86_64/abi.zig - src/arch/x86_64/bits.zig - src/arch/x86_64/encoder.zig - src/arch/x86_64/encodings.zon src/clang.zig src/clang_options.zig src/clang_options_data.zig diff --git a/build.zig b/build.zig index fe52457392e7ad57502fbac7f5c648fa2bf24597..d260353c886a58bf1bed339bad10c96d129430e3 100644 --- a/build.zig +++ b/build.zig @@ -415,7 +415,18 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(check_fmt); const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); - try tests.addCases(b, test_cases_step, test_filters, test_target_filters, target, .{ + try tests.addCases(b, test_cases_step, target, .{ + .test_filters = test_filters, + .test_target_filters = test_target_filters, + .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, + .skip_libc = skip_libc, + }, .{ .skip_translate_c = skip_translate_c, .skip_run_translated_c = skip_run_translated_c, }, .{ @@ -439,6 +450,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the behavior tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{}, .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -448,8 +460,8 @@ pub fn build(b: *std.Build) !void { .skip_linux = skip_linux, .skip_llvm = skip_llvm, .skip_libc = skip_libc, - // 2923515904 was observed on an x86_64-linux-gnu host. - .max_rss = 3100000000, + // 3888779264 was observed on an x86_64-linux-gnu host. + .max_rss = 4000000000, })); test_modules_step.dependOn(tests.addModuleTests(b, .{ @@ -461,6 +473,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the @cImport tests", .optimize_modes = optimization_modes, .include_paths = &.{"test/c_import"}, + .windows_libs = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -481,6 +494,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the compiler_rt tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -502,6 +516,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the zigc tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -523,6 +538,12 @@ pub fn build(b: *std.Build) !void { .desc = "Run the standard library tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{ + "advapi32", + "crypt32", + "iphlpapi", + "ws2_32", + }, .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -720,6 +741,12 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu compiler_mod.addImport("aro", aro_mod); compiler_mod.addImport("aro_translate_c", aro_translate_c_mod); + if (options.target.result.os.tag == .windows) { + compiler_mod.linkSystemLibrary("advapi32", .{}); + compiler_mod.linkSystemLibrary("crypt32", .{}); + compiler_mod.linkSystemLibrary("ws2_32", .{}); + } + return compiler_mod; } @@ -1417,6 +1444,10 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { }), }); + if (b.graph.host.result.os.tag == .windows) { + doctest_exe.root_module.linkSystemLibrary("advapi32", .{}); + } + var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| { std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{ b.build_root, @errorName(err), diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh index dfb440573f1aad33cef0161f300aa1086647a604..335fe60bfb05d3339e0cc19826fc226322516579 100644 --- a/ci/x86_64-linux-debug-llvm.sh +++ b/ci/x86_64-linux-debug-llvm.sh @@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460" PREFIX="$HOME/deps/$CACHE_BASENAME" ZIG="$PREFIX/bin/zig" -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" +export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH" # Make the `zig version` number consistent. # This will affect the cmake command below. diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 395a6770a5d4d920c83c166f1d9ac5f9cf803cd3..be5fe3944695e75bd1ad33622131f775aca08510 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460" PREFIX="$HOME/deps/$CACHE_BASENAME" ZIG="$PREFIX/bin/zig" -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" +export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH" # Make the `zig version` number consistent. # This will affect the cmake command below. diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index b468f65493a89ce0b4c12273a36259c57f5324ba..3352d06a46c845494933e16666c9c5136221e505 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460" PREFIX="$HOME/deps/$CACHE_BASENAME" ZIG="$PREFIX/bin/zig" -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" +export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH" # Make the `zig version` number consistent. # This will affect the cmake command below. diff --git a/doc/langref.html.in b/doc/langref.html.in index 78f5c32384e8a754a20406ab0eddb7f6fe889636..dcf13e812d7ad056387c1879cc94f94226127089 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -374,7 +374,8 @@

Most of the time, it is more appropriate to write to stderr rather than stdout, and whether or not the message is successfully written to the stream is irrelevant. - For this common case, there is a simpler API: + Also, formatted printing often comes in handy. For this common case, + there is a simpler API:

{#code|hello_again.zig#} @@ -3842,37 +3843,6 @@ void do_a_thing(struct Foo *foo) { {#header_close#} {#header_close#} - {#header_open|usingnamespace#} -

- {#syntax#}usingnamespace{#endsyntax#} is a declaration that mixes all the public - declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#}, - or {#link|opaque#}, into the namespace: -

- {#code|test_usingnamespace.zig#} - -

- {#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public - API of a file or package. For example, one might have c.zig with all of the - {#link|C imports|Import from C Header File#}: -

- {#syntax_block|zig|c.zig#} -pub usingnamespace @cImport({ - @cInclude("epoxy/gl.h"); - @cInclude("GLFW/glfw3.h"); - @cDefine("STBI_ONLY_PNG", ""); - @cDefine("STBI_NO_STDIO", ""); - @cInclude("stb_image.h"); -}); - {#end_syntax_block#} -

- The above example demonstrates using {#syntax#}pub{#endsyntax#} to qualify the - {#syntax#}usingnamespace{#endsyntax#} additionally makes the imported declarations - {#syntax#}pub{#endsyntax#}. This can be used to forward declarations, giving precise control - over what declarations a given file exposes. -

- {#header_close#} - - {#header_open|comptime#}

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 { {#header_close#} {#header_open|Async Functions#} -

Async functions regressed with the release of 0.11.0. Their future in - the Zig language is unclear due to multiple unsolved problems:

- -

These problems are surmountable, but it will take time. The Zig team - is currently focused on other priorities.

+

Async functions regressed with the release of 0.11.0. The current plan is to + reintroduce them as a lower level primitive that powers I/O implementations.

+

Tracking issue: Proposal: stackless coroutines as low-level primitives

{#header_close#} {#header_open|Builtin Functions|2col#} @@ -6552,7 +6515,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val

@@ -7372,29 +7335,6 @@ fn readU32Be() u32 {} - - -
{#syntax#}async{#endsyntax#}
- - - {#syntax#}async{#endsyntax#} can be used before a function call to get a pointer to the function's frame when it suspends. - - - - - -
{#syntax#}await{#endsyntax#}
- - - {#syntax#}await{#endsyntax#} can be used to suspend the current function until the frame provided after the {#syntax#}await{#endsyntax#} completes. - {#syntax#}await{#endsyntax#} copies the value returned from the target function's frame to the caller. - - -
{#syntax#}break{#endsyntax#}
@@ -7812,18 +7752,6 @@ fn readU32Be() u32 {} - - -
{#syntax#}usingnamespace{#endsyntax#}
- - - {#syntax#}usingnamespace{#endsyntax#} is a top-level declaration that imports all the public declarations of the operand, - which must be a struct, union, or enum, into the current scope. - - -
{#syntax#}var{#endsyntax#}
@@ -7893,7 +7821,6 @@ ComptimeDecl <- KEYWORD_comptime Block Decl <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block) / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl - / KEYWORD_usingnamespace Expr SEMICOLON FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr @@ -8006,8 +7933,7 @@ TypeExpr <- PrefixTypeOp* ErrorUnionExpr ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? SuffixExpr - <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments - / PrimaryTypeExpr (SuffixOp / FnCallArguments)* + <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* PrimaryTypeExpr <- BUILTINIDENTIFIER FnCallArguments @@ -8183,7 +8109,6 @@ PrefixOp / MINUSPERCENT / AMPERSAND / KEYWORD_try - / KEYWORD_await PrefixTypeOp <- QUESTIONMARK @@ -8404,8 +8329,6 @@ KEYWORD_and <- 'and' end_of_word KEYWORD_anyframe <- 'anyframe' end_of_word KEYWORD_anytype <- 'anytype' end_of_word KEYWORD_asm <- 'asm' end_of_word -KEYWORD_async <- 'async' end_of_word -KEYWORD_await <- 'await' end_of_word KEYWORD_break <- 'break' end_of_word KEYWORD_callconv <- 'callconv' end_of_word KEYWORD_catch <- 'catch' end_of_word @@ -8442,14 +8365,13 @@ KEYWORD_threadlocal <- 'threadlocal' end_of_word KEYWORD_try <- 'try' end_of_word KEYWORD_union <- 'union' end_of_word KEYWORD_unreachable <- 'unreachable' end_of_word -KEYWORD_usingnamespace <- 'usingnamespace' end_of_word KEYWORD_var <- 'var' end_of_word KEYWORD_volatile <- 'volatile' end_of_word KEYWORD_while <- 'while' end_of_word keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and - / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async - / KEYWORD_await / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch + / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm + / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if @@ -8458,7 +8380,7 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable - / KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while + / KEYWORD_var / KEYWORD_volatile / KEYWORD_while {#end_syntax_block#} {#header_close#} {#header_open|Zen#} diff --git a/doc/langref/bad_default_value.zig b/doc/langref/bad_default_value.zig index 98fd498451b64199544f8bee6551baaa3fce6ad7..df38209c49f09d7bcfed1b61a7a56b5e667bd1e1 100644 --- a/doc/langref/bad_default_value.zig +++ b/doc/langref/bad_default_value.zig @@ -17,7 +17,7 @@ pub fn main() !void { .maximum = 0.20, }; const category = threshold.categorize(0.90); - try std.io.getStdOut().writeAll(@tagName(category)); + try std.fs.File.stdout().writeAll(@tagName(category)); } const std = @import("std"); diff --git a/doc/langref/hello.zig b/doc/langref/hello.zig index 8730e464566abdaad34bc3b72eb6c4934744a080..27ea1f689a5e26f6f67d89965d9e1383022cb509 100644 --- a/doc/langref/hello.zig +++ b/doc/langref/hello.zig @@ -1,8 +1,7 @@ const std = @import("std"); pub fn main() !void { - const stdout = std.io.getStdOut().writer(); - try stdout.print("Hello, {s}!\n", .{"world"}); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } // exe=succeed diff --git a/doc/langref/hello_again.zig b/doc/langref/hello_again.zig index d3c9d019dd2c2d3e3a4770e0eb4c1d68489bdbf4..91c3f3d1581ff9d7dd8372b50b3750e5e83e4064 100644 --- a/doc/langref/hello_again.zig +++ b/doc/langref/hello_again.zig @@ -1,7 +1,7 @@ const std = @import("std"); pub fn main() void { - std.debug.print("Hello, world!\n", .{}); + std.debug.print("Hello, {s}!\n", .{"World"}); } // exe=succeed diff --git a/doc/langref/test_usingnamespace.zig b/doc/langref/test_usingnamespace.zig deleted file mode 100644 index 82bb81733a6e9f531c869191d3c3ce3a571ec40b..0000000000000000000000000000000000000000 --- a/doc/langref/test_usingnamespace.zig +++ /dev/null @@ -1,8 +0,0 @@ -test "using std namespace" { - const S = struct { - usingnamespace @import("std"); - }; - try S.testing.expect(true); -} - -// test diff --git a/lib/compiler/aro/aro/Compilation.zig b/lib/compiler/aro/aro/Compilation.zig index 4dcbf9559010ff00525a0f5a764aa77ae36afb81..d9c001d9259eca1f0ad426a0c10b43350261a53d 100644 --- a/lib/compiler/aro/aro/Compilation.zig +++ b/lib/compiler/aro/aro/Compilation.zig @@ -1432,7 +1432,7 @@ fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u defer buf.deinit(); const max = limit orelse std.math.maxInt(u32); - file.reader().readAllArrayList(&buf, max) catch |e| switch (e) { + file.deprecatedReader().readAllArrayList(&buf, max) catch |e| switch (e) { error.StreamTooLong => if (limit == null) return e, else => return e, }; diff --git a/lib/compiler/aro/aro/Diagnostics.zig b/lib/compiler/aro/aro/Diagnostics.zig index 0a65824341f9dba28b0fb28c554ad8de59353fb0..dbf6e29af51311c782af925165925d4cd1b91315 100644 --- a/lib/compiler/aro/aro/Diagnostics.zig +++ b/lib/compiler/aro/aro/Diagnostics.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const Allocator = mem.Allocator; const mem = std.mem; const Source = @import("Source.zig"); @@ -323,12 +324,13 @@ pub fn addExtra( pub fn render(comp: *Compilation, config: std.io.tty.Config) void { if (comp.diagnostics.list.items.len == 0) return; - var m = defaultMsgWriter(config); + var buffer: [1000]u8 = undefined; + var m = defaultMsgWriter(config, &buffer); defer m.deinit(); renderMessages(comp, &m); } -pub fn defaultMsgWriter(config: std.io.tty.Config) MsgWriter { - return MsgWriter.init(config); +pub fn defaultMsgWriter(config: std.io.tty.Config, buffer: []u8) MsgWriter { + return MsgWriter.init(config, buffer); } pub fn renderMessages(comp: *Compilation, m: anytype) void { @@ -449,12 +451,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { }, .normalized => { const f = struct { - pub fn f( - bytes: []const u8, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) !void { + pub fn f(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void { var it: std.unicode.Utf8Iterator = .{ .bytes = bytes, .i = 0, @@ -464,22 +461,16 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { try writer.writeByte(@intCast(codepoint)); } else if (codepoint < 0xFFFF) { try writer.writeAll("\\u"); - try std.fmt.formatInt(codepoint, 16, .upper, .{ - .fill = '0', - .width = 4, - }, writer); + try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 4 }); } else { try writer.writeAll("\\U"); - try std.fmt.formatInt(codepoint, 16, .upper, .{ - .fill = '0', - .width = 8, - }, writer); + try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 8 }); } } } }.f; - printRt(m, prop.msg, .{"{s}"}, .{ - std.fmt.Formatter(f){ .data = msg.extra.normalized }, + printRt(m, prop.msg, .{"{f}"}, .{ + std.fmt.Formatter([]const u8, f){ .data = msg.extra.normalized }, }); }, .none, .offset => m.write(prop.msg), @@ -535,32 +526,31 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind { } const MsgWriter = struct { - w: *std.fs.File.Writer, + writer: *std.io.Writer, config: std.io.tty.Config, fn init(config: std.io.tty.Config, buffer: []u8) MsgWriter { - std.debug.lockStdErr(); return .{ - .w = std.fs.stderr().writer(buffer), + .writer = std.debug.lockStderrWriter(buffer), .config = config, }; } pub fn deinit(m: *MsgWriter) void { - m.w.flush() catch {}; - std.debug.unlockStdErr(); + std.debug.unlockStderrWriter(); + m.* = undefined; } pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void { - m.w.writer().print(fmt, args) catch {}; + m.writer.print(fmt, args) catch {}; } fn write(m: *MsgWriter, msg: []const u8) void { - m.w.writer().writeAll(msg) catch {}; + m.writer.writeAll(msg) catch {}; } fn setColor(m: *MsgWriter, color: std.io.tty.Color) void { - m.config.setColor(m.w.writer(), color) catch {}; + m.config.setColor(m.writer, color) catch {}; } fn location(m: *MsgWriter, path: []const u8, line: u32, col: u32) void { diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig index c89dafe00222feda41d43c273c12929534b6d958..f719e8cc15c7350e470d24c1ae079f4a41372638 100644 --- a/lib/compiler/aro/aro/Driver.zig +++ b/lib/compiler/aro/aro/Driver.zig @@ -519,7 +519,7 @@ fn option(arg: []const u8, name: []const u8) ?[]const u8 { fn addSource(d: *Driver, path: []const u8) !Source { if (mem.eql(u8, "-", path)) { - const stdin = std.io.getStdIn().reader(); + const stdin = std.fs.File.stdin().deprecatedReader(); const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32)); defer d.comp.gpa.free(input); return d.comp.addSourceFromBuffer("", input); @@ -541,7 +541,7 @@ pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{ FatalEr } pub fn renderErrors(d: *Driver) void { - Diagnostics.render(d.comp, d.detectConfig(std.io.getStdErr())); + Diagnostics.render(d.comp, d.detectConfig(std.fs.File.stderr())); } 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_ var macro_buf = std.ArrayList(u8).init(d.comp.gpa); defer macro_buf.deinit(); - const std_out = std.io.getStdOut().writer(); + const std_out = std.fs.File.stdout().deprecatedWriter(); if (try parseArgs(d, std_out, macro_buf.writer(), args)) return; 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( std.fs.cwd().createFile(some, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) }) else - std.io.getStdOut(); + std.fs.File.stdout(); defer if (d.output_name != null) file.close(); - var buf_w = std.io.bufferedWriter(file.writer()); + var buf_w = std.io.bufferedWriter(file.deprecatedWriter()); pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er| return d.fatal("unable to write result: {s}", .{errorDescription(er)}); @@ -704,8 +704,8 @@ fn processSource( defer tree.deinit(); if (d.verbose_ast) { - const stdout = std.io.getStdOut(); - var buf_writer = std.io.bufferedWriter(stdout.writer()); + const stdout = std.fs.File.stdout(); + var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter()); tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {}; buf_writer.flush() catch {}; } @@ -734,8 +734,8 @@ fn processSource( defer ir.deinit(d.comp.gpa); if (d.verbose_ir) { - const stdout = std.io.getStdOut(); - var buf_writer = std.io.bufferedWriter(stdout.writer()); + const stdout = std.fs.File.stdout(); + var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter()); ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {}; buf_writer.flush() catch {}; } @@ -806,10 +806,10 @@ fn processSource( } fn dumpLinkerArgs(items: []const []const u8) !void { - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout().deprecatedWriter(); for (items, 0..) |item, i| { if (i > 0) try stdout.writeByte(' '); - try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)}); + try stdout.print("\"{f}\"", .{std.zig.fmtString(item)}); } try stdout.writeByte('\n'); } diff --git a/lib/compiler/aro/aro/Parser.zig b/lib/compiler/aro/aro/Parser.zig index 5ebd89ec3d433b4a35b5f4282c0af422f058fe87..ada6298a87125a6ac4ff57817667725be6bad22e 100644 --- a/lib/compiler/aro/aro/Parser.zig +++ b/lib/compiler/aro/aro/Parser.zig @@ -500,8 +500,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_ const w = p.strings.writer(); const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes; - try w.print("call to '{s}' declared with attribute error: {}", .{ - p.tokSlice(@"error".__name_tok), std.zig.fmtEscapes(msg_str), + try w.print("call to '{s}' declared with attribute error: {f}", .{ + p.tokSlice(@"error".__name_tok), std.zig.fmtString(msg_str), }); const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); try p.errStr(.error_attribute, usage_tok, str); @@ -512,8 +512,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_ const w = p.strings.writer(); const msg_str = p.comp.interner.get(warning.msg.ref()).bytes; - try w.print("call to '{s}' declared with attribute warning: {}", .{ - p.tokSlice(warning.__name_tok), std.zig.fmtEscapes(msg_str), + try w.print("call to '{s}' declared with attribute warning: {f}", .{ + p.tokSlice(warning.__name_tok), std.zig.fmtString(msg_str), }); const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); try p.errStr(.warning_attribute, usage_tok, str); @@ -542,7 +542,7 @@ fn errDeprecated(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, msg: ?Valu try w.writeAll(reason); if (msg) |m| { const str = p.comp.interner.get(m.ref()).bytes; - try w.print(": {}", .{std.zig.fmtEscapes(str)}); + try w.print(": {f}", .{std.zig.fmtString(str)}); } const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); return p.errStr(tag, tok_i, str); diff --git a/lib/compiler/aro/aro/Preprocessor.zig b/lib/compiler/aro/aro/Preprocessor.zig index e45f6eabc66adaa2dfa9d7240eb85c172bca7df5..c8695edd6baa03aef675b64f86e1d59f983c018d 100644 --- a/lib/compiler/aro/aro/Preprocessor.zig +++ b/lib/compiler/aro/aro/Preprocessor.zig @@ -811,7 +811,7 @@ fn verboseLog(pp: *Preprocessor, raw: RawToken, comptime fmt: []const u8, args: const source = pp.comp.getSource(raw.source); const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start }); - const stderr = std.io.getStdErr().writer(); + const stderr = std.fs.File.stderr().deprecatedWriter(); var buf_writer = std.io.bufferedWriter(stderr); const writer = buf_writer.writer(); defer buf_writer.flush() catch {}; @@ -3262,7 +3262,8 @@ fn printLinemarker( // containing the same bytes as the input regardless of encoding. else => { try w.writeAll("\\x"); - try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, w); + // TODO try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + try w.print("{x:0>2}", .{byte}); }, }; try w.writeByte('"'); diff --git a/lib/compiler/aro/aro/Value.zig b/lib/compiler/aro/aro/Value.zig index 02c9cfc830555d4d0e7c4209c56c1349480e8a02..183c55797607fa5e81a5e7e40d876deac8bd5b30 100644 --- a/lib/compiler/aro/aro/Value.zig +++ b/lib/compiler/aro/aro/Value.zig @@ -961,7 +961,7 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w switch (key) { .null => return w.writeAll("nullptr_t"), .int => |repr| switch (repr) { - inline else => |x| return w.print("{d}", .{x}), + inline .u64, .i64, .big_int => |x| return w.print("{d}", .{x}), }, .float => |repr| switch (repr) { .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 const without_null = bytes[0 .. bytes.len - @intFromEnum(size)]; try w.writeByte('"'); switch (size) { - .@"1" => try w.print("{}", .{std.zig.fmtEscapes(without_null)}), + .@"1" => try w.print("{f}", .{std.zig.fmtString(without_null)}), .@"2" => { var items: [2]u16 = undefined; var i: usize = 0; diff --git a/lib/compiler/aro/backend/Object/Elf.zig b/lib/compiler/aro/backend/Object/Elf.zig index 9b4f347de575098f03fffcb71f2db13d9d74c3dc..ddd66a3c9d1d0f95cd26031f8289436c0f0f14a3 100644 --- a/lib/compiler/aro/backend/Object/Elf.zig +++ b/lib/compiler/aro/backend/Object/Elf.zig @@ -171,7 +171,7 @@ pub fn addRelocation(elf: *Elf, name: []const u8, section_kind: Object.Section, /// strtab /// section headers pub fn finish(elf: *Elf, file: std.fs.File) !void { - var buf_writer = std.io.bufferedWriter(file.writer()); + var buf_writer = std.io.bufferedWriter(file.deprecatedWriter()); const w = buf_writer.writer(); var num_sections: std.elf.Elf64_Half = additional_sections; diff --git a/lib/compiler/aro_translate_c.zig b/lib/compiler/aro_translate_c.zig index e064cfa345055839c5b7049330a3535c670c3a2e..939aede655b6a27d3cbe06a9591f5520bf480f7d 100644 --- a/lib/compiler/aro_translate_c.zig +++ b/lib/compiler/aro_translate_c.zig @@ -1781,7 +1781,8 @@ test "Macro matching" { fn renderErrorsAndExit(comp: *aro.Compilation) noreturn { defer std.process.exit(1); - var writer = aro.Diagnostics.defaultMsgWriter(std.io.tty.detectConfig(std.io.getStdErr())); + var buffer: [1000]u8 = undefined; + var writer = aro.Diagnostics.defaultMsgWriter(std.io.tty.detectConfig(std.fs.File.stderr()), &buffer); defer writer.deinit(); // writer deinit must run *before* exit so that stderr is flushed var saw_error = false; @@ -1824,6 +1825,6 @@ pub fn main() !void { defer tree.deinit(gpa); const formatted = try tree.render(arena); - try std.io.getStdOut().writeAll(formatted); + try std.fs.File.stdout().writeAll(formatted); return std.process.cleanExit(); } diff --git a/lib/compiler/aro_translate_c/ast.zig b/lib/compiler/aro_translate_c/ast.zig index 77e7bf1609b744a0f45412b28aa2c7459702b481..132a07c6c89e07344c6c432ce8df9187ed9963c6 100644 --- a/lib/compiler/aro_translate_c/ast.zig +++ b/lib/compiler/aro_translate_c/ast.zig @@ -849,7 +849,7 @@ const Context = struct { fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { if (std.zig.primitives.isPrimitive(bytes)) return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes}); - return c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(bytes)}); + return c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(bytes, .{ .allow_primitive = true })}); } fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange { @@ -1201,7 +1201,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const compile_error_tok = try c.addToken(.builtin, "@compileError"); _ = try c.addToken(.l_paren, "("); - const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(payload.mangled)}); + const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)}); const err_msg = try c.addNode(.{ .tag = .string_literal, .main_token = err_msg_tok, @@ -2116,7 +2116,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex { defer c.gpa.free(members); for (payload.fields, 0..) |field, i| { - const name_tok = try c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(field.name)}); + const name_tok = try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true })}); _ = try c.addToken(.colon, ":"); const type_expr = try renderNode(c, field.type); @@ -2205,7 +2205,7 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI .main_token = try c.addToken(.period, "."), .data = .{ .node_and_token = .{ lhs, - try c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(field_name)}), + try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true })}), } }, }); } @@ -2681,7 +2681,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { _ = try c.addToken(.l_paren, "("); const res = try c.addNode(.{ .tag = .string_literal, - .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}), + .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}), .data = undefined, }); _ = try c.addToken(.r_paren, ")"); @@ -2765,7 +2765,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { _ = try c.addToken(.l_paren, "("); const res = try c.addNode(.{ .tag = .string_literal, - .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}), + .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}), .data = undefined, }); _ = try c.addToken(.r_paren, ")"); diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 3e1ce6e2e1941417f0dd70e6919efc05fbbd40b5..693e9b4c70efc4189a4dff29ccda3a0536aa07be 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -255,7 +255,7 @@ pub fn main() !void { builder.verbose_llvm_ir = "-"; } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) { builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..]; - } else if (mem.eql(u8, arg, "--verbose-llvm-bc=")) { + } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) { builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..]; } else if (mem.eql(u8, arg, "--verbose-cimport")) { builder.verbose_cimport = true; @@ -719,6 +719,8 @@ fn runStepNames( if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {}; if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {}; + w.writeAll("\n") catch {}; + // Print a fancy tree with build results. var step_stack_copy = try step_stack.clone(gpa); defer step_stack_copy.deinit(gpa); diff --git a/lib/compiler/libc.zig b/lib/compiler/libc.zig index 2f4c26b0cc43570c3bea04c8789c42c7f8fa52cf..0d26b59d24c55668dbb8458283d54844cbf83187 100644 --- a/lib/compiler/libc.zig +++ b/lib/compiler/libc.zig @@ -40,7 +40,7 @@ pub fn main() !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout().deprecatedWriter(); try stdout.writeAll(usage_libc); return std.process.cleanExit(); } else if (mem.eql(u8, arg, "-target")) { @@ -97,7 +97,7 @@ pub fn main() !void { fatal("no include dirs detected for target {s}", .{zig_target}); } - var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); + var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter()); var writer = bw.writer(); for (libc_dirs.libc_include_dir_list) |include_dir| { try writer.writeAll(include_dir); @@ -125,7 +125,7 @@ pub fn main() !void { }; defer libc.deinit(gpa); - var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); + var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter()); try libc.render(bw.writer()); try bw.flush(); } diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index bcd3a69a0c5d84bd9415c17cb64355c208eb19ca..3e383e6ad4490ca898301d78f318fc0b46434751 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -54,7 +54,7 @@ fn cmdObjCopy( fatal("unexpected positional argument: '{s}'", .{arg}); } } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - return std.io.getStdOut().writeAll(usage); + return std.fs.File.stdout().writeAll(usage); } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) { i += 1; if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); @@ -227,8 +227,8 @@ fn cmdObjCopy( if (listen) { var server = try Server.init(.{ .gpa = gpa, - .in = std.io.getStdIn(), - .out = std.io.getStdOut(), + .in = .stdin(), + .out = .stdout(), .zig_version = builtin.zig_version_string, }); defer server.deinit(); @@ -635,11 +635,11 @@ const HexWriter = struct { const payload_bytes = self.getPayloadBytes(); assert(payload_bytes.len <= MAX_PAYLOAD_LEN); - const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ + const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3X}{4X:0>2}" ++ linesep, .{ @as(u8, @intCast(payload_bytes.len)), self.address, @intFromEnum(self.payload), - std.fmt.fmtSliceHexUpper(payload_bytes), + payload_bytes, self.checksum(), }); try file.writeAll(line); @@ -1495,7 +1495,7 @@ const ElfFileHelper = struct { if (size < prefix.len) return null; try in_file.seekTo(offset); - var section_reader = std.io.limitedReader(in_file.reader(), size); + var section_reader = std.io.limitedReader(in_file.deprecatedReader(), size); // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed. const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size)); diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig index 7d0d114841e18af0f556e1c0c1724cacf89b8adb..eb8bf349f50b138cc896c19a44ce212f000a7e74 100644 --- a/lib/compiler/reduce.zig +++ b/lib/compiler/reduce.zig @@ -68,7 +68,7 @@ pub fn main() !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout().deprecatedWriter(); try stdout.writeAll(usage); return std.process.cleanExit(); } else if (mem.eql(u8, arg, "--")) { diff --git a/lib/compiler/reduce/Walk.zig b/lib/compiler/reduce/Walk.zig index 48bd55fd6395207745ffe379f86fb29a3299e829..bc20cb791c77c232996e983e4dd884d3dba832d7 100644 --- a/lib/compiler/reduce/Walk.zig +++ b/lib/compiler/reduce/Walk.zig @@ -160,12 +160,6 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { try walkExpression(w, decl); }, - .@"usingnamespace" => { - try w.transformations.append(.{ .delete_node = decl }); - const expr = ast.nodeData(decl).node; - try walkExpression(w, expr); - }, - .global_var_decl, .local_var_decl, .simple_var_decl, @@ -335,7 +329,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { .address_of, .@"try", .@"resume", - .@"await", .deref, => { return walkExpression(w, ast.nodeData(node).node); @@ -379,12 +372,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => { var buf: [1]Ast.Node.Index = undefined; return walkCall(w, ast.fullCall(&buf, node).?); @@ -525,7 +514,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { .local_var_decl => unreachable, .simple_var_decl => unreachable, .aligned_var_decl => unreachable, - .@"usingnamespace" => unreachable, .test_decl => unreachable, .asm_output => unreachable, .asm_input => unreachable, diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index f27775d6af516bf6586174c5e42ddd86d829e05a..6d4a368b4e803423a856b4dde2e75df71bc2b7df 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -125,13 +125,12 @@ pub const Diagnostics = struct { } pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void { - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - const stderr = std.io.getStdErr().writer(); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); self.renderToWriter(args, stderr, config) catch return; } - pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: anytype, config: std.io.tty.Config) !void { + pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: *std.io.Writer, config: std.io.tty.Config) !void { for (self.errors.items) |err_details| { try renderErrorMessage(writer, config, err_details, args); } @@ -1403,7 +1402,7 @@ test parsePercent { try std.testing.expectError(error.InvalidFormat, parsePercent("~1")); } -pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void { +pub fn renderErrorMessage(writer: *std.io.Writer, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void { try config.setColor(writer, .dim); try writer.writeAll(""); try config.setColor(writer, .reset); @@ -1481,27 +1480,27 @@ pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_detail try writer.writeByte('\n'); try config.setColor(writer, .green); - try writer.writeByteNTimes(' ', prefix.len); + try writer.splatByteAll(' ', prefix.len); // Special case for when the option is *only* a prefix (e.g. invalid option: -) if (err_details.arg_span.prefix_len == arg_with_name.len) { - try writer.writeByteNTimes('^', err_details.arg_span.prefix_len); + try writer.splatByteAll('^', err_details.arg_span.prefix_len); } else { - try writer.writeByteNTimes('~', err_details.arg_span.prefix_len); - try writer.writeByteNTimes(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len); + try writer.splatByteAll('~', err_details.arg_span.prefix_len); + try writer.splatByteAll(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len); if (!err_details.arg_span.point_at_next_arg and err_details.arg_span.value_offset == 0) { try writer.writeByte('^'); - try writer.writeByteNTimes('~', name_slice.len - 1); + try writer.splatByteAll('~', name_slice.len - 1); } else if (err_details.arg_span.value_offset > 0) { - try writer.writeByteNTimes('~', err_details.arg_span.value_offset - err_details.arg_span.name_offset); + try writer.splatByteAll('~', err_details.arg_span.value_offset - err_details.arg_span.name_offset); try writer.writeByte('^'); if (err_details.arg_span.value_offset < arg_with_name.len) { - try writer.writeByteNTimes('~', arg_with_name.len - err_details.arg_span.value_offset - 1); + try writer.splatByteAll('~', arg_with_name.len - err_details.arg_span.value_offset - 1); } } else if (err_details.arg_span.point_at_next_arg) { - try writer.writeByteNTimes('~', arg_with_name.len - err_details.arg_span.name_offset + 1); + try writer.splatByteAll('~', arg_with_name.len - err_details.arg_span.name_offset + 1); try writer.writeByte('^'); if (next_arg_len > 0) { - try writer.writeByteNTimes('~', next_arg_len - 1); + try writer.splatByteAll('~', next_arg_len - 1); } } } diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig index ab4784d9b0046848056880dd191ae8e6a7d826b9..75fe8b0a0e00662c4bbf7c1699926eced8e13b1b 100644 --- a/lib/compiler/resinator/compile.zig +++ b/lib/compiler/resinator/compile.zig @@ -570,7 +570,7 @@ pub const Compiler = struct { switch (predefined_type) { .GROUP_ICON, .GROUP_CURSOR => { // Check for animated icon first - if (ani.isAnimatedIcon(file.reader())) { + if (ani.isAnimatedIcon(file.deprecatedReader())) { // Animated icons are just put into the resource unmodified, // and the resource type changes to ANIICON/ANICURSOR @@ -586,14 +586,14 @@ pub const Compiler = struct { try header.write(writer, self.errContext(node.id)); try file.seekTo(0); - try writeResourceData(writer, file.reader(), header.data_size); + try writeResourceData(writer, file.deprecatedReader(), header.data_size); return; } // isAnimatedIcon moved the file cursor so reset to the start try file.seekTo(0); - const icon_dir = ico.read(self.allocator, file.reader(), try file.getEndPos()) catch |err| switch (err) { + const icon_dir = ico.read(self.allocator, file.deprecatedReader(), try file.getEndPos()) catch |err| switch (err) { error.OutOfMemory => |e| return e, else => |e| { return self.iconReadError( @@ -672,7 +672,7 @@ pub const Compiler = struct { } try file.seekTo(entry.data_offset_from_start_of_file); - var header_bytes = file.reader().readBytesNoEof(16) catch { + var header_bytes = file.deprecatedReader().readBytesNoEof(16) catch { return self.iconReadError( error.UnexpectedEOF, filename_utf8, @@ -803,7 +803,7 @@ pub const Compiler = struct { } try file.seekTo(entry.data_offset_from_start_of_file); - try writeResourceDataNoPadding(writer, file.reader(), entry.data_size_in_bytes); + try writeResourceDataNoPadding(writer, file.deprecatedReader(), entry.data_size_in_bytes); try writeDataPadding(writer, full_data_size); if (self.state.icon_id == std.math.maxInt(u16)) { @@ -859,7 +859,7 @@ pub const Compiler = struct { header.applyMemoryFlags(node.common_resource_attributes, self.source); const file_size = try file.getEndPos(); - const bitmap_info = bmp.read(file.reader(), file_size) catch |err| { + const bitmap_info = bmp.read(file.deprecatedReader(), file_size) catch |err| { const filename_string_index = try self.diagnostics.putString(filename_utf8); return self.addErrorDetailsAndFail(.{ .err = .bmp_read_error, @@ -922,7 +922,7 @@ pub const Compiler = struct { header.data_size = bmp_bytes_to_write; try header.write(writer, self.errContext(node.id)); try file.seekTo(bmp.file_header_len); - const file_reader = file.reader(); + const file_reader = file.deprecatedReader(); try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size); if (bitmap_info.getBitmasksByteLen() > 0) { try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen()); @@ -968,7 +968,7 @@ pub const Compiler = struct { header.data_size = @intCast(file_size); try header.write(writer, self.errContext(node.id)); - var header_slurping_reader = headerSlurpingReader(148, file.reader()); + var header_slurping_reader = headerSlurpingReader(148, file.deprecatedReader()); try writeResourceData(writer, header_slurping_reader.reader(), header.data_size); try self.state.font_dir.add(self.arena, FontDir.Font{ @@ -1002,7 +1002,7 @@ pub const Compiler = struct { // We now know that the data size will fit in a u32 header.data_size = @intCast(data_size); try header.write(writer, self.errContext(node.id)); - try writeResourceData(writer, file.reader(), header.data_size); + try writeResourceData(writer, file.deprecatedReader(), header.data_size); } fn iconReadError( @@ -2947,7 +2947,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype) slurped_header: [size]u8 = [_]u8{0x00} ** size, pub const Error = ReaderType.Error; - pub const Reader = std.io.Reader(*@This(), Error, read); + pub const Reader = std.io.GenericReader(*@This(), Error, read); pub fn read(self: *@This(), buf: []u8) Error!usize { const amt = try self.child_reader.read(buf); @@ -2981,7 +2981,7 @@ pub fn LimitedWriter(comptime WriterType: type) type { bytes_left: u64, pub const Error = error{NoSpaceLeft} || WriterType.Error; - pub const Writer = std.io.Writer(*Self, Error, write); + pub const Writer = std.io.GenericWriter(*Self, Error, write); const Self = @This(); diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index 3fd23dc3004fb51480e902c43aa466b7c866c4d5..864480e7bfe942ae479f9eac7b83b8fe8ed25e77 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const Token = @import("lex.zig").Token; const SourceMappings = @import("source_mapping.zig").SourceMappings; const utils = @import("utils.zig"); @@ -61,16 +62,15 @@ pub const Diagnostics = struct { } pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void { - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - const stderr = std.io.getStdErr().writer(); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); for (self.errors.items) |err_details| { renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return; } } pub fn renderToStdErrDetectTTY(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void { - const tty_config = std.io.tty.detectConfig(std.io.getStdErr()); + const tty_config = std.io.tty.detectConfig(std.fs.File.stderr()); return self.renderToStdErr(cwd, source, tty_config, source_mappings); } @@ -409,15 +409,7 @@ pub const ErrorDetails = struct { failed_to_open_cwd, }; - fn formatToken( - ctx: TokenFormatContext, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = fmt; - _ = options; - + fn formatToken(ctx: TokenFormatContext, writer: *std.io.Writer) std.io.Writer.Error!void { switch (ctx.token.id) { .eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()), else => {}, @@ -441,7 +433,7 @@ pub const ErrorDetails = struct { code_page: SupportedCodePage, }; - fn fmtToken(self: ErrorDetails, source: []const u8) std.fmt.Formatter(formatToken) { + fn fmtToken(self: ErrorDetails, source: []const u8) std.fmt.Formatter(TokenFormatContext, formatToken) { return .{ .data = .{ .token = self.token, .code_page = self.code_page, @@ -452,7 +444,7 @@ pub const ErrorDetails = struct { pub fn render(self: ErrorDetails, writer: anytype, source: []const u8, strings: []const []const u8) !void { switch (self.err) { .unfinished_string_literal => { - return writer.print("unfinished string literal at '{s}', expected closing '\"'", .{self.fmtToken(source)}); + return writer.print("unfinished string literal at '{f}', expected closing '\"'", .{self.fmtToken(source)}); }, .string_literal_too_long => { return writer.print("string literal too long (max is currently {} characters)", .{self.extra.number}); @@ -466,10 +458,14 @@ pub const ErrorDetails = struct { .hint => return, }, .illegal_byte => { - return writer.print("character '{s}' is not allowed", .{std.fmt.fmtSliceEscapeUpper(self.token.slice(source))}); + return writer.print("character '{f}' is not allowed", .{ + std.ascii.hexEscape(self.token.slice(source), .upper), + }); }, .illegal_byte_outside_string_literals => { - return writer.print("character '{s}' is not allowed outside of string literals", .{std.fmt.fmtSliceEscapeUpper(self.token.slice(source))}); + return writer.print("character '{f}' is not allowed outside of string literals", .{ + std.ascii.hexEscape(self.token.slice(source), .upper), + }); }, .illegal_codepoint_outside_string_literals => { // This is somewhat hacky, but we know that: @@ -527,26 +523,26 @@ pub const ErrorDetails = struct { return writer.print("unsupported code page '{s} (id={})' in #pragma code_page", .{ @tagName(code_page), number }); }, .unfinished_raw_data_block => { - return writer.print("unfinished raw data block at '{s}', expected closing '}}' or 'END'", .{self.fmtToken(source)}); + return writer.print("unfinished raw data block at '{f}', expected closing '}}' or 'END'", .{self.fmtToken(source)}); }, .unfinished_string_table_block => { - return writer.print("unfinished STRINGTABLE block at '{s}', expected closing '}}' or 'END'", .{self.fmtToken(source)}); + return writer.print("unfinished STRINGTABLE block at '{f}', expected closing '}}' or 'END'", .{self.fmtToken(source)}); }, .expected_token => { - return writer.print("expected '{s}', got '{s}'", .{ self.extra.expected.nameForErrorDisplay(), self.fmtToken(source) }); + return writer.print("expected '{s}', got '{f}'", .{ self.extra.expected.nameForErrorDisplay(), self.fmtToken(source) }); }, .expected_something_else => { try writer.writeAll("expected "); try self.extra.expected_types.writeCommaSeparated(writer); - return writer.print("; got '{s}'", .{self.fmtToken(source)}); + return writer.print("; got '{f}'", .{self.fmtToken(source)}); }, .resource_type_cant_use_raw_data => switch (self.type) { - .err, .warning => try writer.print("expected '', found '{s}' (resource type '{s}' can't use raw data)", .{ self.fmtToken(source), self.extra.resource.nameForErrorDisplay() }), - .note => try writer.print("if '{s}' is intended to be a filename, it must be specified as a quoted string literal", .{self.fmtToken(source)}), + .err, .warning => try writer.print("expected '', found '{f}' (resource type '{s}' can't use raw data)", .{ self.fmtToken(source), self.extra.resource.nameForErrorDisplay() }), + .note => try writer.print("if '{f}' is intended to be a filename, it must be specified as a quoted string literal", .{self.fmtToken(source)}), .hint => return, }, .id_must_be_ordinal => { - try writer.print("id of resource type '{s}' must be an ordinal (u16), got '{s}'", .{ self.extra.resource.nameForErrorDisplay(), self.fmtToken(source) }); + try writer.print("id of resource type '{s}' must be an ordinal (u16), got '{f}'", .{ self.extra.resource.nameForErrorDisplay(), self.fmtToken(source) }); }, .name_or_id_not_allowed => { 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 { try writer.writeAll("ASCII character not equivalent to virtual key code"); }, .empty_menu_not_allowed => { - try writer.print("empty menu of type '{s}' not allowed", .{self.fmtToken(source)}); + try writer.print("empty menu of type '{f}' not allowed", .{self.fmtToken(source)}); }, .rc_would_miscompile_version_value_padding => switch (self.type) { .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 { .string_already_defined => switch (self.type) { .err, .warning => { const language = self.extra.string_and_language.language; - 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 }); + 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 }); }, .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 }), .hint => return, @@ -642,7 +638,7 @@ pub const ErrorDetails = struct { 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) }); }, .invalid_accelerator_key => { - try writer.print("invalid accelerator key '{s}': {s}", .{ self.fmtToken(source), @tagName(self.extra.accelerator_error.err) }); + try writer.print("invalid accelerator key '{f}': {s}", .{ self.fmtToken(source), @tagName(self.extra.accelerator_error.err) }); }, .accelerator_type_required => { 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 const truncated_str = "<...truncated...>"; -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 { +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 { if (err_details.type == .hint) return; 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 try tty_config.setColor(writer, .green); const num_spaces = truncated_visual_info.point_offset - truncated_visual_info.before_len; - try writer.writeByteNTimes(' ', num_spaces); - try writer.writeByteNTimes('~', truncated_visual_info.before_len); + try writer.splatByteAll(' ', num_spaces); + try writer.splatByteAll('~', truncated_visual_info.before_len); try writer.writeByte('^'); - try writer.writeByteNTimes('~', truncated_visual_info.after_len); + try writer.splatByteAll('~', truncated_visual_info.after_len); try writer.writeByte('\n'); try tty_config.setColor(writer, .reset); diff --git a/lib/compiler/resinator/lex.zig b/lib/compiler/resinator/lex.zig index cfb75e4c5b53e3516fe9371f3d58182b96bf5ade..0b276b92f5ec51198cfa459d826dc396cb150e25 100644 --- a/lib/compiler/resinator/lex.zig +++ b/lib/compiler/resinator/lex.zig @@ -237,7 +237,9 @@ pub const Lexer = struct { } pub fn dump(self: *Self, token: *const Token) void { - std.debug.print("{s}:{d}: {s}\n", .{ @tagName(token.id), token.line_number, std.fmt.fmtSliceEscapeLower(token.slice(self.buffer)) }); + std.debug.print("{s}:{d}: {f}\n", .{ + @tagName(token.id), token.line_number, std.ascii.hexEscape(token.slice(self.buffer), .lower), + }); } pub const LexMethod = enum { diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 8344a16b25ea18f892f3582ee6b3e749dd549ef7..903e0a2f719a3beb20e80c07e7b08814a988954c 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -22,14 +22,14 @@ pub fn main() !void { defer arena_state.deinit(); const arena = arena_state.allocator(); - const stderr = std.io.getStdErr(); + const stderr = std.fs.File.stderr(); const stderr_config = std.io.tty.detectConfig(stderr); const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); if (args.len < 2) { - try renderErrorMessage(stderr.writer(), stderr_config, .err, "expected zig lib dir as first argument", .{}); + try renderErrorMessage(std.debug.lockStderrWriter(&.{}), stderr_config, .err, "expected zig lib dir as first argument", .{}); std.process.exit(1); } const zig_lib_dir = args[1]; @@ -44,7 +44,7 @@ pub fn main() !void { var error_handler: ErrorHandler = switch (zig_integration) { true => .{ .server = .{ - .out = std.io.getStdOut(), + .out = std.fs.File.stdout(), .in = undefined, // won't be receiving messages .receive_fifo = undefined, // won't be receiving messages }, @@ -81,15 +81,15 @@ pub fn main() !void { defer options.deinit(); if (options.print_help_and_exit) { - const stdout = std.io.getStdOut(); - try cli.writeUsage(stdout.writer(), "zig rc"); + const stdout = std.fs.File.stdout(); + try cli.writeUsage(stdout.deprecatedWriter(), "zig rc"); return; } // Don't allow verbose when integrating with Zig via stdout options.verbose = false; - const stdout_writer = std.io.getStdOut().writer(); + const stdout_writer = std.fs.File.stdout().deprecatedWriter(); if (options.verbose) { try options.dumpVerbose(stdout_writer); try stdout_writer.writeByte('\n'); @@ -290,7 +290,7 @@ pub fn main() !void { }; defer depfile.close(); - const depfile_writer = depfile.writer(); + const depfile_writer = depfile.deprecatedWriter(); var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer); switch (options.depfile_fmt) { .json => { @@ -343,7 +343,7 @@ pub fn main() !void { switch (err) { error.DuplicateResource => { const duplicate_resource = resources.list.items[cvtres_diagnostics.duplicate_resource]; - try error_handler.emitMessage(allocator, .err, "duplicate resource [id: {}, type: {}, language: {}]", .{ + try error_handler.emitMessage(allocator, .err, "duplicate resource [id: {f}, type: {f}, language: {f}]", .{ duplicate_resource.name_value, fmtResourceType(duplicate_resource.type_value), duplicate_resource.language, @@ -352,7 +352,7 @@ pub fn main() !void { error.ResourceDataTooLong => { const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource]; try error_handler.emitMessage(allocator, .err, "resource has a data length that is too large to be written into a coff section", .{}); - try error_handler.emitMessage(allocator, .note, "the resource with the invalid size is [id: {}, type: {}, language: {}]", .{ + try error_handler.emitMessage(allocator, .note, "the resource with the invalid size is [id: {f}, type: {f}, language: {f}]", .{ overflow_resource.name_value, fmtResourceType(overflow_resource.type_value), overflow_resource.language, @@ -361,7 +361,7 @@ pub fn main() !void { error.TotalResourceDataTooLong => { const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource]; try error_handler.emitMessage(allocator, .err, "total resource data exceeds the maximum of the coff 'size of raw data' field", .{}); - try error_handler.emitMessage(allocator, .note, "size overflow occurred when attempting to write this resource: [id: {}, type: {}, language: {}]", .{ + try error_handler.emitMessage(allocator, .note, "size overflow occurred when attempting to write this resource: [id: {f}, type: {f}, language: {f}]", .{ overflow_resource.name_value, fmtResourceType(overflow_resource.type_value), overflow_resource.language, @@ -471,7 +471,7 @@ const IoStream = struct { allocator: std.mem.Allocator, }; pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError; - pub const Writer = std.io.Writer(WriterContext, WriteError, write); + pub const Writer = std.io.GenericWriter(WriterContext, WriteError, write); pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize { switch (ctx.self.*) { @@ -645,7 +645,9 @@ const ErrorHandler = union(enum) { }, .tty => { // extra newline to separate this line from the aro errors - try renderErrorMessage(std.io.getStdErr().writer(), self.tty, .err, "{s}\n", .{fail_msg}); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + try renderErrorMessage(stderr, self.tty, .err, "{s}\n", .{fail_msg}); aro.Diagnostics.render(comp, self.tty); }, } @@ -690,7 +692,9 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, .tty => { - try renderErrorMessage(std.io.getStdErr().writer(), self.tty, msg_type, format, args); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + try renderErrorMessage(stderr, self.tty, msg_type, format, args); }, } } diff --git a/lib/compiler/resinator/res.zig b/lib/compiler/resinator/res.zig index b4fcd53f9d0f4099cb59eb7d340a2fce7708e417..4e1953233d79779dea49569a22e8fd6aa792c92c 100644 --- a/lib/compiler/resinator/res.zig +++ b/lib/compiler/resinator/res.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const rc = @import("rc.zig"); const ResourceType = rc.ResourceType; const CommonResourceAttributes = rc.CommonResourceAttributes; @@ -163,14 +164,7 @@ pub const Language = packed struct(u16) { return @bitCast(self); } - pub fn format( - language: Language, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - _ = fmt; - _ = options; + pub fn format(language: Language, w: *std.io.Writer) std.io.Writer.Error!void { const language_id = language.asInt(); const language_name = language_name: { if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| { @@ -181,7 +175,7 @@ pub const Language = packed struct(u16) { } break :language_name ""; }; - try out_stream.print("{s} (0x{X})", .{ language_name, language_id }); + try w.print("{s} (0x{X})", .{ language_name, language_id }); } }; @@ -445,47 +439,33 @@ pub const NameOrOrdinal = union(enum) { } } - pub fn format( - self: NameOrOrdinal, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - _ = fmt; - _ = options; + pub fn format(self: NameOrOrdinal, w: *std.io.Writer) !void { switch (self) { .name => |name| { - try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)}); + try w.print("{f}", .{std.unicode.fmtUtf16Le(name)}); }, .ordinal => |ordinal| { - try out_stream.print("{d}", .{ordinal}); + try w.print("{d}", .{ordinal}); }, } } - fn formatResourceType( - self: NameOrOrdinal, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - _ = fmt; - _ = options; + fn formatResourceType(self: NameOrOrdinal, w: *std.io.Writer) std.io.Writer.Error!void { switch (self) { .name => |name| { - try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)}); + try w.print("{f}", .{std.unicode.fmtUtf16Le(name)}); }, .ordinal => |ordinal| { if (std.enums.tagName(RT, @enumFromInt(ordinal))) |predefined_type_name| { - try out_stream.print("{s}", .{predefined_type_name}); + try w.print("{s}", .{predefined_type_name}); } else { - try out_stream.print("{d}", .{ordinal}); + try w.print("{d}", .{ordinal}); } }, } } - pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(formatResourceType) { + pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(NameOrOrdinal, formatResourceType) { return .{ .data = type_value }; } }; diff --git a/lib/compiler/resinator/utils.zig b/lib/compiler/resinator/utils.zig index b69222fe037379509e534f924d620dab229f3913..840833fbcdcfc81885cdb5d05fe4f565a17b1a3b 100644 --- a/lib/compiler/resinator/utils.zig +++ b/lib/compiler/resinator/utils.zig @@ -86,7 +86,7 @@ pub const ErrorMessageType = enum { err, warning, note }; /// Used for generic colored errors/warnings/notes, more context-specific error messages /// are handled elsewhere. -pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void { +pub fn renderErrorMessage(writer: *std.io.Writer, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void { switch (msg_type) { .err => { try config.setColor(writer, .bold); diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index ce0d958ebd601a1ec40c84aebacb78db9b9e6122..baaf361a5370a4efe6022baffa8dfa8db897aba4 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -303,7 +303,7 @@ pub fn mainSimple() anyerror!void { var failed: u64 = 0; // we don't want to bring in File and Writer if the backend doesn't support it - const stderr = if (comptime enable_print) std.io.getStdErr() else {}; + const stderr = if (comptime enable_print) std.fs.File.stderr() else {}; for (builtin.test_functions) |test_fn| { if (test_fn.func()) |_| { @@ -330,7 +330,7 @@ pub fn mainSimple() anyerror!void { passed += 1; } if (enable_print and print_summary) { - stderr.writer().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {}; + stderr.deprecatedWriter().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {}; } if (failed != 0) std.process.exit(1); } diff --git a/lib/compiler_rt/clear_cache.zig b/lib/compiler_rt/clear_cache.zig index d5c93e4c5ad3ed4cab1032295e8d6f9f75a5a7b5..6cf819ccf35f82460671d014f7aad01de95fe30b 100644 --- a/lib/compiler_rt/clear_cache.zig +++ b/lib/compiler_rt/clear_cache.zig @@ -86,6 +86,26 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void { const result = std.os.linux.syscall3(.cacheflush, start, end - start, flags); std.debug.assert(result == 0); exportIt(); + } else if (os == .netbsd and mips) { + // Replace with https://github.com/ziglang/zig/issues/23904 in the future. + const cfa: extern struct { + va: usize, + nbytes: usize, + whichcache: u32, + } = .{ + .va = start, + .nbytes = end - start, + .whichcache = 3, // ICACHE | DCACHE + }; + asm volatile ( + \\ syscall + : + : [_] "{$2}" (165), // nr = SYS_sysarch + [_] "{$4}" (0), // op = MIPS_CACHEFLUSH + [_] "{$5}" (&cfa), // args = &cfa + : "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory" + ); + exportIt(); } else if (mips and os == .openbsd) { // TODO //cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE); diff --git a/lib/compiler_rt/emutls.zig b/lib/compiler_rt/emutls.zig index 434d0c09134caff02e1666da49241576f95e21f7..0bb427b651af7c70954e91d0d0c401f20d80f25d 100644 --- a/lib/compiler_rt/emutls.zig +++ b/lib/compiler_rt/emutls.zig @@ -18,7 +18,7 @@ const gcc_word = usize; pub const panic = common.panic; comptime { - if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.os.tag == .openbsd)) { + if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.abi.isOpenHarmony() or builtin.os.tag == .openbsd)) { @export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility }); } } diff --git a/lib/docs/wasm/Walk.zig b/lib/docs/wasm/Walk.zig index 7249685f7bd23b18c3ebd9dbc7cc4308ca2fc3f3..c20f4bbd90c90b17385e4478f8ace86e9720922c 100644 --- a/lib/docs/wasm/Walk.zig +++ b/lib/docs/wasm/Walk.zig @@ -238,12 +238,8 @@ pub const File = struct { .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => { var buf: [1]Ast.Node.Index = undefined; return categorize_call(file_index, node, ast.fullCall(&buf, node).?); @@ -450,7 +446,7 @@ fn parse(file_name: []const u8, source: []u8) Oom!Ast { error.WriteFailed => return error.OutOfMemory, }; } - log.err("{s}:{}:{}: {s}", .{ file_name, err_loc.line + 1, err_loc.column + 1, rendered_err.items }); + log.err("{s}:{d}:{d}: {s}", .{ file_name, err_loc.line + 1, err_loc.column + 1, rendered_err.items }); } return Ast.parse(gpa, "", .zig); } @@ -577,7 +573,6 @@ fn struct_decl( }, .@"comptime", - .@"usingnamespace", => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node), .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) const ast = w.file.get_ast(); switch (ast.nodeTag(node)) { .root => unreachable, // Top-level declaration. - .@"usingnamespace" => unreachable, // Top-level declaration. .test_decl => unreachable, // Top-level declaration. .container_field_init => unreachable, // Top-level declaration. .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) .@"comptime", .@"nosuspend", .@"suspend", - .@"await", .@"resume", .@"try", => 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) .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => { var buf: [1]Ast.Node.Index = undefined; const full = ast.fullCall(&buf, node).?; diff --git a/lib/docs/wasm/html_render.zig b/lib/docs/wasm/html_render.zig index b15ab127c75db5cd03cd5ef8be09678e853c9db0..0dd19ede3af6c25149aa51b697be34f45c9a7f8f 100644 --- a/lib/docs/wasm/html_render.zig +++ b/lib/docs/wasm/html_render.zig @@ -101,8 +101,6 @@ pub fn fileSourceHtml( .keyword_align, .keyword_and, .keyword_asm, - .keyword_async, - .keyword_await, .keyword_break, .keyword_catch, .keyword_comptime, @@ -139,7 +137,6 @@ pub fn fileSourceHtml( .keyword_try, .keyword_union, .keyword_unreachable, - .keyword_usingnamespace, .keyword_var, .keyword_volatile, .keyword_allowzero, diff --git a/lib/docs/wasm/markdown.zig b/lib/docs/wasm/markdown.zig index 09b1f816c3ac2822606a9a0c8b1d0325e4168bb1..11008d2d6108b6fd650bf0d0209a6972e4c7a15b 100644 --- a/lib/docs/wasm/markdown.zig +++ b/lib/docs/wasm/markdown.zig @@ -143,7 +143,7 @@ fn mainImpl() !void { var parser = try Parser.init(gpa); defer parser.deinit(); - var stdin_buf = std.io.bufferedReader(std.io.getStdIn().reader()); + var stdin_buf = std.io.bufferedReader(std.fs.File.stdin().deprecatedReader()); var line_buf = std.ArrayList(u8).init(gpa); defer line_buf.deinit(); while (stdin_buf.reader().streamUntilDelimiter(line_buf.writer(), '\n', null)) { @@ -158,7 +158,7 @@ fn mainImpl() !void { var doc = try parser.endInput(); defer doc.deinit(gpa); - var stdout_buf = std.io.bufferedWriter(std.io.getStdOut().writer()); + var stdout_buf = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter()); try doc.render(stdout_buf.writer()); try stdout_buf.flush(); } diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig index 6bfd40b6f080b2c02f04aa60012cc3145a3e1b86..ce23f63421acc25e96736d0baa44316475201604 100644 --- a/lib/fuzzer.zig +++ b/lib/fuzzer.zig @@ -9,7 +9,8 @@ pub const std_options = std.Options{ .logFn = logOverride, }; -var log_file: ?std.fs.File = null; +var log_file_buffer: [256]u8 = undefined; +var log_file_writer: ?std.fs.File.Writer = null; fn logOverride( comptime level: std.log.Level, @@ -17,15 +18,17 @@ fn logOverride( comptime format: []const u8, args: anytype, ) void { - const f = if (log_file) |f| f else f: { + const fw = if (log_file_writer) |*f| f else f: { const f = fuzzer.cache_dir.createFile("tmp/libfuzzer.log", .{}) catch @panic("failed to open fuzzer log file"); - log_file = f; - break :f f; + log_file_writer = f.writer(&log_file_buffer); + break :f &log_file_writer.?; }; const prefix1 = comptime level.asText(); const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; - f.writer().print(prefix1 ++ prefix2 ++ format ++ "\n", args) catch @panic("failed to write to fuzzer log"); + fw.interface.print(prefix1 ++ prefix2 ++ format ++ "\n", args) catch + @panic("failed to write to fuzzer log"); + fw.interface.flush() catch @panic("failed to flush fuzzer log"); } /// Helps determine run uniqueness in the face of recursion. @@ -226,18 +229,18 @@ const Fuzzer = struct { .read = true, }) catch |e| switch (e) { error.PathAlreadyExists => continue, - else => fatal("unable to create '{}{d}: {s}", .{ f.corpus_directory, i, @errorName(err) }), + else => fatal("unable to create '{f}{d}: {s}", .{ f.corpus_directory, i, @errorName(err) }), }; errdefer input_file.close(); // Initialize the mmap for the current input. f.input = MemoryMappedList.create(input_file, 0, std.heap.page_size_max) catch |e| { - fatal("unable to init memory map for input at '{}{d}': {s}", .{ + fatal("unable to init memory map for input at '{f}{d}': {s}", .{ f.corpus_directory, i, @errorName(e), }); }; break; }, - else => fatal("unable to read '{}{d}': {s}", .{ f.corpus_directory, i, @errorName(err) }), + else => fatal("unable to read '{f}{d}': {s}", .{ f.corpus_directory, i, @errorName(err) }), }; errdefer gpa.free(input); f.corpus.append(gpa, .{ @@ -263,7 +266,7 @@ const Fuzzer = struct { const sub_path = try std.fmt.allocPrint(gpa, "f/{s}", .{f.unit_test_name}); f.corpus_directory = .{ .handle = f.cache_dir.makeOpenPath(sub_path, .{}) catch |err| - fatal("unable to open corpus directory 'f/{s}': {s}", .{ sub_path, @errorName(err) }), + fatal("unable to open corpus directory 'f/{s}': {t}", .{ sub_path, err }), .path = sub_path, }; initNextInput(f); diff --git a/lib/init/src/root.zig b/lib/init/src/root.zig index 70f4bd8d82ed8ac1faf78e24770a975432d05032..9afb8debf24e5e832786cbe0c8151a1b95ed3566 100644 --- a/lib/init/src/root.zig +++ b/lib/init/src/root.zig @@ -5,7 +5,7 @@ pub fn bufferedPrint() !void { // Stdout is for the actual output of your application, for example if you // are implementing gzip, then only the compressed bytes should be sent to // stdout, not any debugging messages. - const stdout_file = std.io.getStdOut().writer(); + const stdout_file = std.fs.File.stdout().deprecatedWriter(); // Buffering can improve performance significantly in print-heavy programs. var bw = std.io.bufferedWriter(stdout_file); const stdout = bw.writer(); diff --git a/lib/libc/mingw/math/x86/floorf.S b/lib/libc/mingw/math/x86/floorf.S deleted file mode 100644 index c6226f82b5efc4f54dff41d586ca4244b5f1e52c..0000000000000000000000000000000000000000 --- a/lib/libc/mingw/math/x86/floorf.S +++ /dev/null @@ -1,51 +0,0 @@ -/** - * This file has no copyright assigned and is placed in the Public Domain. - * This file is part of the mingw-w64 runtime package. - * No warranty is given; refer to the file DISCLAIMER.PD within this package. - */ - #include <_mingw_mac.h> - - .file "floorf.S" - .text - .p2align 4,,15 - .globl __MINGW_USYMBOL(floorf) - .def __MINGW_USYMBOL(floorf); .scl 2; .type 32; .endef -#ifdef __x86_64__ - .seh_proc __MINGW_USYMBOL(floorf) -#endif -__MINGW_USYMBOL(floorf): -#if defined(_AMD64_) || defined(__x86_64__) - subq $40, %rsp - .seh_stackalloc 40 - .seh_endprologue - unpcklps %xmm0, %xmm0 - cvtps2pd %xmm0, %xmm0 - call floor - unpcklpd %xmm0, %xmm0 - cvtpd2ps %xmm0, %xmm0 - addq $40, %rsp - ret - .seh_endproc - .def __MINGW_USYMBOL(floor); .scl 2; .type 32; .endef -#elif defined(_X86_) || defined(__i386__) - flds 4(%esp) - subl $8,%esp - - fstcw 4(%esp) /* store fpu control word */ - - /* We use here %edx although only the low 1 bits are defined. - But none of the operations should care and they are faster - than the 16 bit operations. */ - movl $0x400,%edx /* round towards -oo */ - orl 4(%esp),%edx - andl $0xf7ff,%edx - movl %edx,(%esp) - fldcw (%esp) /* load modified control word */ - - frndint /* round */ - - fldcw 4(%esp) /* restore original control word */ - - addl $8,%esp - ret -#endif diff --git a/lib/libc/mingw/math/x86/floorl.S b/lib/libc/mingw/math/x86/floorl.S deleted file mode 100644 index 6a08d590a116373ae20e13da1c709e18b63f4112..0000000000000000000000000000000000000000 --- a/lib/libc/mingw/math/x86/floorl.S +++ /dev/null @@ -1,63 +0,0 @@ -/** - * This file has no copyright assigned and is placed in the Public Domain. - * This file is part of the mingw-w64 runtime package. - * No warranty is given; refer to the file DISCLAIMER.PD within this package. - */ -#include <_mingw_mac.h> - - .file "floorl.S" - .text -#ifdef __x86_64__ - .align 8 -#else - .align 4 -#endif - .globl __MINGW_USYMBOL(floorl) - .def __MINGW_USYMBOL(floorl); .scl 2; .type 32; .endef -__MINGW_USYMBOL(floorl): -#if defined(_AMD64_) || defined(__x86_64__) - fldt (%rdx) - subq $24,%rsp - - fstcw 8(%rsp) /* store fpu control word */ - - /* We use here %edx although only the low 1 bits are defined. - But none of the operations should care and they are faster - than the 16 bit operations. */ - movl $0x400,%edx /* round towards -oo */ - orl 8(%rsp),%edx - andl $0xf7ff,%edx - movl %edx,(%rsp) - fldcw (%rsp) /* load modified control word */ - - frndint /* round */ - - fldcw 8(%rsp) /* restore original control word */ - - addq $24,%rsp - movq %rcx,%rax - movq $0,8(%rcx) - fstpt (%rcx) - ret -#elif defined(_X86_) || defined(__i386__) - fldt 4(%esp) - subl $8,%esp - - fstcw 4(%esp) /* store fpu control word */ - - /* We use here %edx although only the low 1 bits are defined. - But none of the operations should care and they are faster - than the 16 bit operations. */ - movl $0x400,%edx /* round towards -oo */ - orl 4(%esp),%edx - andl $0xf7ff,%edx - movl %edx,(%esp) - fldcw (%esp) /* load modified control word */ - - frndint /* round */ - - fldcw 4(%esp) /* restore original control word */ - - addl $8,%esp - ret -#endif diff --git a/lib/libc/musl/src/math/aarch64/floor.c b/lib/libc/musl/src/math/aarch64/floor.c deleted file mode 100644 index 50ffdb281b6103df5e44b4bc2f94a0861e502bc0..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/aarch64/floor.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -double floor(double x) -{ - __asm__ ("frintm %d0, %d1" : "=w"(x) : "w"(x)); - return x; -} diff --git a/lib/libc/musl/src/math/aarch64/floorf.c b/lib/libc/musl/src/math/aarch64/floorf.c deleted file mode 100644 index 8d007e9f84ac0c60671f7cc9c54ddd3bc02105f8..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/aarch64/floorf.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -float floorf(float x) -{ - __asm__ ("frintm %s0, %s1" : "=w"(x) : "w"(x)); - return x; -} diff --git a/lib/libc/musl/src/math/floor.c b/lib/libc/musl/src/math/floor.c deleted file mode 100644 index 14a31cd8c4c549750205369a6fe9e71eec8f71c5..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/floor.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "libm.h" - -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 -#define EPS DBL_EPSILON -#elif FLT_EVAL_METHOD==2 -#define EPS LDBL_EPSILON -#endif -static const double_t toint = 1/EPS; - -double floor(double x) -{ - union {double f; uint64_t i;} u = {x}; - int e = u.i >> 52 & 0x7ff; - double_t y; - - if (e >= 0x3ff+52 || x == 0) - return x; - /* y = int(x) - x, where int(x) is an integer neighbor of x */ - if (u.i >> 63) - y = x - toint + toint - x; - else - y = x + toint - toint - x; - /* special case because of non-nearest rounding modes */ - if (e <= 0x3ff-1) { - FORCE_EVAL(y); - return u.i >> 63 ? -1 : 0; - } - if (y > 0) - return x + y - 1; - return x + y; -} diff --git a/lib/libc/musl/src/math/floorf.c b/lib/libc/musl/src/math/floorf.c deleted file mode 100644 index dceec739dbef5c4039fd4a5c7cf5c95ac593777d..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/floorf.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "libm.h" - -float floorf(float x) -{ - union {float f; uint32_t i;} u = {x}; - int e = (int)(u.i >> 23 & 0xff) - 0x7f; - uint32_t m; - - if (e >= 23) - return x; - if (e >= 0) { - m = 0x007fffff >> e; - if ((u.i & m) == 0) - return x; - FORCE_EVAL(x + 0x1p120f); - if (u.i >> 31) - u.i += m; - u.i &= ~m; - } else { - FORCE_EVAL(x + 0x1p120f); - if (u.i >> 31 == 0) - u.i = 0; - else if (u.i << 1) - u.f = -1.0; - } - return u.f; -} diff --git a/lib/libc/musl/src/math/floorl.c b/lib/libc/musl/src/math/floorl.c deleted file mode 100644 index 16aaec48eea8e4e28e747122d6179d95e279981f..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/floorl.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "libm.h" - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long double floorl(long double x) -{ - return floor(x); -} -#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 - -static const long double toint = 1/LDBL_EPSILON; - -long double floorl(long double x) -{ - union ldshape u = {x}; - int e = u.i.se & 0x7fff; - long double y; - - if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0) - return x; - /* y = int(x) - x, where int(x) is an integer neighbor of x */ - if (u.i.se >> 15) - y = x - toint + toint - x; - else - y = x + toint - toint - x; - /* special case because of non-nearest rounding modes */ - if (e <= 0x3fff-1) { - FORCE_EVAL(y); - return u.i.se >> 15 ? -1 : 0; - } - if (y > 0) - return x + y - 1; - return x + y; -} -#endif diff --git a/lib/libc/musl/src/math/i386/floor.s b/lib/libc/musl/src/math/i386/floor.s index 46ba88db535dbd16161fdd95d441c9ea9066eb61..2571fb3ded463336cdc988a1f59a2fe6fb0b3b7c 100644 --- a/lib/libc/musl/src/math/i386/floor.s +++ b/lib/libc/musl/src/math/i386/floor.s @@ -1,20 +1,5 @@ -.global floorf -.type floorf,@function -floorf: - flds 4(%esp) - jmp 1f +/* zig patch: removed `floorl` and `floorf` in favor of using zig compiler_rt's implementations */ -.global floorl -.type floorl,@function -floorl: - fldt 4(%esp) - jmp 1f - -.global floor -.type floor,@function -floor: - fldl 4(%esp) -1: mov $0x7,%al 1: fstcw 4(%esp) mov 5(%esp),%ah mov %al,5(%esp) diff --git a/lib/libc/musl/src/math/i386/floorf.s b/lib/libc/musl/src/math/i386/floorf.s deleted file mode 100644 index bc29f15ce79215d44fb6a5f2c03e12d25bdb81e2..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/i386/floorf.s +++ /dev/null @@ -1 +0,0 @@ -# see floor.s diff --git a/lib/libc/musl/src/math/i386/floorl.s b/lib/libc/musl/src/math/i386/floorl.s deleted file mode 100644 index bc29f15ce79215d44fb6a5f2c03e12d25bdb81e2..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/i386/floorl.s +++ /dev/null @@ -1 +0,0 @@ -# see floor.s diff --git a/lib/libc/musl/src/math/powerpc64/floor.c b/lib/libc/musl/src/math/powerpc64/floor.c deleted file mode 100644 index 4e6804449d98def4458add3f6878c73cae0fddbc..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/powerpc64/floor.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#ifdef _ARCH_PWR5X - -double floor(double x) -{ - __asm__ ("frim %0, %1" : "=d"(x) : "d"(x)); - return x; -} - -#else - -#include "../floor.c" - -#endif diff --git a/lib/libc/musl/src/math/powerpc64/floorf.c b/lib/libc/musl/src/math/powerpc64/floorf.c deleted file mode 100644 index e1031ef4011c77024790bcc324f03a265de839ab..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/powerpc64/floorf.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#ifdef _ARCH_PWR5X - -float floorf(float x) -{ - __asm__ ("frim %0, %1" : "=f"(x) : "f"(x)); - return x; -} - -#else - -#include "../floorf.c" - -#endif diff --git a/lib/libc/musl/src/math/s390x/floor.c b/lib/libc/musl/src/math/s390x/floor.c deleted file mode 100644 index 4c0c7f1adb24413c8e88361451bfe6ac85ea4ae1..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/s390x/floor.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#if defined(__HTM__) || __ARCH__ >= 9 - -double floor(double x) -{ - __asm__ ("fidbra %0, 7, %1, 4" : "=f"(x) : "f"(x)); - return x; -} - -#else - -#include "../floor.c" - -#endif diff --git a/lib/libc/musl/src/math/s390x/floorf.c b/lib/libc/musl/src/math/s390x/floorf.c deleted file mode 100644 index ea28ec7ac57310a9eb047469630172df3af2dbcc..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/s390x/floorf.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#if defined(__HTM__) || __ARCH__ >= 9 - -float floorf(float x) -{ - __asm__ ("fiebra %0, 7, %1, 4" : "=f"(x) : "f"(x)); - return x; -} - -#else - -#include "../floorf.c" - -#endif diff --git a/lib/libc/musl/src/math/s390x/floorl.c b/lib/libc/musl/src/math/s390x/floorl.c deleted file mode 100644 index 8a0e16a2f876f3ad779d8db73a4ff98f6bd2775c..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/s390x/floorl.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#if defined(__HTM__) || __ARCH__ >= 9 - -long double floorl(long double x) -{ - __asm__ ("fixbra %0, 7, %1, 4" : "=f"(x) : "f"(x)); - return x; -} - -#else - -#include "../floorl.c" - -#endif diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 1cde020a4c449dbf110d0c3da787359f41eac07b..27d3a4eaff1b0510ba2625acad691bce925d26e4 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct { pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 { return gen.path orelse { - std.debug.lockStdErr(); - const stderr = std.io.getStdErr(); - dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; - std.debug.unlockStdErr(); + const w = debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {}; + debug.unlockStderrWriter(); @panic("misconfigured build script"); }; } @@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) { var file_path: Cache.Path = .{ .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { - std.debug.lockStdErr(); - const stderr: fs.File = .stderr(); - dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {}; - std.debug.unlockStdErr(); + const w = debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {}; + debug.unlockStderrWriter(); @panic("misconfigured build script"); }, }; @@ -2769,17 +2767,16 @@ fn dumpBadDirnameHelp( const w = debug.lockStderrWriter(&.{}); defer debug.unlockStderrWriter(); - const stderr: fs.File = .stderr(); try w.print(msg, args); - const tty_config = std.io.tty.detectConfig(stderr); + const tty_config = std.io.tty.detectConfig(.stderr()); if (fail_step) |s| { tty_config.setColor(w, .red) catch {}; - try stderr.writeAll(" The step was created by this stack trace:\n"); + try w.writeAll(" The step was created by this stack trace:\n"); tty_config.setColor(w, .reset) catch {}; - s.dump(stderr); + s.dump(w, tty_config); } if (asking_step) |as| { @@ -2787,24 +2784,23 @@ fn dumpBadDirnameHelp( try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); tty_config.setColor(w, .reset) catch {}; - as.dump(stderr); + as.dump(w, tty_config); } tty_config.setColor(w, .red) catch {}; - try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); + try w.writeAll(" Hope that helps. Proceeding to panic.\n"); tty_config.setColor(w, .reset) catch {}; } /// In this function the stderr mutex has already been locked. pub fn dumpBadGetPathHelp( s: *Step, - stderr: fs.File, + w: *std.io.Writer, + tty_config: std.io.tty.Config, src_builder: *Build, asking_step: ?*Step, ) anyerror!void { - var fw = stderr.writer(&.{}); - const bw = &fw.interface; - try bw.print( + try w.print( \\getPath() was called on a GeneratedFile that wasn't built yet. \\ source package path: {s} \\ Is there a missing Step dependency on step '{s}'? @@ -2814,22 +2810,21 @@ pub fn dumpBadGetPathHelp( s.name, }); - const tty_config = std.io.tty.detectConfig(stderr); - tty_config.setColor(&bw, .red) catch {}; - try stderr.writeAll(" The step was created by this stack trace:\n"); - tty_config.setColor(&bw, .reset) catch {}; + tty_config.setColor(w, .red) catch {}; + try w.writeAll(" The step was created by this stack trace:\n"); + tty_config.setColor(w, .reset) catch {}; - s.dump(stderr); + s.dump(w, tty_config); if (asking_step) |as| { - tty_config.setColor(&bw, .red) catch {}; - try bw.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); - tty_config.setColor(&bw, .reset) catch {}; + tty_config.setColor(w, .red) catch {}; + try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); + tty_config.setColor(w, .reset) catch {}; - as.dump(stderr); + as.dump(w, tty_config); } - tty_config.setColor(&bw, .red) catch {}; - try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); - tty_config.setColor(&bw, .reset) catch {}; + tty_config.setColor(w, .red) catch {}; + try w.writeAll(" Hope that helps. Proceeding to panic.\n"); + tty_config.setColor(w, .reset) catch {}; } pub const InstallDir = union(enum) { @@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 { return result_path; } -/// Deprecated; use `std.fmt.hex` instead. -pub fn hex64(x: u64) [16]u8 { - return std.fmt.hex(x); -} - /// A pair of target query and fully resolved target. /// This type is generally required by build system API that need to be given a /// target. The query is kept because the Zig toolchain needs to know which parts diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 727e719cad0a26c4132a9d473957e8b21276a46f..6bef7d380ba096d493c16b1ba7c1b1ad4f4545b4 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -2,6 +2,18 @@ //! This is not a general-purpose cache. It is designed to be fast and simple, //! not to withstand attacks using specially-crafted input. +const Cache = @This(); +const std = @import("std"); +const builtin = @import("builtin"); +const crypto = std.crypto; +const fs = std.fs; +const assert = std.debug.assert; +const testing = std.testing; +const mem = std.mem; +const fmt = std.fmt; +const Allocator = std.mem.Allocator; +const log = std.log.scoped(.cache); + gpa: Allocator, manifest_dir: fs.Dir, hash: HashHelper = .{}, @@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig"); pub const Directory = @import("Cache/Directory.zig"); pub const DepTokenizer = @import("Cache/DepTokenizer.zig"); -const Cache = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const crypto = std.crypto; -const fs = std.fs; -const assert = std.debug.assert; -const testing = std.testing; -const mem = std.mem; -const fmt = std.fmt; -const Allocator = std.mem.Allocator; -const log = std.log.scoped(.cache); - pub fn addPrefix(cache: *Cache, directory: Directory) void { cache.prefixes_buffer[cache.prefixes_len] = directory; cache.prefixes_len += 1; @@ -1118,25 +1118,12 @@ pub const Manifest = struct { if (self.manifest_dirty) { self.manifest_dirty = false; - const gpa = self.cache.gpa; - var contents: std.ArrayListUnmanaged(u8) = .empty; - defer contents.deinit(gpa); - - try contents.appendSlice(gpa, manifest_header ++ "\n"); - for (self.files.keys()) |file| { - try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{ - file.stat.size, - file.stat.inode, - file.stat.mtime, - &file.bin_digest, - file.prefixed_path.prefix, - file.prefixed_path.sub_path, - }); - } - - try manifest_file.setEndPos(contents.items.len); - var pos: usize = 0; - while (pos < contents.items.len) pos += try manifest_file.pwrite(contents.items[pos..], pos); + var buffer: [4000]u8 = undefined; + var fw = manifest_file.writer(&buffer); + writeDirtyManifestToStream(self, &fw) catch |err| switch (err) { + error.WriteFailed => return fw.err.?, + else => |e| return e, + }; } if (self.want_shared_lock) { @@ -1144,6 +1131,21 @@ pub const Manifest = struct { } } + fn writeDirtyManifestToStream(self: *Manifest, fw: *fs.File.Writer) !void { + try fw.interface.writeAll(manifest_header ++ "\n"); + for (self.files.keys()) |file| { + try fw.interface.print("{d} {d} {d} {x} {d} {s}\n", .{ + file.stat.size, + file.stat.inode, + file.stat.mtime, + &file.bin_digest, + file.prefixed_path.prefix, + file.prefixed_path.sub_path, + }); + } + try fw.end(); + } + fn downgradeToSharedLock(self: *Manifest) !void { if (!self.have_exclusive_lock) return; diff --git a/lib/std/Build/Cache/Directory.zig b/lib/std/Build/Cache/Directory.zig index 96931ee55be26e82b65cb0ed0cd758cdcbecd5f9..14a5e8a24de94445a596da42b23d41a7506470e9 100644 --- a/lib/std/Build/Cache/Directory.zig +++ b/lib/std/Build/Cache/Directory.zig @@ -1,5 +1,6 @@ const Directory = @This(); const std = @import("../../std.zig"); +const assert = std.debug.assert; const fs = std.fs; const fmt = std.fmt; const Allocator = std.mem.Allocator; @@ -55,11 +56,10 @@ pub fn closeAndFree(self: *Directory, gpa: Allocator) void { self.* = undefined; } -pub fn format(self: Directory, w: *std.io.Writer, comptime fmt_string: []const u8) !void { - if (fmt_string.len != 0) fmt.invalidFmtError(fmt_string, self); +pub fn format(self: Directory, writer: *std.io.Writer) std.io.Writer.Error!void { if (self.path) |p| { - try w.writeAll(p); - try w.writeAll(fs.path.sep_str); + try writer.writeAll(p); + try writer.writeAll(fs.path.sep_str); } } diff --git a/lib/std/Build/Cache/Path.zig b/lib/std/Build/Cache/Path.zig index 5f9c1409a3f1cd961972a83eafc76a588d164da5..a0a58067fc00262966304c17c5d245ef1820e3d2 100644 --- a/lib/std/Build/Cache/Path.zig +++ b/lib/std/Build/Cache/Path.zig @@ -1,3 +1,10 @@ +const Path = @This(); +const std = @import("../../std.zig"); +const assert = std.debug.assert; +const fs = std.fs; +const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; + root_dir: Cache.Directory, /// The path, relative to the root dir, that this `Path` represents. /// Empty string means the root_dir is the path. @@ -137,46 +144,55 @@ pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 { } pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 { - return std.fmt.allocPrintZ(allocator, "{f}", .{p}); + return std.fmt.allocPrintSentinel(allocator, "{f}", .{p}, 0); } -pub fn format(self: Path, w: *std.io.Writer, comptime fmt_string: []const u8) !void { - if (fmt_string.len == 1) { - // Quote-escape the string. - const stringEscape = std.zig.stringEscape; - const f = switch (fmt_string[0]) { - 'q' => "", - '\'' => "\'", - else => @compileError("unsupported format string: " ++ fmt_string), - }; - if (self.root_dir.path) |p| { - try stringEscape(p, w, f); - if (self.sub_path.len > 0) try stringEscape(fs.path.sep_str, w, f); - } - if (self.sub_path.len > 0) { - try stringEscape(self.sub_path, w, f); - } - return; +pub fn fmtEscapeString(path: Path) std.fmt.Formatter(Path, formatEscapeString) { + return .{ .data = path }; +} + +pub fn formatEscapeString(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void { + if (path.root_dir.path) |p| { + try std.zig.stringEscape(p, writer); + if (path.sub_path.len > 0) try std.zig.stringEscape(fs.path.sep_str, writer); + } + if (path.sub_path.len > 0) { + try std.zig.stringEscape(path.sub_path, writer); } - if (fmt_string.len > 0) - std.fmt.invalidFmtError(fmt_string, self); +} + +pub fn fmtEscapeChar(path: Path) std.fmt.Formatter(Path, formatEscapeChar) { + return .{ .data = path }; +} + +pub fn formatEscapeChar(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void { + if (path.root_dir.path) |p| { + try std.zig.charEscape(p, writer); + if (path.sub_path.len > 0) try std.zig.charEscape(fs.path.sep_str, writer); + } + if (path.sub_path.len > 0) { + try std.zig.charEscape(path.sub_path, writer); + } +} + +pub fn format(self: Path, writer: *std.io.Writer) std.io.Writer.Error!void { if (std.fs.path.isAbsolute(self.sub_path)) { - try w.writeAll(self.sub_path); + try writer.writeAll(self.sub_path); return; } if (self.root_dir.path) |p| { - try w.writeAll(p); + try writer.writeAll(p); if (self.sub_path.len > 0) { - try w.writeAll(fs.path.sep_str); - try w.writeAll(self.sub_path); + try writer.writeAll(fs.path.sep_str); + try writer.writeAll(self.sub_path); } return; } if (self.sub_path.len > 0) { - try w.writeAll(self.sub_path); + try writer.writeAll(self.sub_path); return; } - try w.writeByte('.'); + try writer.writeByte('.'); } pub fn eql(self: Path, other: Path) bool { @@ -218,9 +234,3 @@ pub const TableAdapter = struct { return a.eql(b); } }; - -const Path = @This(); -const std = @import("../../std.zig"); -const fs = std.fs; -const Allocator = std.mem.Allocator; -const Cache = std.Build.Cache; diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 963b6aefbf9bcfbecdf4fe815df925efdba0c5b1..28f8781dd196eb3aab810ebf245b233dc942f55b 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -124,9 +124,10 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par const show_stderr = compile.step.result_stderr.len > 0; if (show_error_msgs or show_compile_errors or show_stderr) { - const bw = std.debug.lockStderrWriter(&.{}); + var buf: [256]u8 = undefined; + const w = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, bw, false) catch {}; + build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, false) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { @@ -151,9 +152,10 @@ fn fuzzWorkerRun( run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) { error.MakeFailed => { - const bw = std.debug.lockStderrWriter(&.{}); + var buf: [256]u8 = undefined; + const w = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, bw, false) catch {}; + build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, w, false) catch {}; return; }, else => { diff --git a/lib/std/Build/Fuzz/WebServer.zig b/lib/std/Build/Fuzz/WebServer.zig index 9c9ba00f4e550613fe41f5af6e92b7ac2fee343c..6ea50f72e8bc3dbe8564a75794749bfb5a8afd90 100644 --- a/lib/std/Build/Fuzz/WebServer.zig +++ b/lib/std/Build/Fuzz/WebServer.zig @@ -176,7 +176,7 @@ fn serveFile( // We load the file with every request so that the user can make changes to the file // and refresh the HTML page without restarting this server. const file_contents = ws.zig_lib_directory.handle.readFileAlloc(name, gpa, .limited(10 * 1024 * 1024)) catch |err| { - log.err("failed to read '{f}{s}': {s}", .{ ws.zig_lib_directory, name, @errorName(err) }); + log.err("failed to read '{f}{s}': {t}", .{ ws.zig_lib_directory, name, err }); return error.AlreadyReported; }; defer gpa.free(file_contents); diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index d9c098113f1bc905def4c822007ee0946ded9b1c..0fa8a9a6234fcd94955c27bc2d8b4f5e6ab71669 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -186,7 +186,7 @@ pub const IncludeDir = union(enum) { .embed_path => |lazy_path| { // Special case: this is a single arg. const resolved = lazy_path.getPath3(b, asking_step); - const arg = b.fmt("--embed-dir={}", .{resolved}); + const arg = b.fmt("--embed-dir={f}", .{resolved}); return zig_args.append(arg); }, }; @@ -572,7 +572,7 @@ pub fn appendZigProcessFlags( try zig_args.append(switch (unwind_tables) { .none => "-fno-unwind-tables", .sync => "-funwind-tables", - .@"async" => "-fasync-unwind-tables", + .async => "-fasync-unwind-tables", }); } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 2fd4c7daaef29ea8d6f0247af2169e2b2189b2ca..087000094215ced38f5ddfa091de39bf4da6f226 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -286,28 +286,25 @@ pub fn cast(step: *Step, comptime T: type) ?*T { } /// For debugging purposes, prints identifying information about this Step. -pub fn dump(step: *Step, file: std.fs.File) void { - var fw = file.writer(&.{}); - const bw = &fw.interface; - const tty_config = std.io.tty.detectConfig(file); +pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void { const debug_info = std.debug.getSelfDebugInfo() catch |err| { - bw.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ + w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ @errorName(err), }) catch {}; return; }; if (step.getStackTrace()) |stack_trace| { - bw.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; - std.debug.writeStackTrace(stack_trace, &bw, debug_info, tty_config) catch |err| { - bw.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {}; + w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; + std.debug.writeStackTrace(stack_trace, w, debug_info, tty_config) catch |err| { + w.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {}; return; }; } else { const field = "debug_stack_frames_count"; comptime assert(@hasField(Build, field)); - tty_config.setColor(&bw, .yellow) catch {}; - bw.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; - tty_config.setColor(&bw, .reset) catch {}; + tty_config.setColor(w, .yellow) catch {}; + w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; + tty_config.setColor(w, .reset) catch {}; } } @@ -483,9 +480,9 @@ pub fn evalZigProcess( pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus { const b = s.owner; const src_path = src_lazy_path.getPath3(b, s); - try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{}", .{src_path}), dest_path }); + try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path }); return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| { - return s.fail("unable to update file from '{}' to '{s}': {s}", .{ + return s.fail("unable to update file from '{f}' to '{s}': {s}", .{ src_path, dest_path, @errorName(err), }); }; diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 02a9c8279fba9151b032302c2a3bf322240f54c9..55e1bb0114ca7bf8cde6984da1419922b4d47078 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -230,16 +230,11 @@ const ComputeCompareExpected = struct { literal: u64, }, - pub fn format( - value: ComputeCompareExpected, - bw: *Writer, - comptime fmt: []const u8, - ) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); - try bw.print("{s} ", .{@tagName(value.op)}); + pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void { + try w.print("{t} ", .{value.op}); switch (value.value) { - .variable => |name| try bw.writeAll(name), - .literal => |x| try bw.print("{x}", .{x}), + .variable => |name| try w.writeAll(name), + .literal => |x| try w.print("{x}", .{x}), } } }; @@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { null, .of(u64), null, - ) catch |err| return step.fail("unable to read '{f'}': {s}", .{ src_path, @errorName(err) }); + ) catch |err| return step.fail("unable to read '{f}': {s}", .{ + std.fmt.alt(src_path, .formatEscapeChar), @errorName(err), + }); var vars: std.StringHashMap(u64) = .init(gpa); for (check_object.checks.items) |chk| { @@ -606,7 +603,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { // we either format message string with escaped codes, or not to aid debugging // the failed test. const fmtMessageString = struct { - fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { + fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(Ctx, formatMessageString) { return .{ .data = .{ .kind = kind, .msg = msg, @@ -618,15 +615,10 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { msg: []const u8, }; - fn formatMessageString( - ctx: Ctx, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; + fn formatMessageString(ctx: Ctx, w: *Writer) !void { switch (ctx.kind) { - .dump_section => try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), - else => try bw.writeAll(ctx.msg), + .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}), + else => try w.writeAll(ctx.msg), } } }.fmtMessageString; @@ -882,9 +874,9 @@ const MachODumper = struct { try bw.writeByte('\n'); } - fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, bw: *Writer) !void { + fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: *Writer) !void { // print header first - try bw.print( + try writer.print( \\LC {d} \\cmd {s} \\cmdsize {d} @@ -893,8 +885,8 @@ const MachODumper = struct { switch (lc.cmd()) { .SEGMENT_64 => { const seg = lc.cast(macho.segment_command_64).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\segname {s} \\vmaddr {x} \\vmsize {x} @@ -909,8 +901,8 @@ const MachODumper = struct { }); for (lc.getSections()) |sect| { - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\sectname {s} \\addr {x} \\size {x} @@ -932,8 +924,8 @@ const MachODumper = struct { .REEXPORT_DYLIB, => { const dylib = lc.cast(macho.dylib_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\name {s} \\timestamp {d} \\current version {x} @@ -948,16 +940,16 @@ const MachODumper = struct { .MAIN => { const main = lc.cast(macho.entry_point_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\entryoff {x} \\stacksize {x} , .{ main.entryoff, main.stacksize }); }, .RPATH => { - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\path {s} , .{ lc.getRpathPathName(), @@ -966,8 +958,8 @@ const MachODumper = struct { .UUID => { const uuid = lc.cast(macho.uuid_command).?; - try bw.writeByte('\n'); - try bw.print("uuid {x}", .{&uuid.uuid}); + try writer.writeByte('\n'); + try writer.print("uuid {x}", .{&uuid.uuid}); }, .DATA_IN_CODE, @@ -975,8 +967,8 @@ const MachODumper = struct { .CODE_SIGNATURE, => { const llc = lc.cast(macho.linkedit_data_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\dataoff {x} \\datasize {x} , .{ llc.dataoff, llc.datasize }); @@ -984,8 +976,8 @@ const MachODumper = struct { .DYLD_INFO_ONLY => { const dlc = lc.cast(macho.dyld_info_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\rebaseoff {x} \\rebasesize {x} \\bindoff {x} @@ -1012,8 +1004,8 @@ const MachODumper = struct { .SYMTAB => { const slc = lc.cast(macho.symtab_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\symoff {x} \\nsyms {x} \\stroff {x} @@ -1028,8 +1020,8 @@ const MachODumper = struct { .DYSYMTAB => { const dlc = lc.cast(macho.dysymtab_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\ilocalsym {x} \\nlocalsym {x} \\iextdefsym {x} @@ -1052,8 +1044,8 @@ const MachODumper = struct { .BUILD_VERSION => { const blc = lc.cast(macho.build_version_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\platform {s} \\minos {d}.{d}.{d} \\sdk {d}.{d}.{d} @@ -1069,12 +1061,12 @@ const MachODumper = struct { blc.ntools, }); for (lc.getBuildVersionTools()) |tool| { - try bw.writeByte('\n'); + try writer.writeByte('\n'); switch (tool.tool) { - .CLANG, .SWIFT, .LD, .LLD, .ZIG => try bw.print("tool {s}\n", .{@tagName(tool.tool)}), - else => |x| try bw.print("tool {d}\n", .{@intFromEnum(x)}), + .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}), + else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}), } - try bw.print( + try writer.print( \\version {d}.{d}.{d} , .{ tool.version >> 16, @@ -1090,8 +1082,8 @@ const MachODumper = struct { .VERSION_MIN_TVOS, => { const vlc = lc.cast(macho.version_min_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\version {d}.{d}.{d} \\sdk {d}.{d}.{d} , .{ @@ -1943,58 +1935,58 @@ const ElfDumper = struct { try bw.print("entry {x}\n", .{ctx.hdr.e_entry}); } - fn dumpPhdrs(ctx: ObjectContext, bw: *Writer) !void { + fn dumpPhdrs(ctx: ObjectContext, writer: *Writer) !void { if (ctx.phdrs.len == 0) return; - try bw.writeAll("program headers\n"); + try writer.writeAll("program headers\n"); for (ctx.phdrs, 0..) |phdr, phndx| { - try bw.print("phdr {d}\n", .{phndx}); - try bw.print("type {f}\n", .{fmtPhType(phdr.p_type)}); - try bw.print("vaddr {x}\n", .{phdr.p_vaddr}); - try bw.print("paddr {x}\n", .{phdr.p_paddr}); - try bw.print("offset {x}\n", .{phdr.p_offset}); - try bw.print("memsz {x}\n", .{phdr.p_memsz}); - try bw.print("filesz {x}\n", .{phdr.p_filesz}); - try bw.print("align {x}\n", .{phdr.p_align}); + try writer.print("phdr {d}\n", .{phndx}); + try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)}); + try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); + try writer.print("paddr {x}\n", .{phdr.p_paddr}); + try writer.print("offset {x}\n", .{phdr.p_offset}); + try writer.print("memsz {x}\n", .{phdr.p_memsz}); + try writer.print("filesz {x}\n", .{phdr.p_filesz}); + try writer.print("align {x}\n", .{phdr.p_align}); { const flags = phdr.p_flags; - try bw.writeAll("flags"); - if (flags > 0) try bw.writeByte(' '); + try writer.writeAll("flags"); + if (flags > 0) try writer.writeByte(' '); if (flags & elf.PF_R != 0) { - try bw.writeByte('R'); + try writer.writeByte('R'); } if (flags & elf.PF_W != 0) { - try bw.writeByte('W'); + try writer.writeByte('W'); } if (flags & elf.PF_X != 0) { - try bw.writeByte('E'); + try writer.writeByte('E'); } if (flags & elf.PF_MASKOS != 0) { - try bw.writeAll("OS"); + try writer.writeAll("OS"); } if (flags & elf.PF_MASKPROC != 0) { - try bw.writeAll("PROC"); + try writer.writeAll("PROC"); } - try bw.writeByte('\n'); + try writer.writeByte('\n'); } } } - fn dumpShdrs(ctx: ObjectContext, bw: *Writer) !void { + fn dumpShdrs(ctx: ObjectContext, writer: *Writer) !void { if (ctx.shdrs.len == 0) return; - try bw.writeAll("section headers\n"); + try writer.writeAll("section headers\n"); for (ctx.shdrs, 0..) |shdr, shndx| { - try bw.print("shdr {d}\n", .{shndx}); - try bw.print("name {s}\n", .{ctx.getSectionName(shndx)}); - try bw.print("type {f}\n", .{fmtShType(shdr.sh_type)}); - try bw.print("addr {x}\n", .{shdr.sh_addr}); - try bw.print("offset {x}\n", .{shdr.sh_offset}); - try bw.print("size {x}\n", .{shdr.sh_size}); - try bw.print("addralign {x}\n", .{shdr.sh_addralign}); + try writer.print("shdr {d}\n", .{shndx}); + try writer.print("name {s}\n", .{ctx.getSectionName(shndx)}); + try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)}); + try writer.print("addr {x}\n", .{shdr.sh_addr}); + try writer.print("offset {x}\n", .{shdr.sh_offset}); + try writer.print("size {x}\n", .{shdr.sh_size}); + try writer.print("addralign {x}\n", .{shdr.sh_addralign}); // TODO dump formatted sh_flags } } @@ -2263,16 +2255,11 @@ const ElfDumper = struct { return str[0..std.mem.indexOfScalar(u8, str, 0).?]; } - fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) { + fn fmtShType(sh_type: u32) std.fmt.Formatter(u32, formatShType) { return .{ .data = sh_type }; } - fn formatShType( - sh_type: u32, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; + fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void { const name = switch (sh_type) { elf.SHT_NULL => "NULL", elf.SHT_PROGBITS => "PROGBITS", @@ -2298,26 +2285,21 @@ const ElfDumper = struct { elf.SHT_GNU_VERNEED => "VERNEED", elf.SHT_GNU_VERSYM => "VERSYM", else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) { - return try bw.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); + return try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) { - return try bw.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); + return try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) { - return try bw.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); + return try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); } else "UNKNOWN", }; - try bw.writeAll(name); + try writer.writeAll(name); } - fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) { + fn fmtPhType(ph_type: u32) std.fmt.Formatter(u32, formatPhType) { return .{ .data = ph_type }; } - fn formatPhType( - ph_type: u32, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; + fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void { const p_type = switch (ph_type) { elf.PT_NULL => "NULL", elf.PT_LOAD => "LOAD", @@ -2332,12 +2314,12 @@ const ElfDumper = struct { elf.PT_GNU_STACK => "GNU_STACK", elf.PT_GNU_RELRO => "GNU_RELRO", else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) { - return try bw.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); + return try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) { - return try bw.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); + return try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); } else "UNKNOWN", }; - try bw.writeAll(p_type); + try writer.writeAll(p_type); } }; diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 3987f038d93f89d71690080692225a46769a19bd..356ea4e34e8b75b52f2a0c39b0f9e89a7efe074f 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking const maybe_path: ?*GeneratedFile = @field(compile, tag_name); const generated_file = maybe_path orelse { - std.debug.lockStdErr(); - const stderr: fs.File = .stderr(); - - std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {}; - + const w = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {}; + std.debug.unlockStderrWriter(); @panic("missing emit option for " ++ tag_name); }; const path = generated_file.path orelse { - std.debug.lockStdErr(); - const stderr: fs.File = .stderr(); - - std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {}; - + const w = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {}; + std.debug.unlockStderrWriter(); @panic(tag_name ++ " is null. Is there a missing step dependency?"); }; diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index 45525aeeac1ce573c7dea8087bd01874c5629704..c408fa6b0c76a155cf53d3682643949448bf9aaf 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -198,7 +198,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; const header_text = "This file was generated by ConfigHeader using the Zig Build System."; const c_generated_line = "/* " ++ header_text ++ " */\n"; @@ -335,7 +335,7 @@ fn render_autoconf_at( ) !void { const build = step.owner; const allocator = build.allocator; - const bw = &aw.interface; + const bw = &aw.writer; const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); for (used) |*u| u.* = false; @@ -553,7 +553,7 @@ fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void { .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }), .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }), // TODO: use C-specific escaping instead of zig string literals - .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtEscapes(string) }), + .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), } } @@ -565,7 +565,7 @@ fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void { .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }), .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }), // TODO: use nasm-specific escaping instead of zig string literals - .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtEscapes(string) }), + .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }), } } @@ -753,17 +753,17 @@ fn testReplaceVariablesAutoconfAt( expected: []const u8, values: std.StringArrayHashMap(Value), ) !void { - var output: std.io.Writer.Allocating = .init(allocator); - defer output.deinit(); + var aw: std.io.Writer.Allocating = .init(allocator); + defer aw.deinit(); const used = try allocator.alloc(bool, values.count()); for (used) |*u| u.* = false; defer allocator.free(used); - try expand_variables_autoconf_at(&output.interface, contents, values, used); + try expand_variables_autoconf_at(&aw.writer, contents, values, used); for (used) |u| if (!u) return error.UnusedValue; - try std.testing.expectEqualStrings(expected, output.getWritten()); + try std.testing.expectEqualStrings(expected, aw.getWritten()); } fn testReplaceVariablesCMake( diff --git a/lib/std/Build/Step/Options.zig b/lib/std/Build/Step/Options.zig index 9dfe0a6774224bd6aec4c989e2eea9150228ec97..6f8c40b1d57d9378fea2fe8b26e52185067a2c06 100644 --- a/lib/std/Build/Step/Options.zig +++ b/lib/std/Build/Step/Options.zig @@ -62,7 +62,7 @@ fn printType( for (value) |slice| { try out.appendNTimes(gpa, ' ', indent); - try out.print(gpa, " \"{f}\",\n", .{std.zig.fmtEscapes(slice)}); + try out.print(gpa, " \"{f}\",\n", .{std.zig.fmtString(slice)}); } if (name != null) { @@ -76,28 +76,28 @@ fn printType( []const u8 => { if (name) |some| { try out.print(gpa, "pub const {f}: []const u8 = \"{f}\";", .{ - std.zig.fmtId(some), std.zig.fmtEscapes(value), + std.zig.fmtId(some), std.zig.fmtString(value), }); } else { - try out.print(gpa, "\"{f}\",", .{std.zig.fmtEscapes(value)}); + try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)}); } return out.appendSlice(gpa, "\n"); }, [:0]const u8 => { if (name) |some| { - try out.print(gpa, "pub const {f}: [:0]const u8 = \"{f}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) }); + try out.print(gpa, "pub const {f}: [:0]const u8 = \"{f}\";", .{ std.zig.fmtId(some), std.zig.fmtString(value) }); } else { - try out.print(gpa, "\"{f}\",", .{std.zig.fmtEscapes(value)}); + try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)}); } return out.appendSlice(gpa, "\n"); }, ?[]const u8 => { if (name) |some| { - try out.print(gpa, "pub const {}: ?[]const u8 = ", .{std.zig.fmtId(some)}); + try out.print(gpa, "pub const {f}: ?[]const u8 = ", .{std.zig.fmtId(some)}); } if (value) |payload| { - try out.print(gpa, "\"{f}\"", .{std.zig.fmtEscapes(payload)}); + try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)}); } else { try out.appendSlice(gpa, "null"); } @@ -111,11 +111,11 @@ fn printType( }, ?[:0]const u8 => { if (name) |some| { - try out.print(gpa, "pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(some)}); + try out.print(gpa, "pub const {f}: ?[:0]const u8 = ", .{std.zig.fmtId(some)}); } if (value) |payload| { - try out.print(gpa, "\"{f}\"", .{std.zig.fmtEscapes(payload)}); + try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)}); } else { try out.appendSlice(gpa, "null"); } @@ -142,11 +142,11 @@ fn printType( if (value.pre) |some| { try out.appendNTimes(gpa, ' ', indent); - try out.print(gpa, " .pre = \"{f}\",\n", .{std.zig.fmtEscapes(some)}); + try out.print(gpa, " .pre = \"{f}\",\n", .{std.zig.fmtString(some)}); } if (value.build) |some| { try out.appendNTimes(gpa, ' ', indent); - try out.print(gpa, " .build = \"{f}\",\n", .{std.zig.fmtEscapes(some)}); + try out.print(gpa, " .build = \"{f}\",\n", .{std.zig.fmtString(some)}); } if (name != null) { @@ -162,7 +162,7 @@ fn printType( switch (@typeInfo(T)) { .array => { if (name) |some| { - try out.print(gpa, "pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); + try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); } try out.print(gpa, "{s} {{\n", .{@typeName(T)}); @@ -186,7 +186,7 @@ fn printType( } if (name) |some| { - try out.print(gpa, "pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); + try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); } try out.print(gpa, "&[_]{s} {{\n", .{@typeName(p.child)}); @@ -206,7 +206,7 @@ fn printType( }, .optional => { if (name) |some| { - try out.print(gpa, "pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); + try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) }); } if (value) |inner| { @@ -243,10 +243,10 @@ fn printType( try printEnum(options, out, T, info, indent); if (name) |some| { - try out.print(gpa, "pub const {f}: {f} = .{fp_};\n", .{ + try out.print(gpa, "pub const {f}: {f} = .{f};\n", .{ std.zig.fmtId(some), std.zig.fmtId(@typeName(T)), - std.zig.fmtId(@tagName(value)), + std.zig.fmtIdFlags(@tagName(value), .{ .allow_underscore = true, .allow_primitive = true }), }); } return; @@ -295,7 +295,9 @@ fn printEnum( inline for (val.fields) |field| { try out.appendNTimes(gpa, ' ', indent); - try out.print(gpa, " {fp} = {d},\n", .{ std.zig.fmtId(field.name), field.value }); + try out.print(gpa, " {f} = {d},\n", .{ + std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true }), field.value, + }); } if (!val.is_exhaustive) { @@ -313,7 +315,7 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T: if (gop.found_existing) return; try out.appendNTimes(gpa, ' ', indent); - try out.print(gpa, "pub const {} = ", .{std.zig.fmtId(@typeName(T))}); + try out.print(gpa, "pub const {f} = ", .{std.zig.fmtId(@typeName(T))}); switch (val.layout) { .@"extern" => try out.appendSlice(gpa, "extern struct"), @@ -330,9 +332,15 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T: // If the type name doesn't contains a '.' the type is from zig builtins. if (std.mem.containsAtLeast(u8, type_name, 1, ".")) { - try out.print(gpa, " {p_}: {}", .{ std.zig.fmtId(field.name), std.zig.fmtId(type_name) }); + try out.print(gpa, " {f}: {f}", .{ + std.zig.fmtIdFlags(field.name, .{ .allow_underscore = true, .allow_primitive = true }), + std.zig.fmtId(type_name), + }); } else { - try out.print(gpa, " {p_}: {s}", .{ std.zig.fmtId(field.name), type_name }); + try out.print(gpa, " {f}: {s}", .{ + std.zig.fmtIdFlags(field.name, .{ .allow_underscore = true, .allow_primitive = true }), + type_name, + }); } if (field.defaultValue()) |default_value| { @@ -377,7 +385,9 @@ fn printStructValue( } else { inline for (struct_val.fields) |field| { try out.appendNTimes(gpa, ' ', indent); - try out.print(gpa, " .{p_} = ", .{std.zig.fmtId(field.name)}); + try out.print(gpa, " .{f} = ", .{ + std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true, .allow_underscore = true }), + }); const field_name = @field(val, field.name); switch (@typeInfo(@TypeOf(field_name))) { @@ -405,7 +415,8 @@ pub fn addOptionPath( name: []const u8, path: LazyPath, ) void { - options.args.append(.{ + const arena = options.step.owner.allocator; + options.args.append(arena, .{ .name = options.step.owner.dupe(name), .path = path.dupe(options.step.owner), }) catch @panic("OOM"); diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 8439807b2aef584b9707e78241d7a3f639c41820..38ebd4a20cf39898ee27725797b4d15760165858 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1015,16 +1015,17 @@ fn populateGeneratedPaths( } } -fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); +fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer) std.io.Writer.Error!void { if (term) |t| switch (t) { - .Exited => |code| try w.print("exited with code {}", .{code}), - .Signal => |sig| try w.print("terminated with signal {}", .{sig}), - .Stopped => |sig| try w.print("stopped with signal {}", .{sig}), - .Unknown => |code| try w.print("terminated for unknown reason with code {}", .{code}), - } else try w.writeAll("exited with any code"); + .Exited => |code| try w.print("exited with code {d}", .{code}), + .Signal => |sig| try w.print("terminated with signal {d}", .{sig}), + .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}), + .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}), + } else { + try w.writeAll("exited with any code"); + } } -fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { +fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(?std.process.Child.Term, formatTerm) { return .{ .data = term }; } diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig index 95aa6f5280af66acc3cf284c996764c130d82743..d6dec6880630e8c894c72eb7bd71d5a6a7c60d26 100644 --- a/lib/std/Build/Watch.zig +++ b/lib/std/Build/Watch.zig @@ -659,7 +659,7 @@ const Os = switch (builtin.os.tag) { path.root_dir.handle.fd else posix.openat(path.root_dir.handle.fd, path.sub_path, dir_open_flags, 0) catch |err| { - fatal("failed to open directory {}: {s}", .{ path, @errorName(err) }); + fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) }); }; // Empirically the dir has to stay open or else no events are triggered. errdefer if (!skip_open_dir) posix.close(dir_fd); diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig index c1df140374c4730517039e2ac9e61ba8eb99fe10..52b5908693face576e12c2c5ac2eee695414afaf 100644 --- a/lib/std/SemanticVersion.zig +++ b/lib/std/SemanticVersion.zig @@ -150,15 +150,10 @@ fn parseNum(text: []const u8) error{ InvalidVersion, Overflow }!usize { }; } -pub fn format( - self: Version, - bw: *std.io.Writer, - comptime fmt: []const u8, -) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); - try bw.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); - if (self.pre) |pre| try bw.print("-{s}", .{pre}); - if (self.build) |build| try bw.print("+{s}", .{build}); +pub fn format(self: Version, w: *std.io.Writer) std.io.Writer.Error!void { + try w.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); + if (self.pre) |pre| try w.print("-{s}", .{pre}); + if (self.build) |build| try w.print("+{s}", .{build}); } const expect = std.testing.expect; @@ -200,7 +195,7 @@ test format { "1.0.0+0.build.1-rc.10000aaa-kk-0.1", "5.4.0-1018-raspi", "5.7.123", - }) |valid| try std.testing.expectFmt(valid, "{}", .{try parse(valid)}); + }) |valid| try std.testing.expectFmt(valid, "{f}", .{try parse(valid)}); // Invalid version strings should be rejected. for ([_][]const u8{ @@ -267,12 +262,12 @@ test format { // Valid version string that may overflow. const big_valid = "99999999999999999999999.999999999999999999.99999999999999999"; if (parse(big_valid)) |ver| { - try std.testing.expectFmt(big_valid, "{}", .{ver}); + try std.testing.expectFmt(big_valid, "{f}", .{ver}); } else |err| try expect(err == error.Overflow); // Invalid version string that may overflow. const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12"; - if (parse(big_invalid)) |ver| std.debug.panic("expected error, found {}", .{ver}) else |_| {} + if (parse(big_invalid)) |ver| std.debug.panic("expected error, found {f}", .{ver}) else |_| {} } test "precedence" { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 5b634ca99aa8fbee7a7112219bf7389e73126975..91deb9ddc1342da1288f0f292db03b2ead93f810 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -301,24 +301,13 @@ pub const Os = struct { /// This function is defined to serialize a Zig source code representation of this /// type, that, when parsed, will deserialize into the same data. - pub fn format(ver: WindowsVersion, bw: *std.io.Writer, comptime fmt_str: []const u8) std.io.Writer.Error!void { - const maybe_name = std.enums.tagName(WindowsVersion, ver); - if (comptime std.mem.eql(u8, fmt_str, "s")) { - if (maybe_name) |name| - try bw.print(".{s}", .{name}) - else - try bw.print(".{d}", .{@intFromEnum(ver)}); - } else if (comptime std.mem.eql(u8, fmt_str, "c")) { - if (maybe_name) |name| - try bw.print(".{s}", .{name}) - else - try bw.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)}); - } else if (fmt_str.len == 0) { - if (maybe_name) |name| - try bw.print("WindowsVersion.{s}", .{name}) - else - try bw.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)}); - } else std.fmt.invalidFmtError(fmt_str, ver); + pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void { + if (std.enums.tagName(WindowsVersion, wv)) |name| { + var vecs: [2][]const u8 = .{ ".", name }; + return w.writeVecAll(&vecs); + } else { + return w.print("@enumFromInt(0x{X:0>8})", .{wv}); + } } }; @@ -1686,7 +1675,7 @@ pub const Cpu = struct { pub fn fromCallingConvention(cc: std.builtin.CallingConvention.Tag) []const Arch { return switch (cc) { .auto, - .@"async", + .async, .naked, .@"inline", => unreachable, diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 95f50df3e5430031256f5ed59b52968171b14d69..9fcbeacdd1ed21df46e4c2e18536e0f56a406600 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -165,10 +165,18 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); defer file.close(); +<<<<<<< HEAD var fw = file.writer(&.{}); fw.interface.writeAll(name) catch |err| switch (err) { error.WriteFailed => return fw.err.?, }; +||||||| edf785db0f + + try file.writer().writeAll(name); +======= + + try file.deprecatedWriter().writeAll(name); +>>>>>>> origin/master return; }, .windows => { @@ -280,11 +288,23 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); +<<<<<<< HEAD var fr = file.reader(&.{}); const n = fr.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) { error.ReadFailed => return fr.err.?, }; return if (n == 0) null else buffer[0 .. n - 1]; +||||||| edf785db0f + + const data_len = try file.reader().readAll(buffer_ptr[0 .. max_name_len + 1]); + + return if (data_len >= 1) buffer[0 .. data_len - 1] else null; +======= + + const data_len = try file.deprecatedReader().readAll(buffer_ptr[0 .. max_name_len + 1]); + + return if (data_len >= 1) buffer[0 .. data_len - 1] else null; +>>>>>>> origin/master }, .windows => { const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len); @@ -1164,7 +1184,7 @@ const LinuxThreadImpl = struct { fn getCurrentId() Id { return tls_thread_id orelse { - const tid = @as(u32, @bitCast(linux.gettid())); + const tid: u32 = @bitCast(linux.gettid()); tls_thread_id = tid; return tid; }; diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index add94a47a0e914aa56b73c7399930bbc55777af5..b610562708b6383148611fe960f0ffe6b12dfe1b 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -3,12 +3,10 @@ const std = @import("std.zig"); const testing = std.testing; +const Uri = @This(); const Allocator = std.mem.Allocator; -const assert = std.debug.assert; const Writer = std.io.Writer; -const Uri = @This(); - scheme: []const u8, user: ?Component = null, password: ?Component = null, @@ -65,7 +63,7 @@ pub const Component = union(enum) { return switch (component) { .raw => |raw| raw, .percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_| - try std.fmt.bufPrint(buffer, "{fraw}", .{component}) + try std.fmt.bufPrint(buffer, "{f}", .{std.fmt.alt(component, .formatRaw)}) else percent_encoded, }; @@ -85,16 +83,9 @@ pub const Component = union(enum) { }; } - pub fn format(component: Component, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - if (fmt.len == 0) { - try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{ - @tagName(component), - std.zig.fmtEscapes(switch (component) { - .raw, .percent_encoded => |string| string, - }), - }); - } else if (comptime std.mem.eql(u8, fmt, "raw")) switch (component) { - .raw => |raw| try bw.writeAll(raw), + pub fn formatRaw(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try w.writeAll(raw), .percent_encoded => |percent_encoded| { var start: usize = 0; var index: usize = 0; @@ -103,51 +94,75 @@ pub const Component = union(enum) { if (percent_encoded.len - index < 2) continue; const percent_encoded_char = std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue; - try bw.print("{s}{c}", .{ + try w.print("{s}{c}", .{ percent_encoded[start..percent], percent_encoded_char, }); start = percent + 3; index = percent + 3; } - try bw.writeAll(percent_encoded[start..]); + try w.writeAll(percent_encoded[start..]); }, - } else if (comptime std.mem.eql(u8, fmt, "%")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isUnreserved), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else if (comptime std.mem.eql(u8, fmt, "user")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isUserChar), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else if (comptime std.mem.eql(u8, fmt, "password")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isPasswordChar), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else if (comptime std.mem.eql(u8, fmt, "host")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isHostChar), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else if (comptime std.mem.eql(u8, fmt, "path")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isPathChar), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else if (comptime std.mem.eql(u8, fmt, "query")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isQueryChar), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else if (comptime std.mem.eql(u8, fmt, "fragment")) switch (component) { - .raw => |raw| try percentEncode(bw, raw, isFragmentChar), - .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), - } else @compileError("invalid format string '" ++ fmt ++ "'"); - } - - pub fn percentEncode( - bw: *Writer, - raw: []const u8, - comptime isValidChar: fn (u8) bool, - ) Writer.Error!void { + } + } + + pub fn formatEscaped(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isUnreserved), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn formatUser(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isUserChar), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn formatPassword(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isPasswordChar), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn formatHost(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isHostChar), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn formatPath(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isPathChar), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn formatQuery(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isQueryChar), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn formatFragment(component: Component, w: *Writer) Writer.Error!void { + switch (component) { + .raw => |raw| try percentEncode(w, raw, isFragmentChar), + .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), + } + } + + pub fn percentEncode(w: *Writer, raw: []const u8, comptime isValidChar: fn (u8) bool) Writer.Error!void { var start: usize = 0; for (raw, 0..) |char, index| { if (isValidChar(char)) continue; - try bw.print("{s}%{X:0>2}", .{ raw[start..index], char }); + try w.print("{s}%{X:0>2}", .{ raw[start..index], char }); start = index + 1; } - try bw.writeAll(raw[start..]); + try w.writeAll(raw[start..]); } }; @@ -264,76 +279,91 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri { return uri; } -pub const WriteToStreamOptions = struct { - /// When true, include the scheme part of the URI. - scheme: bool = false, - /// When true, include the user and password part of the URI. Ignored if `authority` is false. - authentication: bool = false, - /// When true, include the authority part of the URI. - authority: bool = false, - /// When true, include the path part of the URI. - path: bool = false, - /// When true, include the query part of the URI. Ignored when `path` is false. - query: bool = false, - /// When true, include the fragment part of the URI. Ignored when `path` is false. - fragment: bool = false, - /// When true, include the port part of the URI. Ignored when `port` is null. - port: bool = true, -}; +pub fn format(uri: *const Uri, writer: *Writer) Writer.Error!void { + return writeToStream(uri, writer, .all); +} -pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *Writer) Writer.Error!void { - if (options.scheme) { - try bw.print("{s}:", .{uri.scheme}); - if (options.authority and uri.host != null) { - try bw.writeAll("//"); +pub fn writeToStream(uri: *const Uri, writer: *Writer, flags: Format.Flags) Writer.Error!void { + if (flags.scheme) { + try writer.print("{s}:", .{uri.scheme}); + if (flags.authority and uri.host != null) { + try writer.writeAll("//"); } } - if (options.authority) { - if (options.authentication and uri.host != null) { + if (flags.authority) { + if (flags.authentication and uri.host != null) { if (uri.user) |user| { - try bw.print("{fuser}", .{user}); + try user.formatUser(writer); if (uri.password) |password| { - try bw.print(":{fpassword}", .{password}); + try writer.writeByte(':'); + try password.formatPassword(writer); } - try bw.writeByte('@'); + try writer.writeByte('@'); } } if (uri.host) |host| { - try bw.print("{fhost}", .{host}); - if (options.port) { - if (uri.port) |port| try bw.print(":{d}", .{port}); + try host.formatHost(writer); + if (flags.port) { + if (uri.port) |port| try writer.print(":{d}", .{port}); } } } - if (options.path) { - try bw.print("{fpath}", .{ - if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path, - }); - if (options.query) { - if (uri.query) |query| try bw.print("?{fquery}", .{query}); + if (flags.path) { + const uri_path: Component = if (uri.path.isEmpty()) .{ .percent_encoded = "/" } else uri.path; + try uri_path.formatPath(writer); + if (flags.query) { + if (uri.query) |query| { + try writer.writeByte('?'); + try query.formatQuery(writer); + } } - if (options.fragment) { - if (uri.fragment) |fragment| try bw.print("#{ffragment}", .{fragment}); + if (flags.fragment) { + if (uri.fragment) |fragment| { + try writer.writeByte('#'); + try fragment.formatFragment(writer); + } } } } -pub fn format(uri: Uri, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0; - const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0; - const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0; - const path = comptime std.mem.indexOfScalar(u8, fmt, '/') != null or fmt.len == 0; - const query = comptime std.mem.indexOfScalar(u8, fmt, '?') != null or fmt.len == 0; - const fragment = comptime std.mem.indexOfScalar(u8, fmt, '#') != null or fmt.len == 0; +pub const Format = struct { + uri: *const Uri, + flags: Flags = .{}, - return writeToStream(uri, .{ - .scheme = scheme, - .authentication = authentication, - .authority = authority, - .path = path, - .query = query, - .fragment = fragment, - }, bw); + pub const Flags = struct { + /// When true, include the scheme part of the URI. + scheme: bool = false, + /// When true, include the user and password part of the URI. Ignored if `authority` is false. + authentication: bool = false, + /// When true, include the authority part of the URI. + authority: bool = false, + /// When true, include the path part of the URI. + path: bool = false, + /// When true, include the query part of the URI. Ignored when `path` is false. + query: bool = false, + /// When true, include the fragment part of the URI. Ignored when `path` is false. + fragment: bool = false, + /// When true, include the port part of the URI. Ignored when `port` is null. + port: bool = true, + + pub const all: Flags = .{ + .scheme = true, + .authentication = true, + .authority = true, + .path = true, + .query = true, + .fragment = true, + .port = true, + }; + }; + + pub fn default(f: Format, writer: *Writer) Writer.Error!void { + return writeToStream(f.uri, writer, f.flags); + } +}; + +pub fn fmt(uri: *const Uri, flags: Format.Flags) std.fmt.Formatter(Format, Format.default) { + return .{ .data = .{ .uri = uri, .flags = flags } }; } /// The return value will contain strings pointing into the original `text`. @@ -464,9 +494,8 @@ test remove_dot_segments { fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component { var aux: Writer = .fixed(aux_buf.*); if (!base.isEmpty()) { - aux.print("{fpath}", .{base}) catch return error.NoSpaceLeft; - aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse - return remove_dot_segments(new); + base.formatPath(&aux) catch return error.NoSpaceLeft; + aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse return remove_dot_segments(new); } aux.print("/{s}", .{new}) catch return error.NoSpaceLeft; const merged_path = remove_dot_segments(aux.buffered()); @@ -745,8 +774,11 @@ test "Special test" { test "URI percent encoding" { try std.testing.expectFmt( "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad", - "{%}", - .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }}, + "{f}", + .{std.fmt.alt( + @as(Component, .{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }), + .formatEscaped, + )}, ); } @@ -755,7 +787,10 @@ test "URI percent decoding" { const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*; - try std.testing.expectFmt(expected, "{fraw}", .{Component{ .percent_encoded = &input }}); + try std.testing.expectFmt(expected, "{f}", .{std.fmt.alt( + @as(Component, .{ .percent_encoded = &input }), + .formatRaw, + )}); var output: [expected.len]u8 = undefined; try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); @@ -767,7 +802,10 @@ test "URI percent decoding" { const expected = "/abc%"; var input = expected.*; - try std.testing.expectFmt(expected, "{fraw}", .{Component{ .percent_encoded = &input }}); + try std.testing.expectFmt(expected, "{f}", .{std.fmt.alt( + @as(Component, .{ .percent_encoded = &input }), + .formatRaw, + )}); var output: [expected.len]u8 = undefined; try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); @@ -781,7 +819,9 @@ test "URI query encoding" { const parsed = try Uri.parse(address); // format the URI to percent encode it - try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{/?}", .{parsed}); + try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{f}", .{ + parsed.fmt(.{ .path = true, .query = true }), + }); } test "format" { @@ -795,7 +835,9 @@ test "format" { .query = null, .fragment = null, }; - try std.testing.expectFmt("file:/foo/bar/baz", "{;/?#}", .{uri}); + try std.testing.expectFmt("file:/foo/bar/baz", "{f}", .{ + uri.fmt(.{ .scheme = true, .path = true, .query = true, .fragment = true }), + }); } test "URI malformed input" { diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 12b9fa0f28f64449361fa3031f0e6baf22f283e8..6bd425e8f51ba9e891c067ca1786cb9e72e5209c 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -339,9 +339,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty } pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { + const gpa = self.allocator; var unmanaged = self.moveToUnmanaged(); - try unmanaged.print(self.allocator, fmt, args); - self.* = unmanaged.toManaged(self.allocator); + defer self.* = unmanaged.toManaged(gpa); + try unmanaged.print(gpa, fmt, args); } /// Append a value to the list `n` times. @@ -907,7 +908,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig try self.ensureUnusedCapacity(gpa, fmt.len); var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self); defer self.* = aw.toArrayList(); - return aw.interface.print(fmt, args) catch |err| switch (err) { + return aw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; } diff --git a/lib/std/ascii.zig b/lib/std/ascii.zig index a88b637ec002c4c47bceb3e5c07639b28e87db92..99bebf09ab2178b5f3080dc01ed4858bcdab477b 100644 --- a/lib/std/ascii.zig +++ b/lib/std/ascii.zig @@ -10,6 +10,10 @@ const std = @import("std"); +pub const lowercase = "abcdefghijklmnopqrstuvwxyz"; +pub const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +pub const letters = lowercase ++ uppercase; + /// The C0 control codes of the ASCII encoding. /// /// 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 { pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool { return orderIgnoreCase(lhs, rhs) == .lt; } + +pub const HexEscape = struct { + bytes: []const u8, + charset: *const [16]u8, + + pub const upper_charset = "0123456789ABCDEF"; + pub const lower_charset = "0123456789abcdef"; + + pub fn format(se: HexEscape, w: *std.io.Writer) std.io.Writer.Error!void { + const charset = se.charset; + + var buf: [4]u8 = undefined; + buf[0] = '\\'; + buf[1] = 'x'; + + for (se.bytes) |c| { + if (std.ascii.isPrint(c)) { + try w.writeByte(c); + } else { + buf[2] = charset[c >> 4]; + buf[3] = charset[c & 15]; + try w.writeAll(&buf); + } + } + } +}; + +/// Replaces non-ASCII bytes with hex escapes. +pub fn hexEscape(bytes: []const u8, case: std.fmt.Case) std.fmt.Formatter(HexEscape, HexEscape.format) { + return .{ .data = .{ .bytes = bytes, .charset = switch (case) { + .lower => HexEscape.lower_charset, + .upper => HexEscape.upper_charset, + } } }; +} + +test hexEscape { + try std.testing.expectFmt("abc 123", "{f}", .{hexEscape("abc 123", .lower)}); + try std.testing.expectFmt("ab\\xffc", "{f}", .{hexEscape("ab\xffc", .lower)}); + try std.testing.expectFmt("abc 123", "{f}", .{hexEscape("abc 123", .upper)}); + try std.testing.expectFmt("ab\\xFFc", "{f}", .{hexEscape("ab\xffc", .upper)}); +} diff --git a/lib/std/base64.zig b/lib/std/base64.zig index e88b72343984e11497bb46353525595d8300d7bb..a84f4a0b4f0bf1bdd2a2627a9e21918e8e46ae6f 100644 --- a/lib/std/base64.zig +++ b/lib/std/base64.zig @@ -108,7 +108,7 @@ pub const Base64Encoder = struct { } } - // dest must be compatible with std.io.Writer's writeAll interface + // dest must be compatible with std.io.GenericWriter's writeAll interface pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void { var chunker = window(u8, source, 3, 3); while (chunker.next()) |chunk| { @@ -118,8 +118,8 @@ pub const Base64Encoder = struct { } } - // destWriter must be compatible with std.io.Writer's writeAll interface - // sourceReader must be compatible with std.io.Reader's read interface + // destWriter must be compatible with std.io.GenericWriter's writeAll interface + // sourceReader must be compatible with `std.io.GenericReader` read interface pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void { while (true) { var tempSource: [3]u8 = undefined; diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index 1a4407e6876f3233fea591a41704d93b44366027..7864dfb7754f7b00b93c164dac869618e16e12f3 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -277,7 +277,7 @@ pub fn BoundedArrayAligned( @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") else - std.io.Writer(*Self, error{Overflow}, appendWrite); + std.io.GenericWriter(*Self, error{Overflow}, appendWrite); /// Initializes a writer which will write into the array. pub fn writer(self: *Self) Writer { @@ -285,7 +285,7 @@ pub fn BoundedArrayAligned( } /// Same as `appendSlice` except it returns the number of bytes written, which is always the same - /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API. + /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API. fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { try self.appendSlice(m); return m.len; diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 2d8f1d1ed06904f22f2e426e7e0691cde267ce3c..f0cf10758269e01ecf9f5236bb8a0f1d44d5bbdb 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -34,23 +34,21 @@ pub const StackTrace = struct { index: usize, instruction_addresses: []usize, - pub fn format(st: StackTrace, bw: *std.io.Writer, comptime fmt: []const u8) !void { - comptime if (fmt.len != 0) unreachable; - + pub fn format(self: StackTrace, writer: *std.io.Writer) std.io.Writer.Error!void { // TODO: re-evaluate whether to use format() methods at all. // Until then, avoid an error when using DebugAllocator with WebAssembly // where it tries to call detectTTYConfig here. if (builtin.os.tag == .freestanding) return 0; const debug_info = std.debug.getSelfDebugInfo() catch |err| { - return bw.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{ + return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{ @errorName(err), }); }; - const tty_config = std.io.tty.detectConfig(.stderr()); - try bw.writeAll("\n"); - std.debug.writeStackTrace(st, bw, debug_info, tty_config) catch |err| { - try bw.print("Unable to print stack trace: {s}\n", .{@errorName(err)}); + const tty_config = std.io.tty.detectConfig(std.fs.File.stderr()); + try writer.writeAll("\n"); + std.debug.writeStackTrace(self, writer, debug_info, tty_config) catch |err| { + try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)}); }; } }; @@ -195,8 +193,6 @@ pub const CallingConvention = union(enum(u8)) { pub const C: CallingConvention = .c; /// Deprecated; use `.naked`. pub const Naked: CallingConvention = .naked; - /// Deprecated; use `.@"async"`. - pub const Async: CallingConvention = .@"async"; /// Deprecated; use `.@"inline"`. pub const Inline: CallingConvention = .@"inline"; /// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`. @@ -244,7 +240,7 @@ pub const CallingConvention = union(enum(u8)) { /// The calling convention of a function that can be called with `async` syntax. An `async` call /// of a runtime-known function must target a function with this calling convention. /// Comptime-known functions with other calling conventions may be coerced to this one. - @"async", + async, /// Functions with this calling convention have no prologue or epilogue, making the function /// uncallable in regular Zig code. This can be useful when integrating with assembly. @@ -847,7 +843,7 @@ pub const LinkMode = enum { pub const UnwindTables = enum { none, sync, - @"async", + async, }; /// This data structure is used by the Zig language code generation and @@ -862,32 +858,23 @@ pub const WasiExecModel = enum { pub const CallModifier = enum { /// Equivalent to function call syntax. auto, - - /// Equivalent to async keyword used with function call syntax. - async_kw, - /// Prevents tail call optimization. This guarantees that the return /// address will point to the callsite, as opposed to the callsite's /// callsite. If the call is otherwise required to be tail-called /// or inlined, a compile error is emitted instead. never_tail, - /// Guarantees that the call will not be inlined. If the call is /// otherwise required to be inlined, a compile error is emitted instead. never_inline, - /// Asserts that the function call will not suspend. This allows a /// non-async function to call an async function. - no_async, - + no_suspend, /// Guarantees that the call will be generated with tail call optimization. /// If this is not possible, a compile error is emitted instead. always_tail, - /// Guarantees that the call will be inlined at the callsite. /// If this is not possible, a compile error is emitted instead. always_inline, - /// Evaluates the call at compile-time. If the call cannot be completed at /// compile-time, a compile error is emitted instead. compile_time, diff --git a/lib/std/c.zig b/lib/std/c.zig index aabb54de497cc1172b2ebd37422ef480e2b269c7..e652b10ce078ba90eb0db15312c1814394426322 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10412,7 +10412,10 @@ pub const sigfillset = switch (native_os) { }; pub const sigaddset = private.sigaddset; -pub const sigemptyset = private.sigemptyset; +pub const sigemptyset = switch (native_os) { + .netbsd => private.__sigemptyset14, + else => private.sigemptyset, +}; pub const sigdelset = private.sigdelset; pub const sigismember = private.sigismember; @@ -11268,6 +11271,7 @@ const private = struct { extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int; extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; + extern "c" fn __sigemptyset14(set: ?*sigset_t) c_int; extern "c" fn __sigfillset14(set: ?*sigset_t) c_int; extern "c" fn __sigprocmask14(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; extern "c" fn __socket30(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; diff --git a/lib/std/compress/xz.zig b/lib/std/compress/xz.zig index 4514c8857f30ce64f78b76e4f596ff03a4fe881a..445d103098aef106a729b2e014191f63d413d348 100644 --- a/lib/std/compress/xz.zig +++ b/lib/std/compress/xz.zig @@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type { const Self = @This(); pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error; - pub const Reader = std.io.Reader(*Self, Error, read); + pub const Reader = std.io.GenericReader(*Self, Error, read); allocator: Allocator, block_decoder: block.Decoder(ReaderType), diff --git a/lib/std/compress/xz/block.zig b/lib/std/compress/xz/block.zig index a3c289dfb85fc478c4d9816c1c771a80b47e09ee..6253341f3652c2c5e2967357fc65935f95d54368 100644 --- a/lib/std/compress/xz/block.zig +++ b/lib/std/compress/xz/block.zig @@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type { ReaderType.Error || DecodeError || Allocator.Error; - pub const Reader = std.io.Reader(*Self, Error, read); + pub const Reader = std.io.GenericReader(*Self, Error, read); allocator: Allocator, inner_reader: ReaderType, diff --git a/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig b/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig index f580c54546dd69a36ffda14bc256332b0dcdee91..b6c0ab20d42bd4d5c0f63d9406f81384f2becfc8 100644 --- a/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig +++ b/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig @@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void { self.data.ptr = begin; } -pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize); +pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize); /// Warning: This writer writes backwards. `fn print` will NOT work as expected. pub fn writer(self: *ArrayListReverse) Writer { return .{ .context = self }; diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index 8e74fe7899852639b328cc14be7dac28eefa9536..7c2d5434fbad85b7ae085eb9a54e3cc229b45b77 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -383,12 +383,28 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { for (&d.s, v) |*dv, vv| dv.* +%= vv; } +<<<<<<< HEAD pub fn writer(this: *@This(), buffer: []u8) Writer { return .{ .context = this, .vtable = &.{ .drain = drain }, .buffer = buffer, }; +||||||| edf785db0f + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; +======= + pub const Error = error{}; + pub const Writer = std.io.GenericWriter(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; +>>>>>>> origin/master } fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 01a80d309c162d5699ca4309aa7044cc2b8e644a..00a9606f537ec85c6c865a469ebe64098c4a255e 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -222,7 +222,8 @@ pub fn unlockStderrWriter() void { /// Print to stderr, unbuffered, and silently returning on failure. Intended /// for use in "printf debugging". Use `std.log` functions for proper logging. pub fn print(comptime fmt: []const u8, args: anytype) void { - const bw = lockStderrWriter(&.{}); + var buffer: [32]u8 = undefined; + const bw = lockStderrWriter(&buffer); defer unlockStderrWriter(); nosuspend bw.print(fmt, args) catch return; } @@ -307,7 +308,7 @@ test dumpHexFallible { var aw: std.io.Writer.Allocating = .init(std.testing.allocator); defer aw.deinit(); - try dumpHexFallible(&aw.interface, .no_color, bytes); + try dumpHexFallible(&aw.writer, .no_color, bytes); const expected = try std.fmt.allocPrint(std.testing.allocator, \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........ \\{x:0>[2]} 01 12 13 ... @@ -1228,9 +1229,9 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi } test printLineFromFileAnyOs { - var output = std.ArrayList(u8).init(std.testing.allocator); - defer output.deinit(); - const output_stream = output.writer(); + var aw: Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); + const output_stream = &aw.writer; const allocator = std.testing.allocator; const join = std.fs.path.join; @@ -1252,8 +1253,8 @@ test printLineFromFileAnyOs { try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); - try expectEqualStrings("no new lines in this file, but one is printed anyway\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.getWritten()); + aw.clearRetainingCapacity(); } { const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" }); @@ -1268,12 +1269,12 @@ test printLineFromFileAnyOs { }); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); - try expectEqualStrings("1\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings("1\n", aw.getWritten()); + aw.clearRetainingCapacity(); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 }); - try expectEqualStrings("3\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings("3\n", aw.getWritten()); + aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); @@ -1282,14 +1283,17 @@ test printLineFromFileAnyOs { defer allocator.free(path); const overlap = 10; - var writer = file.writer(); + var buf: [16]u8 = undefined; + var file_writer = file.writer(&buf); + const writer = &file_writer.interface; try writer.splatByteAll('a', std.heap.page_size_min - overlap); try writer.writeByte('\n'); try writer.splatByteAll('a', overlap); + try writer.flush(); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); - try expectEqualStrings(("a" ** overlap) ++ "\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings(("a" ** overlap) ++ "\n", aw.getWritten()); + aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); @@ -1297,12 +1301,13 @@ test printLineFromFileAnyOs { const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); defer allocator.free(path); - var writer = file.writer(); + var file_writer = file.writer(&.{}); + const writer = &file_writer.interface; try writer.splatByteAll('a', std.heap.page_size_max); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); - try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.getWritten()); + aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); @@ -1310,24 +1315,25 @@ test printLineFromFileAnyOs { const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); defer allocator.free(path); - var writer = file.writer(); + var file_writer = file.writer(&.{}); + const writer = &file_writer.interface; try writer.splatByteAll('a', 3 * std.heap.page_size_max); try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); - try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.getWritten()); + aw.clearRetainingCapacity(); try writer.writeAll("a\na"); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); - try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.getWritten()); + aw.clearRetainingCapacity(); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); - try expectEqualStrings("a\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings("a\n", aw.getWritten()); + aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); @@ -1335,18 +1341,19 @@ test printLineFromFileAnyOs { const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" }); defer allocator.free(path); - var writer = file.writer(); + var file_writer = file.writer(&.{}); + const writer = &file_writer.interface; const real_file_start = 3 * std.heap.page_size_min; try writer.splatByteAll('\n', real_file_start); try writer.writeAll("abc\ndef"); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 }); - try expectEqualStrings("abc\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings("abc\n", aw.getWritten()); + aw.clearRetainingCapacity(); try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 }); - try expectEqualStrings("def\n", output.items); - output.clearRetainingCapacity(); + try expectEqualStrings("def\n", aw.getWritten()); + aw.clearRetainingCapacity(); } } @@ -1597,10 +1604,10 @@ test "manage resources correctly" { // self-hosted debug info is still too buggy if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; - const writer = std.io.null_writer; + var discarding: std.io.Writer.Discarding = .init(&.{}); var di = try SelfInfo.open(testing.allocator); defer di.deinit(); - try printSourceAtAddress(&di, writer, showMyTrace(), io.tty.detectConfig(.stderr())); + try printSourceAtAddress(&di, &discarding.writer, showMyTrace(), io.tty.detectConfig(.stderr())); } noinline fn showMyTrace() usize { diff --git a/lib/std/debug/Pdb.zig b/lib/std/debug/Pdb.zig index 599686c14d584094abcb59af2284c805ace451a9..e2c2c2f78ed34bcc673f57bf6f1c78c0c6e8c7ad 100644 --- a/lib/std/debug/Pdb.zig +++ b/lib/std/debug/Pdb.zig @@ -395,7 +395,7 @@ const Msf = struct { streams: []MsfStream, fn init(allocator: Allocator, file: File) !Msf { - const in = file.reader(); + const in = file.deprecatedReader(); const superblock = try in.takeStruct(pdb.SuperBlock); @@ -514,7 +514,7 @@ const MsfStream = struct { var offset = self.pos % self.block_size; try self.in_file.seekTo(block * self.block_size + offset); - const in = self.in_file.reader(); + const in = self.in_file.deprecatedReader(); var size: usize = 0; var rem_buffer = buffer; @@ -562,7 +562,7 @@ const MsfStream = struct { return block * self.block_size + offset; } - pub fn reader(self: *MsfStream) std.io.Reader(*MsfStream, Error, read) { + pub fn reader(self: *MsfStream) std.io.GenericReader(*MsfStream, Error, read) { return .{ .context = self }; } }; diff --git a/lib/std/elf.zig b/lib/std/elf.zig index b7be0a3e81d99d8c7621aa220533659255061ab6..eb08f5b409d596f4ff6ae046d3331a67373f2009 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -508,6 +508,7 @@ pub const Header = struct { }; } +<<<<<<< HEAD pub const ReadError = std.io.Reader.Error || ParseError; pub fn read(r: *std.io.Reader) ReadError!Header { @@ -515,6 +516,19 @@ pub const Header = struct { const result = try parse(@ptrCast(buf)); r.toss(if (result.is_64) @sizeOf(Elf64_Ehdr) else @sizeOf(Elf32_Ehdr)); return result; +||||||| edf785db0f + pub fn read(parse_source: anytype) !Header { + var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; + try parse_source.seekableStream().seekTo(0); + try parse_source.reader().readNoEof(&hdr_buf); + return Header.parse(&hdr_buf); +======= + pub fn read(parse_source: anytype) !Header { + var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; + try parse_source.seekableStream().seekTo(0); + try parse_source.deprecatedReader().readNoEof(&hdr_buf); + return Header.parse(&hdr_buf); +>>>>>>> origin/master } pub const ParseError = error{ @@ -590,14 +604,92 @@ pub const ProgramHeaderIterator = struct { if (it.index >= it.elf_header.phnum) return null; defer it.index += 1; +<<<<<<< HEAD if (it.elf_header.is_64) { var phdr: Elf64_Phdr = undefined; const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index; try it.file_reader.seekTo(offset); try it.file_reader.interface.readSlice(@ptrCast(&phdr)); if (it.elf_header.endian != native_endian) +||||||| edf785db0f + if (self.elf_header.is_64) { + var phdr: Elf64_Phdr = undefined; + const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.reader().readNoEof(mem.asBytes(&phdr)); + + // ELF endianness matches native endianness. + if (self.elf_header.endian == native_endian) return phdr; + + // Convert fields to native endianness. +======= + if (self.elf_header.is_64) { + var phdr: Elf64_Phdr = undefined; + const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr)); + + // ELF endianness matches native endianness. + if (self.elf_header.endian == native_endian) return phdr; + + // Convert fields to native endianness. +>>>>>>> origin/master mem.byteSwapAllFields(Elf64_Phdr, &phdr); +<<<<<<< HEAD return phdr; +||||||| edf785db0f + return phdr; + } + + var phdr: Elf32_Phdr = undefined; + const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.reader().readNoEof(mem.asBytes(&phdr)); + + // ELF endianness does NOT match native endianness. + if (self.elf_header.endian != native_endian) { + // Convert fields to native endianness. + mem.byteSwapAllFields(Elf32_Phdr, &phdr); + } + + // Convert 32-bit header to 64-bit. + return Elf64_Phdr{ + .p_type = phdr.p_type, + .p_offset = phdr.p_offset, + .p_vaddr = phdr.p_vaddr, + .p_paddr = phdr.p_paddr, + .p_filesz = phdr.p_filesz, + .p_memsz = phdr.p_memsz, + .p_flags = phdr.p_flags, + .p_align = phdr.p_align, + }; +======= + return phdr; + } + + var phdr: Elf32_Phdr = undefined; + const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr)); + + // ELF endianness does NOT match native endianness. + if (self.elf_header.endian != native_endian) { + // Convert fields to native endianness. + mem.byteSwapAllFields(Elf32_Phdr, &phdr); + } + + // Convert 32-bit header to 64-bit. + return Elf64_Phdr{ + .p_type = phdr.p_type, + .p_offset = phdr.p_offset, + .p_vaddr = phdr.p_vaddr, + .p_paddr = phdr.p_paddr, + .p_filesz = phdr.p_filesz, + .p_memsz = phdr.p_memsz, + .p_flags = phdr.p_flags, + .p_align = phdr.p_align, + }; +>>>>>>> origin/master } var phdr: Elf32_Phdr = undefined; @@ -624,9 +716,23 @@ pub const SectionHeaderIterator = struct { file_reader: *std.fs.File.Reader, index: usize = 0, +<<<<<<< HEAD pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr { if (it.index >= it.elf_header.shnum) return null; defer it.index += 1; +||||||| edf785db0f + if (self.elf_header.is_64) { + var shdr: Elf64_Shdr = undefined; + const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.reader().readNoEof(mem.asBytes(&shdr)); +======= + if (self.elf_header.is_64) { + var shdr: Elf64_Shdr = undefined; + const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr)); +>>>>>>> origin/master if (it.elf_header.is_64) { var shdr: Elf64_Shdr = undefined; @@ -635,7 +741,65 @@ pub const SectionHeaderIterator = struct { try it.file_reader.interface.readSlice(@ptrCast(&shdr)); if (it.elf_header.endian != native_endian) mem.byteSwapAllFields(Elf64_Shdr, &shdr); +<<<<<<< HEAD return shdr; +||||||| edf785db0f + return shdr; + } + + var shdr: Elf32_Shdr = undefined; + const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.reader().readNoEof(mem.asBytes(&shdr)); + + // ELF endianness does NOT match native endianness. + if (self.elf_header.endian != native_endian) { + // Convert fields to native endianness. + mem.byteSwapAllFields(Elf32_Shdr, &shdr); + } + + // Convert 32-bit header to 64-bit. + return Elf64_Shdr{ + .sh_name = shdr.sh_name, + .sh_type = shdr.sh_type, + .sh_flags = shdr.sh_flags, + .sh_addr = shdr.sh_addr, + .sh_offset = shdr.sh_offset, + .sh_size = shdr.sh_size, + .sh_link = shdr.sh_link, + .sh_info = shdr.sh_info, + .sh_addralign = shdr.sh_addralign, + .sh_entsize = shdr.sh_entsize, + }; +======= + return shdr; + } + + var shdr: Elf32_Shdr = undefined; + const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; + try self.parse_source.seekableStream().seekTo(offset); + try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr)); + + // ELF endianness does NOT match native endianness. + if (self.elf_header.endian != native_endian) { + // Convert fields to native endianness. + mem.byteSwapAllFields(Elf32_Shdr, &shdr); + } + + // Convert 32-bit header to 64-bit. + return Elf64_Shdr{ + .sh_name = shdr.sh_name, + .sh_type = shdr.sh_type, + .sh_flags = shdr.sh_flags, + .sh_addr = shdr.sh_addr, + .sh_offset = shdr.sh_offset, + .sh_size = shdr.sh_size, + .sh_link = shdr.sh_link, + .sh_info = shdr.sh_info, + .sh_addralign = shdr.sh_addralign, + .sh_entsize = shdr.sh_entsize, + }; +>>>>>>> origin/master } var shdr: Elf32_Shdr = undefined; diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 33c0616898bb7d5477f06d232af0cd36f6660bc3..0c51d56e30b42c2105ff2058fc50bf378cee8efb 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -7,7 +7,6 @@ const io = std.io; const math = std.math; const assert = std.debug.assert; const mem = std.mem; -const unicode = std.unicode; const meta = std.meta; const lossyCast = math.lossyCast; const expectFmt = std.testing.expectFmt; @@ -25,10 +24,12 @@ pub const Alignment = enum { right, }; +pub const Case = enum { lower, upper }; + const default_alignment = .right; const default_fill_char = ' '; -/// Deprecated; to be removed after 0.14.0 is tagged. +/// Deprecated in favor of `Options`. pub const FormatOptions = Options; pub const Options = struct { @@ -36,229 +37,78 @@ pub const Options = struct { width: ?usize = null, alignment: Alignment = default_alignment, fill: u8 = default_fill_char, -}; - -/// Renders fmt string with args, calling `writer` with slices of bytes. -/// If `writer` returns an error, the error is returned from `format` and -/// `writer` is not called again. -/// -/// The format string must be comptime-known and may contain placeholders following -/// this format: -/// `{[argument][specifier]:[fill][alignment][width].[precision]}` -/// -/// Above, each word including its surrounding [ and ] is a parameter which you have to replace with something: -/// -/// - *argument* is either the numeric index or the field name of the argument that should be inserted -/// - when using a field name, you are required to enclose the field name (an identifier) in square -/// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...} -/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) -/// - *fill* is a single byte which is used to pad the formatted text -/// - *alignment* is one of the three bytes '<', '^', or '>' to make the text left-, center-, or right-aligned, respectively -/// - *width* is the total width of the field in bytes. This is generally only -/// useful for ASCII text, such as numbers. -/// - *precision* specifies how many decimals a formatted number should have -/// -/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when -/// all parameters after the separator are omitted. -/// Only exception is the *fill* parameter. If a non-zero *fill* character is required at the same time as *width* is specified, -/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*. -/// -/// The *specifier* has several options for types: -/// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes -/// - `s`: -/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination -/// - for slices of u8, print the entire slice as a string without zero-termination -/// - `b64`: output string as standard base64 -/// - `e`: output floating point value in scientific notation -/// - `d`: output numeric value in decimal notation -/// - `b`: output integer value in binary notation -/// - `o`: output integer value in octal notation -/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. -/// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max. -/// - `D`: output nanoseconds as duration -/// - `B`: output bytes in SI units (decimal) -/// - `Bi`: output bytes in IEC units (binary) -/// - `?`: output optional value as either the unwrapped value, or `null`; may be followed by a format specifier for the underlying value. -/// - `!`: 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. -/// - `*`: output the address of the value instead of the value itself. -/// - `any`: output a value of any type using its default format. -/// -/// If a formatted user type contains a function of the type -/// ``` -/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype) !void -/// ``` -/// with `?` being the type formatted, this function will be called instead of the default implementation. -/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type. -/// -/// A user type may be a `struct`, `vector`, `union` or `enum` type. -/// -/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`. -pub fn format(bw: *Writer, comptime fmt: []const u8, args: anytype) Writer.Error!void { - const ArgsType = @TypeOf(args); - const args_type_info = @typeInfo(ArgsType); - if (args_type_info != .@"struct") { - @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); - } - - const fields_info = args_type_info.@"struct".fields; - if (fields_info.len > max_format_args) { - @compileError("32 arguments max are supported per format call"); - } - - @setEvalBranchQuota(2000000); - comptime var arg_state: ArgState = .{ .args_len = fields_info.len }; - comptime var i = 0; - comptime var literal: []const u8 = ""; - inline while (true) { - const start_index = i; - - inline while (i < fmt.len) : (i += 1) { - switch (fmt[i]) { - '{', '}' => break, - else => {}, - } - } - - comptime var end_index = i; - comptime var unescape_brace = false; - - // Handle {{ and }}, those are un-escaped as single braces - if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) { - unescape_brace = true; - // Make the first brace part of the literal... - end_index += 1; - // ...and skip both - i += 2; - } - - literal = literal ++ fmt[start_index..end_index]; - - // We've already skipped the other brace, restart the loop - if (unescape_brace) continue; - - // Write out the literal - if (literal.len != 0) { - try bw.writeAll(literal); - literal = ""; - } - - if (i >= fmt.len) break; - if (fmt[i] == '}') { - @compileError("missing opening {"); - } - - // Get past the { - comptime assert(fmt[i] == '{'); - i += 1; - - const fmt_begin = i; - // Find the closing brace - inline while (i < fmt.len and fmt[i] != '}') : (i += 1) {} - const fmt_end = i; - - if (i >= fmt.len) { - @compileError("missing closing }"); - } - - // Get past the } - comptime assert(fmt[i] == '}'); - i += 1; - - const placeholder = comptime Placeholder.parse(fmt[fmt_begin..fmt_end].*); - const arg_pos = comptime switch (placeholder.arg) { - .none => null, - .number => |pos| pos, - .named => |arg_name| meta.fieldIndex(ArgsType, arg_name) orelse - @compileError("no argument with name '" ++ arg_name ++ "'"), - }; - - const width = switch (placeholder.width) { - .none => null, - .number => |v| v, - .named => |arg_name| blk: { - const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse - @compileError("no argument with name '" ++ arg_name ++ "'"); - _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); - break :blk @field(args, arg_name); - }, - }; - - const precision = switch (placeholder.precision) { - .none => null, - .number => |v| v, - .named => |arg_name| blk: { - const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse - @compileError("no argument with name '" ++ arg_name ++ "'"); - _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); - break :blk @field(args, arg_name); - }, + pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number { + return .{ + .mode = mode, + .case = case, + .precision = o.precision, + .width = o.width, + .alignment = o.alignment, + .fill = o.fill, }; - - const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse - @compileError("too few arguments"); - - try bw.printValue( - placeholder.specifier_arg, - .{ - .fill = placeholder.fill, - .alignment = placeholder.alignment, - .width = width, - .precision = precision, - }, - @field(args, fields_info[arg_to_print].name), - std.options.fmt_max_depth, - ); } +}; - if (comptime arg_state.hasUnusedArgs()) { - const missing_count = arg_state.args_len - @popCount(arg_state.used_args); - switch (missing_count) { - 0 => unreachable, - 1 => @compileError("unused argument in '" ++ fmt ++ "'"), - else => @compileError(comptimePrint("{d}", .{missing_count}) ++ " unused arguments in '" ++ fmt ++ "'"), +pub const Number = struct { + mode: Mode = .decimal, + /// Affects hex digits as well as floating point "inf"/"INF". + case: Case = .lower, + precision: ?usize = null, + width: ?usize = null, + alignment: Alignment = default_alignment, + fill: u8 = default_fill_char, + + pub const Mode = enum { + decimal, + binary, + octal, + hex, + scientific, + + pub fn base(mode: Mode) ?u8 { + return switch (mode) { + .decimal => 10, + .binary => 2, + .octal => 8, + .hex => 16, + .scientific => null, + }; } - } -} + }; +}; -fn cacheString(str: anytype) []const u8 { - return &str; +/// Deprecated in favor of `Writer.print`. +pub fn format(writer: anytype, comptime fmt: []const u8, args: anytype) !void { + var adapter = writer.adaptToNewApi(); + return adapter.new_interface.print(fmt, args) catch |err| switch (err) { + error.WriteFailed => return adapter.err.?, + }; } pub const Placeholder = struct { specifier_arg: []const u8, - fill: u21, + fill: u8, alignment: Alignment, arg: Specifier, width: Specifier, precision: Specifier, - pub fn parse(comptime str: anytype) Placeholder { - const view = std.unicode.Utf8View.initComptime(&str); - comptime var parser = Parser{ - .iter = view.iterator(), - }; - - // Parse the positional argument number - const arg = comptime parser.specifier() catch |err| - @compileError(@errorName(err)); - - // Parse the format specifier - const specifier_arg = comptime parser.until(':'); - - // Skip the colon, if present - if (comptime parser.char()) |ch| { - if (ch != ':') { - @compileError("expected : or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'"); - } + pub fn parse(comptime bytes: []const u8) Placeholder { + var parser: Parser = .{ .bytes = bytes, .i = 0 }; + const arg = parser.specifier() catch |err| @compileError(@errorName(err)); + const specifier_arg = parser.until(':'); + if (parser.char()) |b| { + if (b != ':') @compileError("expected : or }, found '" ++ &[1]u8{b} ++ "'"); } - // Parse the fill character, if present. - // When the width field is also specified, the fill character must + // Parse the fill byte, if present. + // + // When the width field is also specified, the fill byte must // be followed by an alignment specifier, unless it's '0' (zero) - // (in which case it's handled as part of the width specifier) - var fill: ?u21 = comptime if (parser.peek(1)) |ch| - switch (ch) { + // (in which case it's handled as part of the width specifier). + var fill: ?u8 = if (parser.peek(1)) |b| + switch (b) { '<', '^', '>' => parser.char(), else => null, } @@ -266,8 +116,8 @@ pub const Placeholder = struct { null; // Parse the alignment parameter - const alignment: ?Alignment = comptime if (parser.peek(0)) |ch| init: { - switch (ch) { + const alignment: ?Alignment = if (parser.peek(0)) |b| init: { + switch (b) { '<', '^', '>' => { // consume the character break :init switch (parser.char().?) { @@ -283,30 +133,26 @@ pub const Placeholder = struct { // When none of the fill character and the alignment specifier have // been provided, check whether the width starts with a zero. if (fill == null and alignment == null) { - fill = comptime if (parser.peek(0) == '0') '0' else null; + fill = if (parser.peek(0) == '0') '0' else null; } // Parse the width parameter - const width = comptime parser.specifier() catch |err| - @compileError(@errorName(err)); + const width = parser.specifier() catch |err| @compileError(@errorName(err)); // Skip the dot, if present - if (comptime parser.char()) |ch| { - if (ch != '.') { - @compileError("expected . or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'"); - } + if (parser.char()) |b| { + if (b != '.') @compileError("expected . or }, found '" ++ &[1]u8{b} ++ "'"); } // Parse the precision parameter - const precision = comptime parser.specifier() catch |err| - @compileError(@errorName(err)); + const precision = parser.specifier() catch |err| @compileError(@errorName(err)); - if (comptime parser.char()) |ch| { - @compileError("extraneous trailing character '" ++ unicode.utf8EncodeComptime(ch) ++ "'"); - } + if (parser.char()) |b| @compileError("extraneous trailing character '" ++ &[1]u8{b} ++ "'"); + + const specifier_array = specifier_arg[0..specifier_arg.len].*; return .{ - .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), + .specifier_arg = &specifier_array, .fill = fill orelse default_fill_char, .alignment = alignment orelse default_alignment, .arg = arg, @@ -327,93 +173,64 @@ pub const Specifier = union(enum) { /// Allows to implement formatters compatible with std.fmt without replicating /// the standard library behavior. pub const Parser = struct { - iter: std.unicode.Utf8Iterator, + bytes: []const u8, + i: usize, - // Returns a decimal number or null if the current character is not a - // digit pub fn number(self: *@This()) ?usize { var r: ?usize = null; - - while (self.peek(0)) |code_point| { - switch (code_point) { + while (self.peek(0)) |byte| { + switch (byte) { '0'...'9' => { if (r == null) r = 0; r.? *= 10; - r.? += code_point - '0'; + r.? += byte - '0'; }, else => break, } - _ = self.iter.nextCodepoint(); + self.i += 1; } - return r; } - // Returns a substring of the input starting from the current position - // and ending where `ch` is found or until the end if not found - pub fn until(self: *@This(), ch: u21) []const u8 { - const start = self.iter.i; - while (self.peek(0)) |code_point| { - if (code_point == ch) - break; - _ = self.iter.nextCodepoint(); - } - return self.iter.bytes[start..self.iter.i]; + pub fn until(self: *@This(), delimiter: u8) []const u8 { + const start = self.i; + self.i = std.mem.indexOfScalarPos(u8, self.bytes, self.i, delimiter) orelse self.bytes.len; + return self.bytes[start..self.i]; } - // Returns the character pointed to by the iterator if available, or - // null otherwise - pub fn char(self: *@This()) ?u21 { - if (self.iter.nextCodepoint()) |code_point| { - return code_point; - } - return null; + pub fn char(self: *@This()) ?u8 { + const i = self.i; + if (self.bytes.len - i == 0) return null; + self.i = i + 1; + return self.bytes[i]; } - // Returns true if the iterator points to an existing character and - // false otherwise - pub fn maybe(self: *@This(), val: u21) bool { - if (self.peek(0) == val) { - _ = self.iter.nextCodepoint(); + pub fn maybe(self: *@This(), byte: u8) bool { + if (self.peek(0) == byte) { + self.i += 1; return true; } return false; } - // Returns a decimal number or null if the current character is not a - // digit pub fn specifier(self: *@This()) !Specifier { if (self.maybe('[')) { const arg_name = self.until(']'); - - if (!self.maybe(']')) - return @field(anyerror, "Expected closing ]"); - - return Specifier{ .named = arg_name }; + if (!self.maybe(']')) return error.@"Expected closing ]"; + return .{ .named = arg_name }; } - if (self.number()) |i| - return Specifier{ .number = i }; - - return Specifier{ .none = {} }; + if (self.number()) |i| return .{ .number = i }; + return .{ .none = {} }; } - // Returns the n-th next character or null if that's past the end - pub fn peek(self: *@This(), n: usize) ?u21 { - const original_i = self.iter.i; - defer self.iter.i = original_i; - - var i: usize = 0; - var code_point: ?u21 = null; - while (i <= n) : (i += 1) { - code_point = self.iter.nextCodepoint(); - if (code_point == null) return null; - } - return code_point; + pub fn peek(self: *@This(), i: usize) ?u8 { + const peek_index = self.i + i; + if (peek_index >= self.bytes.len) return null; + return self.bytes[peek_index]; } }; pub const ArgSetType = u32; -const max_format_args = @typeInfo(ArgSetType).int.bits; pub const ArgState = struct { next_arg: usize = 0, @@ -441,63 +258,12 @@ pub const ArgState = struct { } }; -test { - _ = float; -} - -pub const Case = enum { lower, upper }; - -fn SliceEscape(comptime case: Case) type { - const charset = "0123456789" ++ if (case == .upper) "ABCDEF" else "abcdef"; - - return struct { - pub fn format( - bytes: []const u8, - bw: *Writer, - comptime fmt: []const u8, - ) !void { - _ = fmt; - var buf: [4]u8 = undefined; - - buf[0] = '\\'; - buf[1] = 'x'; - - for (bytes) |c| { - if (std.ascii.isPrint(c)) { - try bw.writeByte(c); - } else { - buf[2] = charset[c >> 4]; - buf[3] = charset[c & 15]; - try bw.writeAll(&buf); - } - } - } - }; -} - -const formatSliceEscapeLower = SliceEscape(.lower).format; -const formatSliceEscapeUpper = SliceEscape(.upper).format; - -/// Return a Formatter for a []const u8 where every non-printable ASCII -/// character is escaped as \xNN, where NN is the character in lowercase -/// hexadecimal notation. -pub fn fmtSliceEscapeLower(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeLower) { - return .{ .data = bytes }; -} - -/// Return a Formatter for a []const u8 where every non-printable ASCII -/// character is escaped as \xNN, where NN is the character in uppercase -/// hexadecimal notation. -pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeUpper) { - return .{ .data = bytes }; -} - /// Asserts the rendered integer value fits in `buffer`. /// Returns the end index within `buffer`. pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize { - var bw: Writer = .fixed(buffer); - bw.printIntOptions(value, base, case, options) catch unreachable; - return bw.end; + var w: Writer = .fixed(buffer); + w.printInt(value, base, case, options) catch unreachable; + return w.end; } /// Converts values in the range [0, 100) to a base 10 string. @@ -509,35 +275,49 @@ pub fn digits2(value: u8) [2]u8 { } } +/// Deprecated in favor of `Alt`. +pub const Formatter = Alt; + +/// Creates a type suitable for instantiating and passing to a "{f}" placeholder. +pub fn Alt( + comptime Data: type, + comptime formatFn: fn (data: Data, writer: *Writer) Writer.Error!void, +) type { + return struct { + data: Data, + pub inline fn format(self: @This(), writer: *Writer) Writer.Error!void { + try formatFn(self.data, writer); + } + }; +} + +/// Helper for calling alternate format methods besides one named "format". +pub fn alt( + context: anytype, + comptime func_name: @TypeOf(.enum_literal), +) Formatter(@TypeOf(context), @field(@TypeOf(context), @tagName(func_name))) { + return .{ .data = context }; +} + +test alt { + const Example = struct { + number: u8, + + pub fn other(ex: @This(), w: *Writer) Writer.Error!void { + try w.writeByte(ex.number); + } + }; + const ex: Example = .{ .number = 'a' }; + try expectFmt("a", "{f}", .{alt(ex, .other)}); +} + pub const ParseIntError = error{ - /// The result cannot fit in the type specified + /// The result cannot fit in the type specified. Overflow, - - /// The input was empty or contained an invalid character + /// The input was empty or contained an invalid character. InvalidCharacter, }; -/// Creates a Formatter type from a format function. Wrapping data in Formatter(func) causes -/// the data to be formatted using the given function `func`. `func` must be of the following -/// form: -/// -/// fn formatExample( -/// data: T, -/// comptime fmt: []const u8, -/// options: std.fmt.Options, -/// writer: anytype, -/// ) !void; -/// -pub fn Formatter(comptime formatFn: anytype) type { - const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?; - return struct { - data: Data, - pub fn format(self: @This(), writer: *Writer, comptime fmt: []const u8) Writer.Error!void { - try formatFn(self.data, writer, fmt); - } - }; -} - /// Parses the string `buf` as signed or unsigned representation in the /// specified base of an integral value of type `T`. /// @@ -845,17 +625,17 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr /// Count the characters needed for format. pub fn count(comptime fmt: []const u8, args: anytype) usize { var trash_buffer: [64]u8 = undefined; - var w: Writer = .discarding(&trash_buffer); - w.print(fmt, args) catch |err| switch (err) { + var dw: Writer.Discarding = .init(&trash_buffer); + dw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => unreachable, }; - return w.count; + return @intCast(dw.count + dw.writer.end); } pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 { - var aw = try std.io.Writer.Allocating.initCapacity(gpa, fmt.len); + var aw = try Writer.Allocating.initCapacity(gpa, fmt.len); defer aw.deinit(); - aw.interface.print(fmt, args) catch |err| switch (err) { + aw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; return aw.toOwnedSlice(); @@ -867,9 +647,9 @@ pub fn allocPrintSentinel( args: anytype, comptime sentinel: u8, ) Allocator.Error![:sentinel]u8 { - var aw = try std.io.Writer.Allocating.initCapacity(gpa, fmt.len); + var aw = try Writer.Allocating.initCapacity(gpa, fmt.len); defer aw.deinit(); - aw.interface.print(fmt, args) catch |err| switch (err) { + aw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; return aw.toOwnedSliceSentinel(sentinel); @@ -1003,10 +783,6 @@ test "int.padded" { try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)}); try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)}); try expectFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)}); - - try expectFmt("UTF-8: 'ü '", "UTF-8: '{u:<4}'", .{'ü'}); - try expectFmt("UTF-8: ' ü'", "UTF-8: '{u:>4}'", .{'ü'}); - try expectFmt("UTF-8: ' ü '", "UTF-8: '{u:^4}'", .{'ü'}); } test "buffer" { @@ -1036,36 +812,24 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime } test "array" { - { - const value: [3]u8 = "abc".*; - try expectArrayFmt("array: abc\n", "array: {s}\n", value); - try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {d}\n", value); - try expectArrayFmt("array: { 61, 62, 63 }\n", "array: {x}\n", value); - try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); + const value: [3]u8 = "abc".*; + try expectArrayFmt("array: abc\n", "array: {s}\n", value); + try expectArrayFmt("array: 616263\n", "array: {x}\n", value); + try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); - var buf: [100]u8 = undefined; - try expectFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), - "array: {*}\n", - .{&value}, - ); - } - - { - const value = [2][3]u8{ "abc".*, "def".* }; - - try expectArrayFmt("array: { abc, def }\n", "array: {s}\n", value); - try expectArrayFmt("array: { { 97, 98, 99 }, { 100, 101, 102 } }\n", "array: {d}\n", value); - try expectArrayFmt("array: { { 61, 62, 63 }, { 64, 65, 66 } }\n", "array: {x}\n", value); - } + var buf: [100]u8 = undefined; + try expectFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), + "array: {*}\n", + .{&value}, + ); } test "slice" { { const value: []const u8 = "abc"; try expectFmt("slice: abc\n", "slice: {s}\n", .{value}); - try expectFmt("slice: { 97, 98, 99 }\n", "slice: {d}\n", .{value}); - try expectFmt("slice: { 61, 62, 63 }\n", "slice: {x}\n", .{value}); + try expectFmt("slice: 616263\n", "slice: {x}\n", .{value}); try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value}); } { @@ -1079,45 +843,33 @@ test "slice" { try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice}); } - try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); { var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; - var runtime_zero: usize = 0; - _ = &runtime_zero; - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); + const input: []const u32 = &int_slice; + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input}); } { const S1 = struct { x: u8, }; const struct_slice: []const S1 = &[_]S1{ S1{ .x = 8 }, S1{ .x = 42 } }; - try expectFmt("slice: { fmt.test.slice.S1{ .x = 8 }, fmt.test.slice.S1{ .x = 42 } }", "slice: {any}", .{struct_slice}); + try expectFmt("slice: { .{ .x = 8 }, .{ .x = 42 } }", "slice: {any}", .{struct_slice}); } { const S2 = struct { x: u8, - pub fn format(s: @This(), comptime _: []const u8, _: std.fmt.Options, writer: anytype) !void { + pub fn format(s: @This(), writer: *Writer) Writer.Error!void { try writer.print("S2({})", .{s.x}); } }; const struct_slice: []const S2 = &[_]S2{ S2{ .x = 8 }, S2{ .x = 42 } }; - try expectFmt("slice: { S2(8), S2(42) }", "slice: {any}", .{struct_slice}); + try expectFmt("slice: { .{ .x = 8 }, .{ .x = 42 } }", "slice: {any}", .{struct_slice}); } } -test "escape non-printable" { - try expectFmt("abc 123", "{s}", .{fmtSliceEscapeLower("abc 123")}); - try expectFmt("ab\\xffc", "{s}", .{fmtSliceEscapeLower("ab\xffc")}); - try expectFmt("abc 123", "{s}", .{fmtSliceEscapeUpper("abc 123")}); - try expectFmt("ab\\xFFc", "{s}", .{fmtSliceEscapeUpper("ab\xffc")}); -} - test "pointer" { { const value = @as(*align(1) i32, @ptrFromInt(0xdeadbeef)); @@ -1141,11 +893,6 @@ test "cstr" { "cstr: {s}\n", .{@as([*c]const u8, @ptrCast("Test C"))}, ); - try expectFmt( - "cstr: Test C\n", - "cstr: {s:10}\n", - .{@as([*c]const u8, @ptrCast("Test C"))}, - ); } test "struct" { @@ -1154,8 +901,8 @@ test "struct" { field: u8, }; const value = Struct{ .field = 42 }; - try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{value}); - try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); + try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{value}); + try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{&value}); } { const Struct = struct { @@ -1163,7 +910,7 @@ test "struct" { b: u1, }; const value = Struct{ .a = 0, .b = 1 }; - try expectFmt("struct: fmt.test.struct.Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); + try expectFmt("struct: .{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); } const S = struct { @@ -1176,11 +923,11 @@ test "struct" { .b = error.Unused, }; - try expectFmt("fmt.test.struct.S{ .a = 456, .b = error.Unused }", "{}", .{inst}); + try expectFmt(".{ .a = 456, .b = error.Unused }", "{}", .{inst}); // Tuples - try expectFmt("{ }", "{}", .{.{}}); - try expectFmt("{ -1 }", "{}", .{.{-1}}); - try expectFmt("{ -1, 42, 2.5e4 }", "{}", .{.{ -1, 42, 0.25e5 }}); + try expectFmt(".{ }", "{}", .{.{}}); + try expectFmt(".{ -1 }", "{}", .{.{-1}}); + try expectFmt(".{ -1, 42, 25000 }", "{}", .{.{ -1, 42, 0.25e5 }}); } test "enum" { @@ -1189,15 +936,15 @@ test "enum" { Two, }; const value = Enum.Two; - try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{value}); - try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{&value}); - try expectFmt("enum: fmt.test.enum.Enum.One\n", "enum: {}\n", .{Enum.One}); - try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{Enum.Two}); + try expectFmt("enum: .Two\n", "enum: {}\n", .{value}); + try expectFmt("enum: .Two\n", "enum: {}\n", .{&value}); + try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One}); + try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two}); // test very large enum to verify ct branch quota is large enough // TODO: https://github.com/ziglang/zig/issues/15609 if (!((builtin.cpu.arch == .wasm32) and builtin.mode == .Debug)) { - try expectFmt("enum: os.windows.win32error.Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION}); + try expectFmt("enum: .INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION}); } const E = enum { @@ -1208,7 +955,7 @@ test "enum" { const inst = E.Two; - try expectFmt("fmt.test.enum.E.Two", "{}", .{inst}); + try expectFmt(".Two", "{}", .{inst}); } test "non-exhaustive enum" { @@ -1217,13 +964,17 @@ test "non-exhaustive enum" { Two = 0xbeef, _, }; - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {}\n", .{Enum.One}); - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {}\n", .{Enum.Two}); - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))}); - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {x}\n", .{Enum.One}); - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {x}\n", .{Enum.Two}); - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {X}\n", .{Enum.Two}); - try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(1234)\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))}); + try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One}); + try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two}); + try expectFmt("enum: @enumFromInt(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))}); + try expectFmt("enum: f\n", "enum: {x}\n", .{Enum.One}); + try expectFmt("enum: beef\n", "enum: {x}\n", .{Enum.Two}); + try expectFmt("enum: BEEF\n", "enum: {X}\n", .{Enum.Two}); + try expectFmt("enum: 1234\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))}); + + try expectFmt("enum: 15\n", "enum: {d}\n", .{Enum.One}); + try expectFmt("enum: 48879\n", "enum: {d}\n", .{Enum.Two}); + try expectFmt("enum: 4660\n", "enum: {d}\n", .{@as(Enum, @enumFromInt(0x1234))}); } test "float.scientific" { @@ -1349,41 +1100,6 @@ test "float.libc.sanity" { try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1518338049))))}); } -test "custom" { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, - - pub fn format( - self: SelfType, - comptime fmt: []const u8, - options: Options, - writer: anytype, - ) !void { - _ = options; - if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { - return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y }); - } else if (comptime std.mem.eql(u8, fmt, "d")) { - return std.fmt.format(writer, "{d:.3}x{d:.3}", .{ self.x, self.y }); - } else { - @compileError("unknown format character: '" ++ fmt ++ "'"); - } - } - }; - - var value = Vec2{ - .x = 10.2, - .y = 2.22, - }; - try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); - try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); - - // same thing but not passing a pointer - try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); - try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); -} - test "union" { const TU = union(enum) { float: f32, @@ -1400,18 +1116,13 @@ test "union" { int: u32, }; - const tu_inst = TU{ .int = 123 }; - const uu_inst = UU{ .int = 456 }; - const eu_inst = EU{ .float = 321.123 }; + const tu_inst: TU = .{ .int = 123 }; + const uu_inst: UU = .{ .int = 456 }; + const eu_inst: EU = .{ .float = 321.123 }; - try expectFmt("fmt.test.union.TU{ .int = 123 }", "{}", .{tu_inst}); - - var buf: [100]u8 = undefined; - const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); - try std.testing.expectEqualStrings("fmt.test.union.UU@", uu_result[0..18]); - - const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); - try std.testing.expectEqualStrings("fmt.test.union.EU@", eu_result[0..18]); + try expectFmt(".{ .int = 123 }", "{}", .{tu_inst}); + try expectFmt(".{ ... }", "{}", .{uu_inst}); + try expectFmt(".{ .float = 321.123, .int = 1134596030 }", "{}", .{eu_inst}); } test "struct.self-referential" { @@ -1425,7 +1136,7 @@ test "struct.self-referential" { }; inst.a = &inst; - 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}); + try expectFmt(".{ .a = .{ .a = .{ .a = .{ ... } } } }", "{}", .{inst}); } test "struct.zero-size" { @@ -1440,7 +1151,7 @@ test "struct.zero-size" { const a = A{}; const b = B{ .a = a, .c = 0 }; - try expectFmt("fmt.test.struct.zero-size.B{ .a = fmt.test.struct.zero-size.A{ }, .c = 0 }", "{}", .{b}); + try expectFmt(".{ .a = .{ }, .c = 0 }", "{}", .{b}); } /// Encodes a sequence of bytes as hexadecimal digits. @@ -1551,33 +1262,17 @@ test "enum-literal" { test "padding" { try expectFmt("Simple", "{s}", .{"Simple"}); - try expectFmt(" true", "{:10}", .{true}); - try expectFmt(" true", "{:>10}", .{true}); - try expectFmt("======true", "{:=>10}", .{true}); - try expectFmt("true======", "{:=<10}", .{true}); - try expectFmt(" true ", "{:^10}", .{true}); - try expectFmt("===true===", "{:=^10}", .{true}); - try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"}); - try expectFmt("==================Filled", "{s:=>24}", .{"Filled"}); - try expectFmt(" Centered ", "{s:^24}", .{"Centered"}); - try expectFmt("-", "{s:-^1}", .{""}); - try expectFmt("==crêpe===", "{s:=^10}", .{"crêpe"}); - try expectFmt("=====crêpe", "{s:=>10}", .{"crêpe"}); - try expectFmt("crêpe=====", "{s:=<10}", .{"crêpe"}); + try expectFmt(" 1234", "{:10}", .{1234}); + try expectFmt(" 1234", "{:>10}", .{1234}); + try expectFmt("======1234", "{:=>10}", .{1234}); + try expectFmt("1234======", "{:=<10}", .{1234}); + try expectFmt(" 1234 ", "{:^10}", .{1234}); + try expectFmt("===1234===", "{:=^10}", .{1234}); try expectFmt("====a", "{c:=>5}", .{'a'}); try expectFmt("==a==", "{c:=^5}", .{'a'}); try expectFmt("a====", "{c:=<5}", .{'a'}); } -test "padding fill char utf" { - try expectFmt("──crêpe───", "{s:─^10}", .{"crêpe"}); - try expectFmt("─────crêpe", "{s:─>10}", .{"crêpe"}); - try expectFmt("crêpe─────", "{s:─<10}", .{"crêpe"}); - try expectFmt("────a", "{c:─>5}", .{'a'}); - try expectFmt("──a──", "{c:─^5}", .{'a'}); - try expectFmt("a────", "{c:─<5}", .{'a'}); -} - test "decimal float padding" { const number: f32 = 3.1415; try expectFmt("left-pad: **3.142\n", "left-pad: {d:*>7.3}\n", .{number}); @@ -1620,17 +1315,17 @@ test "named arguments" { test "runtime width specifier" { const width: usize = 9; - try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); - try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); - try expectFmt(" hello", "{s:[1]}", .{ "hello", width }); - try expectFmt("42 hello", "{d} {s:[2]}", .{ 42, "hello", width }); + try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width }); + try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width }); + try expectFmt(" 12345", "{d:[1]}", .{ 12345, width }); + try expectFmt("42 12345", "{d} {d:[2]}", .{ 42, 12345, width }); } test "runtime precision specifier" { const number: f32 = 3.1415; const precision: usize = 2; - try expectFmt("3.14e0", "{:1.[1]}", .{ number, precision }); - try expectFmt("3.14e0", "{:1.[precision]}", .{ .number = number, .precision = precision }); + try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision }); + try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision }); } test "recursive format function" { @@ -1639,16 +1334,16 @@ test "recursive format function" { Leaf: i32, Branch: struct { left: *const R, right: *const R }, - pub fn format(self: R, comptime _: []const u8, _: std.fmt.Options, writer: anytype) !void { + pub fn format(self: R, writer: *Writer) Writer.Error!void { return switch (self) { - .Leaf => |n| std.fmt.format(writer, "Leaf({})", .{n}), - .Branch => |b| std.fmt.format(writer, "Branch({}, {})", .{ b.left, b.right }), + .Leaf => |n| writer.print("Leaf({})", .{n}), + .Branch => |b| writer.print("Branch({f}, {f})", .{ b.left, b.right }), }; } }; - var r = R{ .Leaf = 1 }; - try expectFmt("Leaf(1)\n", "{}\n", .{&r}); + var r: R = .{ .Leaf = 1 }; + try expectFmt("Leaf(1)\n", "{f}\n", .{&r}); } pub const hex_charset = "0123456789abcdef"; @@ -1682,54 +1377,39 @@ test hex { test "parser until" { { // return substring till ':' - var parser: Parser = .{ - .iter = .{ .bytes = "abc:1234", .i = 0 }, - }; + var parser: Parser = .{ .bytes = "abc:1234", .i = 0 }; try testing.expectEqualStrings("abc", parser.until(':')); } { // return the entire string - `ch` not found - var parser: Parser = .{ - .iter = .{ .bytes = "abc1234", .i = 0 }, - }; + var parser: Parser = .{ .bytes = "abc1234", .i = 0 }; try testing.expectEqualStrings("abc1234", parser.until(':')); } { // substring is empty - `ch` is the only character - var parser: Parser = .{ - .iter = .{ .bytes = ":", .i = 0 }, - }; + var parser: Parser = .{ .bytes = ":", .i = 0 }; try testing.expectEqualStrings("", parser.until(':')); } { // empty string and `ch` not found - var parser: Parser = .{ - .iter = .{ .bytes = "", .i = 0 }, - }; + var parser: Parser = .{ .bytes = "", .i = 0 }; try testing.expectEqualStrings("", parser.until(':')); } { // substring starts at index 2 and goes upto `ch` - var parser: Parser = .{ - .iter = .{ .bytes = "abc:1234", .i = 2 }, - }; + var parser: Parser = .{ .bytes = "abc:1234", .i = 2 }; try testing.expectEqualStrings("c", parser.until(':')); } { // substring starts at index 4 and goes upto the end - `ch` not found - var parser: Parser = .{ - .iter = .{ .bytes = "abc1234", .i = 4 }, - }; + var parser: Parser = .{ .bytes = "abc1234", .i = 4 }; try testing.expectEqualStrings("234", parser.until(':')); } } test "parser peek" { { // start iteration from the first index - var parser: Parser = .{ - .iter = .{ .bytes = "hello world", .i = 0 }, - }; - + var parser: Parser = .{ .bytes = "hello world", .i = 0 }; try testing.expectEqual('h', parser.peek(0)); try testing.expectEqual('e', parser.peek(1)); try testing.expectEqual(' ', parser.peek(5)); @@ -1738,9 +1418,7 @@ test "parser peek" { } { // start iteration from the second last index - var parser: Parser = .{ - .iter = .{ .bytes = "hello world!", .i = 10 }, - }; + var parser: Parser = .{ .bytes = "hello world!", .i = 10 }; try testing.expectEqual('d', parser.peek(0)); try testing.expectEqual('!', parser.peek(1)); @@ -1748,18 +1426,14 @@ test "parser peek" { } { // start iteration beyond the length of the string - var parser: Parser = .{ - .iter = .{ .bytes = "hello", .i = 5 }, - }; + var parser: Parser = .{ .bytes = "hello", .i = 5 }; try testing.expectEqual(null, parser.peek(0)); try testing.expectEqual(null, parser.peek(1)); } { // empty string - var parser: Parser = .{ - .iter = .{ .bytes = "", .i = 0 }, - }; + var parser: Parser = .{ .bytes = "", .i = 0 }; try testing.expectEqual(null, parser.peek(0)); try testing.expectEqual(null, parser.peek(2)); @@ -1768,78 +1442,78 @@ test "parser peek" { test "parser char" { // character exists - iterator at 0 - var parser: Parser = .{ .iter = .{ .bytes = "~~hello", .i = 0 } }; + var parser: Parser = .{ .bytes = "~~hello", .i = 0 }; try testing.expectEqual('~', parser.char()); // character exists - iterator in the middle - parser = .{ .iter = .{ .bytes = "~~hello", .i = 3 } }; + parser = .{ .bytes = "~~hello", .i = 3 }; try testing.expectEqual('e', parser.char()); // character exists - iterator at the end - parser = .{ .iter = .{ .bytes = "~~hello", .i = 6 } }; + parser = .{ .bytes = "~~hello", .i = 6 }; try testing.expectEqual('o', parser.char()); // character doesn't exist - iterator beyond the length of the string - parser = .{ .iter = .{ .bytes = "~~hello", .i = 7 } }; + parser = .{ .bytes = "~~hello", .i = 7 }; try testing.expectEqual(null, parser.char()); } test "parser maybe" { // character exists - iterator at 0 - var parser: Parser = .{ .iter = .{ .bytes = "hello world", .i = 0 } }; + var parser: Parser = .{ .bytes = "hello world", .i = 0 }; try testing.expect(parser.maybe('h')); // character exists - iterator at space - parser = .{ .iter = .{ .bytes = "hello world", .i = 5 } }; + parser = .{ .bytes = "hello world", .i = 5 }; try testing.expect(parser.maybe(' ')); // character exists - iterator at the end - parser = .{ .iter = .{ .bytes = "hello world", .i = 10 } }; + parser = .{ .bytes = "hello world", .i = 10 }; try testing.expect(parser.maybe('d')); // character doesn't exist - iterator beyond the length of the string - parser = .{ .iter = .{ .bytes = "hello world", .i = 11 } }; + parser = .{ .bytes = "hello world", .i = 11 }; try testing.expect(!parser.maybe('e')); } test "parser number" { // input is a single digit natural number - iterator at 0 - var parser: Parser = .{ .iter = .{ .bytes = "7", .i = 0 } }; + var parser: Parser = .{ .bytes = "7", .i = 0 }; try testing.expect(7 == parser.number()); // input is a two digit natural number - iterator at 1 - parser = .{ .iter = .{ .bytes = "29", .i = 1 } }; + parser = .{ .bytes = "29", .i = 1 }; try testing.expect(9 == parser.number()); // input is a two digit natural number - iterator beyond the length of the string - parser = .{ .iter = .{ .bytes = "32", .i = 2 } }; + parser = .{ .bytes = "32", .i = 2 }; try testing.expectEqual(null, parser.number()); // input is an integer - parser = .{ .iter = .{ .bytes = "0", .i = 0 } }; + parser = .{ .bytes = "0", .i = 0 }; try testing.expect(0 == parser.number()); // input is a negative integer - parser = .{ .iter = .{ .bytes = "-2", .i = 0 } }; + parser = .{ .bytes = "-2", .i = 0 }; try testing.expectEqual(null, parser.number()); // input is a string - parser = .{ .iter = .{ .bytes = "no_number", .i = 2 } }; + parser = .{ .bytes = "no_number", .i = 2 }; try testing.expectEqual(null, parser.number()); // input is a single character string - parser = .{ .iter = .{ .bytes = "n", .i = 0 } }; + parser = .{ .bytes = "n", .i = 0 }; try testing.expectEqual(null, parser.number()); // input is an empty string - parser = .{ .iter = .{ .bytes = "", .i = 0 } }; + parser = .{ .bytes = "", .i = 0 }; try testing.expectEqual(null, parser.number()); } test "parser specifier" { { // input string is a digit; iterator at 0 const expected: Specifier = Specifier{ .number = 1 }; - var parser: Parser = .{ .iter = .{ .bytes = "1", .i = 0 } }; + var parser: Parser = .{ .bytes = "1", .i = 0 }; const result = try parser.specifier(); try testing.expect(expected.number == result.number); @@ -1847,7 +1521,7 @@ test "parser specifier" { { // input string is a two digit number; iterator at 0 const digit: Specifier = Specifier{ .number = 42 }; - var parser: Parser = .{ .iter = .{ .bytes = "42", .i = 0 } }; + var parser: Parser = .{ .bytes = "42", .i = 0 }; const result = try parser.specifier(); try testing.expect(digit.number == result.number); @@ -1855,7 +1529,7 @@ test "parser specifier" { { // input string is a two digit number digit; iterator at 1 const digit: Specifier = Specifier{ .number = 8 }; - var parser: Parser = .{ .iter = .{ .bytes = "28", .i = 1 } }; + var parser: Parser = .{ .bytes = "28", .i = 1 }; const result = try parser.specifier(); try testing.expect(digit.number == result.number); @@ -1863,7 +1537,7 @@ test "parser specifier" { { // input string is a two digit number with square brackets; iterator at 0 const digit: Specifier = Specifier{ .named = "15" }; - var parser: Parser = .{ .iter = .{ .bytes = "[15]", .i = 0 } }; + var parser: Parser = .{ .bytes = "[15]", .i = 0 }; const result = try parser.specifier(); try testing.expectEqualStrings(digit.named, result.named); @@ -1871,21 +1545,21 @@ test "parser specifier" { { // input string is not a number and contains square brackets; iterator at 0 const digit: Specifier = Specifier{ .named = "hello" }; - var parser: Parser = .{ .iter = .{ .bytes = "[hello]", .i = 0 } }; + var parser: Parser = .{ .bytes = "[hello]", .i = 0 }; const result = try parser.specifier(); try testing.expectEqualStrings(digit.named, result.named); } { // input string is not a number and doesn't contain closing square bracket; iterator at 0 - var parser: Parser = .{ .iter = .{ .bytes = "[hello", .i = 0 } }; + var parser: Parser = .{ .bytes = "[hello", .i = 0 }; const result = parser.specifier(); try testing.expectError(@field(anyerror, "Expected closing ]"), result); } { // input string is not a number and doesn't contain closing square bracket; iterator at 2 - var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello", .i = 2 } }; + var parser: Parser = .{ .bytes = "[[[[hello", .i = 2 }; const result = parser.specifier(); try testing.expectError(@field(anyerror, "Expected closing ]"), result); @@ -1893,7 +1567,7 @@ test "parser specifier" { { // input string is not a number and contains unbalanced square brackets; iterator at 0 const digit: Specifier = Specifier{ .named = "[[hello" }; - var parser: Parser = .{ .iter = .{ .bytes = "[[[hello]", .i = 0 } }; + var parser: Parser = .{ .bytes = "[[[hello]", .i = 0 }; const result = try parser.specifier(); try testing.expectEqualStrings(digit.named, result.named); @@ -1901,7 +1575,7 @@ test "parser specifier" { { // input string is not a number and contains unbalanced square brackets; iterator at 1 const digit: Specifier = Specifier{ .named = "[[hello" }; - var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]", .i = 1 } }; + var parser: Parser = .{ .bytes = "[[[[hello]]]]]", .i = 1 }; const result = try parser.specifier(); try testing.expectEqualStrings(digit.named, result.named); @@ -1909,9 +1583,13 @@ test "parser specifier" { { // input string is neither a digit nor a named argument const char: Specifier = Specifier{ .none = {} }; - var parser: Parser = .{ .iter = .{ .bytes = "hello", .i = 0 } }; + var parser: Parser = .{ .bytes = "hello", .i = 0 }; const result = try parser.specifier(); try testing.expectEqual(char.none, result.none); } } + +test { + _ = float; +} diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index d50ed6f109eaf09014f19f131fedef87c3dc51e9..c3cc767a00317243f91ddb45dc0ea2e201755e97 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1,3 +1,20 @@ +const builtin = @import("builtin"); +const Os = std.builtin.Os; +const native_os = builtin.os.tag; +const is_windows = native_os == .windows; + +const File = @This(); +const std = @import("../std.zig"); +const Allocator = std.mem.Allocator; +const posix = std.posix; +const io = std.io; +const math = std.math; +const assert = std.debug.assert; +const linux = std.os.linux; +const windows = std.os.windows; +const maxInt = std.math.maxInt; +const Alignment = std.mem.Alignment; + /// The OS-specific file descriptor or file handle. handle: Handle, @@ -844,7 +861,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize { return posix.write(self.handle, bytes); } -/// One-shot alternative to `std.io.Writer.writeAll` via `writer`. +/// Deprecated in favor of `Writer`. pub fn writeAll(self: File, bytes: []const u8) WriteError!void { var index: usize = 0; while (index < bytes.len) { @@ -900,6 +917,8 @@ pub const Reader = struct { file: File, err: ?ReadError = null, mode: Reader.Mode = .positional, + /// Tracks the true seek position in the file. To obtain the logical + /// position, subtract the buffer size from this value. pos: u64 = 0, size: ?u64 = null, size_err: ?GetEndPosError = null, @@ -1008,7 +1027,7 @@ pub const Reader = struct { }; var remaining = std.math.cast(u64, offset) orelse return seek_err; while (remaining > 0) { - const n = discard(&r.interface, .limited(remaining)) catch |err| { + const n = discard(&r.interface, .limited64(remaining)) catch |err| { r.seek_err = err; return err; }; @@ -1043,7 +1062,7 @@ pub const Reader = struct { const max_buffers_len = 16; fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize { - const r: *Reader = @fieldParentPtr("interface", io_reader); + const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); switch (r.mode) { .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { error.Unimplemented => { @@ -1067,10 +1086,14 @@ pub const Reader = struct { const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) { error.Unseekable => { r.mode = r.mode.toStreaming(); - if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch { - r.mode = .failure; - return error.ReadFailed; - }; + const pos = r.pos; + if (pos != 0) { + r.pos = 0; + r.seekBy(@intCast(pos)) catch { + r.mode = .failure; + return error.ReadFailed; + }; + } return 0; }, else => |e| { @@ -1113,7 +1136,7 @@ pub const Reader = struct { } fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize { - const r: *Reader = @fieldParentPtr("interface", io_reader); + const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); const file = r.file; const pos = r.pos; switch (r.mode) { @@ -1195,10 +1218,14 @@ pub const Reader = struct { const n = r.file.pread(dest, r.pos) catch |err| switch (err) { error.Unseekable => { r.mode = r.mode.toStreaming(); - if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch { - r.mode = .failure; - return error.ReadFailed; - }; + const pos = r.pos; + if (pos != 0) { + r.pos = 0; + r.seekBy(@intCast(pos)) catch { + r.mode = .failure; + return error.ReadFailed; + }; + } return 0; }, else => |e| { @@ -1246,6 +1273,8 @@ pub const Writer = struct { file: File, err: ?WriteError = null, mode: Writer.Mode = .positional, + /// Tracks the true seek position in the file. To obtain the logical + /// position, add the buffer size to this value. pos: u64 = 0, sendfile_err: ?SendfileError = null, copy_file_range_err: ?CopyFileRangeError = null, @@ -1308,110 +1337,162 @@ pub const Writer = struct { }; } - pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { - const w: *Writer = @fieldParentPtr("interface", io_writer); + pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { + const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); const handle = w.file.handle; - const buffered = io_writer.buffered(); - var splat_buffer: [256]u8 = undefined; - if (is_windows) { - var i: usize = 0; - while (i < buffered.len) { - const n = windows.WriteFile(handle, buffered[i..], null) catch |err| { + const buffered = io_w.buffered(); + if (is_windows) switch (w.mode) { + .positional, .positional_reading => { + if (buffered.len != 0) { + const n = windows.WriteFile(handle, buffered, w.pos) catch |err| { + w.err = err; + return error.WriteFailed; + }; + w.pos += n; + return io_w.consume(n); + } + for (data[0 .. data.len - 1]) |buf| { + if (buf.len == 0) continue; + const n = windows.WriteFile(handle, buf, w.pos) catch |err| { + w.err = err; + return error.WriteFailed; + }; + w.pos += n; + return io_w.consume(n); + } + const pattern = data[data.len - 1]; + if (pattern.len == 0 or splat == 0) return 0; + const n = windows.WriteFile(handle, pattern, w.pos) catch |err| { w.err = err; - w.pos += i; - _ = io_writer.consume(i); return error.WriteFailed; }; - i += n; - if (data.len > 0 and buffered.len - i < n) { - w.pos += i; - return io_writer.consume(i); + w.pos += n; + return io_w.consume(n); + }, + .streaming, .streaming_reading => { + if (buffered.len != 0) { + const n = windows.WriteFile(handle, buffered, null) catch |err| { + w.err = err; + return error.WriteFailed; + }; + w.pos += n; + return io_w.consume(n); + } + for (data[0 .. data.len - 1]) |buf| { + if (buf.len == 0) continue; + const n = windows.WriteFile(handle, buf, null) catch |err| { + w.err = err; + return error.WriteFailed; + }; + w.pos += n; + return io_w.consume(n); } - } - if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) { - w.pos += i; - return io_writer.consume(i); - } - const n = windows.WriteFile(handle, data[0], null) catch |err| { - w.err = err; - return 0; - }; - w.pos += n; - return n; - } - if (data.len == 0) { - var i: usize = 0; - while (i < buffered.len) { - i += std.posix.write(handle, buffered) catch |err| { + const pattern = data[data.len - 1]; + if (pattern.len == 0 or splat == 0) return 0; + const n = windows.WriteFile(handle, pattern, null) catch |err| { + std.debug.print("windows write file failed3: {t}\n", .{err}); w.err = err; - w.pos += i; - _ = io_writer.consume(i); return error.WriteFailed; }; - } - w.pos += i; - return io_writer.consumeAll(); - } + w.pos += n; + return io_w.consume(n); + }, + .failure => return error.WriteFailed, + }; var iovecs: [max_buffers_len]std.posix.iovec_const = undefined; var len: usize = 0; if (buffered.len > 0) { iovecs[len] = .{ .base = buffered.ptr, .len = buffered.len }; len += 1; } - for (data) |d| { + for (data[0 .. data.len - 1]) |d| { if (d.len == 0) continue; - if (iovecs.len - len == 0) break; iovecs[len] = .{ .base = d.ptr, .len = d.len }; len += 1; + if (iovecs.len - len == 0) break; } - switch (splat) { - 0 => if (data[data.len - 1].len != 0) { - len -= 1; + const pattern = data[data.len - 1]; + if (iovecs.len - len != 0) switch (splat) { + 0 => {}, + 1 => if (pattern.len != 0) { + iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len }; + len += 1; }, - 1 => {}, - else => switch (data[data.len - 1].len) { + else => switch (pattern.len) { 0 => {}, 1 => { + const splat_buffer_candidate = io_w.buffer[io_w.end..]; + var backup_buffer: [64]u8 = undefined; + const splat_buffer = if (splat_buffer_candidate.len >= backup_buffer.len) + splat_buffer_candidate + else + &backup_buffer; const memset_len = @min(splat_buffer.len, splat); const buf = splat_buffer[0..memset_len]; - @memset(buf, data[data.len - 1][0]); - iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len }; + @memset(buf, pattern[0]); + iovecs[len] = .{ .base = buf.ptr, .len = buf.len }; + len += 1; var remaining_splat = splat - buf.len; - while (remaining_splat > splat_buffer.len and len < iovecs.len) { - iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len }; + while (remaining_splat > splat_buffer.len and iovecs.len - len != 0) { + assert(buf.len == splat_buffer.len); + iovecs[len] = .{ .base = splat_buffer.ptr, .len = splat_buffer.len }; + len += 1; remaining_splat -= splat_buffer.len; - len += 1; } - if (remaining_splat > 0 and len < iovecs.len) { - iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat }; + if (remaining_splat > 0 and iovecs.len - len != 0) { + iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat }; len += 1; } - return std.posix.writev(handle, iovecs[0..len]) catch |err| { - w.err = err; - return error.WriteFailed; - }; }, - else => for (0..splat - 1) |_| { + else => for (0..splat) |_| { + iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len }; + len += 1; if (iovecs.len - len == 0) break; - iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].len }; - len += 1; }, }, - } - const n = std.posix.writev(handle, iovecs[0..len]) catch |err| { - w.err = err; - return error.WriteFailed; }; - w.pos += n; - return io_writer.consume(n); + if (len == 0) return 0; + switch (w.mode) { + .positional, .positional_reading => { + const n = std.posix.pwritev(handle, iovecs[0..len], w.pos) catch |err| switch (err) { + error.Unseekable => { + w.mode = w.mode.toStreaming(); + const pos = w.pos; + if (pos != 0) { + w.pos = 0; + w.seekTo(@intCast(pos)) catch { + w.mode = .failure; + return error.WriteFailed; + }; + } + return 0; + }, + else => |e| { + w.err = e; + return error.WriteFailed; + }, + }; + w.pos += n; + return io_w.consume(n); + }, + .streaming, .streaming_reading => { + const n = std.posix.writev(handle, iovecs[0..len]) catch |err| { + w.err = err; + return error.WriteFailed; + }; + w.pos += n; + return io_w.consume(n); + }, + .failure => return error.WriteFailed, + } } pub fn sendFile( - io_writer: *std.io.Writer, + io_w: *std.io.Writer, file_reader: *Reader, limit: std.io.Limit, ) std.io.Writer.FileError!usize { - const w: *Writer = @fieldParentPtr("interface", io_writer); + const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); const out_fd = w.file.handle; const in_fd = file_reader.file.handle; // TODO try using copy_file_range on FreeBSD @@ -1422,7 +1503,7 @@ pub const Writer = struct { if (w.sendfile_err != null) break :sf; // Linux sendfile does not support headers. const buffered = limit.slice(file_reader.interface.buffer); - if (io_writer.end != 0 or buffered.len != 0) return drain(io_writer, &.{buffered}, 1); + if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1); const max_count = 0x7ffff000; // Avoid EINVAL. var off: std.os.linux.off_t = undefined; const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) { @@ -1446,10 +1527,14 @@ pub const Writer = struct { const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) { error.Unseekable => { file_reader.mode = file_reader.mode.toStreaming(); - if (file_reader.pos != 0) file_reader.seekBy(@intCast(file_reader.pos)) catch { - file_reader.mode = .failure; - return error.ReadFailed; - }; + const pos = file_reader.pos; + if (pos != 0) { + file_reader.pos = 0; + file_reader.seekBy(@intCast(pos)) catch { + file_reader.mode = .failure; + return error.ReadFailed; + }; + } return 0; }, else => |e| { @@ -1465,21 +1550,21 @@ pub const Writer = struct { w.pos += n; return n; } - const copy_file_range_fn = switch (native_os) { + const copy_file_range = switch (native_os) { .freebsd => std.os.freebsd.copy_file_range, - .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else null, - else => null, + .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {}, + else => {}, }; - if (copy_file_range_fn) |copy_file_range| cfr: { + if (@TypeOf(copy_file_range) != void) cfr: { if (w.copy_file_range_err != null) break :cfr; const buffered = limit.slice(file_reader.interface.buffer); - if (io_writer.end != 0 or buffered.len != 0) return drain(io_writer, &.{buffered}, 1); + if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1); var off_in: i64 = undefined; var off_out: i64 = undefined; const off_in_ptr: ?*i64 = switch (file_reader.mode) { .positional_reading, .streaming_reading => return error.Unimplemented, .positional => p: { - off_in = file_reader.pos; + off_in = @intCast(file_reader.pos); break :p &off_in; }, .streaming => null, @@ -1488,7 +1573,7 @@ pub const Writer = struct { const off_out_ptr: ?*i64 = switch (w.mode) { .positional_reading, .streaming_reading => return error.Unimplemented, .positional => p: { - off_out = w.pos; + off_out = @intCast(w.pos); break :p &off_out; }, .streaming => null, @@ -1542,19 +1627,35 @@ pub const Writer = struct { } pub fn seekTo(w: *Writer, offset: u64) SeekError!void { - if (w.seek_err) |err| return err; switch (w.mode) { .positional, .positional_reading => { w.pos = offset; }, .streaming, .streaming_reading => { + if (w.seek_err) |err| return err; posix.lseek_SET(w.file.handle, offset) catch |err| { w.seek_err = err; return err; }; + w.pos = offset; }, + .failure => return w.seek_err.?, } } + + pub const EndError = SetEndPosError || std.io.Writer.Error; + + /// Flushes any buffered data and sets the end position of the file. + /// + /// If not overwriting existing contents, then calling `interface.flush` + /// directly is sufficient. + /// + /// Flush failure is handled by setting `err` so that it can be handled + /// along with other write failures. + pub fn end(w: *Writer) EndError!void { + try w.interface.flush(); + return w.file.setEndPos(w.pos); + } }; /// Defaults to positional reading; falls back to streaming. @@ -1568,9 +1669,10 @@ pub fn reader(file: File, buffer: []u8) Reader { /// Positional is more threadsafe, since the global seek position is not /// affected, but when such syscalls are not available, preemptively choosing /// `Reader.Mode.streaming` will skip a failed syscall. -pub fn readerStreaming(file: File) Reader { +pub fn readerStreaming(file: File, buffer: []u8) Reader { return .{ .file = file, + .interface = Reader.initInterface(buffer), .mode = .streaming, .seek_err = error.Unseekable, }; @@ -1753,20 +1855,3 @@ pub fn downgradeLock(file: File) LockError!void { }; } } - -const builtin = @import("builtin"); -const Os = std.builtin.Os; -const native_os = builtin.os.tag; -const is_windows = native_os == .windows; - -const File = @This(); -const std = @import("../std.zig"); -const Allocator = std.mem.Allocator; -const posix = std.posix; -const io = std.io; -const math = std.math; -const assert = std.debug.assert; -const linux = std.os.linux; -const windows = std.os.windows; -const maxInt = std.math.maxInt; -const Alignment = std.mem.Alignment; diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index f17c1553a55257c1e8d9a03be0f1754e77900867..1cf4dc3c64101b589ba95e659d7489bb490a9182 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -146,30 +146,28 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 { return out[0 .. out.len - 1 :0]; } -pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter(formatJoin) { +pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter([]const []const u8, formatJoin) { return .{ .data = paths }; } -fn formatJoin(paths: []const []const u8, bw: *std.io.Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); - +fn formatJoin(paths: []const []const u8, w: *std.io.Writer) std.io.Writer.Error!void { const first_path_idx = for (paths, 0..) |p, idx| { if (p.len != 0) break idx; } else return; - try bw.writeAll(paths[first_path_idx]); // first component + try w.writeAll(paths[first_path_idx]); // first component var prev_path = paths[first_path_idx]; for (paths[first_path_idx + 1 ..]) |this_path| { if (this_path.len == 0) continue; // skip empty components const prev_sep = isSep(prev_path[prev_path.len - 1]); const this_sep = isSep(this_path[0]); if (!prev_sep and !this_sep) { - try bw.writeByte(sep); + try w.writeByte(sep); } if (prev_sep and this_sep) { - try bw.writeAll(this_path[1..]); // skip redundant separator + try w.writeAll(this_path[1..]); // skip redundant separator } else { - try bw.writeAll(this_path); + try w.writeAll(this_path); } prev_path = this_path; } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 92902f1d723811ea23a28e3b743f2d6cd87e418a..ece7f79a742764de3d1601d9a53372135a028c32 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1798,11 +1798,11 @@ test "walker" { var num_walked: usize = 0; while (try walker.next()) |entry| { testing.expect(expected_basenames.has(entry.basename)) catch |err| { - std.debug.print("found unexpected basename: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.basename)}); + std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)}); return err; }; testing.expect(expected_paths.has(entry.path)) catch |err| { - std.debug.print("found unexpected path: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.path)}); + std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)}); return err; }; // make sure that the entry.dir is the containing dir diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 2f5332be9f9cbe1138655a2975a3c51f7fb4314f..51e4fe44a2ca4583081a06209c8776d7546c51ac 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -287,7 +287,7 @@ fn rawCAlloc( ) ?[*]u8 { _ = context; _ = return_address; - assert(alignment.compare(.lte, comptime .fromByteUnits(@alignOf(std.c.max_align_t)))); + assert(alignment.compare(.lte, .of(std.c.max_align_t))); // Note that this pointer cannot be aligncasted to max_align_t because if // len is < max_align_t then the alignment can be smaller. For example, if // max_align_t is 16, but the user requests 8 bytes, there is no built-in diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig index e3a76a06c14d01bbd77b47b6a36786a24aa8b4ae..130eae66f83987f316686fce45478f1422a9d861 100644 --- a/lib/std/heap/arena_allocator.zig +++ b/lib/std/heap/arena_allocator.zig @@ -42,7 +42,7 @@ pub const ArenaAllocator = struct { data: usize, node: std.SinglyLinkedList.Node = .{}, }; - const BufNode_alignment: Alignment = .fromByteUnits(@alignOf(BufNode)); + const BufNode_alignment: Alignment = .of(BufNode); pub fn init(child_allocator: Allocator) ArenaAllocator { return (State{}).promote(child_allocator); diff --git a/lib/std/heap/debug_allocator.zig b/lib/std/heap/debug_allocator.zig index 73721e810f225e9adf2f8e385aeba8831b8d0de3..df4bb76f0ff2249806d5f07d5a9b54bb69cf2233 100644 --- a/lib/std/heap/debug_allocator.zig +++ b/lib/std/heap/debug_allocator.zig @@ -1054,7 +1054,7 @@ const TraceKind = enum { free, }; -const test_config = Config{}; +const test_config: Config = .{}; test "small allocations - free in same order" { var gpa = DebugAllocator(test_config){}; diff --git a/lib/std/http.zig b/lib/std/http.zig index 51a4e2c318430c590144b23dc228ec1b84a70a5a..d3eac58d93d9130174cd979965326b01acc5e3fd 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -42,8 +42,8 @@ pub const Method = enum(u64) { return x; } - pub fn write(self: Method, w: anytype) !void { - const bytes = std.mem.asBytes(&@intFromEnum(self)); + pub fn format(self: Method, w: *std.io.Writer) std.io.Writer.Error!void { + const bytes: []const u8 = @ptrCast(&@intFromEnum(self)); const str = std.mem.sliceTo(bytes, 0); try w.writeAll(str); } diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 6ef90641b1ac21cdc9f1a344f41ad3c3c7beb62c..61a9eeb5c30d04eb3ef14c87d2f288fc7c9bb55a 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -920,7 +920,7 @@ pub const Request = struct { .authority = connection.proxied, .path = true, .query = true, - }, w); + }); } try w.writeByte(' '); try w.writeAll(@tagName(r.version)); @@ -1280,9 +1280,18 @@ pub const basic_authorization = struct { } pub fn valueLengthFromUri(uri: Uri) usize { - // TODO don't abuse formatted printing to count percent encoded characters - const user_len = std.fmt.count("{fuser}", .{uri.user orelse Uri.Component.empty}); - const password_len = std.fmt.count("{fpassword}", .{uri.password orelse Uri.Component.empty}); + const user: Uri.Component = uri.user orelse .empty; + const password: Uri.Component = uri.password orelse .empty; + + var dw: std.io.Writer.Discarding = .init(&.{}); + user.formatUser(&dw.writer) catch unreachable; // discarding + const user_len = dw.count + dw.writer.end; + + dw.count = 0; + dw.writer.end = 0; + password.formatPassword(&dw.writer) catch unreachable; // discarding + const password_len = dw.count + dw.writer.end; + return valueLength(@intCast(user_len), @intCast(password_len)); } diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index 914491f9d1177492101f956241e63fcca6c870de..130d2803d61ec5f7c5ae0c38dd88f9d854106a21 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -405,10 +405,8 @@ test "general client/server API coverage" { fn handleRequest(request: *http.Server.Request, listen_port: u16) !void { const log = std.log.scoped(.server); - log.info("{} {s} {s}", .{ - request.head.method, - @tagName(request.head.version), - request.head.target, + log.info("{f} {s} {s}", .{ + request.head.method, @tagName(request.head.version), request.head.target, }); const gpa = std.testing.allocator; diff --git a/lib/std/io.zig b/lib/std/io.zig index 8dcde790690eb90a2671931c2166b282f3b64271..d27338fe5f31bf50abab5626da13e6d7a9c5572d 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -19,6 +19,12 @@ pub const Limit = enum(usize) { return @enumFromInt(n); } + /// Any value grater than `std.math.maxInt(usize)` is interpreted to mean + /// `.unlimited`. + pub fn limited64(n: u64) Limit { + return @enumFromInt(@min(n, std.math.maxInt(usize))); + } + pub fn countVec(data: []const []const u8) Limit { var total: usize = 0; for (data) |d| total += d.len; @@ -33,6 +39,10 @@ pub const Limit = enum(usize) { return @min(n, @intFromEnum(l)); } + pub fn minInt64(l: Limit, n: u64) usize { + return @min(n, @intFromEnum(l)); + } + pub fn slice(l: Limit, s: []u8) []u8 { return s[0..l.minInt(s.len)]; } diff --git a/lib/std/io/DeprecatedReader.zig b/lib/std/io/DeprecatedReader.zig new file mode 100644 index 0000000000000000000000000000000000000000..3f2429c3aead2a048179dde86991f87b2f59cba8 --- /dev/null +++ b/lib/std/io/DeprecatedReader.zig @@ -0,0 +1,386 @@ +context: *const anyopaque, +readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize, + +pub const Error = anyerror; + +/// Returns the number of bytes read. It may be less than buffer.len. +/// If the number of bytes read is 0, it means end of stream. +/// End of stream is not an error condition. +pub fn read(self: Self, buffer: []u8) anyerror!usize { + return self.readFn(self.context, buffer); +} + +/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it +/// means the stream reached the end. Reaching the end of a stream is not an error +/// condition. +pub fn readAll(self: Self, buffer: []u8) anyerror!usize { + return readAtLeast(self, buffer, buffer.len); +} + +/// Returns the number of bytes read, calling the underlying read +/// function the minimal number of times until the buffer has at least +/// `len` bytes filled. If the number read is less than `len` it means +/// the stream reached the end. Reaching the end of the stream is not +/// an error condition. +pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize { + assert(len <= buffer.len); + var index: usize = 0; + while (index < len) { + const amt = try self.read(buffer[index..]); + if (amt == 0) break; + index += amt; + } + return index; +} + +/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead. +pub fn readNoEof(self: Self, buf: []u8) anyerror!void { + const amt_read = try self.readAll(buf); + if (amt_read < buf.len) return error.EndOfStream; +} + +/// Appends to the `std.ArrayList` contents by reading from the stream +/// until end of stream is found. +/// If the number of bytes appended would exceed `max_append_size`, +/// `error.StreamTooLong` is returned +/// and the `std.ArrayList` has exactly `max_append_size` bytes appended. +pub fn readAllArrayList( + self: Self, + array_list: *std.ArrayList(u8), + max_append_size: usize, +) anyerror!void { + return self.readAllArrayListAligned(null, array_list, max_append_size); +} + +pub fn readAllArrayListAligned( + self: Self, + comptime alignment: ?Alignment, + array_list: *std.ArrayListAligned(u8, alignment), + max_append_size: usize, +) anyerror!void { + try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); + const original_len = array_list.items.len; + var start_index: usize = original_len; + while (true) { + array_list.expandToCapacity(); + const dest_slice = array_list.items[start_index..]; + const bytes_read = try self.readAll(dest_slice); + start_index += bytes_read; + + if (start_index - original_len > max_append_size) { + array_list.shrinkAndFree(original_len + max_append_size); + return error.StreamTooLong; + } + + if (bytes_read != dest_slice.len) { + array_list.shrinkAndFree(start_index); + return; + } + + // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. + try array_list.ensureTotalCapacity(start_index + 1); + } +} + +/// Allocates enough memory to hold all the contents of the stream. If the allocated +/// memory would be greater than `max_size`, returns `error.StreamTooLong`. +/// Caller owns returned memory. +/// If this function returns an error, the contents from the stream read so far are lost. +pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 { + var array_list = std.ArrayList(u8).init(allocator); + defer array_list.deinit(); + try self.readAllArrayList(&array_list, max_size); + return try array_list.toOwnedSlice(); +} + +/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. +/// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found. +/// Does not include the delimiter in the result. +/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the +/// `std.ArrayList` is populated with `max_size` bytes from the stream. +pub fn readUntilDelimiterArrayList( + self: Self, + array_list: *std.ArrayList(u8), + delimiter: u8, + max_size: usize, +) anyerror!void { + array_list.shrinkRetainingCapacity(0); + try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); +} + +/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead. +/// Allocates enough memory to read until `delimiter`. If the allocated +/// memory would be greater than `max_size`, returns `error.StreamTooLong`. +/// Caller owns returned memory. +/// If this function returns an error, the contents from the stream read so far are lost. +pub fn readUntilDelimiterAlloc( + self: Self, + allocator: mem.Allocator, + delimiter: u8, + max_size: usize, +) anyerror![]u8 { + var array_list = std.ArrayList(u8).init(allocator); + defer array_list.deinit(); + try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size); + return try array_list.toOwnedSlice(); +} + +/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. +/// Reads from the stream until specified byte is found. If the buffer is not +/// large enough to hold the entire contents, `error.StreamTooLong` is returned. +/// If end-of-stream is found, `error.EndOfStream` is returned. +/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The +/// delimiter byte is written to the output buffer but is not included +/// in the returned slice. +pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 { + var fbs = std.io.fixedBufferStream(buf); + try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len); + const output = fbs.getWritten(); + buf[output.len] = delimiter; // emulating old behaviour + return output; +} + +/// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead. +/// Allocates enough memory to read until `delimiter` or end-of-stream. +/// If the allocated memory would be greater than `max_size`, returns +/// `error.StreamTooLong`. If end-of-stream is found, returns the rest +/// of the stream. If this function is called again after that, returns +/// null. +/// Caller owns returned memory. +/// If this function returns an error, the contents from the stream read so far are lost. +pub fn readUntilDelimiterOrEofAlloc( + self: Self, + allocator: mem.Allocator, + delimiter: u8, + max_size: usize, +) anyerror!?[]u8 { + var array_list = std.ArrayList(u8).init(allocator); + defer array_list.deinit(); + self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) { + error.EndOfStream => if (array_list.items.len == 0) { + return null; + }, + else => |e| return e, + }; + return try array_list.toOwnedSlice(); +} + +/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead. +/// Reads from the stream until specified byte is found. If the buffer is not +/// large enough to hold the entire contents, `error.StreamTooLong` is returned. +/// If end-of-stream is found, returns the rest of the stream. If this +/// function is called again after that, returns null. +/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The +/// delimiter byte is written to the output buffer but is not included +/// in the returned slice. +pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) anyerror!?[]u8 { + var fbs = std.io.fixedBufferStream(buf); + self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) { + error.EndOfStream => if (fbs.getWritten().len == 0) { + return null; + }, + + else => |e| return e, + }; + const output = fbs.getWritten(); + buf[output.len] = delimiter; // emulating old behaviour + return output; +} + +/// Appends to the `writer` contents by reading from the stream until `delimiter` is found. +/// Does not write the delimiter itself. +/// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`, +/// returns `error.StreamTooLong` and finishes appending. +/// If `optional_max_size` is null, appending is unbounded. +pub fn streamUntilDelimiter( + self: Self, + writer: anytype, + delimiter: u8, + optional_max_size: ?usize, +) anyerror!void { + if (optional_max_size) |max_size| { + for (0..max_size) |_| { + const byte: u8 = try self.readByte(); + if (byte == delimiter) return; + try writer.writeByte(byte); + } + return error.StreamTooLong; + } else { + while (true) { + const byte: u8 = try self.readByte(); + if (byte == delimiter) return; + try writer.writeByte(byte); + } + // Can not throw `error.StreamTooLong` since there are no boundary. + } +} + +/// Reads from the stream until specified byte is found, discarding all data, +/// including the delimiter. +/// If end-of-stream is found, this function succeeds. +pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void { + while (true) { + const byte = self.readByte() catch |err| switch (err) { + error.EndOfStream => return, + else => |e| return e, + }; + if (byte == delimiter) return; + } +} + +/// Reads 1 byte from the stream or returns `error.EndOfStream`. +pub fn readByte(self: Self) anyerror!u8 { + var result: [1]u8 = undefined; + const amt_read = try self.read(result[0..]); + if (amt_read < 1) return error.EndOfStream; + return result[0]; +} + +/// Same as `readByte` except the returned byte is signed. +pub fn readByteSigned(self: Self) anyerror!i8 { + return @as(i8, @bitCast(try self.readByte())); +} + +/// Reads exactly `num_bytes` bytes and returns as an array. +/// `num_bytes` must be comptime-known +pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 { + var bytes: [num_bytes]u8 = undefined; + try self.readNoEof(&bytes); + return bytes; +} + +/// Reads bytes until `bounded.len` is equal to `num_bytes`, +/// or the stream ends. +/// +/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()` +pub fn readIntoBoundedBytes( + self: Self, + comptime num_bytes: usize, + bounded: *std.BoundedArray(u8, num_bytes), +) anyerror!void { + while (bounded.len < num_bytes) { + // get at most the number of bytes free in the bounded array + const bytes_read = try self.read(bounded.unusedCapacitySlice()); + if (bytes_read == 0) return; + + // bytes_read will never be larger than @TypeOf(bounded.len) + // due to `self.read` being bounded by `bounded.unusedCapacitySlice()` + bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read)); + } +} + +/// Reads at most `num_bytes` and returns as a bounded array. +pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) { + var result = std.BoundedArray(u8, num_bytes){}; + try self.readIntoBoundedBytes(num_bytes, &result); + return result; +} + +pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { + const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8)); + return mem.readInt(T, &bytes, endian); +} + +pub fn readVarInt( + self: Self, + comptime ReturnType: type, + endian: std.builtin.Endian, + size: usize, +) anyerror!ReturnType { + assert(size <= @sizeOf(ReturnType)); + var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; + const bytes = bytes_buf[0..size]; + try self.readNoEof(bytes); + return mem.readVarInt(ReturnType, bytes, endian); +} + +/// Optional parameters for `skipBytes` +pub const SkipBytesOptions = struct { + buf_size: usize = 512, +}; + +// `num_bytes` is a `u64` to match `off_t` +/// Reads `num_bytes` bytes from the stream and discards them +pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void { + var buf: [options.buf_size]u8 = undefined; + var remaining = num_bytes; + + while (remaining > 0) { + const amt = @min(remaining, options.buf_size); + try self.readNoEof(buf[0..amt]); + remaining -= amt; + } +} + +/// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice +pub fn isBytes(self: Self, slice: []const u8) anyerror!bool { + var i: usize = 0; + var matches = true; + while (i < slice.len) : (i += 1) { + if (slice[i] != try self.readByte()) { + matches = false; + } + } + return matches; +} + +pub fn readStruct(self: Self, comptime T: type) anyerror!T { + // Only extern and packed structs have defined in-memory layout. + comptime assert(@typeInfo(T).@"struct".layout != .auto); + var res: [1]T = undefined; + try self.readNoEof(mem.sliceAsBytes(res[0..])); + return res[0]; +} + +pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { + var res = try self.readStruct(T); + if (native_endian != endian) { + mem.byteSwapAllFields(T, &res); + } + return res; +} + +/// Reads an integer with the same size as the given enum's tag type. If the integer matches +/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`. +/// TODO optimization taking advantage of most fields being in order +pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum { + const E = error{ + /// An integer was read, but it did not match any of the tags in the supplied enum. + InvalidValue, + }; + const type_info = @typeInfo(Enum).@"enum"; + const tag = try self.readInt(type_info.tag_type, endian); + + inline for (std.meta.fields(Enum)) |field| { + if (tag == field.value) { + return @field(Enum, field.name); + } + } + + return E.InvalidValue; +} + +/// Reads the stream until the end, ignoring all the data. +/// Returns the number of bytes discarded. +pub fn discard(self: Self) anyerror!u64 { + var trash: [4096]u8 = undefined; + var index: u64 = 0; + while (true) { + const n = try self.read(&trash); + if (n == 0) return index; + index += n; + } +} + +const std = @import("../std.zig"); +const Self = @This(); +const math = std.math; +const assert = std.debug.assert; +const mem = std.mem; +const testing = std.testing; +const native_endian = @import("builtin").target.cpu.arch.endian(); +const Alignment = std.mem.Alignment; + +test { + _ = @import("Reader/test.zig"); +} diff --git a/lib/std/io/DeprecatedWriter.zig b/lib/std/io/DeprecatedWriter.zig new file mode 100644 index 0000000000000000000000000000000000000000..391b9853570100e5adecc1e2b0d562372f911aa7 --- /dev/null +++ b/lib/std/io/DeprecatedWriter.zig @@ -0,0 +1,109 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const mem = std.mem; +const native_endian = @import("builtin").target.cpu.arch.endian(); + +context: *const anyopaque, +writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize, + +const Self = @This(); +pub const Error = anyerror; + +pub fn write(self: Self, bytes: []const u8) anyerror!usize { + return self.writeFn(self.context, bytes); +} + +pub fn writeAll(self: Self, bytes: []const u8) anyerror!void { + var index: usize = 0; + while (index != bytes.len) { + index += try self.write(bytes[index..]); + } +} + +pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void { + return std.fmt.format(self, format, args); +} + +pub fn writeByte(self: Self, byte: u8) anyerror!void { + const array = [1]u8{byte}; + return self.writeAll(&array); +} + +pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void { + var bytes: [256]u8 = undefined; + @memset(bytes[0..], byte); + + var remaining: usize = n; + while (remaining > 0) { + const to_write = @min(remaining, bytes.len); + try self.writeAll(bytes[0..to_write]); + remaining -= to_write; + } +} + +pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) anyerror!void { + var i: usize = 0; + while (i < n) : (i += 1) { + try self.writeAll(bytes); + } +} + +pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void { + var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined; + mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian); + return self.writeAll(&bytes); +} + +pub fn writeStruct(self: Self, value: anytype) anyerror!void { + // Only extern and packed structs have defined in-memory layout. + comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto); + return self.writeAll(mem.asBytes(&value)); +} + +pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void { + // TODO: make sure this value is not a reference type + if (native_endian == endian) { + return self.writeStruct(value); + } else { + var copy = value; + mem.byteSwapAllFields(@TypeOf(value), ©); + return self.writeStruct(copy); + } +} + +pub fn writeFile(self: Self, file: std.fs.File) anyerror!void { + // TODO: figure out how to adjust std lib abstractions so that this ends up + // doing sendfile or maybe even copy_file_range under the right conditions. + var buf: [4000]u8 = undefined; + while (true) { + const n = try file.readAll(&buf); + try self.writeAll(buf[0..n]); + if (n < buf.len) return; + } +} + +/// Helper for bridging to the new `Writer` API while upgrading. +pub fn adaptToNewApi(self: *const Self) Adapter { + return .{ + .derp_writer = self.*, + .new_interface = .{ + .buffer = &.{}, + .vtable = &.{ .drain = Adapter.drain }, + }, + }; +} + +pub const Adapter = struct { + derp_writer: Self, + new_interface: std.io.Writer, + err: ?Error = null, + + fn drain(w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { + _ = splat; + const a: *@This() = @fieldParentPtr("new_interface", w); + return a.derp_writer.write(data[0]) catch |err| { + a.err = err; + return error.WriteFailed; + }; + } +}; diff --git a/lib/std/io/Reader.zig b/lib/std/io/Reader.zig index d26c18955b754388dc71f6c996b4bd153a3e86e8..518fdf2568240e5f17725459cc56399ebf7b1bc1 100644 --- a/lib/std/io/Reader.zig +++ b/lib/std/io/Reader.zig @@ -26,7 +26,8 @@ pub const VTable = struct { /// Returns the number of bytes written, which will be at minimum `0` and /// at most `limit`. The number returned, including zero, does not indicate /// end of stream. `limit` is guaranteed to be at least as large as the - /// buffer capacity of `w`. + /// buffer capacity of `w`, a value whose minimum size is determined by the + /// stream implementation. /// /// The reader's internal logical seek position moves forward in accordance /// with the number of bytes returned from this function. @@ -35,7 +36,15 @@ pub const VTable = struct { /// sizes combined with short reads (returning a value less than `limit`) /// in order to minimize complexity. /// - /// This function is always called when `buffer` is empty. + /// Although this function is usually called when `buffer` is empty, it is + /// also called when it needs to be filled more due to the API user + /// requesting contiguous memory. In either case, the existing buffer data + /// should be ignored; new data written to `w`. + /// + /// In addition to, or instead of writing to `w`, the implementation may + /// choose to store data in `buffer`, modifying `seek` and `end` + /// accordingly. Stream implementations are encouraged to take advantage of + /// this if simplifies the logic. stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize, /// Consumes bytes from the internally tracked stream position without @@ -55,6 +64,8 @@ pub const VTable = struct { /// The default implementation is is based on calling `stream`, borrowing /// `buffer` to construct a temporary `Writer` and ignoring the written /// data. + /// + /// This function is only called when `buffer` is empty. discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard, }; @@ -102,7 +113,7 @@ const ending_state: Reader = .fixed(&.{}); pub const ending: *Reader = @constCast(&ending_state); pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited { - return Limited.init(r, limit, buffer); + return .init(r, limit, buffer); } /// 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 { r.seek += n; return n; } - const before = w.count; const n = try r.vtable.stream(r, w, limit); assert(n <= @intFromEnum(limit)); - assert(w.count == before + n); return n; } @@ -154,19 +163,13 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize { pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize { assert(r.seek == 0); assert(r.end == 0); - var w: Writer = .discarding(r.buffer); - const n = r.stream(&w, limit) catch |err| switch (err) { + var dw: Writer.Discarding = .init(r.buffer); + const n = r.stream(&dw.writer, limit) catch |err| switch (err) { error.WriteFailed => unreachable, error.ReadFailed => return error.ReadFailed, error.EndOfStream => return error.EndOfStream, }; - if (n > @intFromEnum(limit)) { - const over_amt = n - @intFromEnum(limit); - r.seek = w.end - over_amt; - r.end = w.end; - assert(r.end <= w.buffer.len); // limit may be exceeded only by an amount within buffer capacity. - return @intFromEnum(limit); - } + assert(n <= @intFromEnum(limit)); return n; } @@ -193,7 +196,7 @@ pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize { /// Consumes the stream until the end, ignoring all the data, returning the /// number of bytes discarded. pub fn discardRemaining(r: *Reader) ShortError!usize { - var offset: usize = r.end; + var offset: usize = r.end - r.seek; r.seek = 0; r.end = 0; while (true) { @@ -262,10 +265,9 @@ pub fn appendRemaining( error.EndOfStream => break, error.ReadFailed => return error.ReadFailed, }; - if (n >= dest.len) { + if (n > dest.len) { r.end = n - dest.len; list.items.len += dest.len; - if (n == dest.len) return; return error.StreamTooLong; } list.items.len += n; @@ -320,22 +322,29 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize { }, .writer = .{ .buffer = if (first.len >= r.buffer.len) first else r.buffer, - .vtable = &Writer.VectorWrapper.vtable, + .vtable = Writer.VectorWrapper.vtable, }, }; var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) { error.WriteFailed => { + assert(!wrapper.used); if (wrapper.writer.buffer.ptr == first.ptr) { remaining -= wrapper.writer.end; } else { + assert(wrapper.writer.end <= r.buffer.len); r.end = wrapper.writer.end; } break; }, else => |e| return e, }; - if (wrapper.writer.buffer.ptr != first.ptr) { - r.end = n; + if (!wrapper.used) { + if (wrapper.writer.buffer.ptr == first.ptr) { + remaining -= n; + } else { + assert(n <= r.buffer.len); + r.end = n; + } break; } if (n < first.len) { @@ -352,6 +361,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize { remaining -= mid.len; n -= mid.len; } + assert(n <= r.buffer.len); r.end = n; break; } @@ -441,7 +451,7 @@ pub fn toss(r: *Reader, n: usize) void { } /// Equivalent to `toss(r.bufferedLen())`. -pub fn tossAll(r: *Reader) void { +pub fn tossBuffered(r: *Reader) void { r.seek = 0; r.end = 0; } @@ -553,7 +563,7 @@ pub fn discardShort(r: *Reader, n: usize) ShortError!usize { /// See also: /// * `peek` /// * `readSliceShort` -pub fn readSlice(r: *Reader, buffer: []u8) Error!void { +pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void { const n = try readSliceShort(r, buffer); if (n != buffer.len) return error.EndOfStream; } @@ -567,7 +577,7 @@ pub fn readSlice(r: *Reader, buffer: []u8) Error!void { /// only if the stream reached the end. /// /// See also: -/// * `readSlice` +/// * `readSliceAll` pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { const in_buffer = r.buffer[r.seek..r.end]; const copy_len = @min(buffer.len, in_buffer.len); @@ -588,17 +598,16 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { }, .writer = .{ .buffer = if (remaining.len >= r.buffer.len) remaining else r.buffer, - .vtable = &Writer.VectorWrapper.vtable, + .vtable = Writer.VectorWrapper.vtable, }, }; const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) { error.WriteFailed => { - if (wrapper.writer.buffer.ptr != remaining.ptr) { + if (!wrapper.used) { assert(r.seek == 0); r.seek = remaining.len; r.end = wrapper.writer.end; @memcpy(remaining, r.buffer[0..remaining.len]); - return buffer.len; } return buffer.len; }, @@ -626,7 +635,7 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { /// comptime-known and matches host endianness. /// /// See also: -/// * `readSlice` +/// * `readSliceAll` /// * `readSliceEndianAlloc` pub inline fn readSliceEndian( r: *Reader, @@ -634,7 +643,7 @@ pub inline fn readSliceEndian( buffer: []Elem, endian: std.builtin.Endian, ) Error!void { - try readSlice(r, @ptrCast(buffer)); + try readSliceAll(r, @ptrCast(buffer)); if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem); } @@ -651,15 +660,16 @@ pub inline fn readSliceEndianAlloc( ) ReadAllocError![]Elem { const dest = try allocator.alloc(Elem, len); errdefer allocator.free(dest); - try readSlice(r, @ptrCast(dest)); + try readSliceAll(r, @ptrCast(dest)); if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem); return dest; } -pub fn readSliceAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 { +/// Shortcut for calling `readSliceAll` with a buffer provided by `allocator`. +pub fn readAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 { const dest = try allocator.alloc(u8, len); errdefer allocator.free(dest); - try readSlice(r, dest); + try readSliceAll(r, dest); return dest; } @@ -692,6 +702,17 @@ pub fn takeSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel return result; } +/// Returns a slice of the next bytes of buffered data from the stream until +/// `sentinel` is found, without advancing the seek position. +/// +/// Returned slice has a sentinel; end of stream does not count as a delimiter. +/// +/// Invalidates previously returned values from `peek`. +/// +/// See also: +/// * `takeSentinel` +/// * `peekDelimiterExclusive` +/// * `peekDelimiterInclusive` pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 { const result = try r.peekDelimiterInclusive(sentinel); return result[0 .. result.len - 1 :sentinel]; @@ -732,26 +753,21 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { @branchHint(.likely); return buffer[seek .. end + 1]; } - if (seek > 0) { - const remainder = buffer[seek..]; - @memmove(buffer[0..remainder.len], remainder); - r.end = remainder.len; - r.seek = 0; + if (r.vtable.stream == &endingStream) { + // Protect the `@constCast` of `fixed`. + return error.EndOfStream; } - var writer: Writer = .{ - .buffer = r.buffer, - .vtable = &.{ .drain = Writer.fixedDrain }, - }; - while (r.end < r.buffer.len) { - writer.end = r.end; - const n = r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { + r.rebase(); + while (r.buffer.len - r.end != 0) { + const end_cap = r.buffer[r.end..]; + var writer: Writer = .fixed(end_cap); + const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) { error.WriteFailed => unreachable, else => |e| return e, }; - const prev_end = r.end; - r.end = prev_end + n; - if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], prev_end, delimiter)) |end| { - return r.buffer[0 .. end + 1]; + r.end += n; + if (std.mem.indexOfScalarPos(u8, end_cap[0..n], 0, delimiter)) |end| { + return r.buffer[0 .. r.end - n + end + 1]; } } return error.StreamTooLong; @@ -777,9 +793,10 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) { error.EndOfStream => { - if (r.end == 0) return error.EndOfStream; - r.toss(r.end); - return r.buffer[0..r.end]; + const remaining = r.buffer[r.seek..r.end]; + if (remaining.len == 0) return error.EndOfStream; + r.toss(remaining.len); + return remaining; }, else => |e| return e, }; @@ -807,8 +824,10 @@ pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) { error.EndOfStream => { - if (r.end == 0) return error.EndOfStream; - return r.buffer[0..r.end]; + const remaining = r.buffer[r.seek..r.end]; + if (remaining.len == 0) return error.EndOfStream; + r.toss(remaining.len); + return remaining; }, else => |e| return e, }; @@ -818,37 +837,50 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { /// Appends to `w` contents by reading from the stream until `delimiter` is /// found. Does not write the delimiter itself. /// -/// Returns number of bytes streamed. -pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize { - const amount, const to = try r.readAny(w, delimiter, .unlimited); - return switch (to) { - .delimiter => amount, - .limit => unreachable, - .end => error.EndOfStream, +/// Returns number of bytes streamed, which may be zero, or error.EndOfStream +/// if the delimiter was not found. +/// +/// Asserts buffer capacity of at least one. This function performs better with +/// larger buffers. +/// +/// See also: +/// * `streamDelimiterEnding` +/// * `streamDelimiterLimit` +pub fn streamDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize { + const n = streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) { + error.StreamTooLong => unreachable, // unlimited is passed + else => |e| return e, }; + if (r.seek == r.end) return error.EndOfStream; + return n; } /// Appends to `w` contents by reading from the stream until `delimiter` is found. /// Does not write the delimiter itself. /// -/// Succeeds if stream ends before delimiter found. +/// Returns number of bytes streamed, which may be zero. End of stream can be +/// detected by checking if the next byte in the stream is the delimiter. /// -/// Returns number of bytes streamed. The end is not signaled to the writer. -pub fn readDelimiterEnding( +/// Asserts buffer capacity of at least one. This function performs better with +/// larger buffers. +/// +/// See also: +/// * `streamDelimiter` +/// * `streamDelimiterLimit` +pub fn streamDelimiterEnding( r: *Reader, w: *Writer, delimiter: u8, ) StreamRemainingError!usize { - const amount, const to = try r.readAny(w, delimiter, .unlimited); - return switch (to) { - .delimiter, .end => amount, - .limit => unreachable, + return streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) { + error.StreamTooLong => unreachable, // unlimited is passed + else => |e| return e, }; } -pub const StreamDelimiterLimitedError = StreamRemainingError || error{ - /// Stream ended before the delimiter was found. - EndOfStream, +pub const StreamDelimiterLimitError = error{ + ReadFailed, + WriteFailed, /// The delimiter was not found within the limit. StreamTooLong, }; @@ -856,65 +888,103 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{ /// Appends to `w` contents by reading from the stream until `delimiter` is found. /// Does not write the delimiter itself. /// -/// Returns number of bytes streamed. -pub fn readDelimiterLimit( +/// Returns number of bytes streamed, which may be zero. End of stream can be +/// detected by checking if the next byte in the stream is the delimiter. +/// +/// Asserts buffer capacity of at least one. This function performs better with +/// larger buffers. +pub fn streamDelimiterLimit( r: *Reader, w: *Writer, delimiter: u8, limit: Limit, -) StreamDelimiterLimitedError!usize { - const amount, const to = try r.readAny(w, delimiter, limit); - return switch (to) { - .delimiter => amount, - .limit => error.StreamTooLong, - .end => error.EndOfStream, - }; -} - -fn readAny( - r: *Reader, - w: *Writer, - delimiter: ?u8, - limit: Limit, -) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } { - var amount: usize = 0; - var remaining = limit; - while (remaining.nonzero()) { - const available = remaining.slice(r.peekGreedy(1) catch |err| switch (err) { - error.ReadFailed => |e| return e, - error.EndOfStream => return .{ amount, .end }, +) StreamDelimiterLimitError!usize { + var remaining = @intFromEnum(limit); + while (remaining != 0) { + const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) { + error.ReadFailed => return error.ReadFailed, + error.EndOfStream => return @intFromEnum(limit) - remaining, }); - if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| { + if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| { try w.writeAll(available[0..delimiter_index]); - r.toss(delimiter_index + 1); - return .{ amount + delimiter_index, .delimiter }; - }; + r.toss(delimiter_index); + remaining -= delimiter_index; + return @intFromEnum(limit) - remaining; + } try w.writeAll(available); r.toss(available.len); - amount += available.len; - remaining = remaining.subtract(available.len).?; + remaining -= available.len; } - return .{ amount, .limit }; + return error.StreamTooLong; } /// Reads from the stream until specified byte is found, discarding all data, /// including the delimiter. /// -/// If end of stream is found, this function succeeds. -pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void { - _ = r; - _ = delimiter; - @panic("TODO"); +/// Returns number of bytes discarded, or `error.EndOfStream` if the delimiter +/// is not found. +/// +/// See also: +/// * `discardDelimiterExclusive` +/// * `discardDelimiterLimit` +pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!usize { + const n = discardDelimiterLimit(r, delimiter, .unlimited) catch |err| switch (err) { + error.StreamTooLong => unreachable, // unlimited is passed + else => |e| return e, + }; + if (r.seek == r.end) return error.EndOfStream; + assert(r.buffer[r.seek] == delimiter); + toss(r, 1); + return n + 1; +} + +/// Reads from the stream until specified byte is found, discarding all data, +/// excluding the delimiter. +/// +/// Returns the number of bytes discarded. +/// +/// Succeeds if stream ends before delimiter found. End of stream can be +/// detected by checking if the delimiter is buffered. +/// +/// See also: +/// * `discardDelimiterInclusive` +/// * `discardDelimiterLimit` +pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!usize { + return discardDelimiterLimit(r, delimiter, .unlimited) catch |err| switch (err) { + error.StreamTooLong => unreachable, // unlimited is passed + else => |e| return e, + }; } +pub const DiscardDelimiterLimitError = error{ + ReadFailed, + /// The delimiter was not found within the limit. + StreamTooLong, +}; + /// Reads from the stream until specified byte is found, discarding all data, /// excluding the delimiter. /// -/// Succeeds if stream ends before delimiter found. -pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!void { - _ = r; - _ = delimiter; - @panic("TODO"); +/// Returns the number of bytes discarded. +/// +/// Succeeds if stream ends before delimiter found. End of stream can be +/// detected by checking if the delimiter is buffered. +pub fn discardDelimiterLimit(r: *Reader, delimiter: u8, limit: Limit) DiscardDelimiterLimitError!usize { + var remaining = @intFromEnum(limit); + while (remaining != 0) { + const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) { + error.ReadFailed => return error.ReadFailed, + error.EndOfStream => return @intFromEnum(limit) - remaining, + }); + if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| { + r.toss(delimiter_index); + remaining -= delimiter_index; + return @intFromEnum(limit) - remaining; + } + r.toss(available.len); + remaining -= available.len; + } + return error.StreamTooLong; } /// 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 { @branchHint(.likely); return; } + if (r.seek + n <= r.buffer.len) while (true) { + const end_cap = r.buffer[r.end..]; + var writer: Writer = .fixed(end_cap); + r.end += r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) { + error.WriteFailed => unreachable, + else => |e| return e, + }; + if (r.seek + n <= r.end) return; + }; + if (r.vtable.stream == &endingStream) { + // Protect the `@constCast` of `fixed`. + return error.EndOfStream; + } rebaseCapacity(r, n); var writer: Writer = .{ .buffer = r.buffer, @@ -970,11 +1053,12 @@ pub fn fillMore(r: *Reader) Error!void { pub fn peekByte(r: *Reader) Error!u8 { const buffer = r.buffer[0..r.end]; const seek = r.seek; - if (seek >= buffer.len) { - @branchHint(.unlikely); - try fill(r, 1); + if (seek < buffer.len) { + @branchHint(.likely); + return buffer[seek]; } - return buffer[seek]; + try fill(r, 1); + return r.buffer[r.seek]; } /// 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: /// /// See also: /// * `peekStruct` +/// * `takeStructEndian` pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T { // Only extern and packed structs have defined in-memory layout. comptime assert(@typeInfo(T).@"struct".layout != .auto); @@ -1021,6 +1106,7 @@ pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T { /// /// See also: /// * `takeStruct` +/// * `peekStructEndian` pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T { // Only extern and packed structs have defined in-memory layout. comptime assert(@typeInfo(T).@"struct".layout != .auto); @@ -1031,6 +1117,10 @@ pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T { /// /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` /// when `endian` is comptime-known and matches the host endianness. +/// +/// See also: +/// * `takeStruct` +/// * `peekStructEndian` pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { var res = (try r.takeStruct(T)).*; 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 /// /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` /// when `endian` is comptime-known and matches the host endianness. +/// +/// See also: +/// * `takeStructEndian` +/// * `peekStruct` pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { var res = (try r.peekStruct(T)).*; if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); @@ -1218,146 +1312,295 @@ test fixed { } test peek { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + try testing.expectEqualStrings("ab", try r.peek(2)); + try testing.expectEqualStrings("a", try r.peek(1)); } test peekGreedy { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + try testing.expectEqualStrings("abc", try r.peekGreedy(1)); } test toss { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + r.toss(1); + try testing.expectEqualStrings("bc", r.buffered()); } test take { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + try testing.expectEqualStrings("ab", try r.take(2)); + try testing.expectEqualStrings("c", try r.take(1)); } test takeArray { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + try testing.expectEqualStrings("ab", try r.takeArray(2)); + try testing.expectEqualStrings("c", try r.takeArray(1)); } test peekArray { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + try testing.expectEqualStrings("ab", try r.peekArray(2)); + try testing.expectEqualStrings("a", try r.peekArray(1)); } test discardAll { var r: Reader = .fixed("foobar"); - try r.discard(3); + try r.discardAll(3); try testing.expectEqualStrings("bar", try r.take(3)); - try r.discard(0); - try testing.expectError(error.EndOfStream, r.discard(1)); + try r.discardAll(0); + try testing.expectError(error.EndOfStream, r.discardAll(1)); } test discardRemaining { - return error.Unimplemented; + var r: Reader = .fixed("foobar"); + r.toss(1); + try testing.expectEqual(5, try r.discardRemaining()); + try testing.expectEqual(0, try r.discardRemaining()); } test stream { - return error.Unimplemented; + var out_buffer: [10]u8 = undefined; + var r: Reader = .fixed("foobar"); + var w: Writer = .fixed(&out_buffer); + // Short streams are possible with this function but not with fixed. + try testing.expectEqual(2, try r.stream(&w, .limited(2))); + try testing.expectEqualStrings("fo", w.buffered()); + try testing.expectEqual(4, try r.stream(&w, .unlimited)); + try testing.expectEqualStrings("foobar", w.buffered()); } test takeSentinel { - return error.Unimplemented; + var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab", try r.takeSentinel('\n')); + try testing.expectError(error.EndOfStream, r.takeSentinel('\n')); + try testing.expectEqualStrings("c", try r.peek(1)); } test peekSentinel { - return error.Unimplemented; + var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab", try r.peekSentinel('\n')); + try testing.expectEqualStrings("ab", try r.peekSentinel('\n')); } test takeDelimiterInclusive { - return error.Unimplemented; + var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab\n", try r.takeDelimiterInclusive('\n')); + try testing.expectError(error.EndOfStream, r.takeDelimiterInclusive('\n')); } test peekDelimiterInclusive { - return error.Unimplemented; + var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab\n", try r.peekDelimiterInclusive('\n')); + try testing.expectEqualStrings("ab\n", try r.peekDelimiterInclusive('\n')); + r.toss(3); + try testing.expectError(error.EndOfStream, r.peekDelimiterInclusive('\n')); } test takeDelimiterExclusive { - return error.Unimplemented; + var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab", try r.takeDelimiterExclusive('\n')); + try testing.expectEqualStrings("c", try r.takeDelimiterExclusive('\n')); + try testing.expectError(error.EndOfStream, r.takeDelimiterExclusive('\n')); } test peekDelimiterExclusive { - return error.Unimplemented; + var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n')); + try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n')); + r.toss(3); + try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n')); } -test readDelimiter { - return error.Unimplemented; +test streamDelimiter { + var out_buffer: [10]u8 = undefined; + var r: Reader = .fixed("foo\nbars"); + var w: Writer = .fixed(&out_buffer); + try testing.expectEqual(3, try r.streamDelimiter(&w, '\n')); + try testing.expectEqualStrings("foo", w.buffered()); + try testing.expectEqual(0, try r.streamDelimiter(&w, '\n')); + r.toss(1); + try testing.expectError(error.EndOfStream, r.streamDelimiter(&w, '\n')); } -test readDelimiterEnding { - return error.Unimplemented; +test streamDelimiterEnding { + var out_buffer: [10]u8 = undefined; + var r: Reader = .fixed("foo\nbars"); + var w: Writer = .fixed(&out_buffer); + try testing.expectEqual(3, try r.streamDelimiterEnding(&w, '\n')); + try testing.expectEqualStrings("foo", w.buffered()); + r.toss(1); + try testing.expectEqual(4, try r.streamDelimiterEnding(&w, '\n')); + try testing.expectEqualStrings("foobars", w.buffered()); + try testing.expectEqual(0, try r.streamDelimiterEnding(&w, '\n')); + try testing.expectEqual(0, try r.streamDelimiterEnding(&w, '\n')); } -test readDelimiterLimit { - return error.Unimplemented; +test streamDelimiterLimit { + var out_buffer: [10]u8 = undefined; + var r: Reader = .fixed("foo\nbars"); + var w: Writer = .fixed(&out_buffer); + try testing.expectError(error.StreamTooLong, r.streamDelimiterLimit(&w, '\n', .limited(2))); + try testing.expectEqual(1, try r.streamDelimiterLimit(&w, '\n', .limited(3))); + try testing.expectEqualStrings("\n", try r.take(1)); + try testing.expectEqual(4, try r.streamDelimiterLimit(&w, '\n', .unlimited)); + try testing.expectEqualStrings("foobars", w.buffered()); } test discardDelimiterExclusive { - return error.Unimplemented; + var r: Reader = .fixed("foob\nar"); + try testing.expectEqual(4, try r.discardDelimiterExclusive('\n')); + try testing.expectEqualStrings("\n", try r.take(1)); + try testing.expectEqual(2, try r.discardDelimiterExclusive('\n')); + try testing.expectEqual(0, try r.discardDelimiterExclusive('\n')); } test discardDelimiterInclusive { - return error.Unimplemented; + var r: Reader = .fixed("foob\nar"); + try testing.expectEqual(5, try r.discardDelimiterInclusive('\n')); + try testing.expectError(error.EndOfStream, r.discardDelimiterInclusive('\n')); +} + +test discardDelimiterLimit { + var r: Reader = .fixed("foob\nar"); + try testing.expectError(error.StreamTooLong, r.discardDelimiterLimit('\n', .limited(4))); + try testing.expectEqual(0, try r.discardDelimiterLimit('\n', .limited(2))); + try testing.expectEqualStrings("\n", try r.take(1)); + try testing.expectEqual(2, try r.discardDelimiterLimit('\n', .unlimited)); + try testing.expectEqual(0, try r.discardDelimiterLimit('\n', .unlimited)); } test fill { - return error.Unimplemented; + var r: Reader = .fixed("abc"); + try r.fill(1); + try r.fill(3); } test takeByte { - return error.Unimplemented; + var r: Reader = .fixed("ab"); + try testing.expectEqual('a', try r.takeByte()); + try testing.expectEqual('b', try r.takeByte()); + try testing.expectError(error.EndOfStream, r.takeByte()); } test takeByteSigned { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 255, 5 }); + try testing.expectEqual(-1, try r.takeByteSigned()); + try testing.expectEqual(5, try r.takeByteSigned()); + try testing.expectError(error.EndOfStream, r.takeByteSigned()); } test takeInt { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 0x12, 0x34, 0x56 }); + try testing.expectEqual(0x1234, try r.takeInt(u16, .big)); + try testing.expectError(error.EndOfStream, r.takeInt(u16, .little)); } test takeVarInt { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 0x12, 0x34, 0x56 }); + try testing.expectEqual(0x123456, try r.takeVarInt(u64, .big, 3)); + try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1)); } test takeStruct { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 }); + const S = extern struct { a: u8, b: u16 }; + switch (native_endian) { + .little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStruct(S)).*), + .big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStruct(S)).*), + } + try testing.expectError(error.EndOfStream, r.takeStruct(S)); } test peekStruct { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 }); + const S = extern struct { a: u8, b: u16 }; + switch (native_endian) { + .little => { + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*); + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*); + }, + .big => { + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*); + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*); + }, + } } test takeStructEndian { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 }); + const S = extern struct { a: u8, b: u16 }; + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.takeStructEndian(S, .big)); + try testing.expectError(error.EndOfStream, r.takeStructEndian(S, .little)); } test peekStructEndian { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 }); + const S = extern struct { a: u8, b: u16 }; + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.peekStructEndian(S, .big)); + try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), try r.peekStructEndian(S, .little)); } test takeEnum { - return error.Unimplemented; + var r: Reader = .fixed(&.{ 2, 0, 1 }); + const E1 = enum(u8) { a, b, c }; + const E2 = enum(u16) { _ }; + try testing.expectEqual(E1.c, try r.takeEnum(E1, .little)); + try testing.expectEqual(@as(E2, @enumFromInt(0x0001)), try r.takeEnum(E2, .big)); } test takeLeb128 { - return error.Unimplemented; + var r: Reader = .fixed("\xc7\x9f\x7f\x80"); + try testing.expectEqual(-12345, try r.takeLeb128(i64)); + try testing.expectEqual(0x80, try r.peekByte()); + try testing.expectError(error.EndOfStream, r.takeLeb128(i64)); } test readSliceShort { - return error.Unimplemented; + var r: Reader = .fixed("HelloFren"); + var buf: [5]u8 = undefined; + try testing.expectEqual(5, try r.readSliceShort(&buf)); + try testing.expectEqualStrings("Hello", buf[0..5]); + try testing.expectEqual(4, try r.readSliceShort(&buf)); + try testing.expectEqualStrings("Fren", buf[0..4]); + try testing.expectEqual(0, try r.readSliceShort(&buf)); } test readVec { - return error.Unimplemented; + var r: Reader = .fixed(std.ascii.letters); + var flat_buffer: [52]u8 = undefined; + var bufs: [2][]u8 = .{ + flat_buffer[0..26], + flat_buffer[26..], + }; + // Short reads are possible with this function but not with fixed. + try testing.expectEqual(26 * 2, try r.readVec(&bufs)); + try testing.expectEqualStrings(std.ascii.letters[0..26], bufs[0]); + try testing.expectEqualStrings(std.ascii.letters[26..], bufs[1]); +} + +test readVecLimit { + var r: Reader = .fixed(std.ascii.letters); + var flat_buffer: [52]u8 = undefined; + var bufs: [2][]u8 = .{ + flat_buffer[0..26], + flat_buffer[26..], + }; + // Short reads are possible with this function but not with fixed. + try testing.expectEqual(50, try r.readVecLimit(&bufs, .limited(50))); + try testing.expectEqualStrings(std.ascii.letters[0..26], bufs[0]); + try testing.expectEqualStrings(std.ascii.letters[26..50], bufs[1][0..24]); } test "expected error.EndOfStream" { // Unit test inspired by https://github.com/ziglang/zig/issues/17733 - var r: std.io.Reader = .fixed(""); - try std.testing.expectError(error.EndOfStream, r.readEnum(enum(u8) { a, b }, .little)); - try std.testing.expectError(error.EndOfStream, r.isBytes("foo")); + var buffer: [3]u8 = undefined; + var r: std.io.Reader = .fixed(&buffer); + r.end = 0; // capacity 3, but empty + try std.testing.expectError(error.EndOfStream, r.takeEnum(enum(u8) { a, b }, .little)); + try std.testing.expectError(error.EndOfStream, r.take(3)); } fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { @@ -1389,25 +1632,51 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize { test "readAlloc when the backing reader provides one byte at a time" { const OneByteReader = struct { str: []const u8, - curr: usize, + i: usize, + reader: Reader, - fn read(self: *@This(), dest: []u8) usize { - if (self.str.len <= self.curr or dest.len == 0) - return 0; - - dest[0] = self.str[self.curr]; - self.curr += 1; + fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { + assert(@intFromEnum(limit) >= 1); + const self: *@This() = @fieldParentPtr("reader", r); + if (self.str.len - self.i == 0) return error.EndOfStream; + try w.writeByte(self.str[self.i]); + self.i += 1; return 1; } }; - const str = "This is a test"; - var one_byte_stream: OneByteReader = .init(str); - const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1); + var one_byte_stream: OneByteReader = .{ + .str = str, + .i = 0, + .reader = .{ + .buffer = &.{}, + .vtable = &.{ .stream = OneByteReader.stream }, + .seek = 0, + .end = 0, + }, + }; + const res = try one_byte_stream.reader.allocRemaining(std.testing.allocator, .unlimited); defer std.testing.allocator.free(res); try std.testing.expectEqualStrings(str, res); } +test "takeDelimiterInclusive when it rebases" { + const written_line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"; + var buffer: [128]u8 = undefined; + var tr: std.testing.Reader = .init(&buffer, &.{ + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + }); + const r = &tr.interface; + for (0..6) |_| { + try std.testing.expectEqualStrings(written_line, try r.takeDelimiterInclusive('\n')); + } +} + /// Provides a `Reader` implementation by passing data from an underlying /// reader through `Hasher.update`. /// diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index c1371d34342dd2544e586d002316598ff1b325b6..73d504c437a1b758e45d6fc255ae5626a1d5f3bd 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -14,12 +14,6 @@ vtable: *const VTable, buffer: []u8, /// In `buffer` before this are buffered bytes, after this is `undefined`. end: usize = 0, -/// Tracks total number of bytes written to this `Writer`. This value -/// only increases. In the case of fixed mode, this value always equals `end`. -/// -/// This value is maintained by the interface; `VTable` function -/// implementations need not modify it. -count: usize = 0, pub const VTable = struct { /// Sends bytes to the logical sink. A write will only be sent here if it @@ -37,6 +31,10 @@ pub const VTable = struct { /// The last element of `data` is repeated as necessary so that it is /// written `splat` number of times, which may be zero. /// + /// This function may not be called if the data to be written could have + /// been stored in `buffer` instead, including when the amount of data to + /// be written is zero and the buffer capacity is zero. + /// /// Number of bytes consumed from `data` is returned, excluding bytes from /// `buffer`. /// @@ -113,8 +111,7 @@ pub const FileError = error{ Unimplemented, }; -/// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless -/// modified externally, `count` will always equal `end`. +/// Writes to `buffer` and returns `error.WriteFailed` when it is full. pub fn fixed(buffer: []u8) Writer { return .{ .vtable = &.{ .drain = fixedDrain }, @@ -122,8 +119,8 @@ pub fn fixed(buffer: []u8) Writer { }; } -pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) { - return .{ .out = w, .hasher = hasher }; +pub fn hashed(w: *Writer, hasher: anytype, buffer: []u8) Hashed(@TypeOf(hasher)) { + return .initHasher(w, hasher, buffer); } pub const failing: Writer = .{ @@ -133,16 +130,6 @@ pub const failing: Writer = .{ }, }; -pub fn discarding(buffer: []u8) Writer { - return .{ - .vtable = &.{ - .drain = discardingDrain, - .sendFile = discardingSendFile, - }, - .buffer = buffer, - }; -} - /// Returns the contents not yet drained. pub fn buffered(w: *const Writer) []u8 { return w.buffer[0..w.end]; @@ -174,53 +161,26 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz assert(data.len > 0); const buffer = w.buffer; const count = countSplat(data, splat); - if (w.end + count > buffer.len) { - const n = try w.vtable.drain(w, data, splat); - w.count += n; - return n; - } - w.count += count; - for (data) |bytes| { + if (w.end + count > buffer.len) return w.vtable.drain(w, data, splat); + for (data[0 .. data.len - 1]) |bytes| { @memcpy(buffer[w.end..][0..bytes.len], bytes); w.end += bytes.len; } const pattern = data[data.len - 1]; - if (splat == 0) { - @branchHint(.unlikely); - w.end -= pattern.len; - return count; - } - const remaining_splat = splat - 1; switch (pattern.len) { 0 => {}, 1 => { - @memset(buffer[w.end..][0..remaining_splat], pattern[0]); - w.end += remaining_splat; + @memset(buffer[w.end..][0..splat], pattern[0]); + w.end += splat; }, - else => { - const new_end = w.end + pattern.len * remaining_splat; - while (w.end < new_end) : (w.end += pattern.len) { - @memcpy(buffer[w.end..][0..pattern.len], pattern); - } + else => for (0..splat) |_| { + @memcpy(buffer[w.end..][0..pattern.len], pattern); + w.end += pattern.len; }, } return count; } -/// Equivalent to `writeSplat` but writes at most `limit` bytes. -pub fn writeSplatLimit( - w: *Writer, - data: []const []const u8, - splat: usize, - limit: Limit, -) Error!usize { - _ = w; - _ = data; - _ = splat; - _ = limit; - @panic("TODO"); -} - /// Returns how many bytes were consumed from `header` and `data`. pub fn writeSplatHeader( w: *Writer, @@ -232,38 +192,40 @@ pub fn writeSplatHeader( if (new_end <= w.buffer.len) { @memcpy(w.buffer[w.end..][0..header.len], header); w.end = new_end; - w.count += header.len; return header.len + try writeSplat(w, data, splat); } var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size. var i: usize = 1; vecs[0] = header; - for (data) |buf| { + for (data[0 .. data.len - 1]) |buf| { if (buf.len == 0) continue; vecs[i] = buf; i += 1; if (vecs.len - i == 0) break; } - const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1; - const n = try w.vtable.drain(w, vecs[0..i], new_splat); - w.count += n; - return n; + const pattern = data[data.len - 1]; + const new_splat = s: { + if (pattern.len == 0 or vecs.len - i == 0) break :s 1; + vecs[i] = pattern; + i += 1; + break :s splat; + }; + return w.vtable.drain(w, vecs[0..i], new_splat); } -/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes. -pub fn writeSplatHeaderLimit( - w: *Writer, - header: []const u8, - data: []const []const u8, - splat: usize, - limit: Limit, -) Error!usize { - _ = w; - _ = header; - _ = data; - _ = splat; - _ = limit; - @panic("TODO"); +test "writeSplatHeader splatting avoids buffer aliasing temptation" { + const initial_buf = try testing.allocator.alloc(u8, 8); + var aw: std.io.Writer.Allocating = .initOwnedSlice(testing.allocator, initial_buf); + defer aw.deinit(); + // This test assumes 8 vector buffer in this function. + const n = try aw.writer.writeSplatHeader("header which is longer than buf ", &.{ + "1", "2", "3", "4", "5", "6", "foo", "bar", "foo", + }, 3); + try testing.expectEqual(41, n); + try testing.expectEqualStrings( + "header which is longer than buf 123456foo", + aw.writer.buffered(), + ); } /// Drains all remaining buffered data. @@ -386,12 +348,18 @@ pub const WritableVectorIterator = struct { pub const VectorWrapper = struct { writer: Writer, it: WritableVectorIterator, - pub const vtable: VTable = .{ .drain = fixedDrain }; + /// Tracks whether the "writable vector" API was used. + used: bool = false, + pub const vtable: *const VTable = &unique_vtable_allocation; + /// This is intended to be constant but it must be a unique address for + /// `@fieldParentPtr` to work. + var unique_vtable_allocation: VTable = .{ .drain = fixedDrain }; }; pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator { - if (w.vtable == &VectorWrapper.vtable) { + if (w.vtable == VectorWrapper.vtable) { const wrapper: *VectorWrapper = @fieldParentPtr("writer", w); + wrapper.used = true; return wrapper.it; } return .{ .first = try writableSliceGreedy(w, 1) }; @@ -419,7 +387,6 @@ pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void { pub fn undo(w: *Writer, n: usize) void { w.end -= n; - w.count -= n; } /// After calling `writableSliceGreedy`, this function tracks how many bytes @@ -430,13 +397,11 @@ pub fn advance(w: *Writer, n: usize) void { const new_end = w.end + n; assert(new_end <= w.buffer.len); w.end = new_end; - w.count += n; } /// After calling `writableVector`, this function tracks how many bytes were /// written to it. pub fn advanceVector(w: *Writer, n: usize) usize { - w.count += n; return consume(w, n); } @@ -494,12 +459,9 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize { @branchHint(.likely); @memcpy(w.buffer[w.end..][0..bytes.len], bytes); w.end += bytes.len; - w.count += bytes.len; return bytes.len; } - const n = try w.vtable.drain(w, &.{bytes}, 1); - w.count += n; - return n; + return w.vtable.drain(w, &.{bytes}, 1); } /// Asserts `buffer` capacity exceeds `preserve_length`. @@ -509,7 +471,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro @branchHint(.likely); @memcpy(w.buffer[w.end..][0..bytes.len], bytes); w.end += bytes.len; - w.count += bytes.len; return bytes.len; } const temp_end = w.end -| preserve_length; @@ -517,7 +478,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro w.end = temp_end; defer w.end += preserved.len; const n = try w.vtable.drain(w, &.{bytes}, 1); - w.count += n; assert(w.end <= temp_end + preserved.len); @memmove(w.buffer[w.end..][0..preserved.len], preserved); return n; @@ -542,23 +502,207 @@ pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) E while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]); } -pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void { - try std.fmt.format(w, format, args); +/// Renders fmt string with args, calling `writer` with slices of bytes. +/// If `writer` returns an error, the error is returned from `format` and +/// `writer` is not called again. +/// +/// The format string must be comptime-known and may contain placeholders following +/// this format: +/// `{[argument][specifier]:[fill][alignment][width].[precision]}` +/// +/// Above, each word including its surrounding [ and ] is a parameter which you have to replace with something: +/// +/// - *argument* is either the numeric index or the field name of the argument that should be inserted +/// - when using a field name, you are required to enclose the field name (an identifier) in square +/// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...} +/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) +/// - *fill* is a single byte which is used to pad formatted numbers. +/// - *alignment* is one of the three bytes '<', '^', or '>' to make numbers +/// left, center, or right-aligned, respectively. +/// - Not all specifiers support alignment. +/// - Alignment is not Unicode-aware; appropriate only when used with raw bytes or ASCII. +/// - *width* is the total width of the field in bytes. This only applies to number formatting. +/// - *precision* specifies how many decimals a formatted number should have. +/// +/// Note that most of the parameters are optional and may be omitted. Also you +/// can leave out separators like `:` and `.` when all parameters after the +/// separator are omitted. +/// +/// Only exception is the *fill* parameter. If a non-zero *fill* character is +/// required at the same time as *width* is specified, one has to specify +/// *alignment* as well, as otherwise the digit following `:` is interpreted as +/// *width*, not *fill*. +/// +/// The *specifier* has several options for types: +/// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes +/// - `s`: +/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination +/// - for slices of u8, print the entire slice as a string without zero-termination +/// - `t`: +/// - for enums and tagged unions: prints the tag name +/// - for error sets: prints the error name +/// - `b64`: output string as standard base64 +/// - `e`: output floating point value in scientific notation +/// - `d`: output numeric value in decimal notation +/// - `b`: output integer value in binary notation +/// - `o`: output integer value in octal notation +/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. +/// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max. +/// - `D`: output nanoseconds as duration +/// - `B`: output bytes in SI units (decimal) +/// - `Bi`: output bytes in IEC units (binary) +/// - `?`: output optional value as either the unwrapped value, or `null`; may be followed by a format specifier for the underlying value. +/// - `!`: 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. +/// - `*`: output the address of the value instead of the value itself. +/// - `any`: output a value of any type using its default format. +/// - `f`: delegates to a method on the type named "format" with the signature `fn (*Writer, args: anytype) Writer.Error!void`. +/// +/// A user type may be a `struct`, `vector`, `union` or `enum` type. +/// +/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`. +/// +/// Asserts `buffer` capacity of at least 2 if a union is printed. This +/// requirement could be lifted by adjusting the code, but if you trigger that +/// assertion it is a clue that you should probably be using a buffer. +pub fn print(w: *Writer, comptime fmt: []const u8, args: anytype) Error!void { + const ArgsType = @TypeOf(args); + const args_type_info = @typeInfo(ArgsType); + if (args_type_info != .@"struct") { + @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); + } + + const fields_info = args_type_info.@"struct".fields; + const max_format_args = @typeInfo(std.fmt.ArgSetType).int.bits; + if (fields_info.len > max_format_args) { + @compileError("32 arguments max are supported per format call"); + } + + @setEvalBranchQuota(fmt.len * 1000); + comptime var arg_state: std.fmt.ArgState = .{ .args_len = fields_info.len }; + comptime var i = 0; + comptime var literal: []const u8 = ""; + inline while (true) { + const start_index = i; + + inline while (i < fmt.len) : (i += 1) { + switch (fmt[i]) { + '{', '}' => break, + else => {}, + } + } + + comptime var end_index = i; + comptime var unescape_brace = false; + + // Handle {{ and }}, those are un-escaped as single braces + if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) { + unescape_brace = true; + // Make the first brace part of the literal... + end_index += 1; + // ...and skip both + i += 2; + } + + literal = literal ++ fmt[start_index..end_index]; + + // We've already skipped the other brace, restart the loop + if (unescape_brace) continue; + + // Write out the literal + if (literal.len != 0) { + try w.writeAll(literal); + literal = ""; + } + + if (i >= fmt.len) break; + + if (fmt[i] == '}') { + @compileError("missing opening {"); + } + + // Get past the { + comptime assert(fmt[i] == '{'); + i += 1; + + const fmt_begin = i; + // Find the closing brace + inline while (i < fmt.len and fmt[i] != '}') : (i += 1) {} + const fmt_end = i; + + if (i >= fmt.len) { + @compileError("missing closing }"); + } + + // Get past the } + comptime assert(fmt[i] == '}'); + i += 1; + + const placeholder_array = fmt[fmt_begin..fmt_end].*; + const placeholder = comptime std.fmt.Placeholder.parse(&placeholder_array); + const arg_pos = comptime switch (placeholder.arg) { + .none => null, + .number => |pos| pos, + .named => |arg_name| std.meta.fieldIndex(ArgsType, arg_name) orelse + @compileError("no argument with name '" ++ arg_name ++ "'"), + }; + + const width = switch (placeholder.width) { + .none => null, + .number => |v| v, + .named => |arg_name| blk: { + const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse + @compileError("no argument with name '" ++ arg_name ++ "'"); + _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); + break :blk @field(args, arg_name); + }, + }; + + const precision = switch (placeholder.precision) { + .none => null, + .number => |v| v, + .named => |arg_name| blk: { + const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse + @compileError("no argument with name '" ++ arg_name ++ "'"); + _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); + break :blk @field(args, arg_name); + }, + }; + + const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse + @compileError("too few arguments"); + + try w.printValue( + placeholder.specifier_arg, + .{ + .fill = placeholder.fill, + .alignment = placeholder.alignment, + .width = width, + .precision = precision, + }, + @field(args, fields_info[arg_to_print].name), + std.options.fmt_max_depth, + ); + } + + if (comptime arg_state.hasUnusedArgs()) { + const missing_count = arg_state.args_len - @popCount(arg_state.used_args); + switch (missing_count) { + 0 => unreachable, + 1 => @compileError("unused argument in '" ++ fmt ++ "'"), + else => @compileError(std.fmt.comptimePrint("{d}", .{missing_count}) ++ " unused arguments in '" ++ fmt ++ "'"), + } + } } /// Calls `drain` as many times as necessary such that `byte` is transferred. pub fn writeByte(w: *Writer, byte: u8) Error!void { while (w.buffer.len - w.end == 0) { const n = try w.vtable.drain(w, &.{&.{byte}}, 1); - if (n > 0) { - w.count += 1; - return; - } + if (n > 0) return; } else { @branchHint(.likely); w.buffer[w.end] = byte; w.end += 1; - w.count += 1; } } @@ -571,7 +715,6 @@ pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!voi @branchHint(.likely); w.buffer[w.end] = byte; w.end += 1; - w.count += 1; } } @@ -625,12 +768,23 @@ pub fn writeStruct(w: *Writer, value: anytype) Error!void { /// comptime-known and matches host endianness. /// TODO: make sure this value is not a reference type pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void { - if (native_endian == endian) { - return w.writeStruct(value); - } else { - var copy = value; - std.mem.byteSwapAllFields(@TypeOf(value), ©); - return w.writeStruct(copy); + switch (@typeInfo(@TypeOf(value))) { + .@"struct" => |info| switch (info.layout) { + .auto => @compileError("ill-defined memory layout"), + .@"extern" => { + if (native_endian == endian) { + return w.writeStruct(value); + } else { + var copy = value; + std.mem.byteSwapAllFields(@TypeOf(value), ©); + return w.writeStruct(copy); + } + }, + .@"packed" => { + return writeInt(w, info.backing_integer.?, @bitCast(value), endian); + }, + }, + else => @compileError("not a struct"), } } @@ -647,14 +801,6 @@ pub inline fn writeSliceEndian( } } -/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)` -pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void { - // copy to storage first, then swap in place - _ = w; - _ = slice; - @panic("TODO"); -} - /// Unlike `writeSplat` and `writeVec`, this function will call into `VTable` /// even if there is enough buffer capacity for the file contents. /// @@ -680,12 +826,10 @@ pub fn sendFileHeader( if (new_end <= w.buffer.len) { @memcpy(w.buffer[w.end..][0..header.len], header); w.end = new_end; - w.count += header.len; return header.len + try w.vtable.sendFile(w, file_reader, limit); } const buffered_contents = limit.slice(file_reader.interface.buffered()); const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1); - w.count += n; file_reader.interface.toss(n - header.len); return n; } @@ -698,6 +842,8 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File return n; } +/// Number of bytes logically written is returned. This excludes bytes from +/// `buffer` because they have already been logically written. pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize { var remaining = @intFromEnum(limit); while (remaining > 0) { @@ -772,16 +918,13 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void { switch (@typeInfo(T)) { .pointer => |info| { try w.writeAll(@typeName(info.child) ++ "@"); - if (info.size == .slice) - try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{}) - else - try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); - return; + const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value); + return w.printInt(int, 16, .lower, .{}); }, .optional => |info| { if (@typeInfo(info.child) == .pointer) { try w.writeAll(@typeName(info.child) ++ "@"); - try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); + try w.printInt(@intFromPtr(value), 16, .lower, .{}); return; } }, @@ -791,6 +934,7 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void { @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); } +/// Asserts `buffer` capacity of at least 2 if `value` is a union. pub fn printValue( w: *Writer, comptime fmt: []const u8, @@ -800,26 +944,181 @@ pub fn printValue( ) Error!void { const T = @TypeOf(value); - if (comptime std.mem.eql(u8, fmt, "*")) { - return w.printAddress(value); + switch (fmt.len) { + 1 => switch (fmt[0]) { + '*' => return w.printAddress(value), + 'f' => return value.format(w), + 'd' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)), + .int, .comptime_int => return printInt(w, value, 10, .lower, options), + .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)), + .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'c' => return w.printAsciiChar(value, options), + 'u' => return w.printUnicodeCodepoint(value), + 'b' => switch (@typeInfo(T)) { + .int, .comptime_int => return printInt(w, value, 2, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options), + .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'o' => switch (@typeInfo(T)) { + .int, .comptime_int => return printInt(w, value, 8, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options), + .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'x' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)), + .int, .comptime_int => return printInt(w, value, 16, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options), + .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)), + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + optionsForbidden(options); + return printHex(w, slice, .lower); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + optionsForbidden(options); + return printHex(w, slice, .lower); + }, + }, + .array => { + const slice: []const u8 = &value; + optionsForbidden(options); + return printHex(w, slice, .lower); + }, + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'X' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)), + .int, .comptime_int => return printInt(w, value, 16, .upper, options), + .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options), + .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)), + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + optionsForbidden(options); + return printHex(w, slice, .upper); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + optionsForbidden(options); + return printHex(w, slice, .upper); + }, + }, + .array => { + const slice: []const u8 = &value; + optionsForbidden(options); + return printHex(w, slice, .upper); + }, + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 's' => switch (@typeInfo(T)) { + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return w.alignBufferOptions(slice, options); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return w.alignBufferOptions(slice, options); + }, + }, + .array => { + const slice: []const u8 = &value; + return w.alignBufferOptions(slice, options); + }, + else => invalidFmtError(fmt, value), + }, + 'B' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printByteSize(value, .decimal, options), + .@"struct" => return value.formatByteSize(w, .decimal), + else => invalidFmtError(fmt, value), + }, + 'D' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printDuration(value, options), + .@"struct" => return value.formatDuration(w), + else => invalidFmtError(fmt, value), + }, + 'e' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)), + .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)), + else => invalidFmtError(fmt, value), + }, + 'E' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)), + .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)), + else => invalidFmtError(fmt, value), + }, + 't' => switch (@typeInfo(T)) { + .error_set => return w.writeAll(@errorName(value)), + .@"enum", .@"union" => return w.writeAll(@tagName(value)), + else => invalidFmtError(fmt, value), + }, + else => {}, + }, + 2 => switch (fmt[0]) { + 'B' => switch (fmt[1]) { + 'i' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printByteSize(value, .binary, options), + .@"struct" => return value.formatByteSize(w, .binary), + else => invalidFmtError(fmt, value), + }, + else => {}, + }, + else => {}, + }, + 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) { + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + optionsForbidden(options); + return w.printBase64(slice); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + optionsForbidden(options); + return w.printBase64(slice); + }, + }, + .array => { + const slice: []const u8 = &value; + optionsForbidden(options); + return w.printBase64(slice); + }, + else => invalidFmtError(fmt, value), + }, + else => {}, } const is_any = comptime std.mem.eql(u8, fmt, ANY); - if (!is_any and std.meta.hasMethod(T, "format")) { - if (fmt.len > 0 and fmt[0] == 'f') { - return value.format(w, fmt[1..]); - } else if (fmt.len == 0) { - // after 0.15.0 is tagged, delete the hasMethod condition and this compile error - @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it"); - } + if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) { + // after 0.15.0 is tagged, delete this compile error and its condition + @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it"); } switch (@typeInfo(T)) { - .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value), - .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value), + .float, .comptime_float => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printFloat(w, value, options.toNumber(.decimal, .lower)); + }, + .int, .comptime_int => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printInt(w, value, 10, .lower, options); + }, .bool => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions(if (value) "true" else "false", options); + const string: []const u8 = if (value) "true" else "false"; + return w.alignBufferOptions(string, options); }, .void => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); @@ -852,50 +1151,30 @@ pub fn printValue( } }, .error_set => { - if (fmt.len == 1 and fmt[0] == 's') return w.writeAll(@errorName(value)); if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - try printErrorSet(w, value); + optionsForbidden(options); + return printErrorSet(w, value); }, - .@"enum" => { - if (fmt.len == 1 and fmt[0] == 's') { - try w.writeAll(@tagName(value)); - return; + .@"enum" => |info| { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + optionsForbidden(options); + if (info.is_exhaustive) { + return printEnumExhaustive(w, value); + } else { + return printEnumNonexhaustive(w, value); } - if (!is_any) { - if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth); - return printValue(w, ANY, options, value, max_depth); - } - const enum_info = @typeInfo(T).@"enum"; - if (enum_info.is_exhaustive) { - var vecs: [3][]const u8 = .{ @typeName(T), ".", @tagName(value) }; - try w.writeVecAll(&vecs); - return; - } - try w.writeAll(@typeName(T)); - @setEvalBranchQuota(3 * enum_info.fields.len); - inline for (enum_info.fields) |field| { - if (@intFromEnum(value) == field.value) { - try w.writeAll("."); - try w.writeAll(@tagName(value)); - return; - } - } - try w.writeByte('('); - try w.printValue(ANY, options, @intFromEnum(value), max_depth); - try w.writeByte(')'); }, .@"union" => |info| { if (!is_any) { if (fmt.len != 0) invalidFmtError(fmt, value); return printValue(w, ANY, options, value, max_depth); } - try w.writeAll(@typeName(T)); if (max_depth == 0) { - try w.writeAll("{ ... }"); + try w.writeAll(".{ ... }"); return; } if (info.tag_type) |UnionTagType| { - try w.writeAll("{ ."); + try w.writeAll(".{ ."); try w.writeAll(@tagName(@as(UnionTagType, value))); try w.writeAll(" = "); inline for (info.fields) |u_field| { @@ -904,9 +1183,22 @@ pub fn printValue( } } try w.writeAll(" }"); - } else { - try w.writeByte('@'); - try w.printIntOptions(@intFromPtr(&value), 16, .lower, options); + } else switch (info.layout) { + .auto => { + return w.writeAll(".{ ... }"); + }, + .@"extern", .@"packed" => { + if (info.fields.len == 0) return w.writeAll(".{}"); + try w.writeAll(".{ "); + inline for (info.fields) |field| { + try w.writeByte('.'); + try w.writeAll(field.name); + try w.writeAll(" = "); + try w.printValue(ANY, options, @field(value, field.name), max_depth - 1); + (try w.writableArray(2)).* = ", ".*; + } + w.buffer[w.end - 2 ..][0..2].* = " }".*; + }, } }, .@"struct" => |info| { @@ -917,10 +1209,10 @@ pub fn printValue( if (info.is_tuple) { // Skip the type and field names when formatting tuples. if (max_depth == 0) { - try w.writeAll("{ ... }"); + try w.writeAll(".{ ... }"); return; } - try w.writeAll("{"); + try w.writeAll(".{"); inline for (info.fields, 0..) |f, i| { if (i == 0) { try w.writeAll(" "); @@ -932,12 +1224,11 @@ pub fn printValue( try w.writeAll(" }"); return; } - try w.writeAll(@typeName(T)); if (max_depth == 0) { - try w.writeAll("{ ... }"); + try w.writeAll(".{ ... }"); return; } - try w.writeAll("{"); + try w.writeAll(".{"); inline for (info.fields, 0..) |f, i| { if (i == 0) { try w.writeAll(" ."); @@ -952,44 +1243,24 @@ pub fn printValue( }, .pointer => |ptr_info| switch (ptr_info.size) { .one => switch (@typeInfo(ptr_info.child)) { - .array, .@"enum", .@"union", .@"struct" => { - return w.printValue(fmt, options, value.*, max_depth); - }, + .array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth), + .@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth), else => { var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; try w.writeVecAll(&buffers); - try w.printIntOptions(@intFromPtr(value), 16, .lower, options); + try w.printInt(@intFromPtr(value), 16, .lower, options); return; }, }, .many, .c => { - if (ptr_info.sentinel() != null) - return w.printValue(fmt, options, std.mem.span(value), max_depth); - if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8) - return w.alignBufferOptions(std.mem.span(value), options); - if (!is_any and fmt.len == 0) - @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); - if (!is_any and fmt.len != 0) - invalidFmtError(fmt, value); + if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); + optionsForbidden(options); try w.printAddress(value); }, .slice => { - if (!is_any and fmt.len == 0) + if (!is_any) @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})"); - if (max_depth == 0) - return w.writeAll("{ ... }"); - if (ptr_info.child == u8) switch (fmt.len) { - 1 => switch (fmt[0]) { - 's' => return w.alignBufferOptions(value, options), - 'x' => return w.printHex(value, .lower), - 'X' => return w.printHex(value, .upper), - else => {}, - }, - 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') { - return w.printBase64(value); - }, - else => {}, - }; + if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); for (value, 0..) |elem, i| { try w.printValue(fmt, options, elem, max_depth - 1); @@ -1000,21 +1271,9 @@ pub fn printValue( try w.writeAll(" }"); }, }, - .array => |info| { - if (fmt.len == 0) - @compileError("cannot format array without a specifier (i.e. {s} or {any})"); - if (max_depth == 0) { - return w.writeAll("{ ... }"); - } - if (info.child == u8) { - if (fmt[0] == 's') { - return w.alignBufferOptions(&value, options); - } else if (fmt[0] == 'x') { - return w.printHex(&value, .lower); - } else if (fmt[0] == 'X') { - return w.printHex(&value, .upper); - } - } + .array => { + if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})"); + if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); for (value, 0..) |elem, i| { try w.printValue(fmt, options, elem, max_depth - 1); @@ -1024,19 +1283,9 @@ pub fn printValue( } try w.writeAll(" }"); }, - .vector => |info| { - if (max_depth == 0) { - return w.writeAll("{ ... }"); - } - try w.writeAll("{ "); - var i: usize = 0; - while (i < info.len) : (i += 1) { - try w.printValue(fmt, options, value[i], max_depth - 1); - if (i < info.len - 1) { - try w.writeAll(", "); - } - } - try w.writeAll(" }"); + .vector => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printVector(w, fmt, options, value, max_depth); }, .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), .type => { @@ -1045,8 +1294,9 @@ pub fn printValue( }, .enum_literal => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - const buffer = [_]u8{'.'} ++ @tagName(value); - return w.alignBufferOptions(buffer, options); + optionsForbidden(options); + var vecs: [2][]const u8 = .{ ".", @tagName(value) }; + return w.writeVecAll(&vecs); }, .null => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); @@ -1056,75 +1306,78 @@ pub fn printValue( } } +fn optionsForbidden(options: std.fmt.Options) void { + assert(options.precision == null); + assert(options.width == null); +} + fn printErrorSet(w: *Writer, error_set: anyerror) Error!void { var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) }; try w.writeVecAll(&vecs); } -pub fn printInt( +fn printEnumExhaustive(w: *Writer, value: anytype) Error!void { + var vecs: [2][]const u8 = .{ ".", @tagName(value) }; + try w.writeVecAll(&vecs); +} + +fn printEnumNonexhaustive(w: *Writer, value: anytype) Error!void { + if (std.enums.tagName(@TypeOf(value), value)) |tag_name| { + var vecs: [2][]const u8 = .{ ".", tag_name }; + try w.writeVecAll(&vecs); + return; + } + try w.writeAll("@enumFromInt("); + try w.printInt(@intFromEnum(value), 10, .lower, .{}); + try w.writeByte(')'); +} + +pub fn printVector( w: *Writer, comptime fmt: []const u8, options: std.fmt.Options, value: anytype, + max_depth: usize, ) Error!void { - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = std.math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else value; + const len = @typeInfo(@TypeOf(value)).vector.len; + if (max_depth == 0) return w.writeAll("{ ... }"); + try w.writeAll("{ "); + inline for (0..len) |i| { + try w.printValue(fmt, options, value[i], max_depth - 1); + if (i < len - 1) try w.writeAll(", "); + } + try w.writeAll(" }"); +} - switch (fmt.len) { - 0 => return w.printIntOptions(int_value, 10, .lower, options), - 1 => switch (fmt[0]) { - 'd' => return w.printIntOptions(int_value, 10, .lower, options), - 'c' => { - if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) { - return w.printAsciiChar(@as(u8, int_value), options); - } else { - @compileError("cannot print integer that is larger than 8 bits as an ASCII character"); - } - }, - 'u' => { - if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) { - return w.printUnicodeCodepoint(@as(u21, int_value), options); - } else { - @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence"); - } - }, - 'b' => return w.printIntOptions(int_value, 2, .lower, options), - 'x' => return w.printIntOptions(int_value, 16, .lower, options), - 'X' => return w.printIntOptions(int_value, 16, .upper, options), - 'o' => return w.printIntOptions(int_value, 8, .lower, options), - 'B' => return w.printByteSize(int_value, .decimal, options), - 'D' => return w.printDuration(int_value, options), - else => invalidFmtError(fmt, value), +// A wrapper around `printIntAny` to avoid the generic explosion of this +// function by funneling smaller integer types through `isize` and `usize`. +pub inline fn printInt( + w: *Writer, + value: anytype, + base: u8, + case: std.fmt.Case, + options: std.fmt.Options, +) Error!void { + switch (@TypeOf(value)) { + isize, usize => {}, + comptime_int => { + if (comptime std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options); + if (comptime std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options); + const Int = std.math.IntFittingRange(value, value); + return printIntAny(w, @as(Int, value), base, case, options); }, - 2 => { - if (fmt[0] == 'B' and fmt[1] == 'i') { - return w.printByteSize(int_value, .binary, options); - } else { - invalidFmtError(fmt, value); - } + else => switch (@typeInfo(@TypeOf(value)).int.signedness) { + .signed => if (std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options), + .unsigned => if (std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options), }, - else => invalidFmtError(fmt, value), } - comptime unreachable; -} - -pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { - return w.alignBufferOptions(@as(*const [1]u8, &c), options); -} - -pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { - return w.alignBufferOptions(bytes, options); -} - -pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void { - var buf: [4]u8 = undefined; - const len = try std.unicode.utf8Encode(c, &buf); - return w.alignBufferOptions(buf[0..len], options); + return printIntAny(w, value, base, case, options); } -pub fn printIntOptions( +/// In general, prefer `printInt` to avoid generic explosion. However this +/// function may be used when optimal codegen for a particular integer type is +/// desired. +pub fn printIntAny( w: *Writer, value: anytype, base: u8, @@ -1132,20 +1385,14 @@ pub fn printIntOptions( options: std.fmt.Options, ) Error!void { assert(base >= 2); - - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = std.math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else value; - - const value_info = @typeInfo(@TypeOf(int_value)).int; + const value_info = @typeInfo(@TypeOf(value)).int; // The type must have the same size as `base` or be wider in order for the // division to work const min_int_bits = comptime @max(value_info.bits, 8); const MinInt = std.meta.Int(.unsigned, min_int_bits); - const abs_value = @abs(int_value); + const abs_value = @abs(value); // The worst case in terms of space needed is base 2, plus 1 for the sign var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined; @@ -1192,41 +1439,69 @@ pub fn printIntOptions( return w.alignBufferOptions(buf[index..], options); } -pub fn printFloat( - w: *Writer, - comptime fmt: []const u8, - options: std.fmt.Options, - value: anytype, -) Error!void { +pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { + return w.alignBufferOptions(@as(*const [1]u8, &c), options); +} + +pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { + return w.alignBufferOptions(bytes, options); +} + +pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void { + var buf: [4]u8 = undefined; + const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { + error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: { + buf[0..3].* = std.unicode.replacement_character_utf8; + break :l 3; + }, + }; + return w.writeAll(buf[0..len]); +} + +/// Uses a larger stack buffer; asserts mode is decimal or scientific. +pub fn printFloat(w: *Writer, value: anytype, options: std.fmt.Number) Error!void { + const mode: std.fmt.float.Mode = switch (options.mode) { + .decimal => .decimal, + .scientific => .scientific, + .binary, .octal, .hex => unreachable, + }; var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined; + const s = std.fmt.float.render(&buf, value, .{ + .mode = mode, + .precision = options.precision, + }) catch |err| switch (err) { + error.BufferTooSmall => "(float)", + }; + return w.alignBuffer(s, options.width orelse s.len, options.alignment, options.fill); +} - if (fmt.len > 1) invalidFmtError(fmt, value); - switch (if (fmt.len == 0) 'e' else fmt[0]) { - 'e' => { - const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { - error.BufferTooSmall => "(float)", - }; - return w.alignBufferOptions(s, options); - }, - 'd' => { - const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { - error.BufferTooSmall => "(float)", - }; - return w.alignBufferOptions(s, options); - }, - 'x' => { - var sub_bw: Writer = .fixed(&buf); - sub_bw.printFloatHexadecimal(value, options.precision) catch unreachable; - return w.alignBufferOptions(sub_bw.buffered(), options); - }, - else => invalidFmtError(fmt, value), +/// Uses a smaller stack buffer; asserts mode is not decimal or scientific. +pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number) Error!void { + var buf: [50]u8 = undefined; // for aligning + var sub_writer: Writer = .fixed(&buf); + switch (options.mode) { + .decimal => unreachable, + .scientific => unreachable, + .binary => @panic("TODO"), + .octal => @panic("TODO"), + .hex => {}, } + printFloatHex(&sub_writer, value, options.case, options.precision) catch unreachable; // buf is large enough + + const printed = sub_writer.buffered(); + return w.alignBuffer(printed, options.width orelse printed.len, options.alignment, options.fill); } -pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void { +pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void { if (std.math.signbit(value)) try w.writeByte('-'); - if (std.math.isNan(value)) return w.writeAll("nan"); - if (std.math.isInf(value)) return w.writeAll("inf"); + if (std.math.isNan(value)) return w.writeAll(switch (case) { + .lower => "nan", + .upper => "NAN", + }); + if (std.math.isInf(value)) return w.writeAll(switch (case) { + .lower => "inf", + .upper => "INF", + }); const T = @TypeOf(value); const TU = std.meta.Int(.unsigned, @bitSizeOf(T)); @@ -1302,7 +1577,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) // +1 for the decimal part. var buf: [1 + mantissa_digits]u8 = undefined; - assert(std.fmt.printInt(&buf, mantissa, 16, .lower, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len); + assert(std.fmt.printInt(&buf, mantissa, 16, case, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len); try w.writeAll("0x"); try w.writeByte(buf[0]); @@ -1319,7 +1594,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) try w.splatByteAll('0', precision - trimmed.len); }; try w.writeAll("p"); - try w.printIntOptions(exponent - exponent_bias, 10, .lower, .{}); + try w.printInt(exponent - exponent_bias, 10, case, .{}); } pub const ByteSizeUnits = enum { @@ -1415,7 +1690,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { }) |unit| { if (ns_remaining >= unit.ns) { const units = ns_remaining / unit.ns; - try w.printIntOptions(units, 10, .lower, .{}); + try w.printInt(units, 10, .lower, .{}); try w.writeByte(unit.sep); ns_remaining -= units * unit.ns; if (ns_remaining == 0) return; @@ -1429,13 +1704,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { }) |unit| { const kunits = ns_remaining * 1000 / unit.ns; if (kunits >= 1000) { - try w.printIntOptions(kunits / 1000, 10, .lower, .{}); + try w.printInt(kunits / 1000, 10, .lower, .{}); const frac = kunits % 1000; if (frac > 0) { // Write up to 3 decimal places var decimal_buf = [_]u8{ '.', 0, 0, 0 }; var inner: Writer = .fixed(decimal_buf[1..]); - inner.printIntOptions(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable; + inner.printInt(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable; var end: usize = 4; while (end > 1) : (end -= 1) { if (decimal_buf[end - 1] != '0') break; @@ -1446,7 +1721,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { } } - try w.printIntOptions(ns_remaining, 10, .lower, .{}); + try w.printInt(ns_remaining, 10, .lower, .{}); try w.writeAll("ns"); } @@ -1456,12 +1731,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void { // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24 var buf: [24]u8 = undefined; - var sub_bw: Writer = .fixed(&buf); - switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { - .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable, - .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable, + var sub_writer: Writer = .fixed(&buf); + if (@TypeOf(nanoseconds) == comptime_int) { + if (nanoseconds >= 0) { + sub_writer.printDurationUnsigned(nanoseconds) catch unreachable; + } else { + sub_writer.printDurationSigned(nanoseconds) catch unreachable; + } + } else switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { + .signed => sub_writer.printDurationSigned(nanoseconds) catch unreachable, + .unsigned => sub_writer.printDurationUnsigned(nanoseconds) catch unreachable, } - return w.alignBufferOptions(sub_bw.buffered(), options); + return w.alignBufferOptions(sub_writer.buffered(), options); } 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 { } } -test "formatValue max_depth" { +test "printValue max_depth" { const Vec2 = struct { const SelfType = @This(); x: f32, y: f32, - pub fn format( - self: SelfType, - comptime fmt: []const u8, - options: std.fmt.Options, - w: *Writer, - ) Error!void { - _ = options; - if (fmt.len == 0) { - return w.print("({d:.3},{d:.3})", .{ self.x, self.y }); - } else { - @compileError("unknown format string: '" ++ fmt ++ "'"); - } + pub fn format(self: SelfType, w: *Writer) Error!void { + return w.print("({d:.3},{d:.3})", .{ self.x, self.y }); } }; const E = enum { @@ -1598,133 +1869,133 @@ test "formatValue max_depth" { var buf: [1000]u8 = undefined; var w: Writer = .fixed(&buf); try w.printValue("", .{}, inst, 0); - try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ ... }", w.buffered()); + try testing.expectEqualStrings(".{ ... }", w.buffered()); - w.reset(); + w = .fixed(&buf); try w.printValue("", .{}, inst, 1); - 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()); + try testing.expectEqualStrings(".{ .a = .{ ... }, .tu = .{ ... }, .e = .Two, .vec = .{ ... } }", w.buffered()); - w.reset(); + w = .fixed(&buf); try w.printValue("", .{}, inst, 2); - 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()); + try testing.expectEqualStrings(".{ .a = .{ .a = .{ ... }, .tu = .{ ... }, .e = .Two, .vec = .{ ... } }, .tu = .{ .ptr = .{ ... } }, .e = .Two, .vec = .{ .x = 10.2, .y = 2.22 } }", w.buffered()); - w.reset(); + w = .fixed(&buf); try w.printValue("", .{}, inst, 3); - 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()); + 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()); const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 }; - w.reset(); + w = .fixed(&buf); try w.printValue("", .{}, vec, 0); try testing.expectEqualStrings("{ ... }", w.buffered()); - w.reset(); + w = .fixed(&buf); try w.printValue("", .{}, vec, 1); try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered()); } test printDuration { - testDurationCase("0ns", 0); - testDurationCase("1ns", 1); - testDurationCase("999ns", std.time.ns_per_us - 1); - testDurationCase("1us", std.time.ns_per_us); - testDurationCase("1.45us", 1450); - testDurationCase("1.5us", 3 * std.time.ns_per_us / 2); - testDurationCase("14.5us", 14500); - testDurationCase("145us", 145000); - testDurationCase("999.999us", std.time.ns_per_ms - 1); - testDurationCase("1ms", std.time.ns_per_ms + 1); - testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2); - testDurationCase("1.11ms", 1110000); - testDurationCase("1.111ms", 1111000); - testDurationCase("1.111ms", 1111100); - testDurationCase("999.999ms", std.time.ns_per_s - 1); - testDurationCase("1s", std.time.ns_per_s); - testDurationCase("59.999s", std.time.ns_per_min - 1); - testDurationCase("1m", std.time.ns_per_min); - testDurationCase("1h", std.time.ns_per_hour); - testDurationCase("1d", std.time.ns_per_day); - testDurationCase("1w", std.time.ns_per_week); - testDurationCase("1y", 365 * std.time.ns_per_day); - testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1 - testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); - testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); - testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); - testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); - testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); - testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); - testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64)); + try testDurationCase("0ns", 0); + try testDurationCase("1ns", 1); + try testDurationCase("999ns", std.time.ns_per_us - 1); + try testDurationCase("1us", std.time.ns_per_us); + try testDurationCase("1.45us", 1450); + try testDurationCase("1.5us", 3 * std.time.ns_per_us / 2); + try testDurationCase("14.5us", 14500); + try testDurationCase("145us", 145000); + try testDurationCase("999.999us", std.time.ns_per_ms - 1); + try testDurationCase("1ms", std.time.ns_per_ms + 1); + try testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2); + try testDurationCase("1.11ms", 1110000); + try testDurationCase("1.111ms", 1111000); + try testDurationCase("1.111ms", 1111100); + try testDurationCase("999.999ms", std.time.ns_per_s - 1); + try testDurationCase("1s", std.time.ns_per_s); + try testDurationCase("59.999s", std.time.ns_per_min - 1); + try testDurationCase("1m", std.time.ns_per_min); + try testDurationCase("1h", std.time.ns_per_hour); + try testDurationCase("1d", std.time.ns_per_day); + try testDurationCase("1w", std.time.ns_per_week); + try testDurationCase("1y", 365 * std.time.ns_per_day); + try testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1 + 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); + 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); + try testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); + try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); + try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); + try testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); + try testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64)); - testing.expectFmt("=======0ns", "{D:=>10}", .{0}); - testing.expectFmt("1ns=======", "{D:=<10}", .{1}); - testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1}); + try testing.expectFmt("=======0ns", "{D:=>10}", .{0}); + try testing.expectFmt("1ns=======", "{D:=<10}", .{1}); + try testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1}); } test printDurationSigned { - testDurationCaseSigned("0ns", 0); - testDurationCaseSigned("1ns", 1); - testDurationCaseSigned("-1ns", -(1)); - testDurationCaseSigned("999ns", std.time.ns_per_us - 1); - testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1)); - testDurationCaseSigned("1us", std.time.ns_per_us); - testDurationCaseSigned("-1us", -(std.time.ns_per_us)); - testDurationCaseSigned("1.45us", 1450); - testDurationCaseSigned("-1.45us", -(1450)); - testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2); - testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2)); - testDurationCaseSigned("14.5us", 14500); - testDurationCaseSigned("-14.5us", -(14500)); - testDurationCaseSigned("145us", 145000); - testDurationCaseSigned("-145us", -(145000)); - testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1); - testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1)); - testDurationCaseSigned("1ms", std.time.ns_per_ms + 1); - testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1)); - testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2); - testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2)); - testDurationCaseSigned("1.11ms", 1110000); - testDurationCaseSigned("-1.11ms", -(1110000)); - testDurationCaseSigned("1.111ms", 1111000); - testDurationCaseSigned("-1.111ms", -(1111000)); - testDurationCaseSigned("1.111ms", 1111100); - testDurationCaseSigned("-1.111ms", -(1111100)); - testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1); - testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1)); - testDurationCaseSigned("1s", std.time.ns_per_s); - testDurationCaseSigned("-1s", -(std.time.ns_per_s)); - testDurationCaseSigned("59.999s", std.time.ns_per_min - 1); - testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1)); - testDurationCaseSigned("1m", std.time.ns_per_min); - testDurationCaseSigned("-1m", -(std.time.ns_per_min)); - testDurationCaseSigned("1h", std.time.ns_per_hour); - testDurationCaseSigned("-1h", -(std.time.ns_per_hour)); - testDurationCaseSigned("1d", std.time.ns_per_day); - testDurationCaseSigned("-1d", -(std.time.ns_per_day)); - testDurationCaseSigned("1w", std.time.ns_per_week); - testDurationCaseSigned("-1w", -(std.time.ns_per_week)); - testDurationCaseSigned("1y", 365 * std.time.ns_per_day); - testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day)); - testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d - testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d - testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); - testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms)); - testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); - testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us)); - testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); - testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1)); - testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); - testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms)); - testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); - testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1)); - testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); - testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999)); - testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64)); - testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1); - testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64)); + try testDurationCaseSigned("0ns", 0); + try testDurationCaseSigned("1ns", 1); + try testDurationCaseSigned("-1ns", -(1)); + try testDurationCaseSigned("999ns", std.time.ns_per_us - 1); + try testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1)); + try testDurationCaseSigned("1us", std.time.ns_per_us); + try testDurationCaseSigned("-1us", -(std.time.ns_per_us)); + try testDurationCaseSigned("1.45us", 1450); + try testDurationCaseSigned("-1.45us", -(1450)); + try testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2); + try testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2)); + try testDurationCaseSigned("14.5us", 14500); + try testDurationCaseSigned("-14.5us", -(14500)); + try testDurationCaseSigned("145us", 145000); + try testDurationCaseSigned("-145us", -(145000)); + try testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1); + try testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1)); + try testDurationCaseSigned("1ms", std.time.ns_per_ms + 1); + try testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1)); + try testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2); + try testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2)); + try testDurationCaseSigned("1.11ms", 1110000); + try testDurationCaseSigned("-1.11ms", -(1110000)); + try testDurationCaseSigned("1.111ms", 1111000); + try testDurationCaseSigned("-1.111ms", -(1111000)); + try testDurationCaseSigned("1.111ms", 1111100); + try testDurationCaseSigned("-1.111ms", -(1111100)); + try testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1); + try testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1)); + try testDurationCaseSigned("1s", std.time.ns_per_s); + try testDurationCaseSigned("-1s", -(std.time.ns_per_s)); + try testDurationCaseSigned("59.999s", std.time.ns_per_min - 1); + try testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1)); + try testDurationCaseSigned("1m", std.time.ns_per_min); + try testDurationCaseSigned("-1m", -(std.time.ns_per_min)); + try testDurationCaseSigned("1h", std.time.ns_per_hour); + try testDurationCaseSigned("-1h", -(std.time.ns_per_hour)); + try testDurationCaseSigned("1d", std.time.ns_per_day); + try testDurationCaseSigned("-1d", -(std.time.ns_per_day)); + try testDurationCaseSigned("1w", std.time.ns_per_week); + try testDurationCaseSigned("-1w", -(std.time.ns_per_week)); + try testDurationCaseSigned("1y", 365 * std.time.ns_per_day); + try testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day)); + try testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d + try testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d + 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); + 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)); + 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); + 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)); + try testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); + try testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1)); + try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); + try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms)); + try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); + try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1)); + try testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); + try testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999)); + try testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64)); + try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1); + try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64)); - testing.expectFmt("=======0ns", "{s:=>10}", .{0}); - testing.expectFmt("1ns=======", "{s:=<10}", .{1}); - testing.expectFmt("-1ns======", "{s:=<10}", .{-(1)}); - testing.expectFmt(" -999ns ", "{s:^10}", .{-(std.time.ns_per_us - 1)}); + try testing.expectFmt("=======0ns", "{D:=>10}", .{0}); + try testing.expectFmt("1ns=======", "{D:=<10}", .{1}); + try testing.expectFmt("-1ns======", "{D:=<10}", .{-(1)}); + try testing.expectFmt(" -999ns ", "{D:^10}", .{-(std.time.ns_per_us - 1)}); } fn testDurationCase(expected: []const u8, input: u64) !void { @@ -1741,7 +2012,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void { try testing.expectEqualStrings(expected, w.buffered()); } -test printIntOptions { +test printInt { try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{}); try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{}); @@ -1757,27 +2028,22 @@ test printIntOptions { try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 }); try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 }); -} -test "printInt with comptime_int" { - var buf: [20]u8 = undefined; - var w: Writer = .fixed(&buf); - try w.printInt(@as(comptime_int, 123456789123456789), "", .{}); - try std.testing.expectEqualStrings("123456789123456789", w.buffered()); + try testPrintIntCase("123456789123456789", @as(comptime_int, 123456789123456789), 10, .lower, .{}); } test "printFloat with comptime_float" { var buf: [20]u8 = undefined; var w: Writer = .fixed(&buf); - try w.printFloat("", .{}, @as(comptime_float, 1.0)); - try std.testing.expectEqualStrings(w.buffered(), "1e0"); - try std.testing.expectFmt("1e0", "{}", .{1.0}); + try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower)); + try testing.expectEqualStrings(w.buffered(), "1e0"); + try testing.expectFmt("1", "{}", .{1.0}); } fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void { var buffer: [100]u8 = undefined; var w: Writer = .fixed(&buffer); - w.printIntOptions(value, base, case, options); + try w.printInt(value, base, case, options); try testing.expectEqualStrings(expected, w.buffered()); } @@ -1798,12 +2064,12 @@ test printByteSize { test "bytes.hex" { const some_bytes = "\xCA\xFE\xBA\xBE"; - try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); - try std.testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); - try std.testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); - try std.testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); + try testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); + try testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); + try testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); + try testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try std.testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); + try testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); } test fixed { @@ -1832,17 +2098,22 @@ test "fixed output" { try w.writeAll("world"); try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld")); - try testing.expectError(error.WriteStreamEnd, w.writeAll("!")); + try testing.expectError(error.WriteFailed, w.writeAll("!")); try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld")); - w.reset(); + w = .fixed(&buffer); + try testing.expect(w.buffered().len == 0); - try testing.expectError(error.WriteStreamEnd, w.writeAll("Hello world!")); + try testing.expectError(error.WriteFailed, w.writeAll("Hello world!")); try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl")); +} - try w.seekTo((try w.getEndPos()) + 1); - try testing.expectError(error.WriteStreamEnd, w.writeAll("H")); +test "writeSplat 0 len splat larger than capacity" { + var buf: [8]u8 = undefined; + var w: std.io.Writer = .fixed(&buf); + const n = try w.writeSplat(&.{"something that overflows buf"}, 0); + try testing.expectEqual(0, n); } 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 return error.WriteFailed; } -pub fn discardingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { - const slice = data[0 .. data.len - 1]; - const pattern = data[slice.len..]; - var written: usize = pattern.len * splat; - for (slice) |bytes| written += bytes.len; - w.end = 0; - return written; -} +pub const Discarding = struct { + count: u64, + writer: Writer, -pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize { - if (File.Handle == void) return error.Unimplemented; - w.end = 0; - if (file_reader.getSize()) |size| { - const n = limit.minInt(size - file_reader.pos); - file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; + pub fn init(buffer: []u8) Discarding { + return .{ + .count = 0, + .writer = .{ + .vtable = &.{ + .drain = Discarding.drain, + .sendFile = Discarding.sendFile, + }, + .buffer = buffer, + }, + }; + } + + pub fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { + const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); + const slice = data[0 .. data.len - 1]; + const pattern = data[slice.len..]; + var written: usize = pattern.len * splat; + for (slice) |bytes| written += bytes.len; + d.count += w.end + written; + w.end = 0; + return written; + } + + pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize { + if (File.Handle == void) return error.Unimplemented; + const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); + d.count += w.end; w.end = 0; - return n; - } else |_| { - // Error is observable on `file_reader` instance, and it is better to - // treat the file as a pipe. - return error.Unimplemented; + if (file_reader.getSize()) |size| { + const n = limit.minInt64(size - file_reader.pos); + file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; + w.end = 0; + d.count += n; + return n; + } else |_| { + // Error is observable on `file_reader` instance, and it is better to + // treat the file as a pipe. + return error.Unimplemented; + } } -} +}; /// Removes the first `n` bytes from `buffer` by shifting buffer contents, /// returning how many bytes are left after consuming the entire buffer, or @@ -1966,28 +2260,27 @@ pub fn Hashed(comptime Hasher: type) type { return struct { out: *Writer, hasher: Hasher, - interface: Writer, + writer: Writer, - pub fn init(out: *Writer) @This() { + pub fn init(out: *Writer, buffer: []u8) @This() { + return .initHasher(out, .{}, buffer); + } + + pub fn initHasher(out: *Writer, hasher: Hasher, buffer: []u8) @This() { return .{ .out = out, - .hasher = .{}, - .interface = .{ - .vtable = &.{@This().drain}, + .hasher = hasher, + .writer = .{ + .buffer = buffer, + .vtable = &.{ .drain = @This().drain }, }, }; } fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { - const this: *@This() = @alignCast(@fieldParentPtr("interface", w)); - if (data.len == 0) { - const buf = w.buffered(); - try this.out.writeAll(buf); - this.hasher.update(buf); - w.end = 0; - return buf.len; - } - const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat); + const this: *@This() = @alignCast(@fieldParentPtr("writer", w)); + const aux = w.buffered(); + const aux_n = try this.out.writeSplatHeader(aux, data, splat); if (aux_n < w.end) { this.hasher.update(w.buffer[0..aux_n]); const remaining = w.buffer[aux_n..w.end]; @@ -1995,29 +2288,20 @@ pub fn Hashed(comptime Hasher: type) type { w.end = remaining.len; return 0; } - this.hasher.update(w.buffered()); + this.hasher.update(aux); const n = aux_n - w.end; w.end = 0; var remaining: usize = n; - const short_data = data[0 .. data.len - @intFromBool(splat == 0)]; - for (short_data) |slice| { - if (remaining < slice.len) { + for (data[0 .. data.len - 1]) |slice| { + if (remaining <= slice.len) { this.hasher.update(slice[0..remaining]); return n; - } else { - remaining -= slice.len; - this.hasher.update(slice); } + remaining -= slice.len; + this.hasher.update(slice); } - const remaining_splat = switch (splat) { - 0, 1 => { - assert(remaining == 0); - return n; - }, - else => splat - 1, - }; const pattern = data[data.len - 1]; - assert(remaining == remaining_splat * pattern.len); + assert(remaining == splat * pattern.len); switch (pattern.len) { 0 => { assert(remaining == 0); @@ -2053,12 +2337,12 @@ pub fn Hashed(comptime Hasher: type) type { /// When using this API, it is not necessary to call `flush`. pub const Allocating = struct { allocator: Allocator, - interface: Writer, + writer: Writer, pub fn init(allocator: Allocator) Allocating { return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .buffer = &.{}, .vtable = &vtable, }, @@ -2068,7 +2352,7 @@ pub const Allocating = struct { pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating { return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .buffer = try allocator.alloc(u8, capacity), .vtable = &vtable, }, @@ -2078,7 +2362,7 @@ pub const Allocating = struct { pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating { return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .buffer = slice, .vtable = &vtable, }, @@ -2090,7 +2374,7 @@ pub const Allocating = struct { defer array_list.* = .empty; return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .vtable = &vtable, .buffer = array_list.allocatedSlice(), .end = array_list.items.len, @@ -2105,14 +2389,14 @@ pub const Allocating = struct { }; pub fn deinit(a: *Allocating) void { - a.allocator.free(a.interface.buffer); + a.allocator.free(a.writer.buffer); a.* = undefined; } /// Returns an array list that takes ownership of the allocated memory. /// Resets the `Allocating` to an empty state. pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) { - const w = &a.interface; + const w = &a.writer; const result: std.ArrayListUnmanaged(u8) = .{ .items = w.buffer[0..w.end], .capacity = w.buffer.len, @@ -2134,13 +2418,11 @@ pub const Allocating = struct { } pub fn getWritten(a: *Allocating) []u8 { - return a.interface.buffered(); + return a.writer.buffered(); } pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void { - const shrink_by = a.interface.end - new_len; - a.interface.end = new_len; - a.interface.count -= shrink_by; + a.writer.end = new_len; } pub fn clearRetainingCapacity(a: *Allocating) void { @@ -2148,15 +2430,18 @@ pub const Allocating = struct { } fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { - const a: *Allocating = @fieldParentPtr("interface", w); + const a: *Allocating = @fieldParentPtr("writer", w); const gpa = a.allocator; const pattern = data[data.len - 1]; const splat_len = pattern.len * splat; var list = a.toArrayList(); defer setArrayList(a, list); const start_len = list.items.len; + // Even if we append no data, this function needs to ensure there is more + // capacity in the buffer to avoid infinite loop, hence the +1 in this loop. + assert(data.len != 0); for (data) |bytes| { - list.ensureUnusedCapacity(gpa, bytes.len + splat_len) catch return error.WriteFailed; + list.ensureUnusedCapacity(gpa, bytes.len + splat_len + 1) catch return error.WriteFailed; list.appendSliceAssumeCapacity(bytes); } if (splat == 0) { @@ -2171,13 +2456,13 @@ pub const Allocating = struct { fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize { if (File.Handle == void) return error.Unimplemented; - const a: *Allocating = @fieldParentPtr("interface", w); + const a: *Allocating = @fieldParentPtr("writer", w); const gpa = a.allocator; var list = a.toArrayList(); defer setArrayList(a, list); const pos = file_reader.pos; const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line; - list.ensureUnusedCapacity(gpa, limit.minInt(additional)) catch return error.WriteFailed; + list.ensureUnusedCapacity(gpa, limit.minInt64(additional)) catch return error.WriteFailed; const dest = limit.slice(list.unusedCapacitySlice()); const n = file_reader.read(dest) catch |err| switch (err) { error.ReadFailed => return error.ReadFailed, @@ -2188,14 +2473,14 @@ pub const Allocating = struct { } fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void { - a.interface.buffer = list.allocatedSlice(); - a.interface.end = list.items.len; + a.writer.buffer = list.allocatedSlice(); + a.writer.end = list.items.len; } test Allocating { - var a: Allocating = .init(std.testing.allocator); + var a: Allocating = .init(testing.allocator); defer a.deinit(); - const w = &a.interface; + const w = &a.writer; const x: i32 = 42; const y: i32 = 1234; diff --git a/lib/std/io/change_detection_stream.zig b/lib/std/io/change_detection_stream.zig index 5ba2bb3c103b4b74a3719ef2671b76e3d6b620f9..d9da1c4a0eb0d0a934a00cf882cccf19b88c35a7 100644 --- a/lib/std/io/change_detection_stream.zig +++ b/lib/std/io/change_detection_stream.zig @@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type { return struct { const Self = @This(); pub const Error = WriterType.Error; - pub const Writer = io.Writer(*Self, Error, write); + pub const Writer = io.GenericWriter(*Self, Error, write); anything_changed: bool, underlying_writer: WriterType, diff --git a/lib/std/io/find_byte_writer.zig b/lib/std/io/find_byte_writer.zig index cb7efac2d9358969cac9cbdfe2c78af565f7627b..fe6836f6037bb1fcb8381c0cfbb0cebc15573a93 100644 --- a/lib/std/io/find_byte_writer.zig +++ b/lib/std/io/find_byte_writer.zig @@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type { return struct { const Self = @This(); pub const Error = UnderlyingWriter.Error; - pub const Writer = io.Writer(*Self, Error, write); + pub const Writer = io.GenericWriter(*Self, Error, write); underlying_writer: UnderlyingWriter, byte_found: bool, diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index 4cc9847e86939983c2b70260a0694280635ea92c..b6bd835f84881a8f9516526efdbda1638a2700f6 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -24,7 +24,7 @@ test "write a file, read it, then delete it" { var file = try tmp.dir.createFile(tmp_file_name, .{}); defer file.close(); - var buf_stream = io.bufferedWriter(file.writer()); + var buf_stream = io.bufferedWriter(file.deprecatedWriter()); const st = buf_stream.writer(); try st.print("begin", .{}); try st.writeAll(data[0..]); @@ -45,7 +45,7 @@ test "write a file, read it, then delete it" { const expected_file_size: u64 = "begin".len + data.len + "end".len; try expectEqual(expected_file_size, file_size); - var buf_stream = io.bufferedReader(file.reader()); + var buf_stream = io.bufferedReader(file.deprecatedReader()); const st = buf_stream.reader(); const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); defer std.testing.allocator.free(contents); @@ -66,7 +66,7 @@ test "BitStreams with File Stream" { var file = try tmp.dir.createFile(tmp_file_name, .{}); defer file.close(); - var bit_stream = io.bitWriter(native_endian, file.writer()); + var bit_stream = io.bitWriter(native_endian, file.deprecatedWriter()); try bit_stream.writeBits(@as(u2, 1), 1); try bit_stream.writeBits(@as(u5, 2), 2); @@ -80,7 +80,7 @@ test "BitStreams with File Stream" { var file = try tmp.dir.openFile(tmp_file_name, .{}); defer file.close(); - var bit_stream = io.bitReader(native_endian, file.reader()); + var bit_stream = io.bitReader(native_endian, file.deprecatedReader()); var out_bits: u16 = undefined; diff --git a/lib/std/io/tty.zig b/lib/std/io/tty.zig index 1e9cd0d9bca03e07d98c70e8d6f071f2da68f698..fa17d9a16def9331c778c9de74abc8a41721639a 100644 --- a/lib/std/io/tty.zig +++ b/lib/std/io/tty.zig @@ -5,36 +5,9 @@ const process = std.process; const windows = std.os.windows; const native_os = builtin.os.tag; -/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr). -/// This includes feature checks for ANSI escape codes and the Windows console API, as well as -/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default. -/// Will attempt to enable ANSI escape code support if necessary/possible. +/// Deprecated in favor of `Config.detect`. pub fn detectConfig(file: File) Config { - const force_color: ?bool = if (builtin.os.tag == .wasi) - null // wasi does not support environment variables - else if (process.hasNonEmptyEnvVarConstant("NO_COLOR")) - false - else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE")) - true - else - null; - - if (force_color == false) return .no_color; - - if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes; - - if (native_os == .windows and file.isTty()) { - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) { - return if (force_color == true) .escape_codes else .no_color; - } - return .{ .windows_api = .{ - .handle = file.handle, - .reset_attributes = info.wAttributes, - } }; - } - - return if (force_color == true) .escape_codes else .no_color; + return .detect(file); } pub const Color = enum { @@ -66,6 +39,38 @@ pub const Config = union(enum) { escape_codes, windows_api: if (native_os == .windows) WindowsContext else void, + /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr). + /// This includes feature checks for ANSI escape codes and the Windows console API, as well as + /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default. + /// Will attempt to enable ANSI escape code support if necessary/possible. + pub fn detect(file: File) Config { + const force_color: ?bool = if (builtin.os.tag == .wasi) + null // wasi does not support environment variables + else if (process.hasNonEmptyEnvVarConstant("NO_COLOR")) + false + else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE")) + true + else + null; + + if (force_color == false) return .no_color; + + if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes; + + if (native_os == .windows and file.isTty()) { + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) { + return if (force_color == true) .escape_codes else .no_color; + } + return .{ .windows_api = .{ + .handle = file.handle, + .reset_attributes = info.wAttributes, + } }; + } + + return if (force_color == true) .escape_codes else .no_color; + } + pub const WindowsContext = struct { handle: File.Handle, reset_attributes: u16, @@ -123,6 +128,7 @@ pub const Config = union(enum) { .dim => windows.FOREGROUND_INTENSITY, .reset => ctx.reset_attributes, }; + try w.flush(); try windows.SetConsoleTextAttribute(ctx.handle, attributes); } else { unreachable; diff --git a/lib/std/json.zig b/lib/std/json.zig index 3fcb256f124b5d205745a4813c8af2071b7c9f85..246c98817e92098373188dd8af489670750340a7 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1,12 +1,12 @@ //! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259 //! //! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs, -//! The `Reader` API connects a `std.io.Reader` to a `Scanner`. +//! The `Reader` API connects a `std.io.GenericReader` to a `Scanner`. //! //! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type. //! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection. //! -//! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.Writer`. +//! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.GenericWriter`. //! The high-level `stringify` serializes a Zig or `Value` type into JSON. const builtin = @import("builtin"); diff --git a/lib/std/json/dynamic.zig b/lib/std/json/dynamic.zig index 499c5c0cf9eac24a8e078cdd8fb4940a4d311943..4754bdb8591232f7bb92e1a076da6df9671405d1 100644 --- a/lib/std/json/dynamic.zig +++ b/lib/std/json/dynamic.zig @@ -51,10 +51,10 @@ pub const Value = union(enum) { } pub fn dump(v: Value) void { - const bw = std.debug.lockStderrWriter(&.{}); + const w = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - json.Stringify.value(v, .{}, bw) catch return; + json.Stringify.value(v, .{}, w) catch return; } pub fn jsonStringify(value: @This(), jws: anytype) !void { diff --git a/lib/std/json/dynamic_test.zig b/lib/std/json/dynamic_test.zig index d54b930fae964886fe2c1a49d49a88ebaec92121..a988e2c2c35555f3a5f127b7ef225e998440b699 100644 --- a/lib/std/json/dynamic_test.zig +++ b/lib/std/json/dynamic_test.zig @@ -251,7 +251,7 @@ test "Value.jsonStringify" { \\ true, \\ 42, \\ 43, - \\ 4.2e1, + \\ 42, \\ "weeee", \\ [ \\ 1, diff --git a/lib/std/json/scanner.zig b/lib/std/json/scanner.zig index 85a058af385bef3a05c653b7f3e6cebda5dc7d94..1836d6775bec3c4115240d166c92561848da918c 100644 --- a/lib/std/json/scanner.zig +++ b/lib/std/json/scanner.zig @@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always }; /// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`. pub const default_max_value_len = 4 * 1024 * 1024; -/// Connects a `std.io.Reader` to a `std.json.Scanner`. +/// Connects a `std.io.GenericReader` to a `std.json.Scanner`. /// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader. pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type { return struct { diff --git a/lib/std/log.zig b/lib/std/log.zig index 2c51b9ddee5f7957d361fce3b4f3d918da450a7e..5bfb812bf3c535f98c31aa38777f619bab4b3ac2 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -45,9 +45,8 @@ //! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix; //! //! // Print the message to stderr, silently ignoring any errors -//! std.debug.lockStdErr(); -//! defer std.debug.unlockStdErr(); -//! const stderr = std.fs.File.stderr().writer(); +//! const stderr = std.debug.lockStderrWriter(&.{}); +//! defer std.debug.unlockStderrWriter(); //! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return; //! } //! @@ -101,8 +100,7 @@ pub const Level = enum { /// The default log level is based on build mode. pub const default_level: Level = switch (builtin.mode) { .Debug => .debug, - .ReleaseSafe => .info, - .ReleaseFast, .ReleaseSmall => .err, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info, }; const level = std.options.log_level; @@ -148,10 +146,10 @@ pub fn defaultLog( ) void { const level_txt = comptime message_level.asText(); const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; - var buffer: [1024]u8 = undefined; - const bw = std.debug.lockStderrWriter(&buffer); + var buffer: [32]u8 = undefined; + const stderr = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); - bw.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; + nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; } /// Returns a scoped logging namespace that logs all messages using the scope diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index a8bb42f3a6a38b8b9dcd7d14668d5e2d3638c726..33938dbd42abf2072cf74cba08d1b429206229a8 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2028,6 +2028,14 @@ pub const Mutable = struct { pub fn normalize(r: *Mutable, length: usize) void { r.len = llnormalize(r.limbs[0..length]); } + + pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void { + return formatNumber(self, w, .{}); + } + + pub fn formatNumber(self: Const, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void { + return self.toConst().formatNumber(w, n); + } }; /// A arbitrary-precision big integer, with a fixed set of immutable limbs. @@ -2317,46 +2325,25 @@ pub const Const = struct { return .{ normalized_res.reconstruct(if (self.positive) .positive else .negative), exactness }; } - /// To allow `std.fmt.format` to work with this type. /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, /// this function will fail to print the string, printing "(BigInt)" instead of a number. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// See `toString` and `toStringAlloc` for a way to print big integers without failure. - pub fn format(self: Const, bw: *std.io.Writer, comptime fmt: []const u8) !void { - comptime var base = 10; - comptime var case: std.fmt.Case = .lower; + pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void { + const available_len = 64; + if (self.limbs.len > available_len) + return w.writeAll("(BigInt)"); - if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) { - base = 10; - case = .lower; - } else if (comptime mem.eql(u8, fmt, "b")) { - base = 2; - case = .lower; - } else if (comptime mem.eql(u8, fmt, "x")) { - base = 16; - case = .lower; - } else if (comptime mem.eql(u8, fmt, "X")) { - base = 16; - case = .upper; - } else { - std.fmt.invalidFmtError(fmt, self); - } + var limbs: [calcToStringLimbsBufferLen(available_len, 10)]Limb = undefined; - const max_str_len = self.sizeInBaseUpperBound(base); - const limbs_len = calcToStringLimbsBufferLen(self.limbs.len, base); - if (bw.writableSliceGreedy(max_str_len + @alignOf(Limb) - 1 + @sizeOf(Limb) * limbs_len)) |buf| { - const limbs: [*]Limb = @alignCast(@ptrCast(std.mem.alignPointer(buf[max_str_len..].ptr, @alignOf(Limb)))); - bw.advance(self.toString(buf[0..max_str_len], base, case, limbs[0..limbs_len])); - return; - } else |_| if (bw.writableSliceGreedy(max_str_len)) |buf| { - const available_len = 64; - var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined; - if (limbs.len >= limbs_len) { - bw.advance(self.toString(buf, base, case, &limbs)); - return; - } - } else |_| {} - try bw.writeAll("(BigInt)"); + const biggest: Const = .{ + .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len), + .positive = false, + }; + var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined; + const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form"); + const len = self.toString(&buf, base, number.case, &limbs); + return w.writeAll(buf[0..len]); } /// Converts self to a string in the requested base. @@ -2926,17 +2913,16 @@ pub const Managed = struct { } /// To allow `std.fmt.format` to work with `Managed`. + pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void { + return formatNumber(self, w, .{}); + } + /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, /// this function will fail to print the string, printing "(BigInt)" instead of a number. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// See `toString` and `toStringAlloc` for a way to print big integers without failure. - pub fn format( - self: Managed, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - return self.toConst().format(fmt, options, out_stream); + pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void { + return self.toConst().formatNumber(w, n); } /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 5a0fda52b213711281a560987fb827c682496776..3683eb2bcd6e2b76cf479886ef1ea111949ffabf 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -3813,13 +3813,10 @@ test "(BigInt) positive" { try a.pow(&a, 64 * @sizeOf(Limb) * 8); try b.sub(&a, &c); - const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a}); - defer testing.allocator.free(a_fmt); + try testing.expectFmt("(BigInt)", "{d}", .{a}); - const b_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{b}); + const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b}); defer testing.allocator.free(b_fmt); - - try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)")); } @@ -3838,10 +3835,10 @@ test "(BigInt) negative" { a.negate(); try b.add(&a, &c); - const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a}); + const a_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{a}); defer testing.allocator.free(a_fmt); - const b_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{b}); + const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b}); defer testing.allocator.free(b_fmt); try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 2e224e5db76a77cd43b4b4352b09d94edb979c06..a5613536c6a417f26ef1d782e3f18d6eb71f688a 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1714,7 +1714,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian) } }, } - return @as(ReturnType, @truncate(result)); + return @truncate(result); } test readVarInt { diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index dc3e3f0325e4a199fea0cf5bf72eade10dc22d0b..b81a916702847ad4e71e3822fd633f4dfbac30f6 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -253,7 +253,7 @@ pub inline fn allocAdvancedWithRetAddr( n: usize, return_address: usize, ) Error![]align(if (alignment) |a| a.toByteUnits() else @alignOf(T)) T { - const a = comptime (alignment orelse Alignment.fromByteUnits(@alignOf(T))); + const a = comptime (alignment orelse Alignment.of(T)); const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address)); return ptr[0..n]; } diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 279a15079981865deb2555f22d899354ea39d68a..160a9f2fba6f2688ca537f34e1946e9317fedf37 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -991,6 +991,7 @@ test "0 sized struct" { test "struct with many fields" { const ManyFields = struct { fn Type(count: comptime_int) type { + @setEvalBranchQuota(50000); var fields: [count]std.builtin.Type.StructField = undefined; for (0..count) |i| { fields[i] = .{ diff --git a/lib/std/net.zig b/lib/std/net.zig index 0a4303b6f410509ac4566566b399e4863c9d5969..38a7675772a9847c7af17d4cd90c485932341d2a 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -164,22 +164,13 @@ pub const Address = extern union { } } - pub fn format( - self: Address, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); + pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void { switch (self.any.family) { - posix.AF.INET => try self.in.format(fmt, options, out_stream), - posix.AF.INET6 => try self.in6.format(fmt, options, out_stream), + posix.AF.INET => try self.in.format(w), + posix.AF.INET6 => try self.in6.format(w), posix.AF.UNIX => { - if (!has_unix_sockets) { - unreachable; - } - - try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)}); + if (!has_unix_sockets) unreachable; + try w.writeAll(std.mem.sliceTo(&self.un.path, 0)); }, else => unreachable, } @@ -352,22 +343,9 @@ pub const Ip4Address = extern struct { self.sa.port = mem.nativeToBig(u16, port); } - pub fn format( - self: Ip4Address, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); - _ = options; - const bytes = @as(*const [4]u8, @ptrCast(&self.sa.addr)); - try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{ - bytes[0], - bytes[1], - bytes[2], - bytes[3], - self.getPort(), - }); + pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void { + const bytes: *const [4]u8 = @ptrCast(&self.sa.addr); + try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() }); } pub fn getOsSockLen(self: Ip4Address) posix.socklen_t { @@ -656,17 +634,10 @@ pub const Ip6Address = extern struct { self.sa.port = mem.nativeToBig(u16, port); } - pub fn format( - self: Ip6Address, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); - _ = options; + pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void { const port = mem.bigToNative(u16, self.sa.port); if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { - try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ + try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{ self.sa.addr[12], self.sa.addr[13], self.sa.addr[14], @@ -714,14 +685,14 @@ pub const Ip6Address = extern struct { longest_len = 0; } - try out_stream.writeAll("["); + try w.writeAll("["); var i: usize = 0; var abbrv = false; while (i < native_endian_parts.len) : (i += 1) { if (i == longest_start) { // Emit "::" for the longest zero run if (!abbrv) { - try out_stream.writeAll(if (i == 0) "::" else ":"); + try w.writeAll(if (i == 0) "::" else ":"); abbrv = true; } i += longest_len - 1; // Skip the compressed range @@ -730,12 +701,12 @@ pub const Ip6Address = extern struct { if (abbrv) { abbrv = false; } - try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]}); + try w.print("{x}", .{native_endian_parts[i]}); if (i != native_endian_parts.len - 1) { - try out_stream.writeAll(":"); + try w.writeAll(":"); } } - try std.fmt.format(out_stream, "]:{}", .{port}); + try w.print("]:{}", .{port}); } pub fn getOsSockLen(self: Ip6Address) posix.socklen_t { @@ -898,7 +869,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis const name_c = try gpa.dupeZ(u8, name); defer gpa.free(name_c); - const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port}); + const port_c = try std.fmt.allocPrintSentinel(gpa, "{}", .{port}, 0); defer gpa.free(port_c); const ws2_32 = windows.ws2_32; diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 88a18094acdb553cbb77476f881229991421071a..02c927d566f0f39a19d067333f1a87779e6d47f5 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -5,20 +5,13 @@ const mem = std.mem; const testing = std.testing; test "parse and render IP addresses at comptime" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; comptime { - var ipAddrBuffer: [16]u8 = undefined; - // Parses IPv6 at comptime const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable; - var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable; - try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3])); + try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr}); - // Parses IPv4 at comptime const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable; - var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable; - try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2])); + try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr}); - // Returns error for invalid IP addresses at comptime try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0)); try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0)); 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" { } test "format IPv6 address with no zero runs" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0); - - var buffer: [50]u8 = undefined; - const result = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; - - try std.testing.expectEqualStrings("[2001:db8:1:2:3:4:5:6]:0", result); + try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr}); } test "parse IPv6 addresses and check compressed form" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - const alloc = testing.allocator; - - // 1) Parse an IPv6 address that should compress to [2001:db8::1:0:0:2]:0 - const addr1 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0); - - // 2) Parse an IPv6 address that should compress to [2001:db8::1:2]:0 - const addr2 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0); - - // 3) Parse an IPv6 address that should compress to [2001:db8:1:0:1::2]:0 - const addr3 = try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0); - - // Print each address in Zig's default "[ipv6]:port" form. - const printed1 = try std.fmt.allocPrint(alloc, "{any}", .{addr1}); - defer testing.allocator.free(printed1); - const printed2 = try std.fmt.allocPrint(alloc, "{any}", .{addr2}); - defer testing.allocator.free(printed2); - const printed3 = try std.fmt.allocPrint(alloc, "{any}", .{addr3}); - defer testing.allocator.free(printed3); - - // Check the exact compressed forms we expect. - try std.testing.expectEqualStrings("[2001:db8::1:0:0:2]:0", printed1); - try std.testing.expectEqualStrings("[2001:db8::1:2]:0", printed2); - try std.testing.expectEqualStrings("[2001:db8:1:0:1::2]:0", printed3); + try std.testing.expectFmt("[2001:db8::1:0:0:2]:0", "{f}", .{ + try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0), + }); + try std.testing.expectFmt("[2001:db8::1:2]:0", "{f}", .{ + try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0), + }); + try std.testing.expectFmt("[2001:db8:1:0:1::2]:0", "{f}", .{ + try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0), + }); } test "parse IPv6 address, check raw bytes" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - const expected_raw: [16]u8 = .{ 0x20, 0x01, 0x0d, 0xb8, // 2001:db8 0x00, 0x00, 0x00, 0x00, // :0000:0000 @@ -82,8 +51,6 @@ test "parse IPv6 address, check raw bytes" { } test "parse and render IPv6 addresses" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - var buffer: [100]u8 = undefined; const ips = [_][]const u8{ "FF01:0:0:0:0:0:0:FB", @@ -111,12 +78,12 @@ test "parse and render IPv6 addresses" { }; for (ips, 0..) |ip, i| { const addr = net.Address.parseIp6(ip, 0) catch unreachable; - var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable; try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); if (builtin.os.tag == .linux) { const addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable; - var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr_via_resolve}) catch unreachable; + var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr_via_resolve}) catch unreachable; try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3])); } } @@ -148,8 +115,6 @@ test "invalid but parseable IPv6 scope ids" { } test "parse and render IPv4 addresses" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - var buffer: [18]u8 = undefined; for ([_][]const u8{ "0.0.0.0", @@ -159,7 +124,7 @@ test "parse and render IPv4 addresses" { "127.0.0.1", }) |ip| { const addr = net.Address.parseIp4(ip, 0) catch unreachable; - var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable; try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); } @@ -175,10 +140,8 @@ test "parse and render UNIX addresses" { if (builtin.os.tag == .wasi) return error.SkipZigTest; if (!net.has_unix_sockets) return error.SkipZigTest; - var buffer: [14]u8 = undefined; const addr = net.Address.initUnix("/tmp/testpath") catch unreachable; - const fmt_addr = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; - try std.testing.expectEqualSlices(u8, "/tmp/testpath", fmt_addr); + try std.testing.expectFmt("/tmp/testpath", "{f}", .{addr}); const too_long = [_]u8{'a'} ** 200; try testing.expectError(error.NameTooLong, net.Address.initUnix(too_long[0..])); diff --git a/lib/std/os/freebsd.zig b/lib/std/os/freebsd.zig index cdc1973e043d35ee0e1ffbb5786d895b5855c6a2..4c68405c22e0b0b2541153a5a7db0e68a21f2f0a 100644 --- a/lib/std/os/freebsd.zig +++ b/lib/std/os/freebsd.zig @@ -4,7 +4,7 @@ const off_t = std.c.off_t; const unexpectedErrno = std.posix.unexpectedErrno; const errno = std.posix.errno; -pub const CopyFileRangeError = error{ +pub const CopyFileRangeError = std.posix.UnexpectedError || error{ /// If infd is not open for reading or outfd is not open for writing, or /// opened for writing with O_APPEND, or if infd and outfd refer to the /// same file. diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index e437e2f66e5d72d7434201acd38e3a361d4f98b5..6526f590fa2eebe40723ae1d51349cb8ba606c10 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -1,4 +1,5 @@ const std = @import("../std.zig"); +const assert = std.debug.assert; /// A protocol is an interface identified by a GUID. pub const protocol = @import("uefi/protocol.zig"); @@ -59,29 +60,19 @@ pub const Guid = extern struct { node: [6]u8, /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format - pub fn format( - self: @This(), - comptime f: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - if (f.len == 0) { - const time_low = @byteSwap(self.time_low); - const time_mid = @byteSwap(self.time_mid); - const time_high_and_version = @byteSwap(self.time_high_and_version); + pub fn format(self: @This(), writer: *std.io.Writer) std.io.Writer.Error!void { + const time_low = @byteSwap(self.time_low); + const time_mid = @byteSwap(self.time_mid); + const time_high_and_version = @byteSwap(self.time_high_and_version); - return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ - std.mem.asBytes(&time_low), - std.mem.asBytes(&time_mid), - std.mem.asBytes(&time_high_and_version), - std.mem.asBytes(&self.clock_seq_high_and_reserved), - std.mem.asBytes(&self.clock_seq_low), - std.mem.asBytes(&self.node), - }); - } else { - std.fmt.invalidFmtError(f, self); - } + return writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ + std.mem.asBytes(&time_low), + std.mem.asBytes(&time_mid), + std.mem.asBytes(&time_high_and_version), + std.mem.asBytes(&self.clock_seq_high_and_reserved), + std.mem.asBytes(&self.clock_seq_low), + std.mem.asBytes(&self.node), + }); } pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 33c2e7a548bd0e0d9301a772e7a98fa8a6b6100a..a388072e7469d9a245e162a5b1e3f82c99dfedc5 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -2812,9 +2812,8 @@ pub fn unexpectedError(err: Win32Error) UnexpectedError { buf_wstr.len, null, ); - std.debug.print("error.Unexpected: GetLastError({}): {}\n", .{ - @intFromEnum(err), - std.unicode.fmtUtf16Le(buf_wstr[0..len]), + std.debug.print("error.Unexpected: GetLastError({d}): {f}\n", .{ + err, std.unicode.fmtUtf16Le(buf_wstr[0..len]), }); std.debug.dumpCurrentStackTrace(@returnAddress()); } diff --git a/lib/std/os/windows/test.zig b/lib/std/os/windows/test.zig index b4cecefbd771ea3c5ddb00d4a8878ddf0ac6eaed..b78e4c323a3e3c9f0be26142bceb778e7d3d15a6 100644 --- a/lib/std/os/windows/test.zig +++ b/lib/std/os/windows/test.zig @@ -30,7 +30,7 @@ fn testToPrefixedFileNoOracle(comptime path: []const u8, comptime expected_path: const expected_path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(expected_path); const actual_path = try windows.wToPrefixedFileW(null, path_utf16); std.testing.expectEqualSlices(u16, expected_path_utf16, actual_path.span()) catch |e| { - std.debug.print("got '{s}', expected '{s}'\n", .{ std.unicode.fmtUtf16Le(actual_path.span()), std.unicode.fmtUtf16Le(expected_path_utf16) }); + std.debug.print("got '{f}', expected '{f}'\n", .{ std.unicode.fmtUtf16Le(actual_path.span()), std.unicode.fmtUtf16Le(expected_path_utf16) }); return e; }; } @@ -48,7 +48,7 @@ fn testToPrefixedFileOnlyOracle(comptime path: []const u8) !void { const zig_result = try windows.wToPrefixedFileW(null, path_utf16); const win32_api_result = try RtlDosPathNameToNtPathName_U(path_utf16); std.testing.expectEqualSlices(u16, win32_api_result.span(), zig_result.span()) catch |e| { - std.debug.print("got '{s}', expected '{s}'\n", .{ std.unicode.fmtUtf16Le(zig_result.span()), std.unicode.fmtUtf16Le(win32_api_result.span()) }); + std.debug.print("got '{f}', expected '{f}'\n", .{ std.unicode.fmtUtf16Le(zig_result.span()), std.unicode.fmtUtf16Le(win32_api_result.span()) }); return e; }; } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 059383a8d34b02d2844134691eda6756e40feaa1..db5b06828b232fa3a02ff74125e572745d18729a 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -651,7 +651,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void { } const file: fs.File = .{ .handle = fd }; - const stream = file.reader(); + const stream = file.deprecatedReader(); stream.readNoEof(buf) catch return error.Unexpected; } @@ -3936,6 +3936,7 @@ pub fn accept( .WSANOTINITIALISED => unreachable, // not initialized WSA .WSAECONNRESET => return error.ConnectionResetByPeer, .WSAEFAULT => unreachable, + .WSAENOTSOCK => return error.FileDescriptorNotASocket, .WSAEINVAL => return error.SocketNotListening, .WSAEMFILE => return error.ProcessFdQuotaExceeded, .WSAENETDOWN => return error.NetworkSubsystemFailed, @@ -4335,7 +4336,7 @@ pub const GetSockOptError = error{ } || UnexpectedError; pub fn getsockopt(fd: socket_t, level: i32, optname: u32, opt: []u8) GetSockOptError!void { - var len: socklen_t = undefined; + var len: socklen_t = @intCast(opt.len); switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) { .SUCCESS => { std.debug.assert(len == opt.len); diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 776fe615b3266b33be3b3dd7d43bb48c7a009f0c..993b8e28cdab790a175eb33d31cb7552099a4352 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -667,7 +667,7 @@ test "mmap" { const file = try tmp.dir.createFile(test_out_file, .{}); defer file.close(); - const stream = file.writer(); + const stream = file.deprecatedWriter(); var i: u32 = 0; while (i < alloc_size / @sizeOf(u32)) : (i += 1) { diff --git a/lib/std/process.zig b/lib/std/process.zig index 7b29405cbf8e9a93e6dcb974fd01a0972b9b5caa..58d16eef1db5cd3d96dcc5e35a187993e4679cd6 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1553,7 +1553,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); defer file.close(); - const reader = file.reader(); + const reader = file.deprecatedReader(); const State = enum { Start, diff --git a/lib/std/start.zig b/lib/std/start.zig index a74b83fe819467844f3fccfcf622046411d8f5c3..68311463b018c3267d38c23ba07d6b06a91c8c83 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -486,7 +486,7 @@ fn _start() callconv(.naked) noreturn { fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { // Switch from the x87 fpu state set by windows to the state expected by the gnu abi. - if (builtin.abi == .gnu) asm volatile ("fninit"); + if (builtin.cpu.arch.isX86() and builtin.abi == .gnu) asm volatile ("fninit"); if (!builtin.single_threaded and !builtin.link_libc) { _ = @import("os/windows/tls.zig"); @@ -499,7 +499,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { // Switch from the x87 fpu state set by windows to the state expected by the gnu abi. - if (builtin.abi == .gnu) asm volatile ("fninit"); + if (builtin.cpu.arch.isX86() and builtin.abi == .gnu) asm volatile ("fninit"); if (!builtin.single_threaded and !builtin.link_libc) { _ = @import("os/windows/tls.zig"); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index c04eaf670caa188d153d59f1cdaa51ce82a394d2..7f2d3e86c5f1df4342a5001b59b3ee6675ea6f95 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -103,7 +103,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void { .error_set, => { if (actual != expected) { - print("expected {}, found {}\n", .{ expected, actual }); + print("expected {any}, found {any}\n", .{ expected, actual }); return error.TestExpectedEqual; } }, @@ -265,9 +265,13 @@ test "expectEqual null" { /// This function is intended to be used only in tests. When the formatted result of the template /// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how -/// they are not equal, then returns an error. It depends on `expectEqualStrings()` for printing +/// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing /// diagnostics. pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void { + if (@inComptime()) { + var buffer: [std.fmt.count(template, args)]u8 = undefined; + return expectEqualStrings(expected, try std.fmt.bufPrint(&buffer, template, args)); + } const actual = try std.fmt.allocPrint(allocator, template, args); defer allocator.free(actual); return expectEqualStrings(expected, actual); @@ -354,9 +358,6 @@ test expectApproxEqRel { /// The colorized output is optional and controlled by the return of `std.io.tty.detectConfig()`. /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void { - if (expected.ptr == actual.ptr and expected.len == actual.len) { - return; - } const diff_index: usize = diff_index: { const shortest = @min(expected.len, actual.len); var index: usize = 0; @@ -365,12 +366,21 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const } break :diff_index if (expected.len == actual.len) return else shortest; }; + if (!backend_can_print) return error.TestExpectedEqual; + const stderr_w = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + failEqualSlices(T, expected, actual, diff_index, stderr_w) catch {}; + return error.TestExpectedEqual; +} - if (!backend_can_print) { - return error.TestExpectedEqual; - } - - print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); +fn failEqualSlices( + comptime T: type, + expected: []const T, + actual: []const T, + diff_index: usize, + w: *std.io.Writer, +) !void { + try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); // TODO: Should this be configurable by the caller? const max_lines: usize = 16; @@ -388,8 +398,6 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)]; const actual_truncated = window_start + actual_window.len < actual.len; - const bw = std.debug.lockStderrWriter(&.{}); - defer std.debug.unlockStderrWriter(); const ttyconf = std.io.tty.detectConfig(.stderr()); var differ = if (T == u8) BytesDiffer{ .expected = expected_window, @@ -406,47 +414,47 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const // that is usually useful. const index_fmt = if (T == u8) "0x{X}" else "{}"; - print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len }); + try w.print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len }); if (window_start > 0) { if (T == u8) { - print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start}); + try w.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start}); } else { - print("... truncated ...\n", .{}); + try w.print("... truncated ...\n", .{}); } } - differ.write(bw) catch {}; + differ.write(w) catch {}; if (expected_truncated) { const end_offset = window_start + expected_window.len; const num_missing_items = expected.len - (window_start + expected_window.len); if (T == u8) { - print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items }); + try w.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items }); } else { - print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items}); + try w.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items}); } } // now reverse expected/actual and print again differ.expected = actual_window; differ.actual = expected_window; - print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len }); + try w.print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len }); if (window_start > 0) { if (T == u8) { - print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start}); + try w.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start}); } else { - print("... truncated ...\n", .{}); + try w.print("... truncated ...\n", .{}); } } - differ.write(bw) catch {}; + differ.write(w) catch {}; if (actual_truncated) { const end_offset = window_start + actual_window.len; const num_missing_items = actual.len - (window_start + actual_window.len); if (T == u8) { - print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items }); + try w.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items }); } else { - print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items}); + try w.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items}); } } - print("\n================================================\n\n", .{}); + try w.print("\n================================================\n\n", .{}); return error.TestExpectedEqual; } @@ -460,17 +468,17 @@ fn SliceDiffer(comptime T: type) type { const Self = @This(); - pub fn write(self: Self, bw: *Writer) !void { + pub fn write(self: Self, writer: *std.io.Writer) !void { for (self.expected, 0..) |value, i| { const full_index = self.start_index + i; const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true; - if (diff) try self.ttyconf.setColor(bw, .red); + if (diff) try self.ttyconf.setColor(writer, .red); if (@typeInfo(T) == .pointer) { - try bw.print("[{}]{*}: {any}\n", .{ full_index, value, value }); + try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value }); } else { - try bw.print("[{}]: {any}\n", .{ full_index, value }); + try writer.print("[{}]: {any}\n", .{ full_index, value }); } - if (diff) try self.ttyconf.setColor(bw, .reset); + if (diff) try self.ttyconf.setColor(writer, .reset); } } }; @@ -481,7 +489,7 @@ const BytesDiffer = struct { actual: []const u8, ttyconf: std.io.tty.Config, - pub fn write(self: BytesDiffer, bw: *Writer) !void { + pub fn write(self: BytesDiffer, writer: *std.io.Writer) !void { var expected_iterator = std.mem.window(u8, self.expected, 16, 16); var row: usize = 0; while (expected_iterator.next()) |chunk| { @@ -491,23 +499,23 @@ const BytesDiffer = struct { const absolute_byte_index = col + row * 16; const diff = if (absolute_byte_index < self.actual.len) self.actual[absolute_byte_index] != byte else true; if (diff) diffs.set(col); - try self.writeDiff(bw, "{X:0>2} ", .{byte}, diff); - if (col == 7) try bw.writeByte(' '); + try self.writeDiff(writer, "{X:0>2} ", .{byte}, diff); + if (col == 7) try writer.writeByte(' '); } - try bw.writeByte(' '); + try writer.writeByte(' '); if (chunk.len < 16) { var missing_columns = (16 - chunk.len) * 3; if (chunk.len < 8) missing_columns += 1; - try bw.splatByteAll(' ', missing_columns); + try writer.splatByteAll(' ', missing_columns); } for (chunk, 0..) |byte, col| { const diff = diffs.isSet(col); if (std.ascii.isPrint(byte)) { - try self.writeDiff(bw, "{c}", .{byte}, diff); + try self.writeDiff(writer, "{c}", .{byte}, diff); } else { // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed if (self.ttyconf == .windows_api) { - try self.writeDiff(bw, ".", .{}, diff); + try self.writeDiff(writer, ".", .{}, diff); continue; } @@ -515,22 +523,22 @@ const BytesDiffer = struct { // We don't want to do this for all control codes because most control codes apart from // the ones that Zig has escape sequences for are likely not very useful to print as symbols. switch (byte) { - '\n' => try self.writeDiff(bw, "␊", .{}, diff), - '\r' => try self.writeDiff(bw, "␍", .{}, diff), - '\t' => try self.writeDiff(bw, "␉", .{}, diff), - else => try self.writeDiff(bw, ".", .{}, diff), + '\n' => try self.writeDiff(writer, "␊", .{}, diff), + '\r' => try self.writeDiff(writer, "␍", .{}, diff), + '\t' => try self.writeDiff(writer, "␉", .{}, diff), + else => try self.writeDiff(writer, ".", .{}, diff), } } } - try bw.writeByte('\n'); + try writer.writeByte('\n'); row += 1; } } - fn writeDiff(self: BytesDiffer, bw: *Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void { - if (diff) try self.ttyconf.setColor(bw, .red); - try bw.print(fmt, args); - if (diff) try self.ttyconf.setColor(bw, .reset); + fn writeDiff(self: BytesDiffer, writer: *std.io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void { + if (diff) try self.ttyconf.setColor(writer, .red); + try writer.print(fmt, args); + if (diff) try self.ttyconf.setColor(writer, .reset); } }; @@ -641,6 +649,11 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir { pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void { if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| { + if (@inComptime()) { + @compileError(std.fmt.comptimePrint("\nexpected:\n{s}\nfound:\n{s}\ndifference starts at index {d}", .{ + expected, actual, diff_index, + })); + } print("\n====== expected this output: =========\n", .{}); printWithVisibleNewlines(expected); print("\n======== instead found this: =========\n", .{}); @@ -1112,7 +1125,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime const arg_i_str = comptime str: { var str_buf: [100]u8 = undefined; const args_i = i + 1; - const str_len = std.fmt.formatIntBuf(&str_buf, args_i, 10, .lower, .{}); + const str_len = std.fmt.printInt(&str_buf, args_i, 10, .lower, .{}); break :str str_buf[0..str_len]; }; @field(args, arg_i_str) = @field(extra_args, field.name); @@ -1142,7 +1155,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime error.OutOfMemory => { if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { print( - "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {}", + "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}", .{ fail_index, needed_alloc_count, @@ -1196,3 +1209,43 @@ pub inline fn fuzz( ) anyerror!void { return @import("root").fuzz(context, testOne, options); } + +/// A `std.io.Reader` that writes a predetermined list of buffers during `stream`. +pub const Reader = struct { + calls: []const Call, + interface: std.io.Reader, + next_call_index: usize, + next_offset: usize, + + pub const Call = struct { + buffer: []const u8, + }; + + pub fn init(buffer: []u8, calls: []const Call) Reader { + return .{ + .next_call_index = 0, + .next_offset = 0, + .interface = .{ + .vtable = &.{ .stream = stream }, + .buffer = buffer, + .seek = 0, + .end = 0, + }, + .calls = calls, + }; + } + + fn stream(io_r: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize { + const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); + if (r.calls.len - r.next_call_index == 0) return error.EndOfStream; + const call = r.calls[r.next_call_index]; + const buffer = limit.sliceConst(call.buffer[r.next_offset..]); + const n = try w.write(buffer); + r.next_offset += n; + if (call.buffer.len - r.next_offset == 0) { + r.next_call_index += 1; + r.next_offset = 0; + } + return n; + } +}; diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 4c6ec1294ba19776524024a4a04c940a607c828c..ef694f33baff409bfa93ca4c74fdbe16860da710 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -9,6 +9,7 @@ const native_endian = builtin.cpu.arch.endian(); /// /// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character pub const replacement_character: u21 = 0xFFFD; +pub const replacement_character_utf8: [3]u8 = utf8EncodeComptime(replacement_character); /// Returns how many bytes the UTF-8 representation would require /// for the given codepoint. @@ -802,14 +803,7 @@ fn testDecode(bytes: []const u8) !u21 { /// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD) /// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of /// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder -fn formatUtf8( - utf8: []const u8, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = fmt; - _ = options; +fn formatUtf8(utf8: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void { var buf: [300]u8 = undefined; // just an arbitrary size var u8len: usize = 0; @@ -898,27 +892,27 @@ fn formatUtf8( /// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD) /// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of /// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder -pub fn fmtUtf8(utf8: []const u8) std.fmt.Formatter(formatUtf8) { +pub fn fmtUtf8(utf8: []const u8) std.fmt.Formatter([]const u8, formatUtf8) { return .{ .data = utf8 }; } test fmtUtf8 { const expectFmt = testing.expectFmt; - try expectFmt("", "{}", .{fmtUtf8("")}); - try expectFmt("foo", "{}", .{fmtUtf8("foo")}); - try expectFmt("𐐷", "{}", .{fmtUtf8("𐐷")}); + try expectFmt("", "{f}", .{fmtUtf8("")}); + try expectFmt("foo", "{f}", .{fmtUtf8("foo")}); + try expectFmt("𐐷", "{f}", .{fmtUtf8("𐐷")}); // Table 3-8. U+FFFD for Non-Shortest Form Sequences - try expectFmt("��������A", "{}", .{fmtUtf8("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82A")}); + try expectFmt("��������A", "{f}", .{fmtUtf8("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82A")}); // Table 3-9. U+FFFD for Ill-Formed Sequences for Surrogates - try expectFmt("��������A", "{}", .{fmtUtf8("\xED\xA0\x80\xED\xBF\xBF\xED\xAFA")}); + try expectFmt("��������A", "{f}", .{fmtUtf8("\xED\xA0\x80\xED\xBF\xBF\xED\xAFA")}); // Table 3-10. U+FFFD for Other Ill-Formed Sequences - try expectFmt("�����A��B", "{}", .{fmtUtf8("\xF4\x91\x92\x93\xFFA\x80\xBFB")}); + try expectFmt("�����A��B", "{f}", .{fmtUtf8("\xF4\x91\x92\x93\xFFA\x80\xBFB")}); // Table 3-11. U+FFFD for Truncated Sequences - try expectFmt("����A", "{}", .{fmtUtf8("\xE1\x80\xE2\xF0\x91\x92\xF1\xBFA")}); + try expectFmt("����A", "{f}", .{fmtUtf8("\xE1\x80\xE2\xF0\x91\x92\xF1\xBFA")}); } fn utf16LeToUtf8ArrayListImpl( @@ -1477,14 +1471,7 @@ test calcWtf16LeLen { /// Print the given `utf16le` string, encoded as UTF-8 bytes. /// Unpaired surrogates are replaced by the replacement character (U+FFFD). -fn formatUtf16Le( - utf16le: []const u16, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = fmt; - _ = options; +fn formatUtf16Le(utf16le: []const u16, writer: *std.io.Writer) std.io.Writer.Error!void { var buf: [300]u8 = undefined; // just an arbitrary size var it = Utf16LeIterator.init(utf16le); var u8len: usize = 0; @@ -1505,23 +1492,23 @@ pub const fmtUtf16le = @compileError("deprecated; renamed to fmtUtf16Le"); /// Return a Formatter for a (potentially ill-formed) UTF-16 LE string, /// which will be converted to UTF-8 during formatting. /// Unpaired surrogates are replaced by the replacement character (U+FFFD). -pub fn fmtUtf16Le(utf16le: []const u16) std.fmt.Formatter(formatUtf16Le) { +pub fn fmtUtf16Le(utf16le: []const u16) std.fmt.Formatter([]const u16, formatUtf16Le) { return .{ .data = utf16le }; } test fmtUtf16Le { const expectFmt = testing.expectFmt; - try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))}); - try expectFmt("", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))}); - try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))}); - try expectFmt("foo", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))}); - try expectFmt("𐐷", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))}); - try expectFmt("퟿", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})}); - try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})}); - try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})}); - try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})}); - try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})}); - try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})}); + try expectFmt("", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))}); + try expectFmt("", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))}); + try expectFmt("foo", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))}); + try expectFmt("foo", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))}); + try expectFmt("𐐷", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))}); + try expectFmt("퟿", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})}); + try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})}); + try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})}); + try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})}); + try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})}); + try expectFmt("", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})}); } fn testUtf8ToUtf16LeStringLiteral(utf8ToUtf16LeStringLiteral_: anytype) !void { diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 8d9346226a1c53b5e19d087d73e63197d7c14a4c..ccdf65b1becd85487625dc9b5ba2f847d543349d 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -54,7 +54,7 @@ pub const Color = enum { pub fn get_tty_conf(color: Color) std.io.tty.Config { return switch (color) { - .auto => std.io.tty.detectConfig(.stderr()), + .auto => std.io.tty.detectConfig(std.fs.File.stderr()), .on => .escape_codes, .off => .no_color, }; @@ -364,138 +364,136 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![ /// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. /// -/// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives -/// and the reserved `_` identifier. -/// - Add `p` to the specifier to render identifiers that shadow primitives unescaped. -/// - Add `_` to the specifier to render the reserved `_` identifier unescaped. -/// - `p` and `_` can be combined, e.g. `{p_}`. +/// See also `fmtIdFlags`. +pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = .{} } }; +} + +/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. /// -pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { - return .{ .data = bytes }; +/// See also `fmtId`. +pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = flags } }; +} + +pub fn fmtIdPU(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true, .allow_underscore = true } } }; +} + +pub fn fmtIdP(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { + return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true } } }; } test fmtId { const expectFmt = std.testing.expectFmt; - try expectFmt("@\"while\"", "{}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{p}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{_}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{p_}", .{fmtId("while")}); - try expectFmt("@\"while\"", "{_p}", .{fmtId("while")}); + try expectFmt("@\"while\"", "{f}", .{fmtId("while")}); + try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true })}); + try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_underscore = true })}); + try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true, .allow_underscore = true })}); - try expectFmt("hello", "{}", .{fmtId("hello")}); - try expectFmt("hello", "{p}", .{fmtId("hello")}); - try expectFmt("hello", "{_}", .{fmtId("hello")}); - try expectFmt("hello", "{p_}", .{fmtId("hello")}); - try expectFmt("hello", "{_p}", .{fmtId("hello")}); + try expectFmt("hello", "{f}", .{fmtId("hello")}); + try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true })}); + try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_underscore = true })}); + try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true, .allow_underscore = true })}); - try expectFmt("@\"type\"", "{}", .{fmtId("type")}); - try expectFmt("type", "{p}", .{fmtId("type")}); - try expectFmt("@\"type\"", "{_}", .{fmtId("type")}); - try expectFmt("type", "{p_}", .{fmtId("type")}); - try expectFmt("type", "{_p}", .{fmtId("type")}); + try expectFmt("@\"type\"", "{f}", .{fmtId("type")}); + try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true })}); + try expectFmt("@\"type\"", "{f}", .{fmtIdFlags("type", .{ .allow_underscore = true })}); + try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true, .allow_underscore = true })}); - try expectFmt("@\"_\"", "{}", .{fmtId("_")}); - try expectFmt("@\"_\"", "{p}", .{fmtId("_")}); - try expectFmt("_", "{_}", .{fmtId("_")}); - try expectFmt("_", "{p_}", .{fmtId("_")}); - try expectFmt("_", "{_p}", .{fmtId("_")}); + try expectFmt("@\"_\"", "{f}", .{fmtId("_")}); + try expectFmt("@\"_\"", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true })}); + try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_underscore = true })}); + try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true, .allow_underscore = true })}); - try expectFmt("@\"i123\"", "{}", .{fmtId("i123")}); - try expectFmt("i123", "{p}", .{fmtId("i123")}); - try expectFmt("@\"4four\"", "{}", .{fmtId("4four")}); - try expectFmt("_underscore", "{}", .{fmtId("_underscore")}); - try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); - try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); + try expectFmt("@\"i123\"", "{f}", .{fmtId("i123")}); + try expectFmt("i123", "{f}", .{fmtIdFlags("i123", .{ .allow_primitive = true })}); + try expectFmt("@\"4four\"", "{f}", .{fmtId("4four")}); + try expectFmt("_underscore", "{f}", .{fmtId("_underscore")}); + try expectFmt("@\"11\\\"23\"", "{f}", .{fmtId("11\"23")}); + try expectFmt("@\"11\\x0f23\"", "{f}", .{fmtId("11\x0F23")}); // These are technically not currently legal in Zig. - try expectFmt("@\"\"", "{}", .{fmtId("")}); - try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")}); + try expectFmt("@\"\"", "{f}", .{fmtId("")}); + try expectFmt("@\"\\x00\"", "{f}", .{fmtId("\x00")}); } -/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. -fn formatId(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void { - const allow_primitive, const allow_underscore = comptime parse_fmt: { - var allow_primitive = false; - var allow_underscore = false; - for (fmt) |char| { - switch (char) { - 'p' => if (!allow_primitive) { - allow_primitive = true; - continue; - }, - '_' => if (!allow_underscore) { - allow_underscore = true; - continue; - }, - else => {}, - } - @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}"); - } - break :parse_fmt .{ allow_primitive, allow_underscore }; +pub const FormatId = struct { + bytes: []const u8, + flags: Flags, + pub const Flags = struct { + allow_primitive: bool = false, + allow_underscore: bool = false, }; - if (isValidId(bytes) and - (allow_primitive or !std.zig.isPrimitive(bytes)) and - (allow_underscore or !isUnderscore(bytes))) - { - return bw.writeAll(bytes); + /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. + fn render(ctx: FormatId, writer: *std.io.Writer) std.io.Writer.Error!void { + const bytes = ctx.bytes; + if (isValidId(bytes) and + (ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and + (ctx.flags.allow_underscore or !isUnderscore(bytes))) + { + return writer.writeAll(bytes); + } + try writer.writeAll("@\""); + try stringEscape(bytes, writer); + try writer.writeByte('"'); } - try bw.writeAll("@\""); - try stringEscape(bytes, bw, ""); - try bw.writeByte('"'); +}; + +/// Return a formatter for escaping a double quoted Zig string. +pub fn fmtString(bytes: []const u8) std.fmt.Formatter([]const u8, stringEscape) { + return .{ .data = bytes }; } -/// Return a Formatter for Zig Escapes of a double quoted string. -/// The format specifier must be one of: -/// * `{}` treats contents as a double-quoted string. -/// * `{'}` treats contents as a single-quoted string. -pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) { +/// Return a formatter for escaping a single quoted Zig string. +pub fn fmtChar(bytes: []const u8) std.fmt.Formatter([]const u8, charEscape) { return .{ .data = bytes }; } -test fmtEscapes { - const expectFmt = std.testing.expectFmt; - try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); - try expectFmt( - \\" \\ hi \x07 \x11 " derp \'" - , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); - try expectFmt( +test fmtString { + try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")}); + try std.testing.expectFmt( \\" \\ hi \x07 \x11 \" derp '" - , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); + , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")}); } -/// Print the string as escaped contents of a double quoted or single-quoted string. -/// Format `{}` treats contents as a double-quoted string. -/// Format `{'}` treats contents as a single-quoted string. -pub fn stringEscape(bytes: []const u8, bw: *Writer, comptime f: []const u8) !void { +test fmtChar { + try std.testing.expectFmt( + \\" \\ hi \x07 \x11 " derp \'" + , "\"{f}\"", .{fmtChar(" \\ hi \x07 \x11 \" derp '")}); +} + +/// Print the string as escaped contents of a double quoted string. +pub fn stringEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { for (bytes) |byte| switch (byte) { - '\n' => try bw.writeAll("\\n"), - '\r' => try bw.writeAll("\\r"), - '\t' => try bw.writeAll("\\t"), - '\\' => try bw.writeAll("\\\\"), - '"' => { - if (f.len == 1 and f[0] == '\'') { - try bw.writeByte('"'); - } else if (f.len == 0) { - try bw.writeAll("\\\""); - } else { - @compileError("expected {} or {'}, found {" ++ f ++ "}"); - } - }, - '\'' => { - if (f.len == 1 and f[0] == '\'') { - try bw.writeAll("\\'"); - } else if (f.len == 0) { - try bw.writeByte('\''); - } else { - @compileError("expected {} or {'}, found {" ++ f ++ "}"); - } + '\n' => try w.writeAll("\\n"), + '\r' => try w.writeAll("\\r"), + '\t' => try w.writeAll("\\t"), + '\\' => try w.writeAll("\\\\"), + '"' => try w.writeAll("\\\""), + '\'' => try w.writeByte('\''), + ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), + else => { + try w.writeAll("\\x"); + try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, - ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte), - // Use hex escapes for rest any unprintable characters. + }; +} + +/// Print the string as escaped contents of a single-quoted string. +pub fn charEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { + for (bytes) |byte| switch (byte) { + '\n' => try w.writeAll("\\n"), + '\r' => try w.writeAll("\\r"), + '\t' => try w.writeAll("\\t"), + '\\' => try w.writeAll("\\\\"), + '"' => try w.writeByte('"'), + '\'' => try w.writeAll("\\'"), + ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), else => { - try bw.writeAll("\\x"); - try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + try w.writeAll("\\x"); + try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, }; } diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 04d9691df57a293f2fe7cc154232af6480808077..0e1b6fff5c25738d8dcef90da4520c20f1dedf1e 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -320,261 +320,261 @@ pub fn rootDecls(tree: Ast) []const Node.Index { } } -pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void { +pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void { switch (parse_error.tag) { .asterisk_after_ptr_deref => { // Note that the token will point at the `.*` but ideally the source // location would point to the `*` after the `.*`. - return bw.writeAll("'.*' cannot be followed by '*'; are you missing a space?"); + return w.writeAll("'.*' cannot be followed by '*'; are you missing a space?"); }, .chained_comparison_operators => { - return bw.writeAll("comparison operators cannot be chained"); + return w.writeAll("comparison operators cannot be chained"); }, .decl_between_fields => { - return bw.writeAll("declarations are not allowed between container fields"); + return w.writeAll("declarations are not allowed between container fields"); }, .expected_block => { - return bw.print("expected block, found '{s}'", .{ + return w.print("expected block, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_block_or_assignment => { - return bw.print("expected block or assignment, found '{s}'", .{ + return w.print("expected block or assignment, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_block_or_expr => { - return bw.print("expected block or expression, found '{s}'", .{ + return w.print("expected block or expression, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_block_or_field => { - return bw.print("expected block or field, found '{s}'", .{ + return w.print("expected block or field, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_container_members => { - return bw.print("expected test, comptime, var decl, or container field, found '{s}'", .{ + return w.print("expected test, comptime, var decl, or container field, found '{s}'", .{ tree.tokenTag(parse_error.token).symbol(), }); }, .expected_expr => { - return bw.print("expected expression, found '{s}'", .{ + return w.print("expected expression, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_expr_or_assignment => { - return bw.print("expected expression or assignment, found '{s}'", .{ + return w.print("expected expression or assignment, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_expr_or_var_decl => { - return bw.print("expected expression or var decl, found '{s}'", .{ + return w.print("expected expression or var decl, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_fn => { - return bw.print("expected function, found '{s}'", .{ + return w.print("expected function, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_inlinable => { - return bw.print("expected 'while' or 'for', found '{s}'", .{ + return w.print("expected 'while' or 'for', found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_labelable => { - return bw.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{ + return w.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_param_list => { - return bw.print("expected parameter list, found '{s}'", .{ + return w.print("expected parameter list, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_prefix_expr => { - return bw.print("expected prefix expression, found '{s}'", .{ + return w.print("expected prefix expression, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_primary_type_expr => { - return bw.print("expected primary type expression, found '{s}'", .{ + return w.print("expected primary type expression, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_pub_item => { - return bw.writeAll("expected function or variable declaration after pub"); + return w.writeAll("expected function or variable declaration after pub"); }, .expected_return_type => { - return bw.print("expected return type expression, found '{s}'", .{ + return w.print("expected return type expression, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_semi_or_else => { - return bw.writeAll("expected ';' or 'else' after statement"); + return w.writeAll("expected ';' or 'else' after statement"); }, .expected_semi_or_lbrace => { - return bw.writeAll("expected ';' or block after function prototype"); + return w.writeAll("expected ';' or block after function prototype"); }, .expected_statement => { - return bw.print("expected statement, found '{s}'", .{ + return w.print("expected statement, found '{s}'", .{ tree.tokenTag(parse_error.token).symbol(), }); }, .expected_suffix_op => { - return bw.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{ + return w.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_type_expr => { - return bw.print("expected type expression, found '{s}'", .{ + return w.print("expected type expression, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_var_decl => { - return bw.print("expected variable declaration, found '{s}'", .{ + return w.print("expected variable declaration, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_var_decl_or_fn => { - return bw.print("expected variable declaration or function, found '{s}'", .{ + return w.print("expected variable declaration or function, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_loop_payload => { - return bw.print("expected loop payload, found '{s}'", .{ + return w.print("expected loop payload, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .expected_container => { - return bw.print("expected a struct, enum or union, found '{s}'", .{ + return w.print("expected a struct, enum or union, found '{s}'", .{ tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), }); }, .extern_fn_body => { - return bw.writeAll("extern functions have no body"); + return w.writeAll("extern functions have no body"); }, .extra_addrspace_qualifier => { - return bw.writeAll("extra addrspace qualifier"); + return w.writeAll("extra addrspace qualifier"); }, .extra_align_qualifier => { - return bw.writeAll("extra align qualifier"); + return w.writeAll("extra align qualifier"); }, .extra_allowzero_qualifier => { - return bw.writeAll("extra allowzero qualifier"); + return w.writeAll("extra allowzero qualifier"); }, .extra_const_qualifier => { - return bw.writeAll("extra const qualifier"); + return w.writeAll("extra const qualifier"); }, .extra_volatile_qualifier => { - return bw.writeAll("extra volatile qualifier"); + return w.writeAll("extra volatile qualifier"); }, .ptr_mod_on_array_child_type => { - return bw.print("pointer modifier '{s}' not allowed on array child type", .{ + return w.print("pointer modifier '{s}' not allowed on array child type", .{ tree.tokenTag(parse_error.token).symbol(), }); }, .invalid_bit_range => { - return bw.writeAll("bit range not allowed on slices and arrays"); + return w.writeAll("bit range not allowed on slices and arrays"); }, .same_line_doc_comment => { - return bw.writeAll("same line documentation comment"); + return w.writeAll("same line documentation comment"); }, .unattached_doc_comment => { - return bw.writeAll("unattached documentation comment"); + return w.writeAll("unattached documentation comment"); }, .test_doc_comment => { - return bw.writeAll("documentation comments cannot be attached to tests"); + return w.writeAll("documentation comments cannot be attached to tests"); }, .comptime_doc_comment => { - return bw.writeAll("documentation comments cannot be attached to comptime blocks"); + return w.writeAll("documentation comments cannot be attached to comptime blocks"); }, .varargs_nonfinal => { - return bw.writeAll("function prototype has parameter after varargs"); + return w.writeAll("function prototype has parameter after varargs"); }, .expected_continue_expr => { - return bw.writeAll("expected ':' before while continue expression"); + return w.writeAll("expected ':' before while continue expression"); }, .expected_semi_after_decl => { - return bw.writeAll("expected ';' after declaration"); + return w.writeAll("expected ';' after declaration"); }, .expected_semi_after_stmt => { - return bw.writeAll("expected ';' after statement"); + return w.writeAll("expected ';' after statement"); }, .expected_comma_after_field => { - return bw.writeAll("expected ',' after field"); + return w.writeAll("expected ',' after field"); }, .expected_comma_after_arg => { - return bw.writeAll("expected ',' after argument"); + return w.writeAll("expected ',' after argument"); }, .expected_comma_after_param => { - return bw.writeAll("expected ',' after parameter"); + return w.writeAll("expected ',' after parameter"); }, .expected_comma_after_initializer => { - return bw.writeAll("expected ',' after initializer"); + return w.writeAll("expected ',' after initializer"); }, .expected_comma_after_switch_prong => { - return bw.writeAll("expected ',' after switch prong"); + return w.writeAll("expected ',' after switch prong"); }, .expected_comma_after_for_operand => { - return bw.writeAll("expected ',' after for operand"); + return w.writeAll("expected ',' after for operand"); }, .expected_comma_after_capture => { - return bw.writeAll("expected ',' after for capture"); + return w.writeAll("expected ',' after for capture"); }, .expected_initializer => { - return bw.writeAll("expected field initializer"); + return w.writeAll("expected field initializer"); }, .mismatched_binary_op_whitespace => { - return bw.print("binary operator '{s}' has whitespace on one side, but not the other", .{tree.tokenTag(parse_error.token).lexeme().?}); + return w.print("binary operator '{s}' has whitespace on one side, but not the other", .{tree.tokenTag(parse_error.token).lexeme().?}); }, .invalid_ampersand_ampersand => { - return bw.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"); + return w.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"); }, .c_style_container => { - return bw.print("'{s} {s}' is invalid", .{ + return w.print("'{s} {s}' is invalid", .{ parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token), }); }, .zig_style_container => { - return bw.print("to declare a container do 'const {s} = {s}'", .{ + return w.print("to declare a container do 'const {s} = {s}'", .{ tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(), }); }, .previous_field => { - return bw.writeAll("field before declarations here"); + return w.writeAll("field before declarations here"); }, .next_field => { - return bw.writeAll("field after declarations here"); + return w.writeAll("field after declarations here"); }, .expected_var_const => { - return bw.writeAll("expected 'var' or 'const' before variable declaration"); + return w.writeAll("expected 'var' or 'const' before variable declaration"); }, .wrong_equal_var_decl => { - return bw.writeAll("variable initialized with '==' instead of '='"); + return w.writeAll("variable initialized with '==' instead of '='"); }, .var_const_decl => { - return bw.writeAll("use 'var' or 'const' to declare variable"); + return w.writeAll("use 'var' or 'const' to declare variable"); }, .extra_for_capture => { - return bw.writeAll("extra capture in for loop"); + return w.writeAll("extra capture in for loop"); }, .for_input_not_captured => { - return bw.writeAll("for input is not captured"); + return w.writeAll("for input is not captured"); }, .invalid_byte => { const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..]; - return bw.print("{s} contains invalid byte: '{f'}'", .{ + return w.print("{s} contains invalid byte: '{f}'", .{ switch (tok_slice[0]) { '\'' => "character literal", '"', '\\' => "string literal", '/' => "comment", else => unreachable, }, - std.zig.fmtEscapes(tok_slice[parse_error.extra.offset..][0..1]), + std.zig.fmtChar(tok_slice[parse_error.extra.offset..][0..1]), }); }, @@ -582,10 +582,10 @@ pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void const found_tag = tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)); const expected_symbol = parse_error.extra.expected_tag.symbol(); switch (found_tag) { - .invalid => return bw.print("expected '{s}', found invalid bytes", .{ + .invalid => return w.print("expected '{s}', found invalid bytes", .{ expected_symbol, }), - else => return bw.print("expected '{s}', found '{s}'", .{ + else => return w.print("expected '{s}', found '{s}'", .{ expected_symbol, found_tag.symbol(), }), } @@ -608,7 +608,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { .negation_wrap, .address_of, .@"try", - .@"await", .optional_type, .@"switch", .switch_comma, @@ -758,27 +757,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { return i - end_offset; }, - .@"usingnamespace" => { - const main_token: TokenIndex = tree.nodeMainToken(n); - const has_visib_token = tree.isTokenPrecededByTags(main_token, &.{.keyword_pub}); - end_offset += @intFromBool(has_visib_token); - return main_token - end_offset; - }, - - .async_call_one, - .async_call_one_comma, - => { - end_offset += 1; // async token - n = tree.nodeData(n).node_and_opt_node[0]; - }, - - .async_call, - .async_call_comma, - => { - end_offset += 1; // async token - n = tree.nodeData(n).node_and_extra[0]; - }, - .container_field_init, .container_field_align, .container_field, @@ -898,14 +876,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { while (true) switch (tree.nodeTag(n)) { .root => return @intCast(tree.tokens.len - 1), - .@"usingnamespace", .bool_not, .negation, .bit_not, .negation_wrap, .address_of, .@"try", - .@"await", .optional_type, .@"suspend", .@"resume", @@ -1024,7 +1000,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { }; }, - .call, .async_call => { + .call => { _, const extra_index = tree.nodeData(n).node_and_extra; const params = tree.extraData(extra_index, Node.SubRange); assert(params.start != params.end); @@ -1043,7 +1019,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { } }, .call_comma, - .async_call_comma, .tagged_union_enum_tag_trailing, => { _, const extra_index = tree.nodeData(n).node_and_extra; @@ -1124,7 +1099,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member }, .call_one, - .async_call_one, => { _, const first_param = tree.nodeData(n).node_and_opt_node; end_offset += 1; // for the rparen @@ -1273,7 +1247,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { n = first_element; }, .call_one_comma, - .async_call_one_comma, .struct_init_one_comma, => { _, const first_field = tree.nodeData(n).node_and_opt_node; @@ -1990,21 +1963,21 @@ pub fn forFull(tree: Ast, node: Node.Index) full.For { pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call { const fn_expr, const first_param = tree.nodeData(node).node_and_opt_node; const params = loadOptionalNodesIntoBuffer(1, buffer, .{first_param}); - return tree.fullCallComponents(.{ + return .{ .ast = .{ .lparen = tree.nodeMainToken(node), .fn_expr = fn_expr, .params = params, - }); + } }; } pub fn callFull(tree: Ast, node: Node.Index) full.Call { const fn_expr, const extra_index = tree.nodeData(node).node_and_extra; const params = tree.extraDataSlice(tree.extraData(extra_index, Node.SubRange), Node.Index); - return tree.fullCallComponents(.{ + return .{ .ast = .{ .lparen = tree.nodeMainToken(node), .fn_expr = fn_expr, .params = params, - }); + } }; } fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl { @@ -2338,18 +2311,6 @@ fn fullForComponents(tree: Ast, info: full.For.Components) full.For { return result; } -fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call { - var result: full.Call = .{ - .ast = info, - .async_token = null, - }; - const first_token = tree.firstToken(info.fn_expr); - if (tree.isTokenPrecededByTags(first_token, &.{.keyword_async})) { - result.async_token = first_token - 1; - } - return result; -} - pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl { return switch (tree.nodeTag(node)) { .global_var_decl => tree.globalVarDecl(node), @@ -2490,8 +2451,8 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm { pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call { return switch (tree.nodeTag(node)) { - .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node), - .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node), + .call, .call_comma => tree.callFull(node), + .call_one, .call_one_comma => tree.callOne(buffer, node), else => null, }; } @@ -2884,7 +2845,6 @@ pub const full = struct { pub const Call = struct { ast: Components, - async_token: ?TokenIndex, pub const Components = struct { lparen: TokenIndex, @@ -3067,12 +3027,6 @@ pub const Node = struct { /// /// The `main_token` field is the first token for the source file. root, - /// `usingnamespace expr;`. - /// - /// The `data` field is a `.node` to expr. - /// - /// The `main_token` field is the `usingnamespace` token. - @"usingnamespace", /// `test {}`, /// `test "name" {}`, /// `test identifier {}`. @@ -3303,8 +3257,6 @@ pub const Node = struct { address_of, /// `try expr`. The `main_token` field is the `try` token. @"try", - /// `await expr`. The `main_token` field is the `await` token. - @"await", /// `?expr`. The `main_token` field is the `?` token. optional_type, /// `[lhs]rhs`. The `main_token` field is the `[` token. @@ -3500,17 +3452,6 @@ pub const Node = struct { /// Same as `call_one` except there is known to be a trailing comma /// before the final rparen. call_one_comma, - /// `async a(b)`, `async a()`. - /// - /// The `data` field is a `.node_and_opt_node`: - /// 1. a `Node.Index` to the function expression. - /// 2. a `Node.OptionalIndex` to the first argument, if any. - /// - /// The `main_token` field is the `(` token. - async_call_one, - /// Same as `async_call_one` except there is known to be a trailing - /// comma before the final rparen. - async_call_one_comma, /// `a(b, c, d)`. /// /// The `data` field is a `.node_and_extra`: @@ -3523,18 +3464,6 @@ pub const Node = struct { /// Same as `call` except there is known to be a trailing comma before /// the final rparen. call_comma, - /// `async a(b, c, d)`. - /// - /// The `data` field is a `.node_and_extra`: - /// 1. a `Node.Index` to the function expression. - /// 2. a `ExtraIndex` to a `SubRange` that stores a `Node.Index` for - /// each argument. - /// - /// The `main_token` field is the `(` token. - async_call, - /// Same as `async_call` except there is known to be a trailing comma - /// before the final rparen. - async_call_comma, /// `switch(a) {}`. /// /// The `data` field is a `.node_and_extra`: diff --git a/lib/std/zig/Ast/Render.zig b/lib/std/zig/Ast/Render.zig index d7e262f4b2c927360688dbfe76989d1782a9e27a..f5113941c3a0d1a60b933c233d7e57c620cdd3e7 100644 --- a/lib/std/zig/Ast/Render.zig +++ b/lib/std/zig/Ast/Render.zig @@ -265,17 +265,6 @@ fn renderMember( return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon }, - .@"usingnamespace" => { - const main_token = tree.nodeMainToken(decl); - const expr = tree.nodeData(decl).node; - if (tree.isTokenPrecededByTags(main_token, &.{.keyword_pub})) { - try renderToken(r, main_token - 1, .space); // pub - } - try renderToken(r, main_token, .space); // usingnamespace - try renderExpression(r, expr, .none); - return renderToken(r, tree.lastToken(expr) + 1, space); // ; - }, - .global_var_decl, .local_var_decl, .simple_var_decl, @@ -594,7 +583,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { .@"try", .@"resume", - .@"await", => { try renderToken(r, tree.nodeMainToken(node), .space); return renderExpression(r, tree.nodeData(node).node, space); @@ -638,12 +626,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => { var buf: [1]Ast.Node.Index = undefined; return renderCall(r, tree.fullCall(&buf, node).?, space); @@ -885,7 +869,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { .local_var_decl => unreachable, .simple_var_decl => unreachable, .aligned_var_decl => unreachable, - .@"usingnamespace" => unreachable, .test_decl => unreachable, .asm_output => unreachable, .asm_input => unreachable, @@ -1584,7 +1567,7 @@ fn renderBuiltinCall( defer r.gpa.free(new_string); try renderToken(r, builtin_token + 1, .none); // ( - try ais.print("\"{f}\"", .{std.zig.fmtEscapes(new_string)}); + try ais.print("\"{f}\"", .{std.zig.fmtString(new_string)}); return renderToken(r, str_lit_token + 1, space); // ) } } @@ -2556,9 +2539,6 @@ fn renderCall( call: Ast.full.Call, space: Space, ) Error!void { - if (call.async_token) |async_token| { - try renderToken(r, async_token, .space); - } try renderExpression(r, call.ast.fn_expr, .none); try renderParamList(r, call.ast.lparen, call.ast.params, space); } @@ -2897,7 +2877,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void .success => |codepoint| { if (codepoint <= 0x7f) { const buf = [1]u8{@as(u8, @intCast(codepoint))}; - try ais.print("{f}", .{std.zig.fmtEscapes(&buf)}); + try ais.print("{f}", .{std.zig.fmtString(&buf)}); } else { try ais.writeAll(escape_sequence); } @@ -2909,7 +2889,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void }, 0x00...('\\' - 1), ('\\' + 1)...0x7f => { const buf = [1]u8{byte}; - try ais.print("{f}", .{std.zig.fmtEscapes(&buf)}); + try ais.print("{f}", .{std.zig.fmtString(&buf)}); pos += 1; }, 0x80...0xff => { diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index c2c8990591f1c062852ba8232b91d30a930ba69f..2b6c61b138f8ddead87763b41be8765ae3cb6bfb 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -442,7 +442,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins const tree = astgen.tree; switch (tree.nodeTag(node)) { .root => unreachable, - .@"usingnamespace" => unreachable, .test_decl => unreachable, .global_var_decl => unreachable, .local_var_decl => unreachable, @@ -510,12 +509,8 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins .number_literal, .call, .call_comma, - .async_call, - .async_call_comma, .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .unreachable_literal, .@"return", .@"if", @@ -547,7 +542,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins .merge_error_sets, .switch_range, .for_range, - .@"await", .bit_not, .negation, .negation_wrap, @@ -642,7 +636,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE switch (tree.nodeTag(node)) { .root => unreachable, // Top-level declaration. - .@"usingnamespace" => unreachable, // Top-level declaration. .test_decl => unreachable, // Top-level declaration. .container_field_init => unreachable, // Top-level declaration. .container_field_align => unreachable, // Top-level declaration. @@ -836,12 +829,8 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => { var buf: [1]Ast.Node.Index = undefined; 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 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node), .@"suspend" => return suspendExpr(gz, scope, node), - .@"await" => return awaitExpr(gz, scope, ri, node), .@"resume" => return resumeExpr(gz, scope, ri, node), .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node), @@ -1259,33 +1247,6 @@ fn suspendExpr( return suspend_inst.toRef(); } -fn awaitExpr( - gz: *GenZir, - scope: *Scope, - ri: ResultInfo, - node: Ast.Node.Index, -) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const tree = astgen.tree; - const rhs_node = tree.nodeData(node).node; - - if (gz.suspend_node.unwrap()) |suspend_node| { - return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{ - try astgen.errNoteNode(suspend_node, "suspend block here", .{}), - }); - } - const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); - const result = if (gz.nosuspend_node != .none) - try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{ - .node = gz.nodeIndexToRelative(node), - .operand = operand, - }) - else - try gz.addUnNode(.@"await", operand, node); - - return rvalue(gz, ri, result, node); -} - fn resumeExpr( gz: *GenZir, scope: *Scope, @@ -2853,7 +2814,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .tag_name, .type_name, .frame_type, - .frame_size, .int_from_float, .float_from_int, .ptr_from_int, @@ -2887,7 +2847,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .min, .c_import, .@"resume", - .@"await", .ret_err_value_code, .ret_ptr, .ret_type, @@ -4739,69 +4698,6 @@ fn comptimeDecl( }); } -fn usingnamespaceDecl( - astgen: *AstGen, - gz: *GenZir, - scope: *Scope, - wip_members: *WipMembers, - node: Ast.Node.Index, -) InnerError!void { - const tree = astgen.tree; - - const old_hasher = astgen.src_hasher; - defer astgen.src_hasher = old_hasher; - astgen.src_hasher = std.zig.SrcHasher.init(.{}); - astgen.src_hasher.update(tree.getNodeSource(node)); - astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); - - const type_expr = tree.nodeData(node).node; - const is_pub = tree.isTokenPrecededByTags(tree.nodeMainToken(node), &.{.keyword_pub}); - - // Up top so the ZIR instruction index marks the start range of this - // top-level declaration. - const decl_inst = try gz.makeDeclaration(node); - wip_members.nextDecl(decl_inst); - astgen.advanceSourceCursorToNode(node); - - // This is just needed for the `setDeclaration` call. - var dummy_gz = gz.makeSubBlock(scope); - defer dummy_gz.unstack(); - - var usingnamespace_gz: GenZir = .{ - .is_comptime = true, - .decl_node_index = node, - .decl_line = astgen.source_line, - .parent = scope, - .astgen = astgen, - .instructions = gz.instructions, - .instructions_top = gz.instructions.items.len, - }; - defer usingnamespace_gz.unstack(); - - const decl_column = astgen.source_column; - - const namespace_inst = try typeExpr(&usingnamespace_gz, &usingnamespace_gz.base, type_expr); - _ = try usingnamespace_gz.addBreak(.break_inline, decl_inst, namespace_inst); - - var hash: std.zig.SrcHash = undefined; - astgen.src_hasher.final(&hash); - try setDeclaration(decl_inst, .{ - .src_hash = hash, - .src_line = usingnamespace_gz.decl_line, - .src_column = decl_column, - .kind = .@"usingnamespace", - .name = .empty, - .is_pub = is_pub, - .is_threadlocal = false, - .linkage = .normal, - .type_gz = &dummy_gz, - .align_gz = &dummy_gz, - .linksection_gz = &dummy_gz, - .addrspace_gz = &dummy_gz, - .value_gz = &usingnamespace_gz, - }); -} - fn testDecl( astgen: *AstGen, gz: *GenZir, @@ -5971,23 +5867,6 @@ fn containerMember( }, }; }, - .@"usingnamespace" => { - const prev_decl_index = wip_members.decl_index; - astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - wip_members.decl_index = prev_decl_index; - try addFailedDeclaration( - wip_members, - gz, - .@"usingnamespace", - .empty, - member_node, - tree.isTokenPrecededByTags(tree.nodeMainToken(member_node), &.{.keyword_pub}), - ); - }, - }; - }, .test_decl => { const prev_decl_index = wip_members.decl_index; // We need to have *some* decl here so that the decl count matches what's expected. @@ -9501,7 +9380,6 @@ fn builtinCall( .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name), .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name), .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type), - .frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size), .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), @@ -9767,16 +9645,6 @@ fn builtinCall( }); return rvalue(gz, ri, result, node); }, - .async_call => { - const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{ - .node = gz.nodeIndexToRelative(node), - .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]), - .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), - .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]), - .args = try expr(gz, scope, .{ .rl = .none }, params[3]), - }); - return rvalue(gz, ri, result, node); - }, .Vector => { const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{ .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type), @@ -10175,11 +10043,8 @@ fn callExpr( const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr); const modifier: std.builtin.CallModifier = blk: { - if (call.async_token != null) { - break :blk .async_kw; - } if (gz.nosuspend_node != .none) { - break :blk .no_async; + break :blk .no_suspend; } break :blk .auto; }; @@ -10451,7 +10316,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev while (true) { switch (tree.nodeTag(node)) { .root, - .@"usingnamespace", .test_decl, .switch_case, .switch_case_inline, @@ -10483,12 +10347,8 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev .switch_comma, .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => return .maybe, .@"return", @@ -10613,7 +10473,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev // Forward the question to the LHS sub-expression. .@"try", - .@"await", .@"comptime", .@"nosuspend", => node = tree.nodeData(node).node, @@ -10664,7 +10523,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In while (true) { switch (tree.nodeTag(node)) { .root, - .@"usingnamespace", .test_decl, .switch_case, .switch_case_inline, @@ -10803,12 +10661,8 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In .switch_comma, .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, .block_two, .block_two_semicolon, .block, @@ -10826,7 +10680,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In // Forward the question to the LHS sub-expression. .@"try", - .@"await", .@"comptime", .@"nosuspend", => node = tree.nodeData(node).node, @@ -10908,7 +10761,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { while (true) { switch (tree.nodeTag(node)) { .root, - .@"usingnamespace", .test_decl, .switch_case, .switch_case_inline, @@ -11047,12 +10899,8 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { .switch_comma, .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, .block_two, .block_two_semicolon, .block, @@ -11079,7 +10927,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { // Forward the question to the LHS sub-expression. .@"try", - .@"await", .@"comptime", .@"nosuspend", => node = tree.nodeData(node).node, @@ -11462,13 +11309,7 @@ fn failWithStrLitError( offset: u32, ) InnerError { const raw_string = bytes[offset..]; - return failOff( - astgen, - token, - @intCast(offset + err.offset()), - "{f}", - .{err.fmt(raw_string)}, - ); + return failOff(astgen, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)}); } fn failNode( @@ -13591,7 +13432,7 @@ fn scanContainer( break :blk .{ .decl, ident }; }, - .@"comptime", .@"usingnamespace" => { + .@"comptime" => { decl_count += 1; continue; }, @@ -13970,7 +13811,6 @@ const DeclarationName = union(enum) { decltest: Ast.TokenIndex, unnamed_test, @"comptime", - @"usingnamespace", }; fn addFailedDeclaration( @@ -14060,7 +13900,6 @@ fn setDeclaration( .@"test" => .@"test", .decltest => .decltest, .@"comptime" => .@"comptime", - .@"usingnamespace" => if (args.is_pub) .pub_usingnamespace else .@"usingnamespace", .@"const" => switch (args.linkage) { .normal => if (args.is_pub) id: { if (has_special_body) break :id .pub_const; diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index 628574349b19a226e399b7154e7051cf85b65aad..52900224ccb08532b196d120ec5d765cb7803056 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/lib/std/zig/AstRlAnnotate.zig @@ -165,10 +165,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI } return false; }, - .@"usingnamespace" => { - _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.type_only); - return false; - }, .test_decl => { _ = try astrl.expr(tree.nodeData(node).opt_token_and_node[1], block, ResultInfo.none); return false; @@ -334,12 +330,8 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => { var buf: [1]Ast.Node.Index = undefined; const full = tree.fullCall(&buf, node).?; @@ -353,11 +345,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI .call, .call_comma, => false, // TODO: once function calls are passed result locations this will change - .async_call_one, - .async_call_one_comma, - .async_call, - .async_call_comma, - => ri.have_ptr, // always use result ptr for frames else => unreachable, }; }, @@ -503,7 +490,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI return false; }, .@"try", - .@"await", .@"nosuspend", => return astrl.expr(tree.nodeData(node).node, block, ri), .grouped_expression, @@ -948,7 +934,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. .tag_name, .type_name, .Frame, - .frame_size, .int_from_float, .float_from_int, .ptr_from_int, @@ -1079,13 +1064,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. _ = try astrl.expr(args[3], block, ResultInfo.none); return false; }, - .async_call => { - _ = try astrl.expr(args[0], block, ResultInfo.none); - _ = try astrl.expr(args[1], block, ResultInfo.none); - _ = try astrl.expr(args[2], block, ResultInfo.none); - _ = try astrl.expr(args[3], block, ResultInfo.none); - return false; // buffer passed as arg for frame data - }, .Vector => { _ = try astrl.expr(args[0], block, ResultInfo.type_only); _ = try astrl.expr(args[1], block, ResultInfo.type_only); diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig index 818362a3719baf7c0d62f1cd775c06dae45e8ab4..5ef28fdfaf7b7348207a4d58a150c42622362e87 100644 --- a/lib/std/zig/BuiltinFn.zig +++ b/lib/std/zig/BuiltinFn.zig @@ -4,7 +4,6 @@ pub const Tag = enum { align_cast, align_of, as, - async_call, atomic_load, atomic_rmw, atomic_store, @@ -55,7 +54,6 @@ pub const Tag = enum { frame, Frame, frame_address, - frame_size, has_decl, has_field, import, @@ -184,13 +182,6 @@ pub const list = list: { .param_count = 2, }, }, - .{ - "@asyncCall", - .{ - .tag = .async_call, - .param_count = 4, - }, - }, .{ "@atomicLoad", .{ @@ -550,13 +541,6 @@ pub const list = list: { .illegal_outside_function = true, }, }, - .{ - "@frameSize", - .{ - .tag = .frame_size, - .param_count = 1, - }, - }, .{ "@hasDecl", .{ diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index b9684a585d8bf3165feacb322fe171eb28900f89..b398d5eb4c72a3602a92b0fb3f4227e3335242cd 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -164,22 +164,22 @@ pub const RenderOptions = struct { pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { var buffer: [256]u8 = undefined; - const bw = std.debug.lockStderrWriter(&buffer); + const w = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); - renderToWriter(eb, options, bw) catch return; + renderToWriter(eb, options, w) catch return; } -pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *Writer) (Writer.Error || std.posix.UnexpectedError)!void { +pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) (Writer.Error || std.posix.UnexpectedError)!void { if (eb.extra.len == 0) return; for (eb.getMessages()) |err_msg| { - try renderErrorMessageToWriter(eb, options, err_msg, bw, "error", .red, 0); + try renderErrorMessageToWriter(eb, options, err_msg, w, "error", .red, 0); } if (options.include_log_text) { const log_text = eb.getCompileLogOutput(); if (log_text.len != 0) { - try bw.writeAll("\nCompile Log Output:\n"); - try bw.writeAll(log_text); + try w.writeAll("\nCompile Log Output:\n"); + try w.writeAll(log_text); } } } @@ -188,73 +188,81 @@ fn renderErrorMessageToWriter( eb: ErrorBundle, options: RenderOptions, err_msg_index: MessageIndex, - bw: *Writer, + w: *Writer, kind: []const u8, color: std.io.tty.Color, indent: usize, ) (Writer.Error || std.posix.UnexpectedError)!void { const ttyconf = options.ttyconf; const err_msg = eb.getErrorMessage(err_msg_index); - const prefix_start = bw.count; if (err_msg.src_loc != .none) { const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); - try bw.splatByteAll(' ', indent); - try ttyconf.setColor(bw, .bold); - try bw.print("{s}:{d}:{d}: ", .{ + var prefix: std.io.Writer.Discarding = .init(&.{}); + try w.splatByteAll(' ', indent); + prefix.count += indent; + try ttyconf.setColor(w, .bold); + try w.print("{s}:{d}:{d}: ", .{ eb.nullTerminatedString(src.data.src_path), src.data.line + 1, src.data.column + 1, }); - try ttyconf.setColor(bw, color); - try bw.writeAll(kind); - try bw.writeAll(": "); + try prefix.writer.print("{s}:{d}:{d}: ", .{ + eb.nullTerminatedString(src.data.src_path), + src.data.line + 1, + src.data.column + 1, + }); + try ttyconf.setColor(w, color); + try w.writeAll(kind); + prefix.count += kind.len; + try w.writeAll(": "); + prefix.count += 2; // This is the length of the part before the error message: // e.g. "file.zig:4:5: error: " - const prefix_len = bw.count - prefix_start; - try ttyconf.setColor(bw, .reset); - try ttyconf.setColor(bw, .bold); + const prefix_len: usize = @intCast(prefix.count); + try ttyconf.setColor(w, .reset); + try ttyconf.setColor(w, .bold); if (err_msg.count == 1) { - try writeMsg(eb, err_msg, bw, prefix_len); - try bw.writeByte('\n'); + try writeMsg(eb, err_msg, w, prefix_len); + try w.writeByte('\n'); } else { - try writeMsg(eb, err_msg, bw, prefix_len); - try ttyconf.setColor(bw, .dim); - try bw.print(" ({d} times)\n", .{err_msg.count}); + try writeMsg(eb, err_msg, w, prefix_len); + try ttyconf.setColor(w, .dim); + try w.print(" ({d} times)\n", .{err_msg.count}); } - try ttyconf.setColor(bw, .reset); + try ttyconf.setColor(w, .reset); if (src.data.source_line != 0 and options.include_source_line) { const line = eb.nullTerminatedString(src.data.source_line); for (line) |b| switch (b) { - '\t' => try bw.writeByte(' '), - else => try bw.writeByte(b), + '\t' => try w.writeByte(' '), + else => try w.writeByte(b), }; - try bw.writeByte('\n'); + try w.writeByte('\n'); // TODO basic unicode code point monospace width const before_caret = src.data.span_main - src.data.span_start; // -1 since span.main includes the caret const after_caret = src.data.span_end -| src.data.span_main -| 1; - try bw.splatByteAll(' ', src.data.column - before_caret); - try ttyconf.setColor(bw, .green); - try bw.splatByteAll('~', before_caret); - try bw.writeByte('^'); - try bw.splatByteAll('~', after_caret); - try bw.writeByte('\n'); - try ttyconf.setColor(bw, .reset); + try w.splatByteAll(' ', src.data.column - before_caret); + try ttyconf.setColor(w, .green); + try w.splatByteAll('~', before_caret); + try w.writeByte('^'); + try w.splatByteAll('~', after_caret); + try w.writeByte('\n'); + try ttyconf.setColor(w, .reset); } for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, bw, "note", .cyan, indent); + try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent); } if (src.data.reference_trace_len > 0 and options.include_reference_trace) { - try ttyconf.setColor(bw, .reset); - try ttyconf.setColor(bw, .dim); - try bw.print("referenced by:\n", .{}); + try ttyconf.setColor(w, .reset); + try ttyconf.setColor(w, .dim); + try w.print("referenced by:\n", .{}); var ref_index = src.end; for (0..src.data.reference_trace_len) |_| { const ref_trace = eb.extraData(ReferenceTrace, ref_index); ref_index = ref_trace.end; if (ref_trace.data.src_loc != .none) { const ref_src = eb.getSourceLocation(ref_trace.data.src_loc); - try bw.print(" {s}: {s}:{d}:{d}\n", .{ + try w.print(" {s}: {s}:{d}:{d}\n", .{ eb.nullTerminatedString(ref_trace.data.decl_name), eb.nullTerminatedString(ref_src.src_path), ref_src.line + 1, @@ -262,36 +270,36 @@ fn renderErrorMessageToWriter( }); } else if (ref_trace.data.decl_name != 0) { const count = ref_trace.data.decl_name; - try bw.print( + try w.print( " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n", .{ count, count + src.data.reference_trace_len - 1 }, ); } else { - try bw.print( + try w.print( " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n", .{}, ); } } - try ttyconf.setColor(bw, .reset); + try ttyconf.setColor(w, .reset); } } else { - try ttyconf.setColor(bw, color); - try bw.splatByteAll(' ', indent); - try bw.writeAll(kind); - try bw.writeAll(": "); - try ttyconf.setColor(bw, .reset); + try ttyconf.setColor(w, color); + try w.splatByteAll(' ', indent); + try w.writeAll(kind); + try w.writeAll(": "); + try ttyconf.setColor(w, .reset); const msg = eb.nullTerminatedString(err_msg.msg); if (err_msg.count == 1) { - try bw.print("{s}\n", .{msg}); + try w.print("{s}\n", .{msg}); } else { - try bw.print("{s}", .{msg}); - try ttyconf.setColor(bw, .dim); - try bw.print(" ({d} times)\n", .{err_msg.count}); + try w.print("{s}", .{msg}); + try ttyconf.setColor(w, .dim); + try w.print(" ({d} times)\n", .{err_msg.count}); } - try ttyconf.setColor(bw, .reset); + try ttyconf.setColor(w, .reset); for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, bw, "note", .cyan, indent + 4); + try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent + 4); } } } @@ -300,13 +308,13 @@ fn renderErrorMessageToWriter( /// to allow for long, good-looking error messages. /// /// This is used to split the message in `@compileError("hello\nworld")` for example. -fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, bw: *Writer, indent: usize) !void { +fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, w: *Writer, indent: usize) !void { var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n'); while (lines.next()) |line| { - try bw.writeAll(line); + try w.writeAll(line); if (lines.index == null) break; - try bw.writeByte('\n'); - try bw.splatByteAll(' ', indent); + try w.writeByte('\n'); + try w.splatByteAll(' ', indent); } } diff --git a/lib/std/zig/Parse.zig b/lib/std/zig/Parse.zig index 34f8de91919e07feaace4205258f77b2e548aa90..fc8b61a40303c32f6311755c87f95fc08afeb496 100644 --- a/lib/std/zig/Parse.zig +++ b/lib/std/zig/Parse.zig @@ -359,16 +359,6 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { } trailing = p.tokenTag(p.tok_i - 1) == .semicolon; }, - .keyword_usingnamespace => { - const opt_node = try p.expectUsingNamespaceRecoverable(); - if (opt_node) |node| { - if (field_state == .seen) { - field_state = .{ .end = node }; - } - try p.scratch.append(p.gpa, node); - } - trailing = p.tokenTag(p.tok_i - 1) == .semicolon; - }, .keyword_const, .keyword_var, .keyword_threadlocal, @@ -496,7 +486,6 @@ fn findNextContainerMember(p: *Parse) void { .keyword_extern, .keyword_inline, .keyword_noinline, - .keyword_usingnamespace, .keyword_threadlocal, .keyword_const, .keyword_var, @@ -601,7 +590,6 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { /// Decl /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block) /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl -/// / KEYWORD_usingnamespace Expr SEMICOLON fn expectTopLevelDecl(p: *Parse) !?Node.Index { const extern_export_inline_token = p.nextToken(); var is_extern: bool = false; @@ -664,10 +652,7 @@ fn expectTopLevelDecl(p: *Parse) !?Node.Index { if (expect_var_or_fn) { return p.fail(.expected_var_decl_or_fn); } - if (p.tokenTag(p.tok_i) != .keyword_usingnamespace) { - return p.fail(.expected_pub_item); - } - return try p.expectUsingNamespace(); + return p.fail(.expected_pub_item); } fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { @@ -680,27 +665,6 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { }; } -fn expectUsingNamespace(p: *Parse) !Node.Index { - const usingnamespace_token = p.assertToken(.keyword_usingnamespace); - const expr = try p.expectExpr(); - try p.expectSemicolon(.expected_semi_after_decl, false); - return p.addNode(.{ - .tag = .@"usingnamespace", - .main_token = usingnamespace_token, - .data = .{ .node = expr }, - }); -} - -fn expectUsingNamespaceRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { - return p.expectUsingNamespace() catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.ParseError => { - p.findNextContainerMember(); - return null; - }, - }; -} - /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr fn parseFnProto(p: *Parse) !?Node.Index { const fn_token = p.eatToken(.keyword_fn) orelse return null; @@ -1688,7 +1652,6 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index { /// / MINUSPERCENT /// / AMPERSAND /// / KEYWORD_try -/// / KEYWORD_await fn parsePrefixExpr(p: *Parse) Error!?Node.Index { const tag: Node.Tag = switch (p.tokenTag(p.tok_i)) { .bang => .bool_not, @@ -1697,7 +1660,6 @@ fn parsePrefixExpr(p: *Parse) Error!?Node.Index { .minus_percent => .negation_wrap, .ampersand => .address_of, .keyword_try => .@"try", - .keyword_await => .@"await", else => return p.parsePrimaryExpr(), }; return try p.addNode(.{ @@ -2385,62 +2347,12 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index { } /// SuffixExpr -/// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments -/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* +/// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* /// /// FnCallArguments <- LPAREN ExprList RPAREN /// /// ExprList <- (Expr COMMA)* Expr? fn parseSuffixExpr(p: *Parse) !?Node.Index { - if (p.eatToken(.keyword_async)) |_| { - var res = try p.expectPrimaryTypeExpr(); - while (true) { - res = try p.parseSuffixOp(res) orelse break; - } - const lparen = p.eatToken(.l_paren) orelse { - try p.warn(.expected_param_list); - return res; - }; - const scratch_top = p.scratch.items.len; - defer p.scratch.shrinkRetainingCapacity(scratch_top); - while (true) { - if (p.eatToken(.r_paren)) |_| break; - const param = try p.expectExpr(); - try p.scratch.append(p.gpa, param); - switch (p.tokenTag(p.tok_i)) { - .comma => p.tok_i += 1, - .r_paren => { - p.tok_i += 1; - break; - }, - .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), - // Likely just a missing comma; give error but continue parsing. - else => try p.warn(.expected_comma_after_arg), - } - } - const comma = (p.tokenTag(p.tok_i - 2)) == .comma; - const params = p.scratch.items[scratch_top..]; - if (params.len <= 1) { - return try p.addNode(.{ - .tag = if (comma) .async_call_one_comma else .async_call_one, - .main_token = lparen, - .data = .{ .node_and_opt_node = .{ - res, - if (params.len >= 1) params[0].toOptional() else .none, - } }, - }); - } else { - return try p.addNode(.{ - .tag = if (comma) .async_call_comma else .async_call, - .main_token = lparen, - .data = .{ .node_and_extra = .{ - res, - try p.addExtra(try p.listToSpan(params)), - } }, - }); - } - } - var res = try p.parsePrimaryTypeExpr() orelse return null; while (true) { const opt_suffix_op = try p.parseSuffixOp(res); diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 17643e6f1e7c45b3b7247ce2946748f4e1c9681b..c53ec630bacc57b2f963eaeacd37d2103ac12fff 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -899,8 +899,6 @@ pub const Inst = struct { type_name, /// Implement builtin `@Frame`. Uses `un_node`. frame_type, - /// Implement builtin `@frameSize`. Uses `un_node`. - frame_size, /// Implements the `@intFromFloat` builtin. /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. @@ -1044,7 +1042,6 @@ pub const Inst = struct { /// Implements `resume` syntax. Uses `un_node` field. @"resume", - @"await", /// A defer statement. /// Uses the `defer` union field. @@ -1241,7 +1238,6 @@ pub const Inst = struct { .tag_name, .type_name, .frame_type, - .frame_size, .int_from_float, .float_from_int, .ptr_from_int, @@ -1279,7 +1275,6 @@ pub const Inst = struct { .min, .c_import, .@"resume", - .@"await", .ret_err_value_code, .extended, .ret_ptr, @@ -1526,7 +1521,6 @@ pub const Inst = struct { .tag_name, .type_name, .frame_type, - .frame_size, .int_from_float, .float_from_int, .ptr_from_int, @@ -1560,7 +1554,6 @@ pub const Inst = struct { .min, .c_import, .@"resume", - .@"await", .ret_err_value_code, .@"break", .break_inline, @@ -1791,7 +1784,6 @@ pub const Inst = struct { .tag_name = .un_node, .type_name = .un_node, .frame_type = .un_node, - .frame_size = .un_node, .int_from_float = .pl_node, .float_from_int = .pl_node, @@ -1852,7 +1844,6 @@ pub const Inst = struct { .make_ptr_const = .un_node, .@"resume" = .un_node, - .@"await" = .un_node, .@"defer" = .@"defer", .defer_err_code = .defer_err_code, @@ -2016,8 +2007,6 @@ pub const Inst = struct { /// Implements the `@errorCast` builtin. /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. error_cast, - /// `operand` is payload index to `UnNode`. - await_nosuspend, /// Implements `@breakpoint`. /// `operand` is `src_node: Ast.Node.Offset`. breakpoint, @@ -2038,9 +2027,6 @@ pub const Inst = struct { /// `operand` is payload index to `Reify`. /// `small` contains `NameStrategy`. reify, - /// Implements the `@asyncCall` builtin. - /// `operand` is payload index to `AsyncCall`. - builtin_async_call, /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins. /// `small` 0=>weak 1=>strong /// `operand` is payload index to `Cmpxchg`. @@ -2689,7 +2675,6 @@ pub const Inst = struct { @"test", decltest, @"comptime", - @"usingnamespace", @"const", @"var", }; @@ -2706,7 +2691,7 @@ pub const Inst = struct { src_column: u32, kind: Kind, - /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`. + /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"` name: NullTerminatedString, /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`. is_pub: bool, @@ -2737,9 +2722,6 @@ pub const Inst = struct { decltest, @"comptime", - @"usingnamespace", - pub_usingnamespace, - const_simple, const_typed, @"const", @@ -2776,8 +2758,6 @@ pub const Inst = struct { return switch (id) { .unnamed_test, .@"comptime", - .@"usingnamespace", - .pub_usingnamespace, => false, else => true, }; @@ -2802,8 +2782,6 @@ pub const Inst = struct { .@"test", .decltest, .@"comptime", - .@"usingnamespace", - .pub_usingnamespace, => false, // these constructs are untyped .const_simple, .pub_const_simple, @@ -2835,8 +2813,6 @@ pub const Inst = struct { .@"test", .decltest, .@"comptime", - .@"usingnamespace", - .pub_usingnamespace, => false, // these constructs are untyped .const_simple, .const_typed, @@ -2879,7 +2855,6 @@ pub const Inst = struct { .@"test" => .@"test", .decltest => .decltest, .@"comptime" => .@"comptime", - .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace", .const_simple, .const_typed, .@"const", @@ -2913,7 +2888,6 @@ pub const Inst = struct { pub fn isPub(id: Id) bool { return switch (id) { - .pub_usingnamespace, .pub_const_simple, .pub_const_typed, .pub_const, @@ -2949,8 +2923,7 @@ pub const Inst = struct { pub const Name = enum(u32) { @"comptime" = std.math.maxInt(u32), - @"usingnamespace" = std.math.maxInt(u32) - 1, - unnamed_test = std.math.maxInt(u32) - 2, + unnamed_test = std.math.maxInt(u32) - 1, /// Other values are `NullTerminatedString` values, i.e. index into /// `string_bytes`. If the byte referenced is 0, the decl is a named /// test, and the actual name begins at the following byte. @@ -2958,13 +2931,13 @@ pub const Inst = struct { pub fn isNamedTest(name: Name, zir: Zir) bool { return switch (name) { - .@"comptime", .@"usingnamespace", .unnamed_test => false, + .@"comptime", .unnamed_test => false, _ => zir.string_bytes[@intFromEnum(name)] == 0, }; } pub fn toString(name: Name, zir: Zir) ?NullTerminatedString { switch (name) { - .@"comptime", .@"usingnamespace", .unnamed_test => return null, + .@"comptime", .unnamed_test => return null, _ => {}, } const idx: u32 = @intFromEnum(name); @@ -3771,14 +3744,6 @@ pub const Inst = struct { b: Ref, }; - pub const AsyncCall = struct { - node: Ast.Node.Offset, - frame_buffer: Ref, - result_ptr: Ref, - fn_ptr: Ref, - args: Ref, - }; - /// Trailing: inst: Index // for every body_len pub const Param = struct { /// Null-terminated string index. @@ -4297,7 +4262,6 @@ fn findTrackableInner( .tag_name, .type_name, .frame_type, - .frame_size, .int_from_float, .float_from_int, .ptr_from_int, @@ -4337,7 +4301,6 @@ fn findTrackableInner( .resolve_inferred_alloc, .make_ptr_const, .@"resume", - .@"await", .save_err_ret_index, .restore_err_ret_index_unconditional, .restore_err_ret_index_fn_entry, @@ -4380,14 +4343,12 @@ fn findTrackableInner( .prefetch, .set_float_mode, .error_cast, - .await_nosuspend, .breakpoint, .disable_instrumentation, .disable_intrinsics, .select, .int_from_error, .error_from_int, - .builtin_async_call, .cmpxchg, .c_va_arg, .c_va_copy, diff --git a/lib/std/zig/ZonGen.zig b/lib/std/zig/ZonGen.zig index 6693a7dbbf9cc27df5687a0c814bcefa843a160b..bb66f76841db8b03d3078e3d06b7f772d0d8f6a3 100644 --- a/lib/std/zig/ZonGen.zig +++ b/lib/std/zig/ZonGen.zig @@ -100,7 +100,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator switch (tree.nodeTag(node)) { .root => unreachable, - .@"usingnamespace" => unreachable, .test_decl => unreachable, .container_field_init => unreachable, .container_field_align => unreachable, @@ -204,12 +203,8 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, .@"return", .if_simple, .@"if", @@ -226,7 +221,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator .switch_comma, .@"nosuspend", .@"suspend", - .@"await", .@"resume", .@"try", .unreachable_literal, @@ -776,13 +770,7 @@ fn lowerStrLitError( raw_string: []const u8, offset: u32, ) Allocator.Error!void { - return ZonGen.addErrorTokOff( - zg, - token, - @intCast(offset + err.offset()), - "{f}", - .{err.fmt(raw_string)}, - ); + return ZonGen.addErrorTokOff(zg, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)}); } fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void { diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 419b12d5cacefb3231cdcad2bb7efd31d16d9393..b3e0311cddc0b77dfe8ed071b3542daef2ae0344 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -1,3 +1,14 @@ +const std = @import("../../std.zig"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const bitcode_writer = @import("bitcode_writer.zig"); +const Builder = @This(); +const builtin = @import("builtin"); +const DW = std.dwarf; +const ir = @import("ir.zig"); +const log = std.log.scoped(.llvm); +const Writer = std.io.Writer; + gpa: Allocator, strip: bool, @@ -90,26 +101,38 @@ pub const String = enum(u32) { const FormatData = struct { string: String, builder: *const Builder, + quote_behavior: ?QuoteBehavior, }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); + fn format(data: FormatData, w: *Writer) Writer.Error!void { assert(data.string != .none); const string_slice = data.string.slice(data.builder) orelse - return bw.print("{d}", .{@intFromEnum(data.string)}); - if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| - return bw.writeAll(string_slice); - try printEscapedString( - string_slice, - if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| - .always_quote - else - .quote_unless_valid_identifier, - bw, - ); + return w.print("{d}", .{@intFromEnum(data.string)}); + const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice); + return printEscapedString(string_slice, quote_behavior, w); } - pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .string = self, .builder = builder } }; + + pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ + .string = self, + .builder = builder, + .quote_behavior = .quote_unless_valid_identifier, + } }; + } + + pub fn fmtQ(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ + .string = self, + .builder = builder, + .quote_behavior = .always_quote, + } }; + } + + pub fn fmtRaw(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ + .string = self, + .builder = builder, + .quote_behavior = null, + } }; } fn fromIndex(index: ?usize) String { @@ -223,7 +246,7 @@ pub const Type = enum(u32) { _, pub const ptr_amdgpu_constant = - @field(Type, std.fmt.comptimePrint("ptr{f }", .{AddrSpace.amdgpu.constant})); + @field(Type, std.fmt.comptimePrint("ptr{f}", .{AddrSpace.amdgpu.constant.fmt(" ")})); pub const Tag = enum(u4) { simple, @@ -648,13 +671,16 @@ pub const Type = enum(u32) { const FormatData = struct { type: Type, builder: *const Builder, + mode: Mode, + + const Mode = enum { default, m, lt, gt, percent }; }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { + fn format(data: FormatData, w: *Writer) Writer.Error!void { assert(data.type != .none); - if (comptime std.mem.eql(u8, fmt_str, "m")) { + if (data.mode == .m) { const item = data.builder.type_items.items[@intFromEnum(data.type)]; switch (item.tag) { - .simple => try bw.writeAll(switch (@as(Simple, @enumFromInt(item.data))) { + .simple => try w.writeAll(switch (@as(Simple, @enumFromInt(item.data))) { .void => "isVoid", .half => "f16", .bfloat => "bf16", @@ -671,36 +697,36 @@ pub const Type = enum(u32) { .function, .vararg_function => |kind| { var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); const params = extra.trail.next(extra.data.params_len, Type, data.builder); - try bw.print("f_{fm}", .{extra.data.ret.fmt(data.builder)}); - for (params) |param| try bw.print("{fm}", .{param.fmt(data.builder)}); + try w.print("f_{f}", .{extra.data.ret.fmt(data.builder, .m)}); + for (params) |param| try w.print("{f}", .{param.fmt(data.builder, .m)}); switch (kind) { .function => {}, - .vararg_function => try bw.writeAll("vararg"), + .vararg_function => try w.writeAll("vararg"), else => unreachable, } - try bw.writeByte('f'); + try w.writeByte('f'); }, - .integer => try bw.print("i{d}", .{item.data}), - .pointer => try bw.print("p{d}", .{item.data}), + .integer => try w.print("i{d}", .{item.data}), + .pointer => try w.print("p{d}", .{item.data}), .target => { var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); const types = extra.trail.next(extra.data.types_len, Type, data.builder); const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); - try bw.print("t{s}", .{extra.data.name.slice(data.builder).?}); - for (types) |ty| try bw.print("_{fm}", .{ty.fmt(data.builder)}); - for (ints) |int| try bw.print("_{d}", .{int}); - try bw.writeByte('t'); + try w.print("t{s}", .{extra.data.name.slice(data.builder).?}); + for (types) |ty| try w.print("_{f}", .{ty.fmt(data.builder, .m)}); + for (ints) |int| try w.print("_{d}", .{int}); + try w.writeByte('t'); }, .vector, .scalable_vector => |kind| { const extra = data.builder.typeExtraData(Type.Vector, item.data); - try bw.print("{s}v{d}{fm}", .{ + try w.print("{s}v{d}{f}", .{ switch (kind) { .vector => "", .scalable_vector => "nx", else => unreachable, }, extra.len, - extra.child.fmt(data.builder), + extra.child.fmt(data.builder, .m), }); }, inline .small_array, .array => |kind| { @@ -709,72 +735,72 @@ pub const Type = enum(u32) { .array => Type.Array, else => unreachable, }, item.data); - try bw.print("a{d}{fm}", .{ extra.length(), extra.child.fmt(data.builder) }); + try w.print("a{d}{f}", .{ extra.length(), extra.child.fmt(data.builder, .m) }); }, .structure, .packed_structure => { var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); - try bw.writeAll("sl_"); - for (fields) |field| try bw.print("{fm}", .{field.fmt(data.builder)}); - try bw.writeByte('s'); + try w.writeAll("sl_"); + for (fields) |field| try w.print("{f}", .{field.fmt(data.builder, .m)}); + try w.writeByte('s'); }, .named_structure => { const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); - try bw.writeAll("s_"); - if (extra.id.slice(data.builder)) |id| try bw.writeAll(id); + try w.writeAll("s_"); + if (extra.id.slice(data.builder)) |id| try w.writeAll(id); }, } return; } - if (std.enums.tagName(Type, data.type)) |name| return bw.writeAll(name); + if (std.enums.tagName(Type, data.type)) |name| return w.writeAll(name); const item = data.builder.type_items.items[@intFromEnum(data.type)]; switch (item.tag) { .simple => unreachable, .function, .vararg_function => |kind| { var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); const params = extra.trail.next(extra.data.params_len, Type, data.builder); - if (!comptime std.mem.eql(u8, fmt_str, ">")) - try bw.print("{f%} ", .{extra.data.ret.fmt(data.builder)}); - if (!comptime std.mem.eql(u8, fmt_str, "<")) { - try bw.writeByte('('); + if (data.mode != .gt) + try w.print("{f} ", .{extra.data.ret.fmt(data.builder, .percent)}); + if (data.mode != .lt) { + try w.writeByte('('); for (params, 0..) |param, index| { - if (index > 0) try bw.writeAll(", "); - try bw.print("{f%}", .{param.fmt(data.builder)}); + if (index > 0) try w.writeAll(", "); + try w.print("{f}", .{param.fmt(data.builder, .percent)}); } switch (kind) { .function => {}, .vararg_function => { - if (params.len > 0) try bw.writeAll(", "); - try bw.writeAll("..."); + if (params.len > 0) try w.writeAll(", "); + try w.writeAll("..."); }, else => unreachable, } - try bw.writeByte(')'); + try w.writeByte(')'); } }, - .integer => try bw.print("i{d}", .{item.data}), - .pointer => try bw.print("ptr{f }", .{@as(AddrSpace, @enumFromInt(item.data))}), + .integer => try w.print("i{d}", .{item.data}), + .pointer => try w.print("ptr{f}", .{@as(AddrSpace, @enumFromInt(item.data)).fmt(" ")}), .target => { var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); const types = extra.trail.next(extra.data.types_len, Type, data.builder); const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); - try bw.print( - \\target({f"} - , .{extra.data.name.fmt(data.builder)}); - for (types) |ty| try bw.print(", {f%}", .{ty.fmt(data.builder)}); - for (ints) |int| try bw.print(", {d}", .{int}); - try bw.writeByte(')'); + try w.print( + \\target({f} + , .{extra.data.name.fmtQ(data.builder)}); + for (types) |ty| try w.print(", {f}", .{ty.fmt(data.builder, .percent)}); + for (ints) |int| try w.print(", {d}", .{int}); + try w.writeByte(')'); }, .vector, .scalable_vector => |kind| { const extra = data.builder.typeExtraData(Type.Vector, item.data); - try bw.print("<{s}{d} x {f%}>", .{ + try w.print("<{s}{d} x {f}>", .{ switch (kind) { .vector => "", .scalable_vector => "vscale x ", else => unreachable, }, extra.len, - extra.child.fmt(data.builder), + extra.child.fmt(data.builder, .percent), }); }, inline .small_array, .array => |kind| { @@ -783,44 +809,45 @@ pub const Type = enum(u32) { .array => Type.Array, else => unreachable, }, item.data); - try bw.print("[{d} x {f%}]", .{ extra.length(), extra.child.fmt(data.builder) }); + try w.print("[{d} x {f}]", .{ extra.length(), extra.child.fmt(data.builder, .percent) }); }, .structure, .packed_structure => |kind| { var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); switch (kind) { .structure => {}, - .packed_structure => try bw.writeByte('<'), + .packed_structure => try w.writeByte('<'), else => unreachable, } - try bw.writeAll("{ "); + try w.writeAll("{ "); for (fields, 0..) |field, index| { - if (index > 0) try bw.writeAll(", "); - try bw.print("{f%}", .{field.fmt(data.builder)}); + if (index > 0) try w.writeAll(", "); + try w.print("{f}", .{field.fmt(data.builder, .percent)}); } - try bw.writeAll(" }"); + try w.writeAll(" }"); switch (kind) { .structure => {}, - .packed_structure => try bw.writeByte('>'), + .packed_structure => try w.writeByte('>'), else => unreachable, } }, .named_structure => { const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); - if (comptime std.mem.eql(u8, fmt_str, "%")) try bw.print("%{f}", .{ + if (data.mode == .percent) try w.print("%{f}", .{ extra.id.fmt(data.builder), }) else switch (extra.body) { - .none => try bw.writeAll("opaque"), + .none => try w.writeAll("opaque"), else => try format(.{ .type = extra.body, .builder = data.builder, - }, bw, fmt_str), + .mode = data.mode, + }, w), } }, } } - pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .type = self, .builder = builder } }; + pub fn fmt(self: Type, builder: *const Builder, mode: FormatData.Mode) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ .type = self, .builder = builder, .mode = mode } }; } const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void); @@ -1128,10 +1155,13 @@ pub const Attribute = union(Kind) { const FormatData = struct { attribute_index: Index, builder: *const Builder, + flags: Flags = .{}, + const Flags = struct { + pound: bool = false, + quote: bool = false, + }; }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); + fn format(data: FormatData, w: *Writer) Writer.Error!void { const attribute = data.attribute_index.toAttribute(data.builder); switch (attribute) { .zeroext, @@ -1204,97 +1234,99 @@ pub const Attribute = union(Kind) { .no_sanitize_address, .no_sanitize_hwaddress, .sanitize_address_dyninit, - => try bw.print(" {s}", .{@tagName(attribute)}), + => try w.print(" {s}", .{@tagName(attribute)}), .byval, .byref, .preallocated, .inalloca, .sret, .elementtype, - => |ty| try bw.print(" {s}({f%})", .{ @tagName(attribute), ty.fmt(data.builder) }), - .@"align" => |alignment| try bw.print("{f }", .{alignment}), + => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }), + .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}), .dereferenceable, .dereferenceable_or_null, - => |size| try bw.print(" {s}({d})", .{ @tagName(attribute), size }), + => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }), .nofpclass => |fpclass| { const Int = @typeInfo(FpClass).@"struct".backing_integer.?; - try bw.print(" {s}(", .{@tagName(attribute)}); + try w.print(" {s}(", .{@tagName(attribute)}); var any = false; var remaining: Int = @bitCast(fpclass); inline for (@typeInfo(FpClass).@"struct".decls) |decl| { const pattern: Int = @bitCast(@field(FpClass, decl.name)); if (remaining & pattern == pattern) { if (!any) { - try bw.writeByte(' '); + try w.writeByte(' '); any = true; } - try bw.writeAll(decl.name); + try w.writeAll(decl.name); remaining &= ~pattern; } } - try bw.writeByte(')'); + try w.writeByte(')'); + }, + .alignstack => |alignment| { + try w.print(" {t}", .{attribute}); + const alignment_bytes = alignment.toByteUnits() orelse return; + if (data.flags.pound) { + try w.print("={d}", .{alignment_bytes}); + } else { + try w.print("({d})", .{alignment_bytes}); + } }, - .alignstack => |alignment| try bw.print( - if (comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null) - " {s}={d}" - else - " {s}({d})", - .{ @tagName(attribute), alignment.toByteUnits() orelse return }, - ), .allockind => |allockind| { - try bw.print(" {s}(\"", .{@tagName(attribute)}); + try w.print(" {t}(\"", .{attribute}); var any = false; inline for (@typeInfo(AllocKind).@"struct".fields) |field| { if (comptime std.mem.eql(u8, field.name, "_")) continue; if (@field(allockind, field.name)) { if (!any) { - try bw.writeByte(','); + try w.writeByte(','); any = true; } - try bw.writeAll(field.name); + try w.writeAll(field.name); } } - try bw.writeAll("\")"); + try w.writeAll("\")"); }, .allocsize => |allocsize| { - try bw.print(" {s}({d}", .{ @tagName(attribute), allocsize.elem_size }); + try w.print(" {t}({d}", .{ attribute, allocsize.elem_size }); if (allocsize.num_elems != AllocSize.none) - try bw.print(",{d}", .{allocsize.num_elems}); - try bw.writeByte(')'); + try w.print(",{d}", .{allocsize.num_elems}); + try w.writeByte(')'); }, .memory => |memory| { - try bw.print(" {s}(", .{@tagName(attribute)}); + try w.print(" {t}(", .{attribute}); var any = memory.other != .none or (memory.argmem == .none and memory.inaccessiblemem == .none); - if (any) try bw.writeAll(@tagName(memory.other)); + if (any) try w.writeAll(@tagName(memory.other)); inline for (.{ "argmem", "inaccessiblemem" }) |kind| { if (@field(memory, kind) != memory.other) { - if (any) try bw.writeAll(", "); - try bw.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) }); + if (any) try w.writeAll(", "); + try w.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) }); any = true; } } - try bw.writeByte(')'); + try w.writeByte(')'); }, .uwtable => |uwtable| if (uwtable != .none) { - try bw.print(" {s}", .{@tagName(attribute)}); - if (uwtable != UwTable.default) try bw.print("({s})", .{@tagName(uwtable)}); + try w.print(" {s}", .{@tagName(attribute)}); + if (uwtable != UwTable.default) try w.print("({s})", .{@tagName(uwtable)}); }, - .vscale_range => |vscale_range| try bw.print(" {s}({d},{d})", .{ + .vscale_range => |vscale_range| try w.print(" {s}({d},{d})", .{ @tagName(attribute), vscale_range.min.toByteUnits().?, vscale_range.max.toByteUnits() orelse 0, }), - .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { - try bw.print(" {f\"}", .{string_attr.kind.fmt(data.builder)}); + .string => |string_attr| if (data.flags.quote) { + try w.print(" {f}", .{string_attr.kind.fmtQ(data.builder)}); if (string_attr.value != .empty) - try bw.print("={f\"}", .{string_attr.value.fmt(data.builder)}); + try w.print("={f}", .{string_attr.value.fmtQ(data.builder)}); }, .none => unreachable, } } - pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .attribute_index = self, .builder = builder } }; + pub fn fmt(self: Index, builder: *const Builder, mode: FormatData.mode) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ .attribute_index = self, .builder = builder, .mode = mode } }; } fn toStorage(self: Index, builder: *const Builder) Storage { @@ -1506,9 +1538,9 @@ pub const Attribute = union(Kind) { pub const UwTable = enum(u32) { none, sync, - @"async", + async, - pub const default = UwTable.@"async"; + pub const default = UwTable.async; }; pub const VScaleRange = packed struct(u32) { @@ -1567,15 +1599,18 @@ pub const Attributes = enum(u32) { const FormatData = struct { attributes: Attributes, builder: *const Builder, + flags: Flags = .{}, + const Flags = Attribute.Index.FormatData.Flags; }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { + fn format(data: FormatData, w: *Writer) Writer.Error!void { for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{ .attribute_index = attribute_index, .builder = data.builder, - }, bw, fmt_str); + .flags = data.flags, + }, w); } - pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .attributes = self, .builder = builder } }; + pub fn fmt(self: Attributes, builder: *const Builder, flags: FormatData.Flags) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ .attributes = self, .builder = builder, .flags = flags } }; } }; @@ -1761,14 +1796,14 @@ pub const Linkage = enum(u4) { extern_weak = 7, external = 0, - pub fn format(self: Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void { - if (self != .external) try bw.print(" {s}", .{@tagName(self)}); + pub fn format(self: Linkage, w: *Writer) Writer.Error!void { + if (self != .external) try w.print(" {s}", .{@tagName(self)}); } - fn formatOptional(data: ?Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void { - if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)}); + fn formatOptional(data: ?Linkage, w: *Writer) Writer.Error!void { + if (data) |linkage| try w.print(" {s}", .{@tagName(linkage)}); } - pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) { + pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(?Linkage, formatOptional) { return .{ .data = self }; } }; @@ -1778,8 +1813,8 @@ pub const Preemption = enum { dso_local, implicit_dso_local, - pub fn format(self: Preemption, bw: *Writer, comptime _: []const u8) Writer.Error!void { - if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)}); + pub fn format(self: Preemption, w: *Writer) Writer.Error!void { + if (self == .dso_local) try w.print(" {s}", .{@tagName(self)}); } }; @@ -1796,8 +1831,7 @@ pub const Visibility = enum(u2) { }; } - pub fn format(self: Visibility, comptime format_string: []const u8, writer: *Writer) Writer.Error!void { - comptime assert(format_string.len == 0); + pub fn format(self: Visibility, writer: *Writer) Writer.Error!void { if (self != .default) try writer.print(" {s}", .{@tagName(self)}); } }; @@ -1807,8 +1841,8 @@ pub const DllStorageClass = enum(u2) { dllimport = 1, dllexport = 2, - pub fn format(self: DllStorageClass, bw: *Writer, comptime _: []const u8) Writer.Error!void { - if (self != .default) try bw.print(" {s}", .{@tagName(self)}); + pub fn format(self: DllStorageClass, w: *Writer) Writer.Error!void { + if (self != .default) try w.print(" {s}", .{@tagName(self)}); } }; @@ -1819,10 +1853,31 @@ pub const ThreadLocal = enum(u3) { initialexec = 3, localexec = 4, - pub fn format(self: ThreadLocal, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self == .default) return; - try bw.print("{s}thread_local", .{prefix}); - if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)}); + pub fn format(tl: ThreadLocal, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .thread_local = tl, .prefix = "" }, w); + } + + pub const Prefixed = struct { + thread_local: ThreadLocal, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.thread_local) { + .default => return, + .generaldynamic => { + var vecs: [2][]const u8 = .{ p.prefix, "thread_local" }; + return w.writeVecAll(&vecs); + }, + else => { + var vecs: [4][]const u8 = .{ p.prefix, "thread_local(", @tagName(p.thread_local), ")" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(tl: ThreadLocal, prefix: []const u8) Prefixed { + return .{ .thread_local = tl, .prefix = prefix }; } }; @@ -1833,8 +1888,8 @@ pub const UnnamedAddr = enum(u2) { unnamed_addr = 1, local_unnamed_addr = 2, - pub fn format(self: UnnamedAddr, bw: *Writer, comptime _: []const u8) Writer.Error!void { - if (self != .default) try bw.print(" {s}", .{@tagName(self)}); + pub fn format(self: UnnamedAddr, w: *Writer) Writer.Error!void { + if (self != .default) try w.print(" {s}", .{@tagName(self)}); } }; @@ -1927,8 +1982,24 @@ pub const AddrSpace = enum(u24) { pub const funcref: AddrSpace = @enumFromInt(20); }; - pub fn format(self: AddrSpace, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); + pub fn format(addr_space: AddrSpace, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .addr_space = addr_space, .prefix = "" }, w); + } + + pub const Prefixed = struct { + addr_space: AddrSpace, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.addr_space) { + .default => return, + else => return w.print("{s}addrspace({d})", .{ p.prefix, p.addr_space }), + } + } + }; + + pub fn fmt(addr_space: AddrSpace, prefix: []const u8) Prefixed { + return .{ .addr_space = addr_space, .prefix = prefix }; } }; @@ -1936,8 +2007,8 @@ pub const ExternallyInitialized = enum { default, externally_initialized, - pub fn format(self: ExternallyInitialized, bw: *Writer, comptime _: []const u8) Writer.Error!void { - if (self != .default) try bw.print(" {s}", .{@tagName(self)}); + pub fn format(self: ExternallyInitialized, w: *Writer) Writer.Error!void { + if (self != .default) try w.print(" {s}", .{@tagName(self)}); } }; @@ -1960,8 +2031,18 @@ pub const Alignment = enum(u6) { return if (self == .default) 0 else (@intFromEnum(self) + 1); } - pub fn format(self: Alignment, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { - try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); + pub const Prefixed = struct { + alignment: Alignment, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + const byte_units = p.alignment.toByteUnits() orelse return; + return w.print("{s}align ({d})", .{ p.prefix, byte_units }); + } + }; + + pub fn fmt(alignment: Alignment, prefix: []const u8) Prefixed { + return .{ .alignment = alignment, .prefix = prefix }; } }; @@ -2034,7 +2115,7 @@ pub const CallConv = enum(u10) { pub const default = CallConv.ccc; - pub fn format(self: CallConv, bw: *Writer, comptime _: []const u8) Writer.Error!void { + pub fn format(self: CallConv, w: *Writer) Writer.Error!void { switch (self) { default => {}, .fastcc, @@ -2088,8 +2169,8 @@ pub const CallConv = enum(u10) { .aarch64_sme_preservemost_from_x2, .m68k_rtdcc, .riscv_vectorcallcc, - => try bw.print(" {s}", .{@tagName(self)}), - _ => try bw.print(" cc{d}", .{@intFromEnum(self)}), + => try w.print(" {s}", .{@tagName(self)}), + _ => try w.print(" cc{d}", .{@intFromEnum(self)}), } } }; @@ -2114,26 +2195,25 @@ pub const StrtabString = enum(u32) { const FormatData = struct { string: StrtabString, builder: *const Builder, + quote_behavior: ?QuoteBehavior, }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); + fn format(data: FormatData, w: *Writer) Writer.Error!void { assert(data.string != .none); const string_slice = data.string.slice(data.builder) orelse - return bw.print("{d}", .{@intFromEnum(data.string)}); - if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| - return bw.writeAll(string_slice); - try printEscapedString( - string_slice, - if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| - .always_quote - else - .quote_unless_valid_identifier, - bw, - ); + return w.print("{d}", .{@intFromEnum(data.string)}); + const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice); + return printEscapedString(string_slice, quote_behavior, w); } - pub fn fmt(self: StrtabString, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .string = self, .builder = builder } }; + pub fn fmt( + self: StrtabString, + builder: *const Builder, + quote_behavior: ?QuoteBehavior, + ) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ + .string = self, + .builder = builder, + .quote_behavior = quote_behavior, + } }; } fn fromIndex(index: ?usize) StrtabString { @@ -2302,12 +2382,12 @@ pub const Global = struct { global: Index, builder: *const Builder, }; - fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void { - try bw.print("@{f}", .{ - data.global.unwrap(data.builder).name(data.builder).fmt(data.builder), + fn format(data: FormatData, w: *Writer) Writer.Error!void { + try w.print("@{f}", .{ + data.global.unwrap(data.builder).name(data.builder).fmt(data.builder, null), }); } - pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { + pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(FormatData, format) { return .{ .data = .{ .global = self, .builder = builder } }; } @@ -4747,24 +4827,23 @@ pub const Function = struct { instruction: Instruction.Index, function: Function.Index, builder: *Builder, + flags: FormatFlags, }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { + fn format(data: FormatData, w: *Writer) Writer.Error!void { + if (data.flags.comma) { if (data.instruction == .none) return; - try bw.writeByte(','); + try w.writeByte(','); } - if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) { + if (data.flags.space) { if (data.instruction == .none) return; - try bw.writeByte(' '); + try w.writeByte(' '); } - if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) try bw.print( - "{f%} ", - .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder)}, + if (data.flags.percent) try w.print( + "{f} ", + .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder, .percent)}, ); assert(data.instruction != .none); - try bw.print("%{f}", .{ + try w.print("%{f}", .{ data.instruction.name(data.function.ptrConst(data.builder)).fmt(data.builder), }); } @@ -4772,8 +4851,14 @@ pub const Function = struct { self: Instruction.Index, function: Function.Index, builder: *Builder, - ) std.fmt.Formatter(format) { - return .{ .data = .{ .instruction = self, .function = function, .builder = builder } }; + flags: FormatFlags, + ) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ + .instruction = self, + .function = function, + .builder = builder, + .flags = flags, + } }; } }; @@ -6270,10 +6355,10 @@ pub const WipFunction = struct { while (true) { gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1); - const unique_name = try wip_name.builder.fmt("{fr}{s}{fr}", .{ - name.fmt(wip_name.builder), + const unique_name = try wip_name.builder.fmt("{f}{s}{f}", .{ + name.fmtRaw(wip_name.builder), sep, - gop.value_ptr.fmt(wip_name.builder), + gop.value_ptr.fmtRaw(wip_name.builder), }); const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name); if (!unique_gop.found_existing) { @@ -6940,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) { normal, @"volatile", - pub fn format(self: MemoryAccessKind, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) }); + pub fn format(memory_access_kind: MemoryAccessKind, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .memory_access_kind = memory_access_kind, .prefix = "" }, w); + } + + pub const Prefixed = struct { + memory_access_kind: MemoryAccessKind, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.memory_access_kind) { + .normal => return, + .@"volatile" => { + var vecs: [2][]const u8 = .{ p.prefix, "volatile" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(memory_access_kind: MemoryAccessKind, prefix: []const u8) Prefixed { + return .{ .memory_access_kind = memory_access_kind, .prefix = prefix }; } }; @@ -6949,10 +7053,27 @@ pub const SyncScope = enum(u1) { singlethread, system, - pub fn format(self: SyncScope, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .system) try bw.print( - \\{s}syncscope("{s}") - , .{ prefix, @tagName(self) }); + pub fn format(sync_scope: SyncScope, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .sync_scope = sync_scope, .prefix = "" }, w); + } + + pub const Prefixed = struct { + sync_scope: SyncScope, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.sync_scope) { + .system => return, + .singlethread => { + var vecs: [2][]const u8 = .{ p.prefix, "syncscope(\"singlethread\")" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(sync_scope: SyncScope, prefix: []const u8) Prefixed { + return .{ .sync_scope = sync_scope, .prefix = prefix }; } }; @@ -6965,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) { acq_rel = 5, seq_cst = 6, - pub fn format(self: AtomicOrdering, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) }); + pub fn format(atomic_ordering: AtomicOrdering, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .atomic_ordering = atomic_ordering, .prefix = "" }, w); + } + + pub const Prefixed = struct { + atomic_ordering: AtomicOrdering, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.atomic_ordering) { + .none => return, + else => { + var vecs: [2][]const u8 = .{ p.prefix, @tagName(p.atomic_ordering) }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(atomic_ordering: AtomicOrdering, prefix: []const u8) Prefixed { + return .{ .atomic_ordering = atomic_ordering, .prefix = prefix }; } }; @@ -7380,22 +7520,21 @@ pub const Constant = enum(u32) { const FormatData = struct { constant: Constant, builder: *Builder, + flags: FormatFlags, }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { + fn format(data: FormatData, w: *Writer) Writer.Error!void { + if (data.flags.comma) { if (data.constant == .no_init) return; - try bw.writeByte(','); + try w.writeByte(','); } - if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) { + if (data.flags.space) { if (data.constant == .no_init) return; - try bw.writeByte(' '); + try w.writeByte(' '); } - if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) - try bw.print("{f%} ", .{data.constant.typeOf(data.builder).fmt(data.builder)}); + if (data.flags.percent) + try w.print("{f} ", .{data.constant.typeOf(data.builder).fmt(data.builder, .percent)}); assert(data.constant != .no_init); - if (std.enums.tagName(Constant, data.constant)) |name| return bw.writeAll(name); + if (std.enums.tagName(Constant, data.constant)) |name| return w.writeAll(name); switch (data.constant.unwrap()) { .constant => |constant| { const item = data.builder.constant_items.get(constant); @@ -7432,13 +7571,13 @@ pub const Constant = enum(u32) { var stack align(@alignOf(ExpectedContents)) = std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa); const allocator = stack.get(); - const str = try bigint.toStringAlloc(allocator, 10, undefined); + const str = bigint.toStringAlloc(allocator, 10, undefined) catch return error.WriteFailed; defer allocator.free(str); - try bw.writeAll(str); + try w.writeAll(str); }, .half, .bfloat, - => |tag| try bw.print("0x{c}{X:0>4}", .{ @as(u8, switch (tag) { + => |tag| try w.print("0x{c}{X:0>4}", .{ @as(u8, switch (tag) { .half => 'H', .bfloat => 'R', else => unreachable, @@ -7469,7 +7608,7 @@ pub const Constant = enum(u32) { ) + 1, else => 0, }; - try bw.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){ + try w.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){ .mantissa = std.math.shl( Mantissa64, repr.mantissa, @@ -7491,13 +7630,13 @@ pub const Constant = enum(u32) { }, .double => { const extra = data.builder.constantExtraData(Double, item.data); - try bw.print("0x{X:0>8}{X:0>8}", .{ extra.hi, extra.lo }); + try w.print("0x{X:0>8}{X:0>8}", .{ extra.hi, extra.lo }); }, .fp128, .ppc_fp128, => |tag| { const extra = data.builder.constantExtraData(Fp128, item.data); - try bw.print("0x{c}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{ + try w.print("0x{c}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{ @as(u8, switch (tag) { .fp128 => 'L', .ppc_fp128 => 'M', @@ -7511,7 +7650,7 @@ pub const Constant = enum(u32) { }, .x86_fp80 => { const extra = data.builder.constantExtraData(Fp80, item.data); - try bw.print("0xK{X:0>4}{X:0>8}{X:0>8}", .{ + try w.print("0xK{X:0>4}{X:0>8}{X:0>8}", .{ extra.hi, extra.lo_hi, extra.lo_lo, }); }, @@ -7520,7 +7659,7 @@ pub const Constant = enum(u32) { .zeroinitializer, .undef, .poison, - => |tag| try bw.writeAll(@tagName(tag)), + => |tag| try w.writeAll(@tagName(tag)), .structure, .packed_structure, .array, @@ -7529,7 +7668,7 @@ pub const Constant = enum(u32) { var extra = data.builder.constantExtraDataTrail(Aggregate, item.data); const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder)); const vals = extra.trail.next(len, Constant, data.builder); - try bw.writeAll(switch (tag) { + try w.writeAll(switch (tag) { .structure => "{ ", .packed_structure => "<{ ", .array => "[", @@ -7537,10 +7676,10 @@ pub const Constant = enum(u32) { else => unreachable, }); for (vals, 0..) |val, index| { - if (index > 0) try bw.writeAll(", "); - try bw.print("{f%}", .{val.fmt(data.builder)}); + if (index > 0) try w.writeAll(", "); + try w.print("{f}", .{val.fmt(data.builder, .{ .percent = true })}); } - try bw.writeAll(switch (tag) { + try w.writeAll(switch (tag) { .structure => " }", .packed_structure => " }>", .array => "]", @@ -7551,30 +7690,30 @@ pub const Constant = enum(u32) { .splat => { const extra = data.builder.constantExtraData(Splat, item.data); const len = extra.type.vectorLen(data.builder); - try bw.writeByte('<'); + try w.writeByte('<'); for (0..len) |index| { - if (index > 0) try bw.writeAll(", "); - try bw.print("{f%}", .{extra.value.fmt(data.builder)}); + if (index > 0) try w.writeAll(", "); + try w.print("{f}", .{extra.value.fmt(data.builder, .{ .percent = true })}); } - try bw.writeByte('>'); + try w.writeByte('>'); }, - .string => try bw.print("c{f\"}", .{ - @as(String, @enumFromInt(item.data)).fmt(data.builder), + .string => try w.print("c{f}", .{ + @as(String, @enumFromInt(item.data)).fmtQ(data.builder), }), .blockaddress => |tag| { const extra = data.builder.constantExtraData(BlockAddress, item.data); const function = extra.function.ptrConst(data.builder); - try bw.print("{s}({f}, {f})", .{ + try w.print("{s}({f}, {f})", .{ @tagName(tag), function.global.fmt(data.builder), - extra.block.toInst(function).fmt(extra.function, data.builder), + extra.block.toInst(function).fmt(extra.function, data.builder, .{}), }); }, .dso_local_equivalent, .no_cfi, => |tag| { const function: Function.Index = @enumFromInt(item.data); - try bw.print("{s} {f}", .{ + try w.print("{s} {f}", .{ @tagName(tag), function.ptrConst(data.builder).global.fmt(data.builder), }); @@ -7586,10 +7725,10 @@ pub const Constant = enum(u32) { .addrspacecast, => |tag| { const extra = data.builder.constantExtraData(Cast, item.data); - try bw.print("{s} ({f%} to {f%})", .{ + try w.print("{s} ({f} to {f})", .{ @tagName(tag), - extra.val.fmt(data.builder), - extra.type.fmt(data.builder), + extra.val.fmt(data.builder, .{ .percent = true }), + extra.type.fmt(data.builder, .percent), }); }, .getelementptr, @@ -7598,13 +7737,13 @@ pub const Constant = enum(u32) { var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data); const indices = extra.trail.next(extra.data.info.indices_len, Constant, data.builder); - try bw.print("{s} ({f%}, {f%}", .{ + try w.print("{s} ({f}, {f}", .{ @tagName(tag), - extra.data.type.fmt(data.builder), - extra.data.base.fmt(data.builder), + extra.data.type.fmt(data.builder, .percent), + extra.data.base.fmt(data.builder, .{ .percent = true }), }); - for (indices) |index| try bw.print(", {f%}", .{index.fmt(data.builder)}); - try bw.writeByte(')'); + for (indices) |index| try w.print(", {f}", .{index.fmt(data.builder, .{ .percent = true })}); + try w.writeByte(')'); }, .add, .@"add nsw", @@ -7616,10 +7755,10 @@ pub const Constant = enum(u32) { .xor, => |tag| { const extra = data.builder.constantExtraData(Binary, item.data); - try bw.print("{s} ({f%}, {f%})", .{ + try w.print("{s} ({f}, {f})", .{ @tagName(tag), - extra.lhs.fmt(data.builder), - extra.rhs.fmt(data.builder), + extra.lhs.fmt(data.builder, .{ .percent = true }), + extra.rhs.fmt(data.builder, .{ .percent = true }), }); }, .@"asm", @@ -7640,19 +7779,23 @@ pub const Constant = enum(u32) { .@"asm sideeffect alignstack inteldialect unwind", => |tag| { const extra = data.builder.constantExtraData(Assembly, item.data); - try bw.print("{s} {f\"}, {f\"}", .{ + try w.print("{s} {f}, {f}", .{ @tagName(tag), - extra.assembly.fmt(data.builder), - extra.constraints.fmt(data.builder), + extra.assembly.fmtQ(data.builder), + extra.constraints.fmtQ(data.builder), }); }, } }, - .global => |global| try bw.print("{f}", .{global.fmt(data.builder)}), + .global => |global| try w.print("{f}", .{global.fmt(data.builder)}), } } - pub fn fmt(self: Constant, builder: *Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .constant = self, .builder = builder } }; + pub fn fmt(self: Constant, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ + .constant = self, + .builder = builder, + .flags = flags, + } }; } }; @@ -7707,23 +7850,26 @@ pub const Value = enum(u32) { value: Value, function: Function.Index, builder: *Builder, + flags: FormatFlags, }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { + fn format(data: FormatData, w: *Writer) Writer.Error!void { switch (data.value.unwrap()) { .instruction => |instruction| try Function.Instruction.Index.format(.{ .instruction = instruction, .function = data.function, .builder = data.builder, - }, bw, fmt_str), + .flags = data.flags, + }, w), .constant => |constant| try Constant.format(.{ .constant = constant, .builder = data.builder, - }, bw, fmt_str), + .flags = data.flags, + }, w), .metadata => unreachable, } } - pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .value = self, .function = function, .builder = builder } }; + pub fn fmt(self: Value, function: Function.Index, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) { + return .{ .data = .{ .value = self, .function = function, .builder = builder, .flags = flags } }; } }; @@ -7753,10 +7899,10 @@ pub const MetadataString = enum(u32) { metadata_string: MetadataString, builder: *const Builder, }; - fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void { - try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw); + fn format(data: FormatData, w: *Writer) Writer.Error!void { + try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, w); } - fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) { + fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(FormatData, format) { return .{ .data = .{ .metadata_string = self, .builder = builder } }; } }; @@ -7918,24 +8064,24 @@ pub const Metadata = enum(u32) { AllCallsDescribed: bool = false, Unused: u2 = 0, - pub fn format(self: DIFlags, bw: *Writer, comptime _: []const u8) Writer.Error!void { + pub fn format(self: DIFlags, w: *Writer) Writer.Error!void { var need_pipe = false; inline for (@typeInfo(DIFlags).@"struct".fields) |field| { switch (@typeInfo(field.type)) { .bool => if (@field(self, field.name)) { - if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; - try bw.print("DIFlag{s}", .{field.name}); + if (need_pipe) try w.writeAll(" | ") else need_pipe = true; + try w.print("DIFlag{s}", .{field.name}); }, .@"enum" => if (@field(self, field.name) != .Zero) { - if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; - try bw.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); + if (need_pipe) try w.writeAll(" | ") else need_pipe = true; + try w.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); }, .int => assert(@field(self, field.name) == 0), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), } } - if (!need_pipe) try bw.writeByte('0'); + if (!need_pipe) try w.writeByte('0'); } }; @@ -7975,24 +8121,24 @@ pub const Metadata = enum(u32) { ObjCDirect: bool = false, Unused: u20 = 0, - pub fn format(self: DISPFlags, bw: *Writer, comptime _: []const u8) Writer.Error!void { + pub fn format(self: DISPFlags, w: *Writer) Writer.Error!void { var need_pipe = false; inline for (@typeInfo(DISPFlags).@"struct".fields) |field| { switch (@typeInfo(field.type)) { .bool => if (@field(self, field.name)) { - if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; - try bw.print("DISPFlag{s}", .{field.name}); + if (need_pipe) try w.writeAll(" | ") else need_pipe = true; + try w.print("DISPFlag{s}", .{field.name}); }, .@"enum" => if (@field(self, field.name) != .Zero) { - if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; - try bw.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); + if (need_pipe) try w.writeAll(" | ") else need_pipe = true; + try w.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); }, .int => assert(@field(self, field.name) == 0), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), } } - if (!need_pipe) try bw.writeByte('0'); + if (!need_pipe) try w.writeByte('0'); } }; @@ -8167,6 +8313,7 @@ pub const Metadata = enum(u32) { formatter: *Formatter, prefix: []const u8 = "", node: Node, + specialized: ?FormatFlags, const Node = union(enum) { none, @@ -8192,15 +8339,14 @@ pub const Metadata = enum(u32) { }; }; }; - fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { + fn format(data: FormatData, w: *Writer) Writer.Error!void { if (data.node == .none) return; - const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S'; - const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str; + const is_specialized = data.specialized != null; - if (data.formatter.need_comma) try bw.writeAll(", "); + if (data.formatter.need_comma) try w.writeAll(", "); defer data.formatter.need_comma = true; - try bw.writeAll(data.prefix); + try w.writeAll(data.prefix); const builder = data.formatter.builder; switch (data.node) { @@ -8215,50 +8361,57 @@ pub const Metadata = enum(u32) { .expression => { var extra = builder.metadataExtraDataTrail(Expression, item.data); const elements = extra.trail.next(extra.data.elements_len, u32, builder); - try bw.writeAll("!DIExpression("); + try w.writeAll("!DIExpression("); for (elements) |element| try format(.{ .formatter = data.formatter, .node = .{ .u64 = element }, - }, bw, "%"); - try bw.writeByte(')'); + .specialized = .{ .percent = true }, + }, w); + try w.writeByte(')'); }, .constant => try Constant.format(.{ .constant = @enumFromInt(item.data), .builder = builder, - }, bw, recurse_fmt_str), + .flags = data.specialized orelse .{}, + }, w), else => unreachable, } }, - .index => |node| try bw.print("!{d}", .{node}), + .index => |node| try w.print("!{d}", .{node}), inline .local_value, .local_metadata => |node, tag| try Value.format(.{ .value = node.value, .function = node.function, .builder = builder, - }, bw, switch (tag) { - .local_value => recurse_fmt_str, - .local_metadata => "%", - else => unreachable, - }), + .flags = switch (tag) { + .local_value => data.specialized orelse .{}, + .local_metadata => .{ .percent = true }, + else => unreachable, + }, + }, w), inline .local_inline, .local_index => |node, tag| { - if (comptime std.mem.eql(u8, recurse_fmt_str, "%")) - try bw.print("{f%} ", .{Type.metadata.fmt(builder)}); + if (data.specialized) |flags| { + if (flags.onlyPercent()) { + try w.print("{f} ", .{Type.metadata.fmt(builder, .percent)}); + } + } try format(.{ .formatter = data.formatter, .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node), - }, bw, "%"); + .specialized = .{ .percent = true }, + }, w); }, - .string => |node| try bw.print((if (is_specialized) "" else "!") ++ "{f}", .{ - node.fmt(builder), + .string => |node| try w.print("{s}{f}", .{ + @as([]const u8, if (is_specialized) "" else "!"), node.fmt(builder), }), - inline .bool, .u32, .u64 => |node| try bw.print("{}", .{node}), - inline .di_flags, .sp_flags => |node| try bw.print("{f}", .{node}), - .raw => |node| try bw.writeAll(node), + inline .bool, .u32, .u64 => |node| try w.print("{}", .{node}), + inline .di_flags, .sp_flags => |node| try w.print("{f}", .{node}), + .raw => |node| try w.writeAll(node), } } - inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype) switch (@TypeOf(node)) { + inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype, special: ?FormatFlags) switch (@TypeOf(node)) { Metadata => Allocator.Error, else => error{}, - }!std.fmt.Formatter(format) { + }!std.fmt.Formatter(FormatData, format) { const Node = @TypeOf(node); const MaybeNode = switch (@typeInfo(Node)) { .optional => Node, @@ -8295,6 +8448,7 @@ pub const Metadata = enum(u32) { .optional, .null => .none, else => unreachable, }, + .specialized = special, } }; } inline fn fmtLocal( @@ -8302,7 +8456,7 @@ pub const Metadata = enum(u32) { prefix: []const u8, value: Value, function: Function.Index, - ) Allocator.Error!std.fmt.Formatter(format) { + ) Allocator.Error!std.fmt.Formatter(FormatData, format) { return .{ .data = .{ .formatter = formatter, .prefix = prefix, @@ -8327,6 +8481,7 @@ pub const Metadata = enum(u32) { }; }, }, + .specialized = null, } }; } fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node { @@ -8366,7 +8521,7 @@ pub const Metadata = enum(u32) { DIGlobalVariableExpression, }, nodes: anytype, - bw: *Writer, + w: *Writer, ) !void { comptime var fmt_str: []const u8 = ""; const names = comptime std.meta.fieldNames(@TypeOf(nodes)); @@ -8383,10 +8538,10 @@ pub const Metadata = enum(u32) { } fmt_str = fmt_str ++ "("; inline for (fields[2..], names) |*field, name| { - fmt_str = fmt_str ++ "{[" ++ name ++ "]fS}"; + fmt_str = fmt_str ++ "{[" ++ name ++ "]f}"; field.* = .{ .name = name, - .type = std.fmt.Formatter(format), + .type = std.fmt.Formatter(FormatData, format), .default_value_ptr = null, .is_comptime = false, .alignment = 0, @@ -8405,8 +8560,9 @@ pub const Metadata = enum(u32) { inline for (names) |name| @field(fmt_args, name) = try formatter.fmt( name ++ ": ", @field(nodes, name), + null, ); - try bw.print(fmt_str, fmt_args); + try w.print(fmt_str, fmt_args); } }; }; @@ -8496,7 +8652,7 @@ pub fn init(options: Options) Allocator.Error!Builder { inline for (.{ 0, 4 }) |addr_space_index| { const addr_space: AddrSpace = @enumFromInt(addr_space_index); assert(self.ptrTypeAssumeCapacity(addr_space) == - @field(Type, std.fmt.comptimePrint("ptr{f }", .{addr_space}))); + @field(Type, std.fmt.comptimePrint("ptr{f}", .{addr_space.fmt(" ")}))); } } @@ -8619,7 +8775,7 @@ pub fn deinit(self: *Builder) void { self.* = undefined; } -pub fn finishModuleAsm(self: *Builder, aw: *std.io.Writer.Allocating) Allocator.Error!void { +pub fn finishModuleAsm(self: *Builder, aw: *Writer.Allocating) Allocator.Error!void { self.module_asm = aw.toArrayList(); if (self.module_asm.getLastOrNull()) |last| if (last != '\n') try self.module_asm.append(self.gpa, '\n'); @@ -8929,11 +9085,11 @@ pub fn getIntrinsic( const name = name: { { - var aw: std.io.Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes); - const bw = &aw.interface; + var aw: Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes); + const w = &aw.writer; defer self.strtab_string_bytes = aw.toArrayList(); - bw.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory; - for (overload) |ty| bw.print(".{fm}", .{ty.fmt(self)}) catch return error.OutOfMemory; + w.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory; + for (overload) |ty| w.print(".{f}", .{ty.fmt(self, .m)}) catch return error.OutOfMemory; } break :name try self.trailingStrtabString(); }; @@ -9348,110 +9504,105 @@ pub fn asmValue( return (try self.asmConst(ty, info, assembly, constraints)).toValue(); } -pub fn dump(self: *Builder) void { +pub fn dump(b: *Builder) void { + var buffer: [4000]u8 = undefined; const stderr: std.fs.File = .stderr(); - self.printBuffered(stderr.writer()) catch {}; + b.printToFile(stderr, &buffer) catch {}; } -pub fn printToFile(self: *Builder, path: []const u8) bool { - var file = std.fs.cwd().createFile(path, .{}) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); - return false; - }; +pub fn printToFilePath(b: *Builder, dir: std.fs.Dir, path: []const u8) !void { + var buffer: [4000]u8 = undefined; + const file = try dir.createFile(path, .{}); defer file.close(); - self.printBuffered(file.writer()) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); - return false; - }; - return true; + try b.printToFile(file, &buffer); } -pub fn printBuffered(self: *Builder, writer: Writer) Writer.Error!void { - var buffer: [4096]u8 = undefined; - var bw = writer.buffered(&buffer); - try self.print(&bw); - try bw.flush(); +pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void { + var fw = file.writer(buffer); + try print(b, &fw.interface); + try fw.interface.flush(); } -pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { +pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void { var need_newline = false; var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; defer metadata_formatter.map.deinit(self.gpa); if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; - if (self.source_filename != .none) try bw.print( + if (need_newline) try w.writeByte('\n') else need_newline = true; + if (self.source_filename != .none) try w.print( \\; ModuleID = '{s}' - \\source_filename = {f"} + \\source_filename = {f} \\ - , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); - if (self.data_layout != .none) try bw.print( - \\target datalayout = {f"} + , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) }); + if (self.data_layout != .none) try w.print( + \\target datalayout = {f} \\ - , .{self.data_layout.fmt(self)}); - if (self.target_triple != .none) try bw.print( - \\target triple = {f"} + , .{self.data_layout.fmtQ(self)}); + if (self.target_triple != .none) try w.print( + \\target triple = {f} \\ - , .{self.target_triple.fmt(self)}); + , .{self.target_triple.fmtQ(self)}); } if (self.module_asm.items.len > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n'); while (line_it.next()) |line| { - try bw.writeAll("module asm "); - try printEscapedString(line, .always_quote, bw); - try bw.writeByte('\n'); + try w.writeAll("module asm "); + try printEscapedString(line, .always_quote, w); + try w.writeByte('\n'); } } if (self.types.count() > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; - for (self.types.keys(), self.types.values()) |id, ty| try bw.print( + if (need_newline) try w.writeByte('\n') else need_newline = true; + for (self.types.keys(), self.types.values()) |id, ty| try w.print( \\%{f} = type {f} \\ - , .{ id.fmt(self), ty.fmt(self) }); + , .{ id.fmt(self), ty.fmt(self, .default) }); } if (self.variables.items.len > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; for (self.variables.items) |variable| { if (variable.global.getReplacement(self) != .none) continue; const global = variable.global.ptrConst(self); metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; - try bw.print( - \\{f} ={f}{f}{f}{f}{f }{f}{f }{f} {s} {f%}{f }{f, }{f} + try w.print( + \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f} \\ , .{ variable.global.fmt(self), - Linkage.fmtOptional(if (global.linkage == .external and - variable.init != .no_init) null else global.linkage), + Linkage.fmtOptional( + if (global.linkage == .external and variable.init != .no_init) null else global.linkage, + ), global.preemption, global.visibility, global.dll_storage_class, - variable.thread_local, + variable.thread_local.fmt(" "), global.unnamed_addr, - global.addr_space, + global.addr_space.fmt(" "), global.externally_initialized, @tagName(variable.mutability), - global.type.fmt(self), - variable.init.fmt(self), - variable.alignment, - try metadata_formatter.fmt("!dbg ", global.dbg), + global.type.fmt(self, .percent), + variable.init.fmt(self, .{ .space = true }), + variable.alignment.fmt(", "), + try metadata_formatter.fmt("!dbg ", global.dbg, null), }); } } if (self.aliases.items.len > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; for (self.aliases.items) |alias| { if (alias.global.getReplacement(self) != .none) continue; const global = alias.global.ptrConst(self); metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; - try bw.print( - \\{f} ={f}{f}{f}{f}{f }{f} alias {f%}, {f%}{f} + try w.print( + \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f} \\ , .{ alias.global.fmt(self), @@ -9459,11 +9610,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { global.preemption, global.visibility, global.dll_storage_class, - alias.thread_local, + alias.thread_local.fmt(" "), global.unnamed_addr, - global.type.fmt(self), - alias.aliasee.fmt(self), - try metadata_formatter.fmt("!dbg ", global.dbg), + global.type.fmt(self, .percent), + alias.aliasee.fmt(self, .{ .percent = true }), + try metadata_formatter.fmt("!dbg ", global.dbg, null), }); } } @@ -9473,17 +9624,17 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { for (0.., self.functions.items) |function_i, function| { if (function.global.getReplacement(self) != .none) continue; - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; const function_index: Function.Index = @enumFromInt(function_i); const global = function.global.ptrConst(self); const params_len = global.type.functionParameters(self).len; const function_attributes = function.attributes.func(self); - if (function_attributes != .none) try bw.print( + if (function_attributes != .none) try w.print( \\; Function Attrs:{f} \\ - , .{function_attributes.fmt(self)}); - try bw.print( - \\{s}{f}{f}{f}{f}{f}{f"} {f%} {f}( + , .{function_attributes.fmt(self, .{})}); + try w.print( + \\{s}{f}{f}{f}{f}{f}{f} {f} {f}( , .{ if (function.instructions.len > 0) "define" else "declare", global.linkage, @@ -9491,45 +9642,45 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { global.visibility, global.dll_storage_class, function.call_conv, - function.attributes.ret(self).fmt(self), - global.type.functionReturn(self).fmt(self), + function.attributes.ret(self).fmt(self, .{}), + global.type.functionReturn(self).fmt(self, .percent), function.global.fmt(self), }); for (0..params_len) |arg| { - if (arg > 0) try bw.writeAll(", "); - try bw.print( - \\{f%}{f"} + if (arg > 0) try w.writeAll(", "); + try w.print( + \\{f}{f} , .{ - global.type.functionParameters(self)[arg].fmt(self), - function.attributes.param(arg, self).fmt(self), + global.type.functionParameters(self)[arg].fmt(self, .percent), + function.attributes.param(arg, self).fmt(self, .{}), }); if (function.instructions.len > 0) - try bw.print(" {f}", .{function.arg(@intCast(arg)).fmt(function_index, self)}) + try w.print(" {f}", .{function.arg(@intCast(arg)).fmt(function_index, self, .{})}) else - try bw.print(" %{d}", .{arg}); + try w.print(" %{d}", .{arg}); } switch (global.type.functionKind(self)) { .normal => {}, .vararg => { - if (params_len > 0) try bw.writeAll(", "); - try bw.writeAll("..."); + if (params_len > 0) try w.writeAll(", "); + try w.writeAll("..."); }, } - try bw.print("){f}{f }", .{ global.unnamed_addr, global.addr_space }); - if (function_attributes != .none) try bw.print(" #{d}", .{ + try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") }); + if (function_attributes != .none) try w.print(" #{d}", .{ (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, }); { metadata_formatter.need_comma = false; defer metadata_formatter.need_comma = undefined; - try bw.print("{f }{f}", .{ - function.alignment, - try metadata_formatter.fmt(" !dbg ", global.dbg), + try w.print("{f}{f}", .{ + function.alignment.fmt(" "), + try metadata_formatter.fmt(" !dbg ", global.dbg, null), }); } if (function.instructions.len > 0) { var block_incoming_len: u32 = undefined; - try bw.writeAll(" {\n"); + try w.writeAll(" {\n"); var maybe_dbg_index: ?u32 = null; for (params_len..function.instructions.len) |instruction_i| { const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i); @@ -9627,11 +9778,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .xor, => |tag| { const extra = function.extraData(Function.Instruction.Binary, instruction.data); - try bw.print(" %{f} = {s} {f%}, {f}", .{ + try w.print(" %{f} = {s} {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), + extra.lhs.fmt(function_index, self, .{ .percent = true }), + extra.rhs.fmt(function_index, self, .{ .percent = true }), }); }, .addrspacecast, @@ -9649,73 +9800,76 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .zext, => |tag| { const extra = function.extraData(Function.Instruction.Cast, instruction.data); - try bw.print(" %{f} = {s} {f%} to {f%}", .{ + try w.print(" %{f} = {s} {f} to {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.val.fmt(function_index, self), - extra.type.fmt(self), + extra.val.fmt(function_index, self, .{ .percent = true }), + extra.type.fmt(self, .percent), }); }, .alloca, .@"alloca inalloca", => |tag| { const extra = function.extraData(Function.Instruction.Alloca, instruction.data); - try bw.print(" %{f} = {s} {f%}{f,%}{f, }{f, }", .{ + try w.print(" %{f} = {s} {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.type.fmt(self), + extra.type.fmt(self, .percent), Value.fmt(switch (extra.len) { .@"1" => .none, else => extra.len, - }, function_index, self), - extra.info.alignment, - extra.info.addr_space, + }, function_index, self, .{ + .comma = true, + .percent = true, + }), + extra.info.alignment.fmt(", "), + extra.info.addr_space.fmt(", "), }); }, .arg => unreachable, .atomicrmw => |tag| { const extra = function.extraData(Function.Instruction.AtomicRmw, instruction.data); - try bw.print(" %{f} = {s}{f } {s} {f%}, {f%}{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {t} {f}, {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - @tagName(extra.info.atomic_rmw_operation), - extra.ptr.fmt(function_index, self), - extra.val.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + tag, + extra.info.access_kind.fmt(" "), + extra.info.atomic_rmw_operation, + extra.ptr.fmt(function_index, self, .{ .percent = true }), + extra.val.fmt(function_index, self, .{ .percent = true }), + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .block => { block_incoming_len = instruction.data; const name = instruction_index.name(&function); if (@intFromEnum(instruction_index) > params_len) - try bw.writeByte('\n'); - try bw.print("{f}:\n", .{name.fmt(self)}); + try w.writeByte('\n'); + try w.print("{f}:\n", .{name.fmt(self)}); continue; }, .br => |tag| { const target: Function.Block.Index = @enumFromInt(instruction.data); - try bw.print(" {s} {f%}", .{ - @tagName(tag), target.toInst(&function).fmt(function_index, self), + try w.print(" {s} {f}", .{ + @tagName(tag), target.toInst(&function).fmt(function_index, self, .{ .percent = true }), }); }, .br_cond => { const extra = function.extraData(Function.Instruction.BrCond, instruction.data); - try bw.print(" br {f%}, {f%}, {f%}", .{ - extra.cond.fmt(function_index, self), - extra.then.toInst(&function).fmt(function_index, self), - extra.@"else".toInst(&function).fmt(function_index, self), + try w.print(" br {f}, {f}, {f}", .{ + extra.cond.fmt(function_index, self, .{ .percent = true }), + extra.then.toInst(&function).fmt(function_index, self, .{ .percent = true }), + extra.@"else".toInst(&function).fmt(function_index, self, .{ .percent = true }), }); metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; switch (extra.weights) { .none => {}, - .unpredictable => try bw.writeAll("!unpredictable !{}"), - _ => try bw.print("{f}", .{ - try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))), + .unpredictable => try w.writeAll("!unpredictable !{}"), + _ => try w.print("{f}", .{ + try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights))), null), }), } }, @@ -9731,42 +9885,42 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { var extra = function.extraDataTrail(Function.Instruction.Call, instruction.data); const args = extra.trail.next(extra.data.args_len, Value, &function); - try bw.writeAll(" "); + try w.writeAll(" "); const ret_ty = extra.data.ty.functionReturn(self); switch (ret_ty) { .void => {}, - else => try bw.print("%{f} = ", .{ + else => try w.print("%{f} = ", .{ instruction_index.name(&function).fmt(self), }), .none => unreachable, } - try bw.print("{s}{f}{f}{f} {f%} {f}(", .{ - @tagName(tag), + try w.print("{t}{f}{f}{f} {f} {f}(", .{ + tag, extra.data.info.call_conv, - extra.data.attributes.ret(self).fmt(self), + extra.data.attributes.ret(self).fmt(self, .{}), extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), switch (extra.data.ty.functionKind(self)) { .normal => ret_ty, .vararg => extra.data.ty, - }.fmt(self), - extra.data.callee.fmt(function_index, self), + }.fmt(self, .percent), + extra.data.callee.fmt(function_index, self, .{}), }); for (0.., args) |arg_index, arg| { - if (arg_index > 0) try bw.writeAll(", "); + if (arg_index > 0) try w.writeAll(", "); metadata_formatter.need_comma = false; defer metadata_formatter.need_comma = undefined; - try bw.print("{f%}{f}{f}", .{ - arg.typeOf(function_index, self).fmt(self), - extra.data.attributes.param(arg_index, self).fmt(self), + try w.print("{f}{f}{f}", .{ + arg.typeOf(function_index, self).fmt(self, .percent), + extra.data.attributes.param(arg_index, self).fmt(self, .{}), try metadata_formatter.fmtLocal(" ", arg, function_index), }); } - try bw.writeByte(')'); + try w.writeByte(')'); if (extra.data.info.has_op_bundle_cold) { - try bw.writeAll(" [ \"cold\"() ]"); + try w.writeAll(" [ \"cold\"() ]"); } const call_function_attributes = extra.data.attributes.func(self); - if (call_function_attributes != .none) try bw.print(" #{d}", .{ + if (call_function_attributes != .none) try w.print(" #{d}", .{ (try attribute_groups.getOrPutValue( self.gpa, call_function_attributes, @@ -9779,27 +9933,27 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { => |tag| { const extra = function.extraData(Function.Instruction.CmpXchg, instruction.data); - try bw.print(" %{f} = {s}{f } {f%}, {f%}, {f%}{f }{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {f}, {f}, {f}{f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - extra.ptr.fmt(function_index, self), - extra.cmp.fmt(function_index, self), - extra.new.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.failure_ordering, - extra.info.alignment, + tag, + extra.info.access_kind.fmt(" "), + extra.ptr.fmt(function_index, self, .{ .percent = true }), + extra.cmp.fmt(function_index, self, .{ .percent = true }), + extra.new.fmt(function_index, self, .{ .percent = true }), + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.failure_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .extractelement => |tag| { const extra = function.extraData(Function.Instruction.ExtractElement, instruction.data); - try bw.print(" %{f} = {s} {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.val.fmt(function_index, self), - extra.index.fmt(function_index, self), + extra.val.fmt(function_index, self, .{ .percent = true }), + extra.index.fmt(function_index, self, .{ .percent = true }), }); }, .extractvalue => |tag| { @@ -9808,29 +9962,29 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { instruction.data, ); const indices = extra.trail.next(extra.data.indices_len, u32, &function); - try bw.print(" %{f} = {s} {f%}", .{ + try w.print(" %{f} = {s} {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.data.val.fmt(function_index, self), + extra.data.val.fmt(function_index, self, .{ .percent = true }), }); - for (indices) |index| try bw.print(", {d}", .{index}); + for (indices) |index| try w.print(", {d}", .{index}); }, .fence => |tag| { const info: MemoryAccessInfo = @bitCast(instruction.data); - try bw.print(" {s}{f }{f }", .{ - @tagName(tag), - info.sync_scope, - info.success_ordering, + try w.print(" {t}{f}{f}", .{ + tag, + info.sync_scope.fmt(" "), + info.success_ordering.fmt(" "), }); }, .fneg, .@"fneg fast", => |tag| { const val: Value = @enumFromInt(instruction.data); - try bw.print(" %{f} = {s} {f%}", .{ + try w.print(" %{f} = {s} {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - val.fmt(function_index, self), + val.fmt(function_index, self, .{ .percent = true }), }); }, .getelementptr, @@ -9841,14 +9995,14 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { instruction.data, ); const indices = extra.trail.next(extra.data.indices_len, Value, &function); - try bw.print(" %{f} = {s} {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.data.type.fmt(self), - extra.data.base.fmt(function_index, self), + extra.data.type.fmt(self, .percent), + extra.data.base.fmt(function_index, self, .{ .percent = true }), }); - for (indices) |index| try bw.print(", {f%}", .{ - index.fmt(function_index, self), + for (indices) |index| try w.print(", {f}", .{ + index.fmt(function_index, self, .{ .percent = true }), }); }, .indirectbr => |tag| { @@ -9856,54 +10010,54 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data); const targets = extra.trail.next(extra.data.targets_len, Function.Block.Index, &function); - try bw.print(" {s} {f%}, [", .{ + try w.print(" {s} {f}, [", .{ @tagName(tag), - extra.data.addr.fmt(function_index, self), + extra.data.addr.fmt(function_index, self, .{ .percent = true }), }); for (0.., targets) |target_index, target| { - if (target_index > 0) try bw.writeAll(", "); - try bw.print("{f%}", .{ - target.toInst(&function).fmt(function_index, self), + if (target_index > 0) try w.writeAll(", "); + try w.print("{f}", .{ + target.toInst(&function).fmt(function_index, self, .{ .percent = true }), }); } - try bw.writeByte(']'); + try w.writeByte(']'); }, .insertelement => |tag| { const extra = function.extraData(Function.Instruction.InsertElement, instruction.data); - try bw.print(" %{f} = {s} {f%}, {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.val.fmt(function_index, self), - extra.elem.fmt(function_index, self), - extra.index.fmt(function_index, self), + extra.val.fmt(function_index, self, .{ .percent = true }), + extra.elem.fmt(function_index, self, .{ .percent = true }), + extra.index.fmt(function_index, self, .{ .percent = true }), }); }, .insertvalue => |tag| { var extra = function.extraDataTrail(Function.Instruction.InsertValue, instruction.data); const indices = extra.trail.next(extra.data.indices_len, u32, &function); - try bw.print(" %{f} = {s} {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.data.val.fmt(function_index, self), - extra.data.elem.fmt(function_index, self), + extra.data.val.fmt(function_index, self, .{ .percent = true }), + extra.data.elem.fmt(function_index, self, .{ .percent = true }), }); - for (indices) |index| try bw.print(", {d}", .{index}); + for (indices) |index| try w.print(", {d}", .{index}); }, .load, .@"load atomic", => |tag| { const extra = function.extraData(Function.Instruction.Load, instruction.data); - try bw.print(" %{f} = {s}{f } {f%}, {f%}{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {f}, {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - extra.type.fmt(self), - extra.ptr.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + tag, + extra.info.access_kind.fmt(" "), + extra.type.fmt(self, .percent), + extra.ptr.fmt(function_index, self, .{ .percent = true }), + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .phi, @@ -9913,64 +10067,64 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { const vals = extra.trail.next(block_incoming_len, Value, &function); const blocks = extra.trail.next(block_incoming_len, Function.Block.Index, &function); - try bw.print(" %{f} = {s} {f%} ", .{ + try w.print(" %{f} = {s} {f} ", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - vals[0].typeOf(function_index, self).fmt(self), + vals[0].typeOf(function_index, self).fmt(self, .percent), }); for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| { - if (incoming_index > 0) try bw.writeAll(", "); - try bw.print("[ {f}, {f} ]", .{ - incoming_val.fmt(function_index, self), - incoming_block.toInst(&function).fmt(function_index, self), + if (incoming_index > 0) try w.writeAll(", "); + try w.print("[ {f}, {f} ]", .{ + incoming_val.fmt(function_index, self, .{}), + incoming_block.toInst(&function).fmt(function_index, self, .{}), }); } }, .ret => |tag| { const val: Value = @enumFromInt(instruction.data); - try bw.print(" {s} {f%}", .{ + try w.print(" {s} {f}", .{ @tagName(tag), - val.fmt(function_index, self), + val.fmt(function_index, self, .{ .percent = true }), }); }, .@"ret void", .@"unreachable", - => |tag| try bw.print(" {s}", .{@tagName(tag)}), + => |tag| try w.print(" {s}", .{@tagName(tag)}), .select, .@"select fast", => |tag| { const extra = function.extraData(Function.Instruction.Select, instruction.data); - try bw.print(" %{f} = {s} {f%}, {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.cond.fmt(function_index, self), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), + extra.cond.fmt(function_index, self, .{ .percent = true }), + extra.lhs.fmt(function_index, self, .{ .percent = true }), + extra.rhs.fmt(function_index, self, .{ .percent = true }), }); }, .shufflevector => |tag| { const extra = function.extraData(Function.Instruction.ShuffleVector, instruction.data); - try bw.print(" %{f} = {s} {f%}, {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - extra.mask.fmt(function_index, self), + extra.lhs.fmt(function_index, self, .{ .percent = true }), + extra.rhs.fmt(function_index, self, .{ .percent = true }), + extra.mask.fmt(function_index, self, .{ .percent = true }), }); }, .store, .@"store atomic", => |tag| { const extra = function.extraData(Function.Instruction.Store, instruction.data); - try bw.print(" {s}{f } {f%}, {f%}{f }{f }{f, }", .{ - @tagName(tag), - extra.info.access_kind, - extra.val.fmt(function_index, self), - extra.ptr.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + try w.print(" {t}{f} {f}, {f}{f}{f}{f}", .{ + tag, + extra.info.access_kind.fmt(" "), + extra.val.fmt(function_index, self, .{ .percent = true }), + extra.ptr.fmt(function_index, self, .{ .percent = true }), + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .@"switch" => |tag| { @@ -9979,80 +10133,80 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { const vals = extra.trail.next(extra.data.cases_len, Constant, &function); const blocks = extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); - try bw.print(" {s} {f%}, {f%} [\n", .{ + try w.print(" {s} {f}, {f} [\n", .{ @tagName(tag), - extra.data.val.fmt(function_index, self), - extra.data.default.toInst(&function).fmt(function_index, self), + extra.data.val.fmt(function_index, self, .{ .percent = true }), + extra.data.default.toInst(&function).fmt(function_index, self, .{ .percent = true }), }); - for (vals, blocks) |case_val, case_block| try bw.print( - " {f%}, {f%}\n", + for (vals, blocks) |case_val, case_block| try w.print( + " {f}, {f}\n", .{ - case_val.fmt(self), - case_block.toInst(&function).fmt(function_index, self), + case_val.fmt(self, .{ .percent = true }), + case_block.toInst(&function).fmt(function_index, self, .{ .percent = true }), }, ); - try bw.writeAll(" ]"); + try w.writeAll(" ]"); metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; switch (extra.data.weights) { .none => {}, - .unpredictable => try bw.writeAll("!unpredictable !{}"), - _ => try bw.print("{f}", .{ - try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))), + .unpredictable => try w.writeAll("!unpredictable !{}"), + _ => try w.print("{f}", .{ + try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights))), null), }), } }, .va_arg => |tag| { const extra = function.extraData(Function.Instruction.VaArg, instruction.data); - try bw.print(" %{f} = {s} {f%}, {f%}", .{ + try w.print(" %{f} = {s} {f}, {f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), - extra.list.fmt(function_index, self), - extra.type.fmt(self), + extra.list.fmt(function_index, self, .{ .percent = true }), + extra.type.fmt(self, .percent), }); }, } if (maybe_dbg_index) |dbg_index| { - try bw.print(", !dbg !{d}", .{dbg_index}); + try w.print(", !dbg !{d}", .{dbg_index}); } - try bw.writeByte('\n'); + try w.writeByte('\n'); } - try bw.writeByte('}'); + try w.writeByte('}'); } - try bw.writeByte('\n'); + try w.writeByte('\n'); } if (attribute_groups.count() > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| - try bw.print( - \\attributes #{d} = {{{f#"} }} + try w.print( + \\attributes #{d} = {{{f} }} \\ - , .{ attribute_group_index, attribute_group.fmt(self) }); + , .{ attribute_group_index, attribute_group.fmt(self, .{ .pound = true, .quote = true }) }); } if (self.metadata_named.count() > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| { const elements: []const Metadata = @ptrCast(self.metadata_extra.items[data.index..][0..data.len]); - try bw.writeByte('!'); - try printEscapedString(name.slice(self), .quote_unless_valid_identifier, bw); - try bw.writeAll(" = !{"); + try w.writeByte('!'); + try printEscapedString(name.slice(self), .quote_unless_valid_identifier, w); + try w.writeAll(" = !{"); metadata_formatter.need_comma = false; defer metadata_formatter.need_comma = undefined; - for (elements) |element| try bw.print("{f}", .{try metadata_formatter.fmt("", element)}); - try bw.writeAll("}\n"); + for (elements) |element| try w.print("{f}", .{try metadata_formatter.fmt("", element, null)}); + try w.writeAll("}\n"); } } if (metadata_formatter.map.count() > 0) { - if (need_newline) try bw.writeByte('\n') else need_newline = true; + if (need_newline) try w.writeByte('\n') else need_newline = true; var metadata_index: usize = 0; while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) { @setEvalBranchQuota(10_000); - try bw.print("!{d} = ", .{metadata_index}); + try w.print("!{d} = ", .{metadata_index}); metadata_formatter.need_comma = false; defer metadata_formatter.need_comma = undefined; @@ -10065,7 +10219,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .scope = location.scope, .inlinedAt = location.inlined_at, .isImplicitCode = false, - }, bw); + }, w); continue; }, .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)), @@ -10081,7 +10235,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .checksumkind = null, .checksum = null, .source = null, - }, bw); + }, w); }, .compile_unit, .@"compile_unit optimized", @@ -10112,7 +10266,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .rangesBaseAddress = null, .sysroot = null, .sdk = null, - }, bw); + }, w); }, .subprogram, .@"subprogram local", @@ -10146,7 +10300,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .thrownTypes = null, .annotations = null, .targetFuncName = null, - }, bw); + }, w); }, .lexical_block => { const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data); @@ -10155,7 +10309,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .file = extra.file, .line = extra.line, .column = extra.column, - }, bw); + }, w); }, .location => { const extra = self.metadataExtraData(Metadata.Location, metadata_item.data); @@ -10165,7 +10319,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .scope = extra.scope, .inlinedAt = extra.inlined_at, .isImplicitCode = false, - }, bw); + }, w); }, .basic_bool_type, .basic_unsigned_type, @@ -10194,7 +10348,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { else => unreachable, }), .flags = null, - }, bw); + }, w); }, .composite_struct_type, .composite_union_type, @@ -10239,7 +10393,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .allocated = null, .rank = null, .annotations = null, - }, bw); + }, w); }, .derived_pointer_type, .derived_member_type, @@ -10272,7 +10426,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .extraData = null, .dwarfAddressSpace = null, .annotations = null, - }, bw); + }, w); }, .subroutine_type => { const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data); @@ -10280,7 +10434,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .flags = null, .cc = null, .types = extra.types_tuple, - }, bw); + }, w); }, .enumerator_unsigned, .enumerator_signed_positive, @@ -10330,7 +10484,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { => false, else => unreachable, }, - }, bw); + }, w); }, .subrange => { const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data); @@ -10339,34 +10493,34 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .lowerBound = extra.lower_bound, .upperBound = null, .stride = null, - }, bw); + }, w); }, .tuple => { var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data); const elements = extra.trail.next(extra.data.elements_len, Metadata, self); - try bw.writeAll("!{"); - for (elements) |element| try bw.print("{[element]f%}", .{ - .element = try metadata_formatter.fmt("", element), + try w.writeAll("!{"); + for (elements) |element| try w.print("{[element]f}", .{ + .element = try metadata_formatter.fmt("", element, .{ .percent = true }), }); - try bw.writeAll("}\n"); + try w.writeAll("}\n"); }, .str_tuple => { var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data); const elements = extra.trail.next(extra.data.elements_len, Metadata, self); - try bw.print("!{{{[str]f%}", .{ - .str = try metadata_formatter.fmt("", extra.data.str), + try w.print("!{{{[str]f}", .{ + .str = try metadata_formatter.fmt("", extra.data.str, .{ .percent = true }), }); - for (elements) |element| try bw.print("{[element]f%}", .{ - .element = try metadata_formatter.fmt("", element), + for (elements) |element| try w.print("{[element]f}", .{ + .element = try metadata_formatter.fmt("", element, .{ .percent = true }), }); - try bw.writeAll("}\n"); + try w.writeAll("}\n"); }, .module_flag => { const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data); - try bw.print("!{{{[behavior]f%}{[name]f%}{[constant]f%}}}\n", .{ - .behavior = try metadata_formatter.fmt("", extra.behavior), - .name = try metadata_formatter.fmt("", extra.name), - .constant = try metadata_formatter.fmt("", extra.constant), + try w.print("!{{{[behavior]f}{[name]f}{[constant]f}}}\n", .{ + .behavior = try metadata_formatter.fmt("", extra.behavior, .{ .percent = true }), + .name = try metadata_formatter.fmt("", extra.name, .{ .percent = true }), + .constant = try metadata_formatter.fmt("", extra.constant, .{ .percent = true }), }); }, .local_var => { @@ -10381,7 +10535,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .flags = null, .@"align" = null, .annotations = null, - }, bw); + }, w); }, .parameter => { const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data); @@ -10395,7 +10549,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .flags = null, .@"align" = null, .annotations = null, - }, bw); + }, w); }, .global_var, .@"global_var local", @@ -10418,7 +10572,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { .templateParams = null, .@"align" = null, .annotations = null, - }, bw); + }, w); }, .global_var_expression => { const extra = @@ -10426,7 +10580,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{ .@"var" = extra.variable, .expr = extra.expression, - }, bw); + }, w); }, } } @@ -10445,18 +10599,18 @@ fn isValidIdentifier(id: []const u8) bool { } const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier }; -fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *Writer) Writer.Error!void { +fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, w: *Writer) Writer.Error!void { const need_quotes = switch (quotes) { .always_quote => true, .quote_unless_valid_identifier => !isValidIdentifier(slice), }; - if (need_quotes) try bw.writeByte('"'); + if (need_quotes) try w.writeByte('"'); for (slice) |byte| switch (byte) { - '\\' => try bw.writeAll("\\\\"), - ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try bw.writeByte(byte), - else => try bw.print("\\{X:0>2}", .{byte}), + '\\' => try w.writeAll("\\\\"), + ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try w.writeByte(byte), + else => try w.print("\\{X:0>2}", .{byte}), }; - if (need_quotes) try bw.writeByte('"'); + if (need_quotes) try w.writeByte('"'); } fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void { @@ -15084,13 +15238,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco return bitcode.toOwnedSlice(); } -const std = @import("../../std.zig"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const bitcode_writer = @import("bitcode_writer.zig"); -const Builder = @This(); -const builtin = @import("builtin"); -const DW = std.dwarf; -const ir = @import("ir.zig"); -const log = std.log.scoped(.llvm); -const Writer = std.io.Writer; +const FormatFlags = struct { + comma: bool = false, + space: bool = false, + percent: bool = false, + + fn onlyPercent(f: FormatFlags) bool { + return !f.comma and !f.space and f.percent; + } +}; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 21c9faee14ea097cbe3623aa1275fb15ae2b2854..7c0a80c704380c3a8253c4ec043c6f0d417742b3 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1,3 +1,9 @@ +const std = @import("std"); +const mem = std.mem; +const print = std.debug.print; +const io = std.io; +const maxInt = std.math.maxInt; + test "zig fmt: remove extra whitespace at start and end of file with comment between" { try testTransform( \\ @@ -341,15 +347,6 @@ test "zig fmt: nosuspend block" { ); } -test "zig fmt: nosuspend await" { - try testCanonical( - \\fn foo() void { - \\ x = nosuspend await y; - \\} - \\ - ); -} - test "zig fmt: container declaration, single line" { try testCanonical( \\const X = struct { foo: i32 }; @@ -1093,18 +1090,6 @@ test "zig fmt: block in slice expression" { ); } -test "zig fmt: async function" { - try testCanonical( - \\pub const Server = struct { - \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.@"async") void, - \\}; - \\test "hi" { - \\ var ptr: fn (i32) callconv(.@"async") void = @ptrCast(other); - \\} - \\ - ); -} - test "zig fmt: whitespace fixes" { try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n", \\test "" { @@ -1549,17 +1534,6 @@ test "zig fmt: spaces around slice operator" { ); } -test "zig fmt: async call in if condition" { - try testCanonical( - \\comptime { - \\ if (async b()) { - \\ a(); - \\ } - \\} - \\ - ); -} - test "zig fmt: 2nd arg multiline string" { try testCanonical( \\comptime { @@ -2770,11 +2744,11 @@ test "zig fmt: preserve spacing" { \\const std = @import("std"); \\ \\pub fn main() !void { - \\ var stdout_file = std.io.getStdOut; - \\ var stdout_file = std.io.getStdOut; + \\ var stdout_file = std.lol.abcd; + \\ var stdout_file = std.lol.abcd; \\ - \\ var stdout_file = std.io.getStdOut; - \\ var stdout_file = std.io.getStdOut; + \\ var stdout_file = std.lol.abcd; + \\ var stdout_file = std.lol.abcd; \\} \\ ); @@ -3946,27 +3920,6 @@ test "zig fmt: inline asm" { ); } -test "zig fmt: async functions" { - try testCanonical( - \\fn simpleAsyncFn() void { - \\ const a = async a.b(); - \\ x += 1; - \\ suspend {} - \\ x += 1; - \\ suspend {} - \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable; - \\ await p; - \\} - \\ - \\test "suspend, resume, await" { - \\ const p: anyframe = async testAsyncSeq(); - \\ resume p; - \\ await p; - \\} - \\ - ); -} - test "zig fmt: nosuspend" { try testCanonical( \\const a = nosuspend foo(); @@ -3989,14 +3942,6 @@ test "zig fmt: Block after if" { ); } -test "zig fmt: usingnamespace" { - try testCanonical( - \\usingnamespace @import("std"); - \\pub usingnamespace @import("std"); - \\ - ); -} - test "zig fmt: string identifier" { try testCanonical( \\const @"a b" = @"c d".@"e f"; @@ -5140,17 +5085,6 @@ test "zig fmt: line comment after multiline single expr if statement with multil ); } -test "zig fmt: respect extra newline between fn and pub usingnamespace" { - try testCanonical( - \\fn foo() void { - \\ bar(); - \\} - \\ - \\pub usingnamespace baz; - \\ - ); -} - test "zig fmt: respect extra newline between switch items" { try testCanonical( \\const a = switch (b) { @@ -5719,34 +5653,6 @@ test "zig fmt: canonicalize symbols (primitive types)" { ); } -// Never unescape names spelled like keywords. -test "zig fmt: canonicalize symbols (keywords)" { - try testCanonical( - \\const @"enum" = struct { - \\ @"error": @"struct" = true, - \\ const @"struct" = bool; - \\}; - \\ - \\fn @"usingnamespace"(@"union": @"enum") error{@"try"}!void { - \\ var @"struct" = @"union"; - \\ @"struct".@"error" = false; - \\ if (@"struct".@"error") { - \\ return @"usingnamespace"(.{ .@"error" = false }); - \\ } else { - \\ return error.@"try"; - \\ } - \\} - \\ - \\test @"usingnamespace" { - \\ try @"usingnamespace"(.{}); - \\ _ = @"return": { - \\ break :@"return" 4; - \\ }; - \\} - \\ - ); -} - test "zig fmt: no space before newline before multiline string" { try testCanonical( \\const S = struct { @@ -6181,29 +6087,6 @@ test "recovery: missing return type" { }); } -test "recovery: continue after invalid decl" { - try testError( - \\fn foo { - \\ inline; - \\} - \\pub test "" { - \\ async a & b; - \\} - , &[_]Error{ - .expected_token, - .expected_pub_item, - .expected_param_list, - }); - try testError( - \\threadlocal test "" { - \\ @a & b; - \\} - , &[_]Error{ - .expected_var_decl, - .expected_param_list, - }); -} - test "recovery: invalid extern/inline" { try testError( \\inline test "" { a & b; } @@ -6232,22 +6115,6 @@ test "recovery: missing semicolon" { }); } -test "recovery: invalid container members" { - try testError( - \\usingnamespace; - \\@foo()+ - \\@bar()@, - \\while (a == 2) { test "" {}} - \\test "" { - \\ a & b - \\} - , &[_]Error{ - .expected_expr, - .expected_comma_after_field, - .expected_semi_after_stmt, - }); -} - // TODO after https://github.com/ziglang/zig/issues/35 is implemented, // we should be able to recover from this *at any indentation level*, // reporting a parse error and yet also parsing all the decls even @@ -6454,12 +6321,6 @@ test "ampersand" { , &.{}); } -const std = @import("std"); -const mem = std.mem; -const print = std.debug.print; -const io = std.io; -const maxInt = std.math.maxInt; - var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { diff --git a/lib/std/zig/string_literal.zig b/lib/std/zig/string_literal.zig index 5d1de962bd75b9e25c44dc723e43176497c63df3..4dfab46203126456611f6a4db15896a03adc1908 100644 --- a/lib/std/zig/string_literal.zig +++ b/lib/std/zig/string_literal.zig @@ -45,50 +45,49 @@ pub const Error = union(enum) { raw_string: []const u8, }; - fn formatMessage(self: FormatMessage, bw: *Writer, comptime f: []const u8) !void { - _ = f; + fn formatMessage(self: FormatMessage, writer: *std.io.Writer) std.io.Writer.Error!void { switch (self.err) { - .invalid_escape_character => |bad_index| try bw.print( + .invalid_escape_character => |bad_index| try writer.print( "invalid escape character: '{c}'", .{self.raw_string[bad_index]}, ), - .expected_hex_digit => |bad_index| try bw.print( + .expected_hex_digit => |bad_index| try writer.print( "expected hex digit, found '{c}'", .{self.raw_string[bad_index]}, ), - .empty_unicode_escape_sequence => try bw.writeAll( + .empty_unicode_escape_sequence => try writer.writeAll( "empty unicode escape sequence", ), - .expected_hex_digit_or_rbrace => |bad_index| try bw.print( + .expected_hex_digit_or_rbrace => |bad_index| try writer.print( "expected hex digit or '}}', found '{c}'", .{self.raw_string[bad_index]}, ), - .invalid_unicode_codepoint => try bw.writeAll( + .invalid_unicode_codepoint => try writer.writeAll( "unicode escape does not correspond to a valid unicode scalar value", ), - .expected_lbrace => |bad_index| try bw.print( + .expected_lbrace => |bad_index| try writer.print( "expected '{{', found '{c}'", .{self.raw_string[bad_index]}, ), - .expected_rbrace => |bad_index| try bw.print( + .expected_rbrace => |bad_index| try writer.print( "expected '}}', found '{c}'", .{self.raw_string[bad_index]}, ), - .expected_single_quote => |bad_index| try bw.print( + .expected_single_quote => |bad_index| try writer.print( "expected single quote ('), found '{c}'", .{self.raw_string[bad_index]}, ), - .invalid_character => |bad_index| try bw.print( + .invalid_character => |bad_index| try writer.print( "invalid byte in string or character literal: '{c}'", .{self.raw_string[bad_index]}, ), - .empty_char_literal => try bw.writeAll( + .empty_char_literal => try writer.writeAll( "empty character literal", ), } } - pub fn fmt(self: @This(), raw_string: []const u8) std.fmt.Formatter(formatMessage) { + pub fn fmt(self: @This(), raw_string: []const u8) std.fmt.Formatter(FormatMessage, formatMessage) { return .{ .data = .{ .err = self, .raw_string = raw_string, @@ -318,6 +317,7 @@ test parseCharLiteral { } /// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type. +/// /// Asserts `bytes` has '"' at beginning and end. pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result { assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"'); diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index 27ebb6c1b73e359e3cb4da1a4e1c45b1f1bcd60d..7ba62eaf5466bc12e20c1f7b57a07c2303894f36 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -388,7 +388,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { const current_arch = builtin.cpu.arch; switch (current_arch) { .arm, .armeb, .thumb, .thumbeb => { - return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null; + return ArmCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null; }, .aarch64, .aarch64_be => { const registers = [12]u64{ @@ -410,13 +410,13 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { return core; }, .sparc64 => { - return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null; + return SparcCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null; }, .powerpc, .powerpcle, .powerpc64, .powerpc64le => { - return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null; + return PowerpcCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null; }, .riscv64, .riscv32 => { - return RiscvCpuinfoParser.parse(current_arch, f.reader()) catch null; + return RiscvCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null; }, else => {}, } diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index 09c79d35626a52f9a8215dbe454b2442aebc23cd..de04ba41fecd0fa040bd4d9d4c9a8a6965775bad 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -17,8 +17,6 @@ pub const Token = struct { .{ "anyframe", .keyword_anyframe }, .{ "anytype", .keyword_anytype }, .{ "asm", .keyword_asm }, - .{ "async", .keyword_async }, - .{ "await", .keyword_await }, .{ "break", .keyword_break }, .{ "callconv", .keyword_callconv }, .{ "catch", .keyword_catch }, @@ -55,7 +53,6 @@ pub const Token = struct { .{ "try", .keyword_try }, .{ "union", .keyword_union }, .{ "unreachable", .keyword_unreachable }, - .{ "usingnamespace", .keyword_usingnamespace }, .{ "var", .keyword_var }, .{ "volatile", .keyword_volatile }, .{ "while", .keyword_while }, @@ -146,8 +143,6 @@ pub const Token = struct { keyword_anyframe, keyword_anytype, keyword_asm, - keyword_async, - keyword_await, keyword_break, keyword_callconv, keyword_catch, @@ -184,7 +179,6 @@ pub const Token = struct { keyword_try, keyword_union, keyword_unreachable, - keyword_usingnamespace, keyword_var, keyword_volatile, keyword_while, @@ -273,8 +267,6 @@ pub const Token = struct { .keyword_anyframe => "anyframe", .keyword_anytype => "anytype", .keyword_asm => "asm", - .keyword_async => "async", - .keyword_await => "await", .keyword_break => "break", .keyword_callconv => "callconv", .keyword_catch => "catch", @@ -311,7 +303,6 @@ pub const Token = struct { .keyword_try => "try", .keyword_union => "union", .keyword_unreachable => "unreachable", - .keyword_usingnamespace => "usingnamespace", .keyword_var => "var", .keyword_volatile => "volatile", .keyword_while => "while", diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index b5fafe5aa81899e7ed5e1458fa9a2ed8b8188317..792a83d70d77b8019eac5542635edb66db2e2abf 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -64,22 +64,14 @@ pub const Error = union(enum) { } }; - fn formatMessage( - self: []const u8, - comptime f: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = f; - _ = options; - + fn formatMessage(self: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { // Just writes the string for now, but we're keeping this behind a formatter so we have // the option to extend it in the future to print more advanced messages (like `Error` // does) without breaking the API. - try writer.writeAll(self); + try w.writeAll(self); } - pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Formatter(Note.formatMessage) { + pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Formatter([]const u8, Note.formatMessage) { return .{ .data = switch (self) { .zoir => |note| note.msg.get(diag.zoir), .type_check => |note| note.msg, @@ -155,21 +147,14 @@ pub const Error = union(enum) { diag: *const Diagnostics, }; - fn formatMessage( - self: FormatMessage, - comptime f: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = f; - _ = options; + fn formatMessage(self: FormatMessage, w: *std.io.Writer) std.io.Writer.Error!void { switch (self.err) { - .zoir => |err| try writer.writeAll(err.msg.get(self.diag.zoir)), - .type_check => |tc| try writer.writeAll(tc.message), + .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)), + .type_check => |tc| try w.writeAll(tc.message), } } - pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Formatter(formatMessage) { + pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Formatter(FormatMessage, formatMessage) { return .{ .data = .{ .err = self, .diag = diag, @@ -241,25 +226,18 @@ pub const Diagnostics = struct { return .{ .diag = self }; } - pub fn format( - self: *const @This(), - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = fmt; - _ = options; + pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void { var errors = self.iterateErrors(); while (errors.next()) |err| { const loc = err.getLocation(self); const msg = err.fmtMessage(self); - try writer.print("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, msg }); + try w.print("{d}:{d}: error: {f}\n", .{ loc.line + 1, loc.column + 1, msg }); var notes = err.iterateNotes(self); while (notes.next()) |note| { const note_loc = note.getLocation(self); const note_msg = note.fmtMessage(self); - try writer.print("{}:{}: note: {s}\n", .{ + try w.print("{d}:{d}: note: {f}\n", .{ note_loc.line + 1, note_loc.column + 1, note_msg, @@ -648,7 +626,7 @@ const Parser = struct { .failure => |err| { const token = self.ast.nodeMainToken(ast_node); const raw_string = self.ast.tokenSlice(token); - return self.failTokenFmt(token, @intCast(err.offset()), "{s}", .{err.fmt(raw_string)}); + return self.failTokenFmt(token, @intCast(err.offset()), "{f}", .{err.fmt(raw_string)}); }, } @@ -1089,7 +1067,10 @@ const Parser = struct { try buf.appendSlice(gpa, msg); inline for (info.fields, 0..) |field_info, i| { if (i != 0) try buf.appendSlice(gpa, ", "); - try buf.print(gpa, "'{p_}'", .{std.zig.fmtId(field_info.name)}); + try buf.print(gpa, "'{f}'", .{std.zig.fmtIdFlags(field_info.name, .{ + .allow_primitive = true, + .allow_underscore = true, + })}); } break :b .{ .token = token, @@ -1300,7 +1281,7 @@ test "std.zon ast errors" { error.ParseZon, fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &diag, .{}), ); - try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{}", .{diag}); + try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{diag}); } test "std.zon comments" { @@ -1322,7 +1303,7 @@ test "std.zon comments" { , &diag, .{})); try std.testing.expectFmt( "1:1: error: expected expression, found 'a document comment'\n", - "{}", + "{f}", .{diag}, ); } @@ -1343,7 +1324,7 @@ test "std.zon failure/oom formatting" { &diag, .{}, )); - try std.testing.expectFmt("", "{}", .{diag}); + try std.testing.expectFmt("", "{f}", .{diag}); } test "std.zon fromSlice syntax error" { @@ -1423,7 +1404,7 @@ test "std.zon unions" { \\1:4: note: supported: 'x', 'y' \\ , - "{}", + "{f}", .{diag}, ); } @@ -1437,7 +1418,7 @@ test "std.zon unions" { error.ParseZon, fromSlice(Union, gpa, ".{.x=1}", &diag, .{}), ); - try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{}", .{diag}); + try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag}); } // Extra field @@ -1449,7 +1430,7 @@ test "std.zon unions" { error.ParseZon, fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}), ); - try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } // No fields @@ -1461,7 +1442,7 @@ test "std.zon unions" { error.ParseZon, fromSlice(Union, gpa, ".{}", &diag, .{}), ); - try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } // Enum literals cannot coerce into untagged unions @@ -1470,7 +1451,7 @@ test "std.zon unions" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{})); - try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } // Unknown field for enum literal coercion @@ -1484,7 +1465,7 @@ test "std.zon unions" { \\1:2: note: supported: 'x' \\ , - "{}", + "{f}", .{diag}, ); } @@ -1495,7 +1476,7 @@ test "std.zon unions" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{})); - try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } } @@ -1551,7 +1532,7 @@ test "std.zon structs" { \\1:12: note: supported: 'x', 'y' \\ , - "{}", + "{f}", .{diag}, ); } @@ -1569,7 +1550,7 @@ test "std.zon structs" { \\1:4: error: duplicate struct field name \\1:12: note: duplicate name here \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Ignore unknown fields @@ -1594,7 +1575,7 @@ test "std.zon structs" { \\1:4: error: unexpected field 'x' \\1:4: note: none expected \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Missing field @@ -1606,7 +1587,7 @@ test "std.zon structs" { error.ParseZon, fromSlice(Vec2, gpa, ".{.x=1.5}", &diag, .{}), ); - try std.testing.expectFmt("1:2: error: missing required field y\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{diag}); } // Default field @@ -1633,7 +1614,7 @@ test "std.zon structs" { try std.testing.expectFmt( \\1:18: error: cannot initialize comptime field \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Enum field (regression test, we were previously getting the field name in an @@ -1663,7 +1644,7 @@ test "std.zon structs" { \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Arrays @@ -1676,7 +1657,7 @@ test "std.zon structs" { \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Slices @@ -1689,7 +1670,7 @@ test "std.zon structs" { \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Tuples @@ -1708,7 +1689,7 @@ test "std.zon structs" { \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Nested @@ -1721,7 +1702,7 @@ test "std.zon structs" { \\1:9: error: types are not available in ZON \\1:9: note: replace the type with '.' \\ - , "{}", .{diag}); + , "{f}", .{diag}); } } } @@ -1766,7 +1747,7 @@ test "std.zon tuples" { error.ParseZon, fromSlice(Tuple, gpa, ".{0.5, true, 123}", &diag, .{}), ); - try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{}", .{diag}); + try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{diag}); } // Extra field @@ -1780,7 +1761,7 @@ test "std.zon tuples" { ); try std.testing.expectFmt( "1:2: error: missing tuple field with index 1\n", - "{}", + "{f}", .{diag}, ); } @@ -1794,7 +1775,7 @@ test "std.zon tuples" { error.ParseZon, fromSlice(Tuple, gpa, ".{.foo = 10.0}", &diag, .{}), ); - try std.testing.expectFmt("1:2: error: expected tuple\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{diag}); } // Struct with missing field names @@ -1806,7 +1787,7 @@ test "std.zon tuples" { error.ParseZon, fromSlice(Struct, gpa, ".{10.0}", &diag, .{}), ); - try std.testing.expectFmt("1:2: error: expected struct\n", "{}", .{diag}); + try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{diag}); } // Comptime field @@ -1826,7 +1807,7 @@ test "std.zon tuples" { try std.testing.expectFmt( \\1:9: error: cannot initialize comptime field \\ - , "{}", .{diag}); + , "{f}", .{diag}); } } @@ -1938,7 +1919,7 @@ test "std.zon arrays and slices" { ); try std.testing.expectFmt( "1:3: error: index 0 outside of array of length 0\n", - "{}", + "{f}", .{diag}, ); } @@ -1953,7 +1934,7 @@ test "std.zon arrays and slices" { ); try std.testing.expectFmt( "1:8: error: index 1 outside of array of length 1\n", - "{}", + "{f}", .{diag}, ); } @@ -1968,7 +1949,7 @@ test "std.zon arrays and slices" { ); try std.testing.expectFmt( "1:2: error: expected 2 array elements; found 1\n", - "{}", + "{f}", .{diag}, ); } @@ -1983,7 +1964,7 @@ test "std.zon arrays and slices" { ); try std.testing.expectFmt( "1:2: error: expected 3 array elements; found 0\n", - "{}", + "{f}", .{diag}, ); } @@ -1998,7 +1979,7 @@ test "std.zon arrays and slices" { error.ParseZon, fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}), ); - try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{diag}); + try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); } // Slice @@ -2009,7 +1990,7 @@ test "std.zon arrays and slices" { error.ParseZon, fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}), ); - try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{diag}); + try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); } } @@ -2023,7 +2004,7 @@ test "std.zon arrays and slices" { error.ParseZon, fromSlice([3]u8, gpa, "'a'", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } // Slice @@ -2034,7 +2015,7 @@ test "std.zon arrays and slices" { error.ParseZon, fromSlice([]u8, gpa, "'a'", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } } @@ -2048,7 +2029,7 @@ test "std.zon arrays and slices" { ); try std.testing.expectFmt( "1:3: error: pointers are not available in ZON\n", - "{}", + "{f}", .{diag}, ); } @@ -2087,7 +2068,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]u8, gpa, "\"abcd\"", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { @@ -2097,7 +2078,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]u8, gpa, "\\\\abcd", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } } @@ -2114,7 +2095,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([4:0]u8, gpa, "\"abcd\"", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { @@ -2124,7 +2105,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([4:0]u8, gpa, "\\\\abcd", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } } @@ -2166,7 +2147,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([:1]const u8, gpa, "\"foo\"", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { @@ -2176,7 +2157,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([:1]const u8, gpa, "\\\\foo", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } } @@ -2188,7 +2169,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]const u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected string\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag}); } // Expecting string literal, getting an incompatible tuple @@ -2199,7 +2180,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]const u8, gpa, ".{false}", &diag, .{}), ); - try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag}); } // Invalid string literal @@ -2210,7 +2191,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]const i8, gpa, "\"\\a\"", &diag, .{}), ); - try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{}", .{diag}); + try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag}); } // Slice wrong child type @@ -2222,7 +2203,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]const i8, gpa, "\"a\"", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { @@ -2232,7 +2213,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]const i8, gpa, "\\\\a", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } } @@ -2245,7 +2226,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]align(2) const u8, gpa, "\"abc\"", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { @@ -2255,7 +2236,7 @@ test "std.zon string literal" { error.ParseZon, fromSlice([]align(2) const u8, gpa, "\\\\abc", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } } @@ -2329,7 +2310,7 @@ test "std.zon enum literals" { \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ , - "{}", + "{f}", .{diag}, ); } @@ -2347,7 +2328,7 @@ test "std.zon enum literals" { \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ , - "{}", + "{f}", .{diag}, ); } @@ -2360,7 +2341,7 @@ test "std.zon enum literals" { error.ParseZon, fromSlice(Enum, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected enum literal\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{diag}); } // Test embedded nulls in an identifier @@ -2373,7 +2354,7 @@ test "std.zon enum literals" { ); try std.testing.expectFmt( "1:2: error: identifier cannot contain null bytes\n", - "{}", + "{f}", .{diag}, ); } @@ -2399,13 +2380,13 @@ test "std.zon parse bool" { \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\1:2: note: precede identifier with '.' for an enum literal \\ - , "{}", .{diag}); + , "{f}", .{diag}); } { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &diag, .{})); - try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{diag}); } } @@ -2478,7 +2459,7 @@ test "std.zon parse int" { )); try std.testing.expectFmt( "1:1: error: type 'i66' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2494,7 +2475,7 @@ test "std.zon parse int" { )); try std.testing.expectFmt( "1:1: error: type 'i66' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2583,7 +2564,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{})); try std.testing.expectFmt( "1:3: error: invalid digit 'a' for decimal base\n", - "{}", + "{f}", .{diag}, ); } @@ -2593,7 +2574,7 @@ test "std.zon parse int" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &diag, .{})); - try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{diag}); } // Failing because an int is out of range @@ -2603,7 +2584,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{})); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2615,7 +2596,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{})); try std.testing.expectFmt( "1:1: error: type 'i8' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2627,7 +2608,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{})); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2639,7 +2620,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{})); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2651,7 +2632,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{})); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", - "{}", + "{f}", .{diag}, ); } @@ -2666,7 +2647,7 @@ test "std.zon parse int" { \\1:2: note: use '0' for an integer zero \\1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Negative integer zero casted to float @@ -2679,7 +2660,7 @@ test "std.zon parse int" { \\1:2: note: use '0' for an integer zero \\1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{}", .{diag}); + , "{f}", .{diag}); } // Negative float 0 is allowed @@ -2695,7 +2676,7 @@ test "std.zon parse int" { try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{})); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", - "{}", + "{f}", .{diag}, ); } @@ -2709,7 +2690,7 @@ test "std.zon parse int" { ); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", - "{}", + "{f}", .{diag}, ); } @@ -2719,7 +2700,7 @@ test "std.zon parse int" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &diag, .{})); - try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{}", .{diag}); + try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{diag}); } // Notes on invalid int literal @@ -2731,7 +2712,7 @@ test "std.zon parse int" { \\1:1: error: number '0123' has leading zero \\1:1: note: use '0o' prefix for octal literals \\ - , "{}", .{diag}); + , "{f}", .{diag}); } } @@ -2744,7 +2725,7 @@ test "std.zon negative char" { try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{})); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", - "{}", + "{f}", .{diag}, ); } @@ -2754,13 +2735,15 @@ test "std.zon negative char" { try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{})); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", - "{}", + "{f}", .{diag}, ); } } test "std.zon parse float" { + if (builtin.cpu.arch == .x86 and builtin.abi == .musl and builtin.link_mode == .dynamic) return error.SkipZigTest; + const gpa = std.testing.allocator; // Test decimals @@ -2841,7 +2824,7 @@ test "std.zon parse float" { try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{})); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", - "{}", + "{f}", .{diag}, ); } @@ -2851,7 +2834,7 @@ test "std.zon parse float" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{})); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } // nan as int not allowed @@ -2859,7 +2842,7 @@ test "std.zon parse float" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{})); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } // inf as int not allowed @@ -2867,7 +2850,7 @@ test "std.zon parse float" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &diag, .{})); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } // -inf as int not allowed @@ -2875,7 +2858,7 @@ test "std.zon parse float" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &diag, .{})); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } // Bad identifier as float @@ -2888,7 +2871,7 @@ test "std.zon parse float" { \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\1:1: note: precede identifier with '.' for an enum literal \\ - , "{}", .{diag}); + , "{f}", .{diag}); } { @@ -2897,7 +2880,7 @@ test "std.zon parse float" { try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{})); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", - "{}", + "{f}", .{diag}, ); } @@ -2910,7 +2893,7 @@ test "std.zon parse float" { error.ParseZon, fromSlice(f32, gpa, "\"foo\"", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{diag}); } } @@ -3154,7 +3137,7 @@ test "std.zon vector" { ); try std.testing.expectFmt( "1:2: error: expected 2 vector elements; found 1\n", - "{}", + "{f}", .{diag}, ); } @@ -3169,7 +3152,7 @@ test "std.zon vector" { ); try std.testing.expectFmt( "1:2: error: expected 2 vector elements; found 3\n", - "{}", + "{f}", .{diag}, ); } @@ -3184,7 +3167,7 @@ test "std.zon vector" { ); try std.testing.expectFmt( "1:8: error: expected type 'f32'\n", - "{}", + "{f}", .{diag}, ); } @@ -3197,7 +3180,7 @@ test "std.zon vector" { error.ParseZon, fromSlice(@Vector(3, u8), gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{diag}); } // Elements should get freed on error @@ -3208,7 +3191,7 @@ test "std.zon vector" { error.ParseZon, fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}), ); - try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{diag}); } } @@ -3332,7 +3315,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag}); } { @@ -3342,7 +3325,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const f32, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag}); } { @@ -3352,7 +3335,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{diag}); } { @@ -3362,7 +3345,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const bool, gpa, "10", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag}); } { @@ -3372,7 +3355,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional struct\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag}); } { @@ -3382,7 +3365,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const struct { i32 }, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag}); } { @@ -3392,7 +3375,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const union { x: void }, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional union\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag}); } { @@ -3402,7 +3385,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const [3]u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { @@ -3412,7 +3395,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(?[3]u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { @@ -3422,7 +3405,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const []u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { @@ -3432,7 +3415,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(?[]u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { @@ -3442,7 +3425,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const []const u8, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional string\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag}); } { @@ -3452,7 +3435,7 @@ test "std.zon add pointers" { error.ParseZon, fromSlice(*const ?*const enum { foo }, gpa, "true", &diag, .{}), ); - try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag}); } } diff --git a/lib/ubsan_rt.zig b/lib/ubsan_rt.zig index ab1727e74580e22dbfdded2e6665fd750a3a83c0..dc0a65dc91181240b459f28e766c878af7843c89 100644 --- a/lib/ubsan_rt.zig +++ b/lib/ubsan_rt.zig @@ -119,24 +119,22 @@ const Value = extern struct { } } - pub fn format(value: Value, bw: *std.io.Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); - + pub fn format(value: Value, writer: *std.io.Writer) std.io.Writer.Error!void { // Work around x86_64 backend limitation. if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) { - return bw.writeAll("(unknown)"); + return writer.writeAll("(unknown)"); } switch (value.td.kind) { .integer => { if (value.td.isSigned()) { - return bw.print("{d}", .{value.getSignedInteger()}); + try writer.print("{d}", .{value.getSignedInteger()}); } else { - return bw.print("{d}", .{value.getUnsignedInteger()}); + try writer.print("{d}", .{value.getUnsignedInteger()}); } }, - .float => return bw.print("{d}", .{value.getFloat()}), - .unknown => return bw.writeAll("(unknown)"), + .float => try writer.print("{d}", .{value.getFloat()}), + .unknown => try writer.writeAll("(unknown)"), } } }; @@ -166,17 +164,12 @@ fn overflowHandler( ) callconv(.c) noreturn { const lhs: Value = .{ .handle = lhs_handle, .td = data.td }; const rhs: Value = .{ .handle = rhs_handle, .td = data.td }; - - const is_signed = data.td.isSigned(); - const fmt = "{s} integer overflow: " ++ "{f} " ++ - operator ++ " {f} cannot be represented in type {s}"; - - panic(@returnAddress(), fmt, .{ - if (is_signed) "signed" else "unsigned", - lhs, - rhs, - data.td.getName(), - }); + const signed_str = if (data.td.isSigned()) "signed" else "unsigned"; + panic( + @returnAddress(), + "{s} integer overflow: {f} " ++ operator ++ " {f} cannot be represented in type {s}", + .{ signed_str, lhs, rhs, data.td.getName() }, + ); } }; @@ -195,11 +188,9 @@ fn negationHandler( value_handle: ValueHandle, ) callconv(.c) noreturn { const value: Value = .{ .handle = value_handle, .td = data.td }; - panic( - @returnAddress(), - "negation of {f} cannot be represented in type {s}", - .{ value, data.td.getName() }, - ); + panic(@returnAddress(), "negation of {f} cannot be represented in type {s}", .{ + value, data.td.getName(), + }); } fn divRemHandlerAbort( @@ -219,11 +210,9 @@ fn divRemHandler( const rhs: Value = .{ .handle = rhs_handle, .td = data.td }; if (rhs.isMinusOne()) { - panic( - @returnAddress(), - "division of {f} by -1 cannot be represented in type {s}", - .{ lhs, data.td.getName() }, - ); + panic(@returnAddress(), "division of {f} by -1 cannot be represented in type {s}", .{ + lhs, data.td.getName(), + }); } else panic(@returnAddress(), "division by zero", .{}); } @@ -353,11 +342,10 @@ fn outOfBounds( index_handle: ValueHandle, ) callconv(.c) noreturn { const index: Value = .{ .handle = index_handle, .td = data.index_type }; - panic( - @returnAddress(), - "index {f} out of bounds for type {s}", - .{ index, data.array_type.getName() }, - ); + panic(@returnAddress(), "index {f} out of bounds for type {s}", .{ + index, + data.array_type.getName(), + }); } const PointerOverflowData = extern struct { @@ -547,11 +535,9 @@ fn loadInvalidValue( value_handle: ValueHandle, ) callconv(.c) noreturn { const value: Value = .{ .handle = value_handle, .td = data.td }; - panic( - @returnAddress(), - "load of value {f}, which is not valid for type {s}", - .{ value, data.td.getName() }, - ); + panic(@returnAddress(), "load of value {f}, which is not valid for type {s}", .{ + value, data.td.getName(), + }); } const InvalidBuiltinData = extern struct { @@ -590,11 +576,7 @@ fn vlaBoundNotPositive( bound_handle: ValueHandle, ) callconv(.c) noreturn { const bound: Value = .{ .handle = bound_handle, .td = data.td }; - panic( - @returnAddress(), - "variable length array bound evaluates to non-positive value {f}", - .{bound}, - ); + panic(@returnAddress(), "variable length array bound evaluates to non-positive value {f}", .{bound}); } const FloatCastOverflowData = extern struct { diff --git a/src/Air.zig b/src/Air.zig index 37dddb27f8d326d06da68ebf6d8bb9542e655541..c9e38415a3e26a2ff360ce4e1886623c5ba86e3f 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -747,7 +747,9 @@ pub const Inst = struct { /// Dest slice may have any alignment; source pointer may have any alignment. /// The two memory regions must not overlap. /// Result type is always void. + /// /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer. + /// /// If the length is compile-time known (due to the destination or /// source being a pointer-to-array), then it is guaranteed to be /// greater than zero. @@ -759,7 +761,9 @@ pub const Inst = struct { /// Dest slice may have any alignment; source pointer may have any alignment. /// The two memory regions may overlap. /// Result type is always void. + /// /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer. + /// /// If the length is compile-time known (due to the destination or /// source being a pointer-to-array), then it is guaranteed to be /// greater than zero. @@ -958,14 +962,13 @@ pub const Inst = struct { return index.unwrap().target; } - pub fn format(index: Index, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); - try bw.writeByte('%'); + pub fn format(index: Index, w: *std.io.Writer) std.io.Writer.Error!void { + try w.writeByte('%'); switch (index.unwrap()) { .ref => {}, - .target => try bw.writeByte('t'), + .target => try w.writeByte('t'), } - try bw.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))}); + try w.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))}); } }; diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig index 9fab03c679e41d39f8beace92873cbbb7d0a39a4..83d5fd0919e00bed08d35512800fb7f4d3d34f36 100644 --- a/src/Air/Liveness.zig +++ b/src/Air/Liveness.zig @@ -1300,10 +1300,10 @@ fn analyzeOperands( // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`. const immediate_death = if (data.live_set.remove(inst)) blk: { - log.debug("[{}] %{}: removed from live set", .{ pass, @intFromEnum(inst) }); + log.debug("[{}] %{d}: removed from live set", .{ pass, @intFromEnum(inst) }); break :blk false; } else blk: { - log.debug("[{}] %{}: immediate death", .{ pass, @intFromEnum(inst) }); + log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) }); break :blk true; }; @@ -1324,7 +1324,7 @@ fn analyzeOperands( const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i)); if ((try data.live_set.fetchPut(gpa, operand, {})) == null) { - log.debug("[{}] %{}: added %{f} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand }); + log.debug("[{}] %{d}: added %{d} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand }); tomb_bits |= mask; } } @@ -2037,15 +2037,15 @@ fn fmtInstSet(set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void)) FmtIns const FmtInstSet = struct { set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void), - pub fn format(val: FmtInstSet, bw: *Writer, comptime _: []const u8) !void { + pub fn format(val: FmtInstSet, w: *std.io.Writer) std.io.Writer.Error!void { if (val.set.count() == 0) { - try bw.writeAll("[no instructions]"); + try w.writeAll("[no instructions]"); return; } var it = val.set.keyIterator(); - try bw.print("%{f}", .{it.next().?.*}); + try w.print("%{f}", .{it.next().?.*}); while (it.next()) |key| { - try bw.print(" %{f}", .{key.*}); + try w.print(" %{f}", .{key.*}); } } }; @@ -2057,15 +2057,14 @@ fn fmtInstList(list: []const Air.Inst.Index) FmtInstList { const FmtInstList = struct { list: []const Air.Inst.Index, - pub fn format(val: FmtInstList, bw: *Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); + pub fn format(val: FmtInstList, w: *std.io.Writer) std.io.Writer.Error!void { if (val.list.len == 0) { - try bw.writeAll("[no instructions]"); + try w.writeAll("[no instructions]"); return; } - try bw.print("%{f}", .{val.list[0]}); + try w.print("%{f}", .{val.list[0]}); for (val.list[1..]) |inst| { - try bw.print(" %{f}", .{inst}); + try w.print(" %{f}", .{inst}); } } }; diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig index 78c42bded50bec569a992a714eeaced77364e1e5..b1e13dbf40aa8ee34ae14b98a975bef6c823c3ac 100644 --- a/src/Air/Liveness/Verify.zig +++ b/src/Air/Liveness/Verify.zig @@ -511,7 +511,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { // The same stuff should be alive after the loop as before it. const gop = try self.loops.getOrPut(self.gpa, inst); - if (gop.found_existing) return invalid("%{}: loop already exists", .{@intFromEnum(inst)}); + if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)}); defer { var live = self.loops.fetchRemove(inst).?; live.value.deinit(self.gpa); @@ -560,7 +560,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { // after the loop as before it. { const gop = try self.loops.getOrPut(self.gpa, inst); - if (gop.found_existing) return invalid("%{}: loop already exists", .{@intFromEnum(inst)}); + if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)}); gop.value_ptr.* = self.live.move(); } defer { @@ -601,7 +601,9 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies return; }; if (dies) { - if (!self.live.remove(operand)) return invalid("%{f}: dead operand %{f} reused and killed again", .{ inst, operand }); + if (!self.live.remove(operand)) return invalid("%{f}: dead operand %{f} reused and killed again", .{ + inst, operand, + }); } else { if (!self.live.contains(operand)) return invalid("%{f}: dead operand %{f} reused", .{ inst, operand }); } diff --git a/src/Air/print.zig b/src/Air/print.zig index b8e27f6bba7ab332429f77fd8d39e755cd71f32d..8ff199c02f1559bdda87f88092e83fb03ba970eb 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -518,13 +518,13 @@ const Writer = struct { if (mask_idx > 0) try s.writeAll(", "); switch (mask_elem.unwrap()) { .elem => |idx| try s.print("elem {d}", .{idx}), - .value => |val| try s.print("val {}", .{Value.fromInterned(val).fmtValue(w.pt)}), + .value => |val| try s.print("val {f}", .{Value.fromInterned(val).fmtValue(w.pt)}), } } try s.writeByte(']'); } - fn writeShuffleTwo(w: *Writer, s: anytype, inst: Air.Inst.Index) Error!void { + fn writeShuffleTwo(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst); try w.writeType(s, unwrapped.result_ty); try s.writeAll(", "); @@ -590,7 +590,7 @@ const Writer = struct { const ip = &w.pt.zcu.intern_pool; const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; try w.writeType(s, .fromInterned(ty_nav.ty)); - try s.print(", '{}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); + try s.print(", '{f}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); } fn writeAtomicLoad(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { @@ -710,7 +710,7 @@ const Writer = struct { } } const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len]; - try s.print(", \"{f}\"", .{std.zig.fmtEscapes(asm_source)}); + try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)}); } fn writeDbgStmt(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { @@ -722,7 +722,7 @@ const Writer = struct { const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; try w.writeOperand(s, inst, 0, pl_op.operand); const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - try s.print(", \"{f}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))}); + try s.print(", \"{f}\"", .{std.zig.fmtString(name.toSlice(w.air))}); } fn writeCall(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { @@ -1010,7 +1010,7 @@ const Writer = struct { fn writeInstRef( w: *Writer, - s: anytype, + s: *std.io.Writer, operand: Air.Inst.Ref, dies: bool, ) Error!void { diff --git a/src/Builtin.zig b/src/Builtin.zig index 92b2dfd8414d25153928aa687f3d0a40be7905f1..b2cb603f53fc7e1251ffd246819fac5bb6d736e3 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -57,49 +57,49 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; \\pub const zig_version_string = "{s}"; - \\pub const zig_backend = std.builtin.CompilerBackend.{fp_}; + \\pub const zig_backend = std.builtin.CompilerBackend.{f}; \\ - \\pub const output_mode: std.builtin.OutputMode = .{fp_}; - \\pub const link_mode: std.builtin.LinkMode = .{fp_}; - \\pub const unwind_tables: std.builtin.UnwindTables = .{fp_}; + \\pub const output_mode: std.builtin.OutputMode = .{f}; + \\pub const link_mode: std.builtin.LinkMode = .{f}; + \\pub const unwind_tables: std.builtin.UnwindTables = .{f}; \\pub const is_test = {}; \\pub const single_threaded = {}; - \\pub const abi: std.Target.Abi = .{fp_}; + \\pub const abi: std.Target.Abi = .{f}; \\pub const cpu: std.Target.Cpu = .{{ - \\ .arch = .{fp_}, - \\ .model = &std.Target.{fp_}.cpu.{fp_}, - \\ .features = std.Target.{fp_}.featureSet(&.{{ + \\ .arch = .{f}, + \\ .model = &std.Target.{f}.cpu.{f}, + \\ .features = std.Target.{f}.featureSet(&.{{ \\ , .{ build_options.version, - std.zig.fmtId(@tagName(zig_backend)), - std.zig.fmtId(@tagName(opts.output_mode)), - std.zig.fmtId(@tagName(opts.link_mode)), - std.zig.fmtId(@tagName(opts.unwind_tables)), + std.zig.fmtIdPU(@tagName(zig_backend)), + std.zig.fmtIdPU(@tagName(opts.output_mode)), + std.zig.fmtIdPU(@tagName(opts.link_mode)), + std.zig.fmtIdPU(@tagName(opts.unwind_tables)), opts.is_test, opts.single_threaded, - std.zig.fmtId(@tagName(target.abi)), - std.zig.fmtId(@tagName(target.cpu.arch)), - std.zig.fmtId(arch_family_name), - std.zig.fmtId(target.cpu.model.name), - std.zig.fmtId(arch_family_name), + std.zig.fmtIdPU(@tagName(target.abi)), + std.zig.fmtIdPU(@tagName(target.cpu.arch)), + std.zig.fmtIdPU(arch_family_name), + std.zig.fmtIdPU(target.cpu.model.name), + std.zig.fmtIdPU(arch_family_name), }); for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| { const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); const is_enabled = target.cpu.features.isEnabled(index); if (is_enabled) { - try buffer.print(" .{fp_},\n", .{std.zig.fmtId(feature.name)}); + try buffer.print(" .{f},\n", .{std.zig.fmtIdPU(feature.name)}); } } try buffer.print( \\ }}), \\}}; \\pub const os: std.Target.Os = .{{ - \\ .tag = .{fp_}, + \\ .tag = .{f}, \\ .version_range = .{{ , - .{std.zig.fmtId(@tagName(target.os.tag))}, + .{std.zig.fmtIdPU(@tagName(target.os.tag))}, ); switch (target.os.versionRange()) { @@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { }), .windows => |windows| try buffer.print( \\ .windows = .{{ - \\ .min = {fc}, - \\ .max = {fc}, + \\ .min = {f}, + \\ .max = {f}, \\ }}}}, \\ , .{ windows.min, windows.max }), @@ -238,8 +238,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { const link_libc = opts.link_libc; try buffer.print( - \\pub const object_format: std.Target.ObjectFormat = .{fp_}; - \\pub const mode: std.builtin.OptimizeMode = .{fp_}; + \\pub const object_format: std.Target.ObjectFormat = .{f}; + \\pub const mode: std.builtin.OptimizeMode = .{f}; \\pub const link_libc = {}; \\pub const link_libcpp = {}; \\pub const have_error_return_tracing = {}; @@ -249,12 +249,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { \\pub const position_independent_code = {}; \\pub const position_independent_executable = {}; \\pub const strip_debug_info = {}; - \\pub const code_model: std.builtin.CodeModel = .{fp_}; + \\pub const code_model: std.builtin.CodeModel = .{f}; \\pub const omit_frame_pointer = {}; \\ , .{ - std.zig.fmtId(@tagName(target.ofmt)), - std.zig.fmtId(@tagName(opts.optimize_mode)), + std.zig.fmtIdPU(@tagName(target.ofmt)), + std.zig.fmtIdPU(@tagName(opts.optimize_mode)), link_libc, opts.link_libcpp, opts.error_tracing, @@ -264,15 +264,15 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { opts.pic, opts.pie, opts.strip, - std.zig.fmtId(@tagName(opts.code_model)), + std.zig.fmtIdPU(@tagName(opts.code_model)), opts.omit_frame_pointer, }); if (target.os.tag == .wasi) { try buffer.print( - \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{fp_}; + \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{f}; \\ - , .{std.zig.fmtId(@tagName(opts.wasi_exec_model))}); + , .{std.zig.fmtIdPU(@tagName(opts.wasi_exec_model))}); } if (opts.is_test) { @@ -317,7 +317,7 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void { if (root_dir.statFile(sub_path)) |stat| { if (stat.size != file.source.?.len) { std.log.warn( - "the cached file '{f}{s}' had the wrong size. Expected {d}, found {d}. " ++ + "the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++ "Overwriting with correct file contents now", .{ file.path.fmt(comp), file.source.?.len, stat.size }, ); diff --git a/src/Compilation.zig b/src/Compilation.zig index d1adbb6322e5c8867dd5c5cf471bba332bc4b620..709415f3b16c8f80508c1c9bfd1d23b31bd00ae3 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -237,7 +237,6 @@ fuzzer_lib: ?CrtFile = null, glibc_so_files: ?glibc.BuiltSharedObjects = null, freebsd_so_files: ?freebsd.BuiltSharedObjects = null, netbsd_so_files: ?netbsd.BuiltSharedObjects = null, -wasi_emulated_libs: []const wasi_libc.CrtFile, /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. @@ -403,9 +402,7 @@ pub const Path = struct { const Formatter = struct { p: Path, comp: *Compilation, - pub fn format(f: Formatter, comptime unused_fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void { - comptime assert(unused_fmt.len == 0); - _ = options; + pub fn format(f: Formatter, w: *std.io.Writer) std.io.Writer.Error!void { const root_path: []const u8 = switch (f.p.root) { .zig_lib => f.comp.dirs.zig_lib.path orelse ".", .global_cache => f.comp.dirs.global_cache.path orelse ".", @@ -734,10 +731,10 @@ pub const Directories = struct { }; if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) { - fatal("zig lib directory '{}' cannot be equal to global cache directory '{}'", .{ zig_lib, global_cache }); + fatal("zig lib directory '{f}' cannot be equal to global cache directory '{f}'", .{ zig_lib, global_cache }); } if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) { - fatal("zig lib directory '{}' cannot be equal to local cache directory '{}'", .{ zig_lib, local_cache }); + fatal("zig lib directory '{f}' cannot be equal to local cache directory '{f}'", .{ zig_lib, local_cache }); } return .{ @@ -1570,12 +1567,6 @@ pub const CreateOptions = struct { framework_dirs: []const []const u8 = &[0][]const u8{}, frameworks: []const Framework = &.{}, windows_lib_names: []const []const u8 = &.{}, - /// These correspond to the WASI libc emulated subcomponents including: - /// * process clocks - /// * getpid - /// * mman - /// * signal - wasi_emulated_libs: []const wasi_libc.CrtFile = &.{}, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an /// executable this field is ignored. @@ -2055,7 +2046,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .function_sections = options.function_sections, .data_sections = options.data_sections, .native_system_include_paths = options.native_system_include_paths, - .wasi_emulated_libs = options.wasi_emulated_libs, .force_undefined_symbols = options.force_undefined_symbols, .link_eh_frame_hdr = link_eh_frame_hdr, .global_cc_argv = options.global_cc_argv, @@ -2070,12 +2060,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), }; - errdefer { - for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); - comp.windows_libs.deinit(gpa); - } - try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len); - for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {}); + comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{}); + errdefer comp.windows_libs.deinit(gpa); // Prevent some footguns by making the "any" fields of config reflect // the default Module settings. @@ -2306,6 +2292,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil if (comp.emit_bin != null and target.ofmt != .c) { if (!comp.skip_linker_dependencies) { + // These DLLs are always loaded into every Windows process. + if (target.os.tag == .windows and is_exe_or_dyn_lib) { + try comp.windows_libs.ensureUnusedCapacity(gpa, 2); + comp.windows_libs.putAssumeCapacity("kernel32", {}); + comp.windows_libs.putAssumeCapacity("ntdll", {}); + } + // If we need to build libc for the target, add work items for it. // We go through the work queue so that building can be done in parallel. // If linking against host libc installation, instead queue up jobs @@ -2381,11 +2374,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil } else if (target.isWasiLibC()) { if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - for (comp.wasi_emulated_libs) |crt_file| { - comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = true; - } - comp.link_task_queue.pending_prelink_tasks += @intCast(comp.wasi_emulated_libs.len); - comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true; comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true; comp.link_task_queue.pending_prelink_tasks += 2; @@ -2399,7 +2387,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // When linking mingw-w64 there are some import libs we always need. try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); - for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {}); + for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); } else { return error.LibCUnavailable; } @@ -2497,7 +2485,6 @@ pub fn destroy(comp: *Compilation) void { comp.c_object_work_queue.deinit(); comp.win32_resource_work_queue.deinit(); - for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); comp.windows_libs.deinit(gpa); { @@ -2994,7 +2981,7 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat break @intCast(i); } } else std.debug.panic( - "missing prefix directory '{s}' ('{}') for '{s}'", + "missing prefix directory '{s}' ('{f}') for '{s}'", .{ @tagName(path.root), want_prefix_dir, path.sub_path }, ); @@ -3333,7 +3320,7 @@ fn emitFromCObject( emit_path.root_dir.handle, emit_path.sub_path, .{}, - ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{ + ) catch |err| log.err("unable to copy '{f}' to '{f}': {s}", .{ src_path, emit_path, @errorName(err), @@ -3681,7 +3668,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"), }, .src_loc = try bundle.addSourceLocation(.{ - .src_path = try bundle.printString("{}", .{file.path.fmt(comp)}), + .src_path = try bundle.printString("{f}", .{file.path.fmt(comp)}), .span_start = start, .span_main = start, .span_end = @intCast(end), @@ -3728,7 +3715,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { assert(!is_retryable); // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors. _ = try file.getTree(zcu); // Tree must be loaded. - const path = try std.fmt.allocPrint(gpa, "{}", .{file.path.fmt(comp)}); + const path = try std.fmt.allocPrint(gpa, "{f}", .{file.path.fmt(comp)}); defer gpa.free(path); if (file.zir != null) { try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path); @@ -3784,8 +3771,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{ - error_msg.msg, - zcu.fmtAnalUnit(anal_unit), + error_msg.msg, zcu.fmtAnalUnit(anal_unit), }); try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error); @@ -4047,7 +4033,7 @@ pub fn addModuleErrorMsg( const err_src_loc = module_err_msg.src_loc.upgrade(zcu); const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| { try eb.addRootErrorMessage(.{ - .msg = try eb.printString("unable to load '{}': {s}", .{ + .msg = try eb.printString("unable to load '{f}': {s}", .{ err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err), }), }); @@ -4110,7 +4096,7 @@ pub fn addModuleErrorMsg( } const src_loc = try eb.addSourceLocation(.{ - .src_path = try eb.printString("{}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}), + .src_path = try eb.printString("{f}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}), .span_start = err_span.start, .span_main = err_span.main, .span_end = err_span.end, @@ -4142,7 +4128,7 @@ pub fn addModuleErrorMsg( const gop = try notes.getOrPutContext(gpa, .{ .msg = try eb.addString(module_note.msg), .src_loc = try eb.addSourceLocation(.{ - .src_path = try eb.printString("{}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}), + .src_path = try eb.printString("{f}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}), .span_start = span.start, .span_main = span.main, .span_end = span.end, @@ -4187,7 +4173,7 @@ fn addReferenceTraceFrame( try ref_traces.append(gpa, .{ .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), .src_loc = try eb.addSourceLocation(.{ - .src_path = try eb.printString("{}", .{src.file_scope.path.fmt(zcu.comp)}), + .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}), .span_start = span.start, .span_main = span.main, .span_end = span.end, @@ -4906,7 +4892,7 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, var walker = try mod_dir.walk(comp.gpa); defer walker.deinit(); - var archiver = std.tar.writer(tar_file.writer().any()); + var archiver = std.tar.writer(tar_file.deprecatedWriter().any()); archiver.prefix = name; while (try walker.next()) |entry| { @@ -4919,13 +4905,13 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, else => continue, } var file = mod_dir.openFile(entry.path, .{}) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to open '{}{s}': {s}", .{ + return comp.lockAndSetMiscFailure(.docs_copy, "unable to open '{f}{s}': {s}", .{ root.fmt(comp), entry.path, @errorName(err), }); }; defer file.close(); archiver.writeFile(entry.path, file) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive '{}{s}': {s}", .{ + return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive '{f}{s}': {s}", .{ root.fmt(comp), entry.path, @errorName(err), }); }; @@ -5055,7 +5041,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, - "unable to create output directory '{}': {s}", + "unable to create output directory '{f}': {s}", .{ docs_path, @errorName(err) }, ); }; @@ -5067,10 +5053,8 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye "main.wasm", .{}, ) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{ - crt_file.full_object_path, - docs_path, - @errorName(err), + return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {s}", .{ + crt_file.full_object_path, docs_path, @errorName(err), }); }; } @@ -6024,16 +6008,15 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 // In .rc files, a " within a quoted string is escaped as "" const fmtRcEscape = struct { - fn formatRcEscape(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); + fn formatRcEscape(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void { for (bytes) |byte| switch (byte) { - '"' => try bw.writeAll("\"\""), - '\\' => try bw.writeAll("\\\\"), - else => try bw.writeByte(byte), + '"' => try writer.writeAll("\"\""), + '\\' => try writer.writeAll("\\\\"), + else => try writer.writeByte(byte), }; } - pub fn fmtRcEscape(bytes: []const u8) std.fmt.Formatter(formatRcEscape) { + pub fn fmtRcEscape(bytes: []const u8) std.fmt.Formatter([]const u8, formatRcEscape) { return .{ .data = bytes }; } }.fmtRcEscape; @@ -6047,7 +6030,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 // 24 is RT_MANIFEST const resource_type = 24; - const input = try std.fmt.allocPrint(arena, "{} {} \"{f}\"", .{ resource_id, resource_type, fmtRcEscape(src_path) }); + const input = try std.fmt.allocPrint(arena, "{d} {d} \"{f}\"", .{ + resource_id, resource_type, fmtRcEscape(src_path), + }); try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input }); @@ -6259,7 +6244,7 @@ fn spawnZigRc( } // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace) - const stderr_reader = child.stderr.?.reader(); + const stderr_reader = child.stderr.?.deprecatedReader(); const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); const term = child.wait() catch |err| { @@ -6474,7 +6459,7 @@ pub fn addCCArgs( try argv.append("-fno-asynchronous-unwind-tables"); try argv.append("-funwind-tables"); }, - .@"async" => try argv.append("-fasynchronous-unwind-tables"), + .async => try argv.append("-fasynchronous-unwind-tables"), } try argv.append("-nostdinc"); @@ -7597,27 +7582,6 @@ fn getCrtPathsInner( }; } -pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { - // Avoid deadlocking on building import libs such as kernel32.lib - // This can happen when the user uses `build-exe foo.obj -lkernel32` and - // then when we create a sub-Compilation for zig libc, it also tries to - // build kernel32.lib. - if (comp.skip_linker_dependencies) return; - const target = &comp.root_mod.resolved_target.result; - if (target.os.tag != .windows or target.ofmt == .c) return; - - // This happens when an `extern "foo"` function is referenced. - // If we haven't seen this library yet and we're targeting Windows, we need - // to queue up a work item to produce the DLL import library for this. - const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name); - if (gop.found_existing) return; - { - errdefer _ = comp.windows_libs.pop(); - gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name); - } - try comp.queueJob(.{ .windows_import_lib = gop.index }); -} - /// This decides the optimization mode for all zig-provided libraries, including /// compiler-rt, libcxx, libc, libunwind, etc. pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { diff --git a/src/IncrementalDebugServer.zig b/src/IncrementalDebugServer.zig index 531b71b4e82435cba41eeae259f9721a00106045..e7c7461e5dc898d63ac9c01d4087909e3d313e01 100644 --- a/src/IncrementalDebugServer.zig +++ b/src/IncrementalDebugServer.zig @@ -142,8 +142,8 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index"); const nav = ip.getNav(nav_index); try w.print( - \\name: '{}' - \\fqn: '{}' + \\name: '{f}' + \\fqn: '{f}' \\status: {s} \\created on generation: {d} \\ @@ -234,7 +234,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons for (unit_info.deps.items, 0..) |dependee, i| { try w.print("[{d}] ", .{i}); switch (dependee) { - .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{}", .{zcu.fmtDependee(dependee)}), + .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}), .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }), .interned => |ip_index| switch (ip.indexToKey(ip_index)) { .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 const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index")); const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type"); try w.print( - \\name: '{}' + \\name: '{f}' \\created on generation: {d} \\ , .{ @@ -365,7 +365,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void { .union_type, .enum_type, .opaque_type, - => try w.print("{}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @intFromEnum(ty.toIntern()) }), + => try w.print("{f}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @intFromEnum(ty.toIntern()) }), else => unreachable, } diff --git a/src/InternPool.zig b/src/InternPool.zig index 942a58684e6544edf5f1b0e67c4d2a5962a2878a..8471a1ad9ee72f1ecfce47424c70843991213ccb 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -518,8 +518,6 @@ pub const Nav = struct { namespace: NamespaceIndex, zir_index: TrackedInst.Index, }, - /// TODO: this is a hack! If #20663 isn't accepted, let's figure out something a bit better. - is_usingnamespace: bool, status: union(enum) { /// This `Nav` is pending semantic analysis. unresolved, @@ -735,7 +733,7 @@ pub const Nav = struct { @"addrspace": std.builtin.AddressSpace, /// Populated only if `bits.status == .type_resolved`. is_threadlocal: bool, - is_usingnamespace: bool, + _: u1 = 0, }; fn unpack(repr: Repr) Nav { @@ -749,7 +747,6 @@ pub const Nav = struct { assert(repr.analysis_zir_index == .none); break :a null; }, - .is_usingnamespace = repr.bits.is_usingnamespace, .status = switch (repr.bits.status) { .unresolved => .unresolved, .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{ @@ -797,7 +794,6 @@ pub const Nav = struct { .is_const = false, .alignment = .none, .@"addrspace" = .generic, - .is_usingnamespace = nav.is_usingnamespace, .is_threadlocal = false, }, .type_resolved => |r| .{ @@ -805,7 +801,6 @@ pub const Nav = struct { .is_const = r.is_const, .alignment = r.alignment, .@"addrspace" = r.@"addrspace", - .is_usingnamespace = nav.is_usingnamespace, .is_threadlocal = r.is_threadlocal, }, .fully_resolved => |r| .{ @@ -813,7 +808,6 @@ pub const Nav = struct { .is_const = r.is_const, .alignment = r.alignment, .@"addrspace" = r.@"addrspace", - .is_usingnamespace = nav.is_usingnamespace, .is_threadlocal = false, }, }, @@ -6865,8 +6859,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { { namespace.pub_decls.deinit(gpa); namespace.priv_decls.deinit(gpa); - namespace.pub_usingnamespace.deinit(gpa); - namespace.priv_usingnamespace.deinit(gpa); namespace.comptime_decls.deinit(gpa); namespace.test_decls.deinit(gpa); } @@ -11502,7 +11494,6 @@ pub fn createNav( .@"linksection" = opts.@"linksection", .@"addrspace" = opts.@"addrspace", } }, - .is_usingnamespace = false, })); return index_unwrapped.wrap(ip); } @@ -11517,8 +11508,6 @@ pub fn createDeclNav( fqn: NullTerminatedString, zir_index: TrackedInst.Index, namespace: NamespaceIndex, - /// TODO: this is hacky! See `Nav.is_usingnamespace`. - is_usingnamespace: bool, ) Allocator.Error!Nav.Index { const navs = ip.getLocal(tid).getMutableNavs(gpa); @@ -11537,7 +11526,6 @@ pub fn createDeclNav( .zir_index = zir_index, }, .status = .unresolved, - .is_usingnamespace = is_usingnamespace, })); return nav; diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 9c0db8387a5adce0ad11c768c1603127c99871ef..08b163eebb23779e87b3ee4e4700a5c6136275cf 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -27,6 +27,22 @@ //! All of this must be done with only referring to the state inside this struct //! because this work will be done in a dedicated thread. +const builtin = @import("builtin"); +const std = @import("std"); +const fs = std.fs; +const assert = std.debug.assert; +const ascii = std.ascii; +const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; +const ThreadPool = std.Thread.Pool; +const WaitGroup = std.Thread.WaitGroup; +const Fetch = @This(); +const git = @import("Fetch/git.zig"); +const Package = @import("../Package.zig"); +const Manifest = Package.Manifest; +const ErrorBundle = std.zig.ErrorBundle; +const native_os = builtin.os.tag; + arena: std.heap.ArenaAllocator, location: Location, location_tok: std.zig.Ast.TokenIndex, @@ -184,7 +200,7 @@ pub const JobQueue = struct { const hash_slice = hash.toSlice(); - try buf.print( + try buf.writer().print( \\ pub const {f} = struct {{ \\ , .{std.zig.fmtId(hash_slice)}); @@ -211,15 +227,15 @@ pub const JobQueue = struct { } try buf.print( - \\ pub const build_root = "{fq}"; + \\ pub const build_root = "{f}"; \\ - , .{fetch.package_root}); + , .{std.fmt.alt(fetch.package_root, .formatEscapeString)}); if (fetch.has_build_zig) { try buf.print( \\ pub const build_zig = @import("{f}"); \\ - , .{std.zig.fmtEscapes(hash_slice)}); + , .{std.zig.fmtString(hash_slice)}); } if (fetch.manifest) |*manifest| { @@ -231,7 +247,7 @@ pub const JobQueue = struct { const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue; try buf.print( " .{{ \"{f}\", \"{f}\" }},\n", - .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) }, + .{ std.zig.fmtEscapes(name), std.zig.fmtString(h.toSlice()) }, ); } @@ -263,7 +279,7 @@ pub const JobQueue = struct { const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue; try buf.print( " .{{ \"{f}\", \"{f}\" }},\n", - .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) }, + .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) }, ); } try buf.appendSlice("};\n"); @@ -420,14 +436,14 @@ pub fn run(f: *Fetch) RunError!void { } if (f.job_queue.read_only) return f.fail( f.name_tok, - try eb.printString("package not found at '{}{s}'", .{ + try eb.printString("package not found at '{f}{s}'", .{ cache_root, pkg_sub_path, }), ); }, else => |e| { try eb.addRootErrorMessage(.{ - .msg = try eb.printString("unable to open global package cache directory '{}{s}': {s}", .{ + .msg = try eb.printString("unable to open global package cache directory '{f}{s}': {s}", .{ cache_root, pkg_sub_path, @errorName(e), }), }); @@ -961,7 +977,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re if (ascii.eqlIgnoreCase(uri.scheme, "file")) { const path = try uri.path.toRawMaybeAlloc(arena); return .{ .file = f.parent_package_root.openFile(path, .{}) catch |err| { - return f.fail(f.location_tok, try eb.printString("unable to open '{}{s}': {s}", .{ + return f.fail(f.location_tok, try eb.printString("unable to open '{f}{s}': {s}", .{ f.parent_package_root, path, @errorName(err), })); } }; @@ -1063,13 +1079,16 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re }); const notes_start = try eb.reserveNotes(notes_len); eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("try .url = \"{;+/}#{}\",", .{ uri, want_oid }), + .msg = try eb.printString("try .url = \"{f}#{f}\",", .{ + uri.fmt(.{ .scheme = true, .authority = true, .path = true }), + want_oid, + }), })); return error.FetchFailed; } var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined; - _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{want_oid}) catch unreachable; + _ = std.fmt.bufPrint(&want_oid_buf, "{f}", .{want_oid}) catch unreachable; var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| { return f.fail(f.location_tok, try eb.printString( "unable to create fetch stream: {s}", @@ -1305,7 +1324,7 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { .{@errorName(err)}, )); if (len == 0) break; - zip_file.writer().writeAll(buf[0..len]) catch |err| return f.fail(f.location_tok, try eb.printString( + zip_file.deprecatedWriter().writeAll(buf[0..len]) catch |err| return f.fail(f.location_tok, try eb.printString( "write temporary zip file failed: {s}", .{@errorName(err)}, )); @@ -1813,28 +1832,6 @@ pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifes } } -const builtin = @import("builtin"); -const std = @import("std"); -const fs = std.fs; -const assert = std.debug.assert; -const ascii = std.ascii; -const Allocator = std.mem.Allocator; -const Cache = std.Build.Cache; -const ThreadPool = std.Thread.Pool; -const WaitGroup = std.Thread.WaitGroup; -const Fetch = @This(); -const git = @import("Fetch/git.zig"); -const Package = @import("../Package.zig"); -const Manifest = Package.Manifest; -const ErrorBundle = std.zig.ErrorBundle; -const native_os = builtin.os.tag; - -test { - _ = Filter; - _ = FileType; - _ = UnpackResult; -} - // Detects executable header: ELF or Macho-O magic header or shebang line. const FileHeader = struct { header: [4]u8 = undefined, @@ -2052,15 +2049,15 @@ const UnpackResult = struct { // output errors to string var errors = try fetch.error_bundle.toOwnedBundle(""); defer errors.deinit(gpa); - var out = std.ArrayList(u8).init(gpa); - defer out.deinit(); - try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer()); + var aw: std.io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer); try std.testing.expectEqualStrings( \\error: unable to unpack \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError \\ note: file 'dir2/file4' has unsupported type 'x' \\ - , out.items); + , aw.getWritten()); } }; @@ -2076,7 +2073,7 @@ test "zip" { { var zip_file = try tmp.dir.createFile("test.zip", .{}); defer zip_file.close(); - var bw = std.io.bufferedWriter(zip_file.writer()); + var bw = std.io.bufferedWriter(zip_file.deprecatedWriter()); var store: [test_files.len]std.zip.testutil.FileStore = undefined; try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{}); try bw.flush(); @@ -2109,7 +2106,7 @@ test "zip with one root folder" { { var zip_file = try tmp.dir.createFile("test.zip", .{}); defer zip_file.close(); - var bw = std.io.bufferedWriter(zip_file.writer()); + var bw = std.io.bufferedWriter(zip_file.deprecatedWriter()); var store: [test_files.len]std.zip.testutil.FileStore = undefined; try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{}); try bw.flush(); @@ -2427,9 +2424,15 @@ const TestFetchBuilder = struct { if (notes_len > 0) { try std.testing.expectEqual(notes_len, em.notes_len); } - var al = std.ArrayList(u8).init(std.testing.allocator); - defer al.deinit(); - try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer()); - try std.testing.expectEqualStrings(msg, al.items); + var aw: std.io.Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); + try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer); + try std.testing.expectEqualStrings(msg, aw.getWritten()); } }; + +test { + _ = Filter; + _ = FileType; + _ = UnpackResult; +} diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index 816d22f724738b9ec45ae4c094037f6e5c97c5af..69cfb3ec663212babfcd1710c9a5604df72d6a24 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -135,9 +135,8 @@ pub const Oid = union(Format) { } else error.InvalidOid; } - pub fn format(oid: Oid, w: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); - try w.print("{x}", .{oid.slice()}); + pub fn format(oid: Oid, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.print("{x}", .{oid.slice()}); } pub fn slice(oid: *const Oid) []const u8 { @@ -697,13 +696,21 @@ pub const Session = struct { fn init(allocator: Allocator, uri: std.Uri) !Location { const scheme = try allocator.dupe(u8, uri.scheme); errdefer allocator.free(scheme); - const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{user}", .{user}) else null; + const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(user, .formatUser), + }) else null; errdefer if (user) |s| allocator.free(s); - const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{password}", .{password}) else null; + const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(password, .formatPassword), + }) else null; errdefer if (password) |s| allocator.free(s); - const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{host}", .{host}) else null; + const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(host, .formatHost), + }) else null; errdefer if (host) |s| allocator.free(s); - const path = try std.fmt.allocPrint(allocator, "{path}", .{uri.path}); + const path = try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(uri.path, .formatPath), + }); errdefer allocator.free(path); // The query and fragment are not used as part of the base server URI. return .{ @@ -734,7 +741,9 @@ pub const Session = struct { fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator { var info_refs_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) }; } @@ -758,7 +767,9 @@ pub const Session = struct { if (request.response.status != .ok) return error.ProtocolError; const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; if (any_redirects_occurred) { - const request_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{request.uri.path}); + const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(request.uri.path, .formatPath), + }); defer session.allocator.free(request_uri_path); if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; var new_uri = request.uri; @@ -845,7 +856,9 @@ pub const Session = struct { pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator { var upload_pack_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; } @@ -962,7 +975,9 @@ pub const Session = struct { ) !FetchStream { var upload_pack_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; } @@ -1058,7 +1073,7 @@ pub const Session = struct { ProtocolError, UnexpectedPacket, }; - pub const Reader = std.io.Reader(*FetchStream, ReadError, read); + pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read); const StreamCode = enum(u8) { pack_data = 1, diff --git a/src/Sema.zig b/src/Sema.zig index 4c279c77c88e000c7158526c57828b4de6b2cd8e..92f6705e4ea31a582a04baef5449aa1a473f1869 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5,6 +5,39 @@ //! Does type checking, comptime control flow, and safety-check generation. //! This is the the heart of the Zig compiler. +const std = @import("std"); +const math = std.math; +const mem = std.mem; +const Allocator = mem.Allocator; +const assert = std.debug.assert; +const log = std.log.scoped(.sema); + +const Sema = @This(); +const Value = @import("Value.zig"); +const MutableValue = @import("mutable_value.zig").MutableValue; +const Type = @import("Type.zig"); +const Air = @import("Air.zig"); +const Zir = std.zig.Zir; +const Zcu = @import("Zcu.zig"); +const trace = @import("tracy.zig").trace; +const Namespace = Zcu.Namespace; +const CompileError = Zcu.CompileError; +const SemaError = Zcu.SemaError; +const LazySrcLoc = Zcu.LazySrcLoc; +const RangeSet = @import("RangeSet.zig"); +const target_util = @import("target.zig"); +const Package = @import("Package.zig"); +const crash_report = @import("crash_report.zig"); +const build_options = @import("build_options"); +const Compilation = @import("Compilation.zig"); +const InternPool = @import("InternPool.zig"); +const Alignment = InternPool.Alignment; +const AnalUnit = InternPool.AnalUnit; +const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; +const Cache = std.Build.Cache; +const LowerZon = @import("Sema/LowerZon.zig"); +const arith = @import("Sema/arith.zig"); + pt: Zcu.PerThread, /// Alias to `zcu.gpa`. gpa: Allocator, @@ -157,39 +190,6 @@ pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc { return &sema.comptime_allocs.items[@intFromEnum(idx)]; } -const std = @import("std"); -const math = std.math; -const mem = std.mem; -const Allocator = mem.Allocator; -const assert = std.debug.assert; -const log = std.log.scoped(.sema); - -const Sema = @This(); -const Value = @import("Value.zig"); -const MutableValue = @import("mutable_value.zig").MutableValue; -const Type = @import("Type.zig"); -const Air = @import("Air.zig"); -const Zir = std.zig.Zir; -const Zcu = @import("Zcu.zig"); -const trace = @import("tracy.zig").trace; -const Namespace = Zcu.Namespace; -const CompileError = Zcu.CompileError; -const SemaError = Zcu.SemaError; -const LazySrcLoc = Zcu.LazySrcLoc; -const RangeSet = @import("RangeSet.zig"); -const target_util = @import("target.zig"); -const Package = @import("Package.zig"); -const crash_report = @import("crash_report.zig"); -const build_options = @import("build_options"); -const Compilation = @import("Compilation.zig"); -const InternPool = @import("InternPool.zig"); -const Alignment = InternPool.Alignment; -const AnalUnit = InternPool.AnalUnit; -const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; -const Cache = std.Build.Cache; -const LowerZon = @import("Sema/LowerZon.zig"); -const arith = @import("Sema/arith.zig"); - pub const default_branch_quota = 1000; pub const InferredErrorSet = struct { @@ -1144,7 +1144,7 @@ fn analyzeBodyInner( // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. if (build_options.enable_logging) { - std.log.scoped(.sema_zir).debug("sema ZIR {} %{d}", .{ path: { + std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: { const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); const file = zcu.fileByIndex(file_index); break :path file.path.fmt(zcu.comp); @@ -1280,7 +1280,6 @@ fn analyzeBodyInner( .tag_name => try sema.zirTagName(block, inst), .type_name => try sema.zirTypeName(block, inst), .frame_type => try sema.zirFrameType(block, inst), - .frame_size => try sema.zirFrameSize(block, inst), .int_from_float => try sema.zirIntFromFloat(block, inst), .float_from_int => try sema.zirFloatFromInt(block, inst), .ptr_from_int => try sema.zirPtrFromInt(block, inst), @@ -1302,7 +1301,6 @@ fn analyzeBodyInner( .mul_add => try sema.zirMulAdd(block, inst), .builtin_call => try sema.zirBuiltinCall(block, inst), .@"resume" => try sema.zirResume(block, inst), - .@"await" => try sema.zirAwait(block, inst), .for_len => try sema.zirForLen(block, inst), .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), @@ -1410,12 +1408,10 @@ fn analyzeBodyInner( .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), .prefetch => try sema.zirPrefetch( block, extended), .error_cast => try sema.zirErrorCast( block, extended), - .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), .select => try sema.zirSelect( block, extended), .int_from_error => try sema.zirIntFromError( block, extended), .error_from_int => try sema.zirErrorFromInt( block, extended), .reify => try sema.zirReify( block, extended, inst), - .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), .cmpxchg => try sema.zirCmpxchg( block, extended), .c_va_arg => try sema.zirCVaArg( block, extended), .c_va_copy => try sema.zirCVaCopy( block, extended), @@ -2767,7 +2763,7 @@ fn zirTupleDecl( const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value }); if (field_init_val.canMutateComptimeVarState(zcu)) { - const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{}", .{field_index}, .no_embedded_nulls); + const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val); } break :init field_init_val.toIntern(); @@ -2864,7 +2860,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us sema.code.nullTerminatedString(str), .no_embedded_nulls, ); - const nav = try sema.lookupIdentifier(block, LazySrcLoc.unneeded, decl_name); // TODO: could we need this src loc? + const nav = try sema.lookupIdentifier(block, decl_name); break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav }); }, .decl_ref => |str| capture: { @@ -2874,7 +2870,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us sema.code.nullTerminatedString(str), .no_embedded_nulls, ); - const nav = try sema.lookupIdentifier(block, LazySrcLoc.unneeded, decl_name); // TODO: could we need this src loc? + const nav = try sema.lookupIdentifier(block, decl_name); break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav }); }, }; @@ -3030,8 +3026,8 @@ pub fn createTypeName( var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; - bw.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; + const w = &aw.writer; + w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; var arg_i: usize = 0; for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { @@ -3044,13 +3040,13 @@ pub fn createTypeName( // result in a compile error. const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat - if (arg_i != 0) bw.writeByte(',') catch return error.OutOfMemory; + if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; // Limiting the depth here helps avoid type names getting too long, which // in turn helps to avoid unreasonably long symbol names for namespaced // symbols. Such names should ideally be human-readable, and additionally, // some tooling may not support very long symbol names. - bw.print("{f}", .{Value.fmtValueSemaFull(.{ + w.print("{f}", .{Value.fmtValueSemaFull(.{ .val = arg_val, .pt = pt, .opt_sema = sema, @@ -3063,7 +3059,7 @@ pub fn createTypeName( else => continue, }; - try bw.writeByte(')'); + w.writeByte(')') catch return error.OutOfMemory; return .{ .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls), .nav = .none, @@ -5578,9 +5574,8 @@ fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp if (operand_ty.arrayLen(zcu) != extra.expect_len) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "expected {} elements for destructure, found {}", .{ - extra.expect_len, - operand_ty.arrayLen(zcu), + const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{ + extra.expect_len, operand_ty.arrayLen(zcu), }); errdefer msg.destroy(sema.gpa); try sema.errNote(destructure_src, msg, "result destructured here", .{}); @@ -5912,26 +5907,25 @@ fn zirCompileLog( var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const writer = &aw.writer; const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); const src_node = extra.data.src_node; const args = sema.code.refSlice(extra.end, extended.small); for (args, 0..) |arg_ref, i| { - if (i != 0) bw.writeAll(", ") catch return error.OutOfMemory; + if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory; const arg = try sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); if (try sema.resolveValueResolveLazy(arg)) |val| { - bw.print("@as({f}, {f})", .{ + writer.print("@as({f}, {f})", .{ arg_ty.fmt(pt), val.fmtValueSema(pt, sema), }) catch return error.OutOfMemory; } else { - bw.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory; + writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory; } } - bw.writeByte('\n') catch return error.OutOfMemory; const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls); @@ -6928,7 +6922,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air inst_data.get(sema.code), .no_embedded_nulls, ); - const nav_index = try sema.lookupIdentifier(block, src, decl_name); + const nav_index = try sema.lookupIdentifier(block, decl_name); return sema.analyzeNavRef(block, src, nav_index); } @@ -6943,16 +6937,16 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air inst_data.get(sema.code), .no_embedded_nulls, ); - const nav = try sema.lookupIdentifier(block, src, decl_name); + const nav = try sema.lookupIdentifier(block, decl_name); return sema.analyzeNavVal(block, src, nav); } -fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { +fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { const pt = sema.pt; const zcu = pt.zcu; var namespace = block.namespace; while (true) { - if (try sema.lookupInNamespace(block, src, namespace, name, false)) |lookup| { + if (try sema.lookupInNamespace(block, namespace, name)) |lookup| { assert(lookup.accessible); return lookup.nav; } @@ -6961,15 +6955,12 @@ fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPoo unreachable; // AstGen detects use of undeclared identifiers. } -/// This looks up a member of a specific namespace. It is affected by `usingnamespace` but -/// only for ones in the specified namespace. +/// This looks up a member of a specific namespace. fn lookupInNamespace( sema: *Sema, block: *Block, - src: LazySrcLoc, namespace_index: InternPool.NamespaceIndex, ident_name: InternPool.NullTerminatedString, - observe_usingnamespace: bool, ) CompileError!?struct { nav: InternPool.Nav.Index, /// If `false`, the declaration is in a different file and is not `pub`. @@ -6978,7 +6969,6 @@ fn lookupInNamespace( } { const pt = sema.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; try pt.ensureNamespaceUpToDate(namespace_index); @@ -6995,75 +6985,7 @@ fn lookupInNamespace( } }); } - if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) { - const gpa = sema.gpa; - var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .empty; - defer checked_namespaces.deinit(gpa); - - // Keep track of name conflicts for error notes. - var candidates: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty; - defer candidates.deinit(gpa); - - try checked_namespaces.put(gpa, namespace, {}); - var check_i: usize = 0; - - while (check_i < checked_namespaces.count()) : (check_i += 1) { - const check_ns = checked_namespaces.keys()[check_i]; - const Pass = enum { @"pub", priv }; - for ([2]Pass{ .@"pub", .priv }) |pass| { - if (pass == .priv and src_file != check_ns.file_scope) { - continue; - } - - const decls, const usingnamespaces = switch (pass) { - .@"pub" => .{ &check_ns.pub_decls, &check_ns.pub_usingnamespace }, - .priv => .{ &check_ns.priv_decls, &check_ns.priv_usingnamespace }, - }; - - if (decls.getKeyAdapted(ident_name, adapter)) |nav_index| { - try candidates.append(gpa, nav_index); - } - - for (usingnamespaces.items) |sub_ns_nav| { - try sema.ensureNavResolved(block, src, sub_ns_nav, .fully); - const sub_ns_ty: Type = .fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val); - const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu)); - try checked_namespaces.put(gpa, sub_ns, {}); - } - } - } - - ignore_self: { - const skip_nav = switch (sema.owner.unwrap()) { - .@"comptime", .type, .func, .memoized_state => break :ignore_self, - .nav_ty, .nav_val => |nav| nav, - }; - var i: usize = 0; - while (i < candidates.items.len) { - if (candidates.items[i] == skip_nav) { - _ = candidates.orderedRemove(i); - } else { - i += 1; - } - } - } - - switch (candidates.items.len) { - 0 => {}, - 1 => return .{ - .nav = candidates.items[0], - .accessible = true, - }, - else => return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "ambiguous reference", .{}); - errdefer msg.destroy(gpa); - for (candidates.items) |candidate| { - try sema.errNote(zcu.navSrcLoc(candidate), msg, "declared here", .{}); - } - break :msg msg; - }), - } - } else if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { + if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { return .{ .nav = nav_index, .accessible = true, @@ -7652,10 +7574,6 @@ fn analyzeCall( const ip = &zcu.intern_pool; const arena = sema.arena; - if (modifier == .async_kw) { - return sema.failWithUseOfAsync(block, call_src); - } - const maybe_func_inst = try sema.funcDeclSrcInst(callee); const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ .base_node_inst = fn_decl_inst, @@ -8047,14 +7965,13 @@ fn analyzeCall( } const call_tag: Air.Inst.Tag = switch (modifier) { - .auto, .no_async => .call, + .auto, .no_suspend => .call, .never_tail => .call_never_tail, .never_inline => .call_never_inline, .always_tail => .call_always_tail, .always_inline, .compile_time, - .async_kw, => unreachable, }; @@ -9417,14 +9334,6 @@ fn resolveGenericBody( return sema.resolveConstDefinedValue(block, src, result, reason); } -/// Given a library name, examines if the library name should end up in -/// `link.File.Options.windows_libs` table (for example, libc is always -/// specified via dedicated flag `link_libc` instead), -/// and puts it there if it doesn't exist. -/// It also dupes the library name which can then be saved as part of the -/// respective `Decl` (either `ExternFn` or `Var`). -/// The liveness of the duped library name is tied to liveness of `Zcu`. -/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). pub fn handleExternLibName( sema: *Sema, block: *Block, @@ -9474,11 +9383,6 @@ pub fn handleExternLibName( .{ lib_name, lib_name }, ); } - comp.addLinkLib(lib_name) catch |err| { - return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ - lib_name, @errorName(err), - }); - }; } } @@ -9543,18 +9447,17 @@ fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool { fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void { const CallingConventionsSupportingVarArgsList = struct { arch: std.Target.Cpu.Arch, - pub fn format(ctx: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); + pub fn format(ctx: @This(), w: *std.io.Writer) std.io.Writer.Error!void { var first = true; for (calling_conventions_supporting_var_args) |cc_inner| { for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| { if (supported_arch == ctx.arch) break; } else continue; // callconv not supported by this arch if (!first) { - try bw.writeAll(", "); + try w.writeAll(", "); } first = false; - try bw.print("'{s}'", .{@tagName(cc_inner)}); + try w.print("'{s}'", .{@tagName(cc_inner)}); } } }; @@ -9989,12 +9892,11 @@ fn finishFunc( .bad_arch => |allowed_archs| { const ArchListFormatter = struct { archs: []const std.Target.Cpu.Arch, - pub fn format(formatter: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); + pub fn format(formatter: @This(), w: *std.io.Writer) std.io.Writer.Error!void { for (formatter.archs, 0..) |arch, i| { if (i != 0) - try bw.writeAll(", "); - try bw.print("'{s}'", .{@tagName(arch)}); + try w.writeAll(", "); + try w.print("'{s}'", .{@tagName(arch)}); } } }; @@ -13965,7 +13867,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const src = block.nodeOffset(inst_data.src_node); const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 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 try sema.checkNamespaceType(block, lhs_src, container_type); const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false; - if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| { + if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { if (lookup.accessible) { return .bool_true; } @@ -14173,7 +14074,7 @@ fn zirShl( }); } } else if (scalar_rhs_ty.isSignedInt(zcu)) { - return sema.fail(block, rhs_src, "shift by signed type '{}'", .{rhs_ty.fmt(pt)}); + return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)}); } 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. const scalar_tag = scalar_ty.zigTypeTag(zcu); if (scalar_tag != .int and scalar_tag != .bool) - return sema.fail(block, operand_src, "bitwise not operation on type '{}'", .{operand_ty.fmt(pt)}); + return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)}); return analyzeBitNot(sema, block, operand, src); } @@ -17094,7 +16995,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; const tree = file.getTree(zcu) catch |err| { // In this case we emit a warning + a less precise source location. - log.warn("unable to load {}: {s}", .{ + log.warn("unable to load {f}: {s}", .{ file.path.fmt(zcu.comp), @errorName(err), }); break :name null; @@ -17122,7 +17023,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; const tree = file.getTree(zcu) catch |err| { // In this case we emit a warning + a less precise source location. - log.warn("unable to load {}: {s}", .{ + log.warn("unable to load {f}: {s}", .{ file.path.fmt(zcu.comp), @errorName(err), }); break :name null; @@ -17755,7 +17656,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, ip.loadEnumType(ty.toIntern()).namespace.toOptional()); + const decls_val = try sema.typeInfoDecls(src, ip.loadEnumType(ty.toIntern()).namespace.toOptional()); const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum"); @@ -17868,7 +17769,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespaceIndex(zcu).toOptional()); + const decls_val = try sema.typeInfoDecls(src, ty.getNamespaceIndex(zcu).toOptional()); const enum_tag_ty_val = try pt.intern(.{ .opt = .{ .ty = (try pt.optionalType(.type_type)).toIntern(), @@ -18063,7 +17964,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu)); + const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); const backing_integer_val = try pt.intern(.{ .opt = .{ .ty = (try pt.optionalType(.type_type)).toIntern(), @@ -18102,7 +18003,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque"); try ty.resolveFields(pt); - const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu)); + const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); const field_values = .{ // decls: []const Declaration, @@ -18124,7 +18025,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn typeInfoDecls( sema: *Sema, - block: *Block, src: LazySrcLoc, opt_namespace: InternPool.OptionalNamespaceIndex, ) CompileError!InternPool.Index { @@ -18140,7 +18040,7 @@ fn typeInfoDecls( var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); defer seen_namespaces.deinit(); - try sema.typeInfoNamespaceDecls(block, src, opt_namespace, declaration_ty, &decl_vals, &seen_namespaces); + try sema.typeInfoNamespaceDecls(opt_namespace, declaration_ty, &decl_vals, &seen_namespaces); const array_decl_ty = try pt.arrayType(.{ .len = decl_vals.items.len, @@ -18174,8 +18074,6 @@ fn typeInfoDecls( fn typeInfoNamespaceDecls( sema: *Sema, - block: *Block, - src: LazySrcLoc, opt_namespace_index: InternPool.OptionalNamespaceIndex, declaration_ty: Type, decl_vals: *std.ArrayList(InternPool.Index), @@ -18231,15 +18129,6 @@ fn typeInfoNamespaceDecls( .storage = .{ .elems = &fields }, } })); } - - for (namespace.pub_usingnamespace.items) |nav| { - if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) { - continue; - } - try sema.ensureNavResolved(block, src, nav, .fully); - const namespace_ty: Type = .fromInterned(ip.getNav(nav).status.fully_resolved.val); - try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces); - } } 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 const uncasted_ty = sema.typeOf(uncasted_operand); if (uncasted_ty.isVector(zcu)) { if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) { - return sema.fail(block, operand_src, "boolean not operation on type '{}'", .{ + return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{ uncasted_ty.fmt(pt), }); } @@ -19406,13 +19295,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air if (host_size != 0) { if (bit_offset >= host_size * 8) { - return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {} starts {} bits after the end of a {} byte host integer", .{ + 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", .{ elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, }); } const elem_bit_size = try elem_ty.bitSizeSema(pt); if (elem_bit_size > host_size * 8 - bit_offset) { - return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {} ends {} bits after the end of a {} byte host integer", .{ + 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", .{ elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size, }); } @@ -20573,7 +20462,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const is_vector = operand_ty.zigTypeTag(zcu) == .vector; const operand_scalar_ty = operand_ty.scalarType(zcu); if (operand_scalar_ty.toIntern() != .bool_type) { - return sema.fail(block, src, "expected 'bool', found '{}'", .{operand_scalar_ty.zigTypeTag(zcu)}); + return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)}); } const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1; @@ -20856,7 +20745,7 @@ fn zirReify( 64 => .f64, 80 => .f80, 128 => .f128, - else => return sema.fail(block, src, "{}-bit float unsupported", .{float.bits}), + else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}), }; return Air.internedToRef(ty.toIntern()); }, @@ -21747,7 +21636,7 @@ fn reifyTuple( return sema.fail( block, src, - "tuple field name '{}' does not match field index {}", + "tuple field name '{d}' does not match field index {d}", .{ field_name_index, field_idx }, ); } @@ -22143,12 +22032,6 @@ fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.failWithUseOfAsync(block, src); } -fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const src = block.nodeOffset(inst_data.src_node); - return sema.failWithUseOfAsync(block, src); -} - fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; @@ -22771,7 +22654,7 @@ fn ptrCastFull( if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{ + const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{ src_info.packed_offset.host_size, dest_info.packed_offset.host_size, }); @@ -22783,7 +22666,7 @@ fn ptrCastFull( if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{ + const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{ src_info.packed_offset.bit_offset, dest_info.packed_offset.bit_offset, }); @@ -23353,7 +23236,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fail( block, operand_src, - "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {} bits", + "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits", .{ scalar_ty.fmt(pt), bits }, ); } @@ -23690,7 +23573,7 @@ fn checkNumericType( .comptime_float, .float, .comptime_int, .int => {}, .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { .comptime_float, .float, .comptime_int, .int => {}, - else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), + else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}), }, else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}), } @@ -24367,7 +24250,7 @@ fn analyzeShuffle( if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); errdefer msg.destroy(sema.gpa); - try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{}' given here", .{ idx, b_ty.fmt(pt) }); + try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) }); break :msg msg; }); } @@ -24795,14 +24678,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier); switch (modifier) { // These can be upgraded to comptime or nosuspend calls. - .auto, .never_tail, .no_async => { + .auto, .never_tail, .no_suspend => { if (block.isComptime()) { if (modifier == .never_tail) { return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); } modifier = .compile_time; } else if (extra.flags.is_nosuspend) { - modifier = .no_async; + modifier = .no_suspend; } }, // 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 modifier = .compile_time; } }, - .async_kw => { - if (extra.flags.is_nosuspend) { - return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); - } - if (block.isComptime()) { - return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{}); - } - }, .never_inline => { if (block.isComptime()) { return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); @@ -25160,7 +25035,7 @@ fn analyzeMinMax( try sema.checkNumericType(block, operand_src, operand_ty); if (operand_ty.zigTypeTag(zcu) != .vector) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(operand_src, "expected vector, found '{}'", .{operand_ty.fmt(pt)}); + const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); errdefer msg.destroy(zcu.gpa); try sema.errNote(operand_srcs[0], msg, "vector operand here", .{}); break :msg msg; @@ -25168,7 +25043,7 @@ fn analyzeMinMax( } if (operand_ty.vectorLen(zcu) != vec_len) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{}'", .{ vec_len, operand_ty.fmt(pt) }); + const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) }); errdefer msg.destroy(zcu.gpa); try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len}); break :msg msg; @@ -25181,7 +25056,7 @@ fn analyzeMinMax( const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag(zcu) == .vector) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{}'", .{first_operand_ty.fmt(pt)}); + const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)}); errdefer msg.destroy(zcu.gpa); try sema.errNote(operand_src, msg, "vector operand here", .{}); break :msg msg; @@ -25816,40 +25691,12 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void }); } -fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { - const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; - const src = block.nodeOffset(extra.node); - return sema.failWithUseOfAsync(block, src); -} - fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); return sema.failWithUseOfAsync(block, src); } -fn zirAwait( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const src = block.nodeOffset(inst_data.src_node); - - return sema.failWithUseOfAsync(block, src); -} - -fn zirAwaitNosuspend( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, -) CompileError!Air.Inst.Ref { - const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; - const src = block.nodeOffset(extra.node); - - return sema.failWithUseOfAsync(block, src); -} - fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -26757,7 +26604,7 @@ fn explainWhyTypeIsNotExtern( } switch (ty.fnCallingConvention(zcu)) { .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}), - .@"async" => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}), + .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}), .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}), else => return, } @@ -27779,7 +27626,7 @@ fn namespaceLookup( const pt = sema.pt; const zcu = pt.zcu; const gpa = sema.gpa; - if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| { + if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { if (!lookup.accessible) { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{ @@ -29312,7 +29159,7 @@ fn coerceExtra( // return sema.fail( // block, // inst_src, - // "type '{f}' cannot represent integer value '{}'", + // "type '{f}' cannot represent integer value '{f}'", // .{ dest_ty.fmt(pt), val }, // ); //} @@ -29519,7 +29366,7 @@ fn coerceExtra( try sema.errNote(param_src, msg, "parameter type declared here", .{}); } - // TODO maybe add "cannot store an error in type '{}'" note + // TODO maybe add "cannot store an error in type '{f}'" note break :msg msg; }; @@ -29867,12 +29714,12 @@ const InMemoryCoercionResult = union(enum) { }, .ptr_bit_range => |bit_range| { if (bit_range.actual_host != bit_range.wanted_host) { - try sema.errNote(src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{ + try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{ bit_range.actual_host, bit_range.wanted_host, }); } if (bit_range.actual_offset != bit_range.wanted_offset) { - try sema.errNote(src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{ + try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{ bit_range.actual_offset, bit_range.wanted_offset, }); } @@ -34989,7 +34836,7 @@ fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ return sema.fail( block, src, - "backing integer type '{f}' has bit size {} but the struct fields have a total bit size of {}", + "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}", .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum }, ); } @@ -35332,11 +35179,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load switch (union_type.flagsUnordered(ip).status) { .none => {}, .field_types_wip => { - const msg = try sema.errMsg( - ty.srcLoc(zcu), - "union '{f}' depends on itself", - .{ty.fmt(pt)}, - ); + const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)}); return sema.failWithOwnedErrorMsg(null, msg); }, .have_field_types, @@ -37330,7 +37173,14 @@ fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg } } -fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, val: Value, intermediate_value_count: u32, start_value_name: InternPool.NullTerminatedString) Allocator.Error!union(enum) { +fn notePathToComptimeAllocPtr( + sema: *Sema, + msg: *Zcu.ErrorMsg, + src: LazySrcLoc, + val: Value, + intermediate_value_count: u32, + start_value_name: InternPool.NullTerminatedString, +) Allocator.Error!union(enum) { done, new_val: Value, } { @@ -37341,9 +37191,9 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, var first_path: std.ArrayListUnmanaged(u8) = .empty; if (intermediate_value_count == 0) { - try first_path.print(arena, "{fi}", .{start_value_name.fmt(ip)}); + try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)}); } else { - try first_path.print(arena, "v{}", .{intermediate_value_count - 1}); + try first_path.print(arena, "v{d}", .{intermediate_value_count - 1}); } const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path); @@ -37373,7 +37223,7 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count}); const deriv_start = @import("print_value.zig").printPtrDerivation( derivation, - &second_path_aw.interface, + &second_path_aw.writer, pt, .lvalue, .{ .str = inter_name }, @@ -37437,7 +37287,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList const backing_enum = union_ty.unionTagTypeHypothetical(zcu); const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?; const field_name = backing_enum.enumFieldName(field_idx, zcu); - try path.print(arena, ".{fi}", .{field_name.fmt(ip)}); + try path.print(arena, ".{f}", .{field_name.fmt(ip)}); return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path); }, .aggregate => |agg| { @@ -37462,7 +37312,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList try path.print(arena, "[{d}]", .{elem_idx}); } else { const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?; - try path.print(arena, ".{fi}", .{name.fmt(ip)}); + try path.print(arena, ".{f}", .{name.fmt(ip)}); }, else => unreachable, } diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index cdbec434052d07c6a74ceb7ca387f3d9d0185e79..8c70a5b7845432c34a430600daf294059cc43939 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -360,11 +360,7 @@ fn fail( fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index { const pt = self.sema.pt; return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) { - error.WrongType => return self.fail( - node, - "expected type '{f}'", - .{res_ty.fmt(pt)}, - ), + error.WrongType => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(pt)}), else => |e| return e, }; } @@ -458,7 +454,7 @@ fn lowerInt( // If lhs is unsigned and rhs is less than 0, we're out of bounds if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail( node, - "type '{f}' cannot represent integer value '{}'", + "type '{f}' cannot represent integer value '{d}'", .{ res_ty.fmt(self.sema.pt), rhs }, ); @@ -478,7 +474,7 @@ fn lowerInt( if (rhs < min_int or rhs > max_int) { return self.fail( node, - "type '{f}' cannot represent integer value '{}'", + "type '{f}' cannot represent integer value '{d}'", .{ res_ty.fmt(self.sema.pt), rhs }, ); } @@ -496,7 +492,7 @@ fn lowerInt( if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) { return self.fail( node, - "type '{f}' cannot represent integer value '{f}'", + "type '{f}' cannot represent integer value '{d}'", .{ res_ty.fmt(self.sema.pt), val }, ); } @@ -517,7 +513,7 @@ fn lowerInt( switch (big_int.setFloat(val, .trunc)) { .inexact => return self.fail( node, - "fractional component prevents float value '{}' from coercion to type '{f}'", + "fractional component prevents float value '{d}' from coercion to type '{f}'", .{ val, res_ty.fmt(self.sema.pt) }, ), .exact => {}, @@ -528,8 +524,8 @@ fn lowerInt( if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { return self.fail( node, - "type '{}' cannot represent integer value '{f}'", - .{ val, res_ty.fmt(self.sema.pt) }, + "type '{f}' cannot represent integer value '{d}'", + .{ res_ty.fmt(self.sema.pt), val }, ); } @@ -550,7 +546,7 @@ fn lowerInt( if (val >= out_of_range) { return self.fail( node, - "type '{f}' cannot represent integer value '{}'", + "type '{f}' cannot represent integer value '{d}'", .{ res_ty.fmt(self.sema.pt), val }, ); } diff --git a/src/Type.zig b/src/Type.zig index d623e5c700f463ae29c2f90f0bf2899844eb2830..199690fcac01f4b8bbd0e8752a0dda384beee11f 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -122,14 +122,13 @@ pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool { return a.toIntern() == b.toIntern(); } -pub fn format(ty: Type, bw: *Writer, comptime f: []const u8) !usize { +pub fn format(ty: Type, writer: *std.io.Writer) !void { _ = ty; - _ = f; - _ = bw; + _ = writer; @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); } -pub const Formatter = std.fmt.Formatter(format2); +pub const Formatter = std.fmt.Formatter(Format, Format.default); pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter { return .{ .data = .{ @@ -138,30 +137,28 @@ pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter { } }; } -const FormatContext = struct { +const Format = struct { ty: Type, pt: Zcu.PerThread, + + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + return print(f.ty, writer, f.pt); + } }; -fn format2(ctx: FormatContext, bw: *Writer, comptime f: []const u8) !void { - comptime assert(f.len == 0); - try print(ctx.ty, bw, ctx.pt); -} - -pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { +pub fn fmtDebug(ty: Type) std.fmt.Formatter(Type, dump) { return .{ .data = ty }; } /// This is a debug function. In order to print types in a meaningful way /// we also need access to the module. -pub fn dump(start_type: Type, bw: *Writer, comptime unused_format_string: []const u8) !void { - comptime assert(unused_format_string.len == 0); - return bw.print("{any}", .{start_type.ip_index}); +pub fn dump(start_type: Type, writer: *std.io.Writer) std.io.Writer.Error!void { + return writer.print("{any}", .{start_type.ip_index}); } /// Prints a name suitable for `@typeName`. /// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels. -pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { +pub fn print(ty: Type, writer: *std.io.Writer, pt: Zcu.PerThread) std.io.Writer.Error!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; switch (ip.indexToKey(ty.toIntern())) { @@ -171,22 +168,22 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { .signed => 'i', .unsigned => 'u', }; - try bw.print("{c}{d}", .{ sign_char, int_type.bits }); + try writer.print("{c}{d}", .{ sign_char, int_type.bits }); }, .ptr_type => { const info = ty.ptrInfo(zcu); if (info.sentinel != .none) switch (info.flags.size) { .one, .c => unreachable, - .many => try bw.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), - .slice => try bw.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), + .many => try writer.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), + .slice => try writer.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), } else switch (info.flags.size) { - .one => try bw.writeAll("*"), - .many => try bw.writeAll("[*]"), - .c => try bw.writeAll("[*c]"), - .slice => try bw.writeAll("[]"), + .one => try writer.writeAll("*"), + .many => try writer.writeAll("[*]"), + .c => try writer.writeAll("[*c]"), + .slice => try writer.writeAll("[]"), } - if (info.flags.is_allowzero and info.flags.size != .c) try bw.writeAll("allowzero "); + if (info.flags.is_allowzero and info.flags.size != .c) try writer.writeAll("allowzero "); if (info.flags.alignment != .none or info.packed_offset.host_size != 0 or info.flags.vector_index != .none) @@ -195,72 +192,72 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { info.flags.alignment else Type.fromInterned(info.child).abiAlignment(pt.zcu); - try bw.print("align({d}", .{alignment.toByteUnits() orelse 0}); + try writer.print("align({d}", .{alignment.toByteUnits() orelse 0}); if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) { - try bw.print(":{d}:{d}", .{ + try writer.print(":{d}:{d}", .{ info.packed_offset.bit_offset, info.packed_offset.host_size, }); } if (info.flags.vector_index == .runtime) { - try bw.writeAll(":?"); + try writer.writeAll(":?"); } else if (info.flags.vector_index != .none) { - try bw.print(":{d}", .{@intFromEnum(info.flags.vector_index)}); + try writer.print(":{d}", .{@intFromEnum(info.flags.vector_index)}); } - try bw.writeAll(") "); + try writer.writeAll(") "); } if (info.flags.address_space != .generic) { - try bw.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)}); + try writer.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)}); } - if (info.flags.is_const) try bw.writeAll("const "); - if (info.flags.is_volatile) try bw.writeAll("volatile "); + if (info.flags.is_const) try writer.writeAll("const "); + if (info.flags.is_volatile) try writer.writeAll("volatile "); - try print(Type.fromInterned(info.child), bw, pt); + try print(Type.fromInterned(info.child), writer, pt); }, .array_type => |array_type| { if (array_type.sentinel == .none) { - try bw.print("[{d}]", .{array_type.len}); - try print(Type.fromInterned(array_type.child), bw, pt); + try writer.print("[{d}]", .{array_type.len}); + try print(Type.fromInterned(array_type.child), writer, pt); } else { - try bw.print("[{d}:{f}]", .{ + try writer.print("[{d}:{f}]", .{ array_type.len, Value.fromInterned(array_type.sentinel).fmtValue(pt), }); - try print(Type.fromInterned(array_type.child), bw, pt); + try print(Type.fromInterned(array_type.child), writer, pt); } }, .vector_type => |vector_type| { - try bw.print("@Vector({d}, ", .{vector_type.len}); - try print(Type.fromInterned(vector_type.child), bw, pt); - try bw.writeAll(")"); + try writer.print("@Vector({d}, ", .{vector_type.len}); + try print(Type.fromInterned(vector_type.child), writer, pt); + try writer.writeAll(")"); }, .opt_type => |child| { - try bw.writeByte('?'); - try print(Type.fromInterned(child), bw, pt); + try writer.writeByte('?'); + try print(Type.fromInterned(child), writer, pt); }, .error_union_type => |error_union_type| { - try print(Type.fromInterned(error_union_type.error_set_type), bw, pt); - try bw.writeByte('!'); + try print(Type.fromInterned(error_union_type.error_set_type), writer, pt); + try writer.writeByte('!'); if (error_union_type.payload_type == .generic_poison_type) { - try bw.writeAll("anytype"); + try writer.writeAll("anytype"); } else { - try print(Type.fromInterned(error_union_type.payload_type), bw, pt); + try print(Type.fromInterned(error_union_type.payload_type), writer, pt); } }, .inferred_error_set_type => |func_index| { const func_nav = ip.getNav(zcu.funcInfo(func_index).owner_nav); - return bw.print("@typeInfo(@typeInfo(@TypeOf({f})).@\"fn\".return_type.?).error_union.error_set", .{ + try writer.print("@typeInfo(@typeInfo(@TypeOf({f})).@\"fn\".return_type.?).error_union.error_set", .{ func_nav.fqn.fmt(ip), }); }, .error_set_type => |error_set_type| { const names = error_set_type.names; - try bw.writeAll("error{"); + try writer.writeAll("error{"); for (names.get(ip), 0..) |name, i| { - if (i != 0) try bw.writeByte(','); - try bw.print("{f}", .{name.fmt(ip)}); + if (i != 0) try writer.writeByte(','); + try writer.print("{f}", .{name.fmt(ip)}); } - try bw.writeAll("}"); + try writer.writeAll("}"); }, .simple_type => |s| switch (s) { .f16, @@ -289,97 +286,99 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { .comptime_float, .noreturn, .adhoc_inferred_error_set, - => return bw.writeAll(@tagName(s)), + => return writer.writeAll(@tagName(s)), .null, .undefined, - => return bw.print("@TypeOf({s})", .{@tagName(s)}), + => return writer.print("@TypeOf({s})", .{@tagName(s)}), - .enum_literal => return bw.writeAll("@Type(.enum_literal)"), + .enum_literal => return writer.writeAll("@Type(.enum_literal)"), .generic_poison => unreachable, }, .struct_type => { const name = ip.loadStructType(ty.toIntern()).name; - return bw.print("{f}", .{name.fmt(ip)}); + try writer.print("{f}", .{name.fmt(ip)}); }, .tuple_type => |tuple| { if (tuple.types.len == 0) { - return bw.writeAll("@TypeOf(.{})"); + return writer.writeAll("@TypeOf(.{})"); } - try bw.writeAll("struct {"); + try writer.writeAll("struct {"); for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| { - try bw.writeAll(if (i == 0) " " else ", "); - if (val != .none) try bw.writeAll("comptime "); - try print(Type.fromInterned(field_ty), bw, pt); - if (val != .none) try bw.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)}); + try writer.writeAll(if (i == 0) " " else ", "); + if (val != .none) try writer.writeAll("comptime "); + try print(Type.fromInterned(field_ty), writer, pt); + if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)}); } - try bw.writeAll(" }"); + try writer.writeAll(" }"); }, .union_type => { const name = ip.loadUnionType(ty.toIntern()).name; - return bw.print("{f}", .{name.fmt(ip)}); + try writer.print("{f}", .{name.fmt(ip)}); }, .opaque_type => { const name = ip.loadOpaqueType(ty.toIntern()).name; - return bw.print("{f}", .{name.fmt(ip)}); + try writer.print("{f}", .{name.fmt(ip)}); }, .enum_type => { const name = ip.loadEnumType(ty.toIntern()).name; - return bw.print("{f}", .{name.fmt(ip)}); + try writer.print("{f}", .{name.fmt(ip)}); }, .func_type => |fn_info| { if (fn_info.is_noinline) { - try bw.writeAll("noinline "); + try writer.writeAll("noinline "); } - try bw.writeAll("fn ("); + try writer.writeAll("fn ("); const param_types = fn_info.param_types.get(&zcu.intern_pool); for (param_types, 0..) |param_ty, i| { - if (i != 0) try bw.writeAll(", "); + if (i != 0) try writer.writeAll(", "); if (std.math.cast(u5, i)) |index| { if (fn_info.paramIsComptime(index)) { - try bw.writeAll("comptime "); + try writer.writeAll("comptime "); } if (fn_info.paramIsNoalias(index)) { - try bw.writeAll("noalias "); + try writer.writeAll("noalias "); } } if (param_ty == .generic_poison_type) { - try bw.writeAll("anytype"); + try writer.writeAll("anytype"); } else { - try print(Type.fromInterned(param_ty), bw, pt); + try print(Type.fromInterned(param_ty), writer, pt); } } if (fn_info.is_var_args) { if (param_types.len != 0) { - try bw.writeAll(", "); + try writer.writeAll(", "); } - try bw.writeAll("..."); + try writer.writeAll("..."); } - try bw.writeAll(") "); + try writer.writeAll(") "); if (fn_info.cc != .auto) print_cc: { if (zcu.getTarget().cCallingConvention()) |ccc| { if (fn_info.cc.eql(ccc)) { - try bw.writeAll("callconv(.c) "); + try writer.writeAll("callconv(.c) "); break :print_cc; } } switch (fn_info.cc) { - .auto, .@"async", .naked, .@"inline" => try bw.print("callconv(.{f}) ", .{std.zig.fmtId(@tagName(fn_info.cc))}), - else => try bw.print("callconv({any}) ", .{fn_info.cc}), + .auto, .async, .naked, .@"inline" => try writer.print("callconv(.{f}) ", .{ + std.zig.fmtId(@tagName(fn_info.cc)), + }), + else => try writer.print("callconv({any}) ", .{fn_info.cc}), } } if (fn_info.return_type == .generic_poison_type) { - try bw.writeAll("anytype"); + try writer.writeAll("anytype"); } else { - try print(Type.fromInterned(fn_info.return_type), bw, pt); + try print(Type.fromInterned(fn_info.return_type), writer, pt); } }, .anyframe_type => |child| { - if (child == .none) return bw.writeAll("anyframe"); - try bw.writeAll("anyframe->"); - try print(Type.fromInterned(child), bw, pt); + if (child == .none) return writer.writeAll("anyframe"); + try writer.writeAll("anyframe->"); + try print(Type.fromInterned(child), writer, pt); }, // values, not types diff --git a/src/Value.zig b/src/Value.zig index 96206b6c2608922d53a91a67aa8488cc83185170..aed97c87548b28206ea9ddbb2356bec46498e922 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -15,31 +15,23 @@ const Value = @This(); ip_index: InternPool.Index, -pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { +pub fn format(val: Value, writer: *std.io.Writer) !void { _ = val; - _ = fmt; - _ = options; _ = writer; @compileError("do not use format values directly; use either fmtDebug or fmtValue"); } /// This is a debug function. In order to print values in a meaningful way /// we also need access to the type. -pub fn dump( - start_val: Value, - comptime fmt: []const u8, - _: std.fmt.FormatOptions, - out_stream: anytype, -) !void { - comptime assert(fmt.len == 0); - try out_stream.print("(interned: {})", .{start_val.toIntern()}); +pub fn dump(start_val: Value, w: std.io.Writer) std.io.Writer.Error!void { + try w.print("(interned: {})", .{start_val.toIntern()}); } -pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) { +pub fn fmtDebug(val: Value) std.fmt.Formatter(Value, dump) { return .{ .data = val }; } -pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.format) { +pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.FormatContext, print_value.format) { return .{ .data = .{ .val = val, .pt = pt, @@ -48,7 +40,7 @@ pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.for } }; } -pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatter(print_value.formatSema) { +pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatter(print_value.FormatContext, print_value.formatSema) { return .{ .data = .{ .val = val, .pt = pt, @@ -57,7 +49,7 @@ pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatte } }; } -pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Formatter(print_value.formatSema) { +pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Formatter(print_value.FormatContext, print_value.formatSema) { return .{ .data = ctx }; } diff --git a/src/Zcu.zig b/src/Zcu.zig index 6aff0eb1052e5ca9b2eb297b09969149f30582da..26ee09cfbfad34e895f120ac56609ae731fa0e03 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -793,10 +793,6 @@ pub const Namespace = struct { pub_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty, /// Members of the namespace which are *not* marked `pub`. priv_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty, - /// All `usingnamespace` declarations in this namespace which are marked `pub`. - pub_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty, - /// All `usingnamespace` declarations in this namespace which are *not* marked `pub`. - priv_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty, /// All `comptime` declarations in this namespace. We store these purely so that incremental /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes. comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty, @@ -1116,7 +1112,7 @@ pub const File = struct { eb: *std.zig.ErrorBundle.Wip, ) !std.zig.ErrorBundle.SourceLocationIndex { return eb.addSourceLocation(.{ - .src_path = try eb.printString("{}", .{file.path.fmt(zcu.comp)}), + .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}), .span_start = 0, .span_main = 0, .span_end = 0, @@ -1137,7 +1133,7 @@ pub const File = struct { const end = start + tree.tokenSlice(tok).len; const loc = std.zig.findLineColumn(source.bytes, start); return eb.addSourceLocation(.{ - .src_path = try eb.printString("{}", .{file.path.fmt(zcu.comp)}), + .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}), .span_start = start, .span_main = start, .span_end = @intCast(end), @@ -1298,9 +1294,6 @@ pub const SrcLoc = struct { .simple_var_decl, .aligned_var_decl, => tree.fullVarDecl(node).?, - .@"usingnamespace" => { - return tree.nodeToSpan(tree.nodeData(node).node); - }, else => unreachable, }; if (full.ast.type_node.unwrap()) |type_node| { @@ -1438,12 +1431,8 @@ pub const SrcLoc = struct { .field_access => tree.nodeData(node).node_and_token[1], .call_one, .call_one_comma, - .async_call_one, - .async_call_one_comma, .call, .call_comma, - .async_call, - .async_call_comma, => blk: { const full = tree.fullCall(&buf, node).?; break :blk tree.lastToken(full.ast.fn_expr); @@ -3306,9 +3295,6 @@ pub fn mapOldZirToNew( // All comptime declarations, in order, for a best-effort match. var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; defer comptime_decls.deinit(gpa); - // All usingnamespace declarations, in order, for a best-effort match. - var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; - defer usingnamespace_decls.deinit(gpa); { var old_decl_it = old_zir.declIterator(match_item.old_inst); @@ -3316,7 +3302,6 @@ pub fn mapOldZirToNew( const old_decl = old_zir.getDeclaration(old_decl_inst); switch (old_decl.kind) { .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), - .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst), .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst), .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), @@ -3327,7 +3312,6 @@ pub fn mapOldZirToNew( var unnamed_test_idx: u32 = 0; var comptime_decl_idx: u32 = 0; - var usingnamespace_decl_idx: u32 = 0; var new_decl_it = new_zir.declIterator(match_item.new_inst); while (new_decl_it.next()) |new_decl_inst| { @@ -3337,7 +3321,6 @@ pub fn mapOldZirToNew( // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name. // * For unnamed tests, we match based on order. // * For comptime blocks, we match based on order. - // * For usingnamespace decls, we match based on order. // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`. const old_decl_inst = switch (new_decl.kind) { .@"comptime" => inst: { @@ -3345,11 +3328,6 @@ pub fn mapOldZirToNew( defer comptime_decl_idx += 1; break :inst comptime_decls.items[comptime_decl_idx]; }, - .@"usingnamespace" => inst: { - if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue; - defer usingnamespace_decl_idx += 1; - break :inst usingnamespace_decls.items[usingnamespace_decl_idx]; - }, .unnamed_test => inst: { if (unnamed_test_idx == unnamed_tests.items.len) continue; defer unnamed_test_idx += 1; @@ -4058,7 +4036,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv if (!comp.config.is_test or file.mod != zcu.main_mod) continue; const want_analysis = switch (decl.kind) { - .@"usingnamespace" => unreachable, .@"const", .@"var" => unreachable, .@"comptime" => unreachable, .unnamed_test => true, @@ -4116,16 +4093,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv } } } - // Incremental compilation does not support `usingnamespace`. - // These are only included to keep good reference traces in non-incremental updates. - for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| { - const unit: AnalUnit = .wrap(.{ .nav_val = nav }); - if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); - } - for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| { - const unit: AnalUnit = .wrap(.{ .nav_val = nav }); - if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); - } continue; } if (unit_queue.pop()) |kv| { @@ -4271,17 +4238,17 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *std.io.Writer) std.io.Writer.Er const cu = ip.getComptimeUnit(cu_id); if (cu.zir_index.resolveFull(ip)) |resolved| { const file_path = zcu.fileByIndex(resolved.file).path; - return writer.print("comptime(inst=('{}', %{}) [{}])", .{ file_path.fmt(zcu.comp), @intFromEnum(resolved.inst), @intFromEnum(cu_id) }); + return writer.print("comptime(inst=('{f}', %{}) [{}])", .{ file_path.fmt(zcu.comp), @intFromEnum(resolved.inst), @intFromEnum(cu_id) }); } else { return writer.print("comptime(inst= [{}])", .{@intFromEnum(cu_id)}); } }, - .nav_val => |nav| return writer.print("nav_val('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), - .nav_ty => |nav| return writer.print("nav_ty('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), - .type => |ty| return writer.print("ty('{}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }), + .nav_val => |nav| return writer.print("nav_val('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), + .nav_ty => |nav| return writer.print("nav_ty('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), + .type => |ty| return writer.print("ty('{f}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }), .func => |func| { const nav = zcu.funcInfo(func).owner_nav; - return writer.print("func('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) }); + return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) }); }, .memoized_state => return writer.writeAll("memoized_state"), } @@ -4298,42 +4265,42 @@ fn formatDependee(data: FormatDependee, writer: *std.io.Writer) std.io.Writer.Er return writer.writeAll("inst()"); }; const file_path = zcu.fileByIndex(info.file).path; - return writer.print("inst('{}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) }); + return writer.print("inst('{f}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) }); }, .nav_val => |nav| { const fqn = ip.getNav(nav).fqn; - return writer.print("nav_val('{}')", .{fqn.fmt(ip)}); + return writer.print("nav_val('{f}')", .{fqn.fmt(ip)}); }, .nav_ty => |nav| { const fqn = ip.getNav(nav).fqn; - return writer.print("nav_ty('{}')", .{fqn.fmt(ip)}); + return writer.print("nav_ty('{f}')", .{fqn.fmt(ip)}); }, .interned => |ip_index| switch (ip.indexToKey(ip_index)) { - .struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}), - .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}), + .struct_type, .union_type, .enum_type => return writer.print("type('{f}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}), + .func => |f| return writer.print("ies('{f}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}), else => unreachable, }, .zon_file => |file| { const file_path = zcu.fileByIndex(file).path; - return writer.print("zon_file('{}')", .{file_path.fmt(zcu.comp)}); + return writer.print("zon_file('{f}')", .{file_path.fmt(zcu.comp)}); }, .embed_file => |ef_idx| { const ef = ef_idx.get(zcu); - return writer.print("embed_file('{}')", .{ef.path.fmt(zcu.comp)}); + return writer.print("embed_file('{f}')", .{ef.path.fmt(zcu.comp)}); }, .namespace => |ti| { const info = ti.resolveFull(ip) orelse { return writer.writeAll("namespace()"); }; const file_path = zcu.fileByIndex(info.file).path; - return writer.print("namespace('{}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) }); + return writer.print("namespace('{f}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) }); }, .namespace_name => |k| { const info = k.namespace.resolveFull(ip) orelse { - return writer.print("namespace(, '{}')", .{k.name.fmt(ip)}); + return writer.print("namespace(, '{f}')", .{k.name.fmt(ip)}); }; const file_path = zcu.fileByIndex(info.file).path; - return writer.print("namespace('{}', %{d}, '{}')", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst), k.name.fmt(ip) }); + return writer.print("namespace('{f}', %{d}, '{f}')", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst), k.name.fmt(ip) }); }, .memoized_state => return writer.writeAll("memoized_state"), } @@ -4374,7 +4341,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); switch (cc) { .auto, .@"inline" => return .ok, - .@"async" => return .{ .bad_backend = backend }, // nothing supports async currently + .async => return .{ .bad_backend = backend }, // nothing supports async currently .naked => {}, // depends only on backend else => for (cc.archs()) |allowed_arch| { if (allowed_arch == target.cpu.arch) break; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 2776d3c860d888e3aa1d779cd1ab1fed4b23c9cd..d4a3d1598f9583e4dd43792870f604cef79baeb4 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -53,7 +53,7 @@ fn deinitFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void { const zcu = pt.zcu; const gpa = zcu.gpa; const file = zcu.fileByIndex(file_index); - log.debug("deinit File {}", .{file.path.fmt(zcu.comp)}); + log.debug("deinit File {f}", .{file.path.fmt(zcu.comp)}); file.path.deinit(gpa); file.unload(gpa); if (file.prev_zir) |prev_zir| { @@ -117,7 +117,7 @@ pub fn updateFile( var lock: std.fs.File.Lock = switch (file.status) { .never_loaded, .retryable_failure => lock: { // First, load the cached ZIR code, if any. - log.debug("AstGen checking cache: {} (local={}, digest={s})", .{ + log.debug("AstGen checking cache: {f} (local={}, digest={s})", .{ file.path.fmt(comp), want_local_cache, &hex_digest, }); @@ -130,11 +130,11 @@ pub fn updateFile( stat.inode == file.stat.inode; if (unchanged_metadata) { - log.debug("unmodified metadata of file: {}", .{file.path.fmt(comp)}); + log.debug("unmodified metadata of file: {f}", .{file.path.fmt(comp)}); return; } - log.debug("metadata changed: {}", .{file.path.fmt(comp)}); + log.debug("metadata changed: {f}", .{file.path.fmt(comp)}); break :lock .exclusive; }, @@ -221,12 +221,12 @@ pub fn updateFile( }; switch (result) { .success => { - log.debug("AstGen cached success: {}", .{file.path.fmt(comp)}); + log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)}); break false; }, .invalid => {}, - .truncated => log.warn("unexpected EOF reading cached ZIR for {}", .{file.path.fmt(comp)}), - .stale => log.debug("AstGen cache stale: {}", .{file.path.fmt(comp)}), + .truncated => log.warn("unexpected EOF reading cached ZIR for {f}", .{file.path.fmt(comp)}), + .stale => log.debug("AstGen cache stale: {f}", .{file.path.fmt(comp)}), } // If we already have the exclusive lock then it is our job to update. @@ -283,7 +283,7 @@ pub fn updateFile( }, } - log.debug("AstGen fresh success: {}", .{file.path.fmt(comp)}); + log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)}); } file.stat = .{ @@ -343,8 +343,9 @@ fn loadZirZoirCache( .zon => Zoir.Header, }; - var buffer: [@sizeOf(Header)]u8 = undefined; + var buffer: [2000]u8 = undefined; var cache_fr = cache_file.reader(&buffer); + cache_fr.size = stat.size; const cache_br = &cache_fr.interface; // 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 defer block.instructions.deinit(gpa); const zir_decl = zir.getDeclaration(inst_resolved.inst); - assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); 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 assert(nav_ty.zigTypeTag(zcu) == .@"fn"); break :is_const true; }, - .@"usingnamespace", .@"const" => true, + .@"const" => true, .@"var" => { try sema.validateVarType( &block, @@ -1243,26 +1243,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr // this resolves the type `type` (which needs no resolution), not the struct itself. try nav_ty.resolveLayout(pt); - // TODO: this is jank. If #20663 is rejected, let's think about how to better model `usingnamespace`. - if (zir_decl.kind == .@"usingnamespace") { - if (nav_ty.toIntern() != .type_type) { - return sema.fail(&block, ty_src, "expected type, found {f}", .{nav_ty.fmt(pt)}); - } - if (nav_val.toType().getNamespace(zcu) == .none) { - return sema.fail(&block, ty_src, "type {f} has no namespace", .{nav_val.toType().fmt(pt)}); - } - ip.resolveNavValue(nav_id, .{ - .val = nav_val.toIntern(), - .is_const = is_const, - .alignment = .none, - .@"linksection" = .none, - .@"addrspace" = .generic, - }); - // TODO: usingnamespace cannot participate in incremental compilation - assert(zcu.analysis_in_progress.swapRemove(anal_unit)); - return .{ .val_changed = true }; - } - const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) { .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen .variable => |v| .{ v.owner_nav == nav_id, false }, @@ -1467,7 +1447,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); const zir_decl = zir.getDeclaration(inst_resolved.inst); - assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); const type_body = zir_decl.type_body.?; var analysis_arena: std.heap.ArenaAllocator = .init(gpa); @@ -1530,7 +1509,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr const is_const = switch (zir_decl.kind) { .@"comptime" => unreachable, - .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true, + .unnamed_test, .@"test", .decltest, .@"const" => true, .@"var" => false, }; @@ -2324,7 +2303,7 @@ pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!voi Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure( .write_builtin_zig, - "unable to write '{}': {s}", + "unable to write '{f}': {s}", .{ file.path.fmt(comp), @errorName(err) }, ); } @@ -2548,7 +2527,6 @@ pub fn scanNamespace( try existing_by_inst.ensureTotalCapacity(gpa, @intCast( namespace.pub_decls.count() + namespace.priv_decls.count() + - namespace.pub_usingnamespace.items.len + namespace.priv_usingnamespace.items.len + namespace.comptime_decls.items.len + namespace.test_decls.items.len, )); @@ -2561,14 +2539,6 @@ pub fn scanNamespace( const zir_index = ip.getNav(nav).analysis.?.zir_index; existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav })); } - for (namespace.pub_usingnamespace.items) |nav| { - const zir_index = ip.getNav(nav).analysis.?.zir_index; - existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav })); - } - for (namespace.priv_usingnamespace.items) |nav| { - const zir_index = ip.getNav(nav).analysis.?.zir_index; - existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav })); - } for (namespace.comptime_decls.items) |cu| { const zir_index = ip.getComptimeUnit(cu).zir_index; existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu })); @@ -2585,8 +2555,6 @@ pub fn scanNamespace( namespace.pub_decls.clearRetainingCapacity(); namespace.priv_decls.clearRetainingCapacity(); - namespace.pub_usingnamespace.clearRetainingCapacity(); - namespace.priv_usingnamespace.clearRetainingCapacity(); namespace.comptime_decls.clearRetainingCapacity(); namespace.test_decls.clearRetainingCapacity(); @@ -2614,7 +2582,6 @@ const ScanDeclIter = struct { /// Decl scanning is run in two passes, so that we can detect when a generated /// name would clash with an explicit name and use a different one. pass: enum { named, unnamed }, - usingnamespace_index: usize = 0, unnamed_test_index: usize = 0, fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString { @@ -2653,12 +2620,6 @@ const ScanDeclIter = struct { if (iter.pass != .unnamed) return; break :name .none; }, - .@"usingnamespace" => name: { - if (iter.pass != .unnamed) return; - const i = iter.usingnamespace_index; - iter.usingnamespace_index += 1; - break :name (try iter.avoidNameConflict("usingnamespace_{d}", .{i})).toOptional(); - }, .unnamed_test => name: { if (iter.pass != .unnamed) return; const i = iter.unnamed_test_index; @@ -2717,7 +2678,7 @@ const ScanDeclIter = struct { const name = maybe_name.unwrap().?; const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name); const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: { - const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace"); + const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); break :nav nav; }; @@ -2729,17 +2690,6 @@ const ScanDeclIter = struct { const want_analysis = switch (decl.kind) { .@"comptime" => unreachable, - .@"usingnamespace" => a: { - if (comp.incremental) { - @panic("'usingnamespace' is not supported by incremental compilation"); - } - if (decl.is_pub) { - try namespace.pub_usingnamespace.append(gpa, nav); - } else { - try namespace.priv_usingnamespace.append(gpa, nav); - } - break :a true; - }, .unnamed_test, .@"test", .decltest => a: { const is_named = decl.kind != .unnamed_test; try namespace.test_decls.append(gpa, nav); @@ -4434,12 +4384,11 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e defer liveness.deinit(gpa); if (build_options.enable_debug_extensions and comp.verbose_air) { - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - const stderr = std.io.getStdErr().writer(); - stderr.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}) catch {}; + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + stderr.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}) catch {}; air.write(stderr, pt, liveness); - stderr.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}) catch {}; + stderr.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)}) catch {}; } if (std.debug.runtime_safety) { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig deleted file mode 100644 index 14ce169af61bba377de1a8b419853a76acce17fd..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/CodeGen.zig +++ /dev/null @@ -1,6401 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const mem = std.mem; -const math = std.math; -const assert = std.debug.assert; -const codegen = @import("../../codegen.zig"); -const Air = @import("../../Air.zig"); -const Mir = @import("Mir.zig"); -const Emit = @import("Emit.zig"); -const Type = @import("../../Type.zig"); -const Value = @import("../../Value.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); -const InternPool = @import("../../InternPool.zig"); -const Compilation = @import("../../Compilation.zig"); -const ErrorMsg = Zcu.ErrorMsg; -const Target = std.Target; -const Allocator = mem.Allocator; -const trace = @import("../../tracy.zig").trace; -const leb128 = std.leb; -const log = std.log.scoped(.codegen); -const build_options = @import("build_options"); -const Alignment = InternPool.Alignment; - -const CodeGenError = codegen.CodeGenError; - -const bits = @import("bits.zig"); -const abi = @import("abi.zig"); -const errUnionPayloadOffset = codegen.errUnionPayloadOffset; -const errUnionErrorOffset = codegen.errUnionErrorOffset; -const RegisterManager = abi.RegisterManager; -const RegisterLock = RegisterManager.RegisterLock; -const Register = bits.Register; -const Instruction = bits.Instruction; -const Condition = bits.Instruction.Condition; -const callee_preserved_regs = abi.callee_preserved_regs; -const c_abi_int_param_regs = abi.c_abi_int_param_regs; -const c_abi_int_return_regs = abi.c_abi_int_return_regs; -const gp = abi.RegisterClass.gp; - -const InnerError = CodeGenError || error{OutOfRegisters}; - -pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { - return null; -} - -gpa: Allocator, -pt: Zcu.PerThread, -air: Air, -liveness: Air.Liveness, -bin_file: *link.File, -target: *const std.Target, -func_index: InternPool.Index, -owner_nav: InternPool.Nav.Index, -args: []MCValue, -ret_mcv: MCValue, -fn_type: Type, -arg_index: u32, -src_loc: Zcu.LazySrcLoc, -stack_align: u32, - -/// MIR Instructions -mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, -/// MIR extra data -mir_extra: std.ArrayListUnmanaged(u32) = .empty, - -/// Byte offset within the source file of the ending curly. -end_di_line: u32, -end_di_column: u32, - -/// The value is an offset into the `Function` `code` from the beginning. -/// To perform the reloc, write 32-bit signed little-endian integer -/// which is a relative jump, based on the address following the reloc. -exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty, - -reused_operands: std.StaticBitSet(Air.Liveness.bpi - 1) = undefined, - -/// We postpone the creation of debug info for function args and locals -/// until after all Mir instructions have been generated. Only then we -/// will know saved_regs_stack_space which is necessary in order to -/// calculate the right stack offsest with respect to the `.fp` register. -dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .empty, - -/// Whenever there is a runtime branch, we push a Branch onto this stack, -/// and pop it off when the runtime branch joins. This provides an "overlay" -/// of the table of mappings from instructions to `MCValue` from within the branch. -/// This way we can modify the `MCValue` for an instruction in different ways -/// within different branches. Special consideration is needed when a branch -/// joins with its parent, to make sure all instructions have the same MCValue -/// across each runtime branch upon joining. -branch_stack: *std.ArrayList(Branch), - -// Key is the block instruction -blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, - -register_manager: RegisterManager = .{}, -/// Maps offset to what is stored there. -stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .empty, -/// Tracks the current instruction allocated to the compare flags -compare_flags_inst: ?Air.Inst.Index = null, - -/// Offset from the stack base, representing the end of the stack frame. -max_end_stack: u32 = 0, -/// Represents the current end stack offset. If there is no existing slot -/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. -next_stack_offset: u32 = 0, - -saved_regs_stack_space: u32 = 0, - -/// Debug field, used to find bugs in the compiler. -air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, - -const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; - -const MCValue = union(enum) { - /// No runtime bits. `void` types, empty structs, u0, enums with 1 - /// tag, etc. - /// - /// TODO Look into deleting this tag and using `dead` instead, - /// since every use of MCValue.none should be instead looking at - /// the type and noticing it is 0 bits. - none, - /// Control flow will not allow this value to be observed. - unreach, - /// No more references to this value remain. - dead, - /// The value is undefined. - undef, - /// A pointer-sized integer that fits in a register. - /// - /// If the type is a pointer, this is the pointer address in - /// virtual address space. - immediate: u64, - /// The value is in a target-specific register. - register: Register, - /// The value is a tuple { wrapped: u32, overflow: u1 } where - /// wrapped is stored in the register and the overflow bit is - /// stored in the C (signed) or V (unsigned) flag of the CPSR. - /// - /// This MCValue is only generated by a add_with_overflow or - /// sub_with_overflow instruction operating on 32- or 64-bit values. - register_with_overflow: struct { reg: Register, flag: bits.Instruction.Condition }, - /// The value is in memory at a hard-coded address. - /// - /// If the type is a pointer, it means the pointer address is at - /// this memory location. - memory: u64, - /// The value is in memory but requires a linker relocation fixup. - linker_load: codegen.LinkerLoad, - /// The value is one of the stack variables. - /// - /// If the type is a pointer, it means the pointer address is in - /// the stack at this offset. - stack_offset: u32, - /// The value is a pointer to one of the stack variables (payload - /// is stack offset). - ptr_stack_offset: u32, - /// The value resides in the N, Z, C, V flags. The value is 1 (if - /// the type is u1) or true (if the type in bool) iff the - /// specified condition is true. - compare_flags: Condition, - /// The value is a function argument passed via the stack. - stack_argument_offset: u32, -}; - -const DbgInfoReloc = struct { - tag: Air.Inst.Tag, - ty: Type, - name: [:0]const u8, - mcv: MCValue, - - fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - switch (reloc.tag) { - .arg, - .dbg_arg_inline, - => try reloc.genArgDbgInfo(function), - - .dbg_var_ptr, - .dbg_var_val, - => try reloc.genVarDbgInfo(function), - - else => unreachable, - } - } - - fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - // TODO: Add a pseudo-instruction or something to defer this work until Emit. - // We aren't allowed to interact with linker state here. - if (true) return; - switch (function.debug_output) { - .dwarf => |dw| { - const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { - .register => |reg| .{ .reg = reg.dwarfNum() }, - .stack_offset, - .stack_argument_offset, - => |offset| blk: { - const adjusted_offset = switch (reloc.mcv) { - .stack_offset => -@as(i32, @intCast(offset)), - .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)), - else => unreachable, - }; - break :blk .{ .plus = .{ - &.{ .breg = Register.x29.dwarfNum() }, - &.{ .consts = adjusted_offset }, - } }; - }, - else => unreachable, // not a possible argument - - }; - try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc); - }, - .plan9 => {}, - .none => {}, - } - } - - fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - // TODO: Add a pseudo-instruction or something to defer this work until Emit. - // We aren't allowed to interact with linker state here. - if (true) return; - switch (function.debug_output) { - .dwarf => |dwarf| { - const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { - .register => |reg| .{ .reg = reg.dwarfNum() }, - .ptr_stack_offset, - .stack_offset, - .stack_argument_offset, - => |offset| blk: { - const adjusted_offset = switch (reloc.mcv) { - .ptr_stack_offset, - .stack_offset, - => -@as(i32, @intCast(offset)), - .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)), - else => unreachable, - }; - break :blk .{ .plus = .{ - &.{ .reg = Register.x29.dwarfNum() }, - &.{ .consts = adjusted_offset }, - } }; - }, - .memory => |address| .{ .constu = address }, - .immediate => |x| .{ .constu = x }, - .none => .empty, - else => blk: { - log.debug("TODO generate debug info for {}", .{reloc.mcv}); - break :blk .empty; - }, - }; - try dwarf.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc); - }, - .plan9 => {}, - .none => {}, - } - } -}; - -const Branch = struct { - inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .empty, - - fn deinit(self: *Branch, gpa: Allocator) void { - self.inst_table.deinit(gpa); - self.* = undefined; - } -}; - -const StackAllocation = struct { - inst: Air.Inst.Index, - /// TODO do we need size? should be determined by inst.ty.abiSize() - size: u32, -}; - -const BlockData = struct { - relocs: std.ArrayListUnmanaged(Mir.Inst.Index), - /// The first break instruction encounters `null` here and chooses a - /// machine code value for the block result, populating this field. - /// Following break instructions encounter that value and use it for - /// the location to store their block results. - mcv: MCValue, -}; - -const BigTomb = struct { - function: *Self, - inst: Air.Inst.Index, - lbt: Air.Liveness.BigTomb, - - fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const dies = bt.lbt.feed(); - const op_index = op_ref.toIndex() orelse return; - if (!dies) return; - bt.function.processDeath(op_index); - } - - fn finishAir(bt: *BigTomb, result: MCValue) void { - const is_used = !bt.function.liveness.isUnused(bt.inst); - if (is_used) { - log.debug("%{d} => {}", .{ bt.inst, result }); - const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); - - switch (result) { - .register => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (bt.function.register_manager.isRegFree(reg)) { - bt.function.register_manager.getRegAssumeFree(reg, bt.inst); - } - }, - .register_with_overflow => |rwo| { - if (bt.function.register_manager.isRegFree(rwo.reg)) { - bt.function.register_manager.getRegAssumeFree(rwo.reg, bt.inst); - } - bt.function.compare_flags_inst = bt.inst; - }, - .compare_flags => |_| { - bt.function.compare_flags_inst = bt.inst; - }, - else => {}, - } - } - bt.function.finishAirBookkeeping(); - } -}; - -const Self = @This(); - -pub fn generate( - lf: *link.File, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - func_index: InternPool.Index, - air: *const Air, - liveness: *const Air.Liveness, -) CodeGenError!Mir { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const func = zcu.funcInfo(func_index); - const fn_type = Type.fromInterned(func.ty); - const file_scope = zcu.navFileScope(func.owner_nav); - const target = &file_scope.mod.?.resolved_target.result; - - var branch_stack = std.ArrayList(Branch).init(gpa); - defer { - assert(branch_stack.items.len == 1); - branch_stack.items[0].deinit(gpa); - branch_stack.deinit(); - } - try branch_stack.append(.{}); - - var function: Self = .{ - .gpa = gpa, - .pt = pt, - .air = air.*, - .liveness = liveness.*, - .target = target, - .bin_file = lf, - .func_index = func_index, - .owner_nav = func.owner_nav, - .args = undefined, // populated after `resolveCallingConventionValues` - .ret_mcv = undefined, // populated after `resolveCallingConventionValues` - .fn_type = fn_type, - .arg_index = 0, - .branch_stack = &branch_stack, - .src_loc = src_loc, - .stack_align = undefined, - .end_di_line = func.rbrace_line, - .end_di_column = func.rbrace_column, - }; - defer function.stack.deinit(gpa); - defer function.blocks.deinit(gpa); - defer function.exitlude_jump_relocs.deinit(gpa); - defer function.dbg_info_relocs.deinit(gpa); - - var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - else => |e| return e, - }; - defer call_info.deinit(&function); - - function.args = call_info.args; - function.ret_mcv = call_info.return_value; - function.stack_align = call_info.stack_align; - function.max_end_stack = call_info.stack_byte_count; - - function.gen() catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), - else => |e| return e, - }; - - for (function.dbg_info_relocs.items) |reloc| { - reloc.genDbgInfo(function) catch |err| - return function.fail("failed to generate debug info: {s}", .{@errorName(err)}); - } - - var mir: Mir = .{ - .instructions = function.mir_instructions.toOwnedSlice(), - .extra = &.{}, // fallible, so assign after errdefer - .max_end_stack = function.max_end_stack, - .saved_regs_stack_space = function.saved_regs_stack_space, - }; - errdefer mir.deinit(gpa); - mir.extra = try function.mir_extra.toOwnedSlice(gpa); - return mir; -} - -fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { - const gpa = self.gpa; - - try self.mir_instructions.ensureUnusedCapacity(gpa, 1); - - const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len); - self.mir_instructions.appendAssumeCapacity(inst); - return result_index; -} - -fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { - return try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); -} - -pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { - const fields = std.meta.fields(@TypeOf(extra)); - try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); - return self.addExtraAssumeCapacity(extra); -} - -pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { - const fields = std.meta.fields(@TypeOf(extra)); - const result = @as(u32, @intCast(self.mir_extra.items.len)); - inline for (fields) |field| { - self.mir_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - i32 => @as(u32, @bitCast(@field(extra, field.name))), - else => @compileError("bad field type"), - }); - } - return result; -} - -fn gen(self: *Self) !void { - const pt = self.pt; - const zcu = pt.zcu; - const cc = self.fn_type.fnCallingConvention(zcu); - if (cc != .naked) { - // stp fp, lr, [sp, #-16]! - _ = try self.addInst(.{ - .tag = .stp, - .data = .{ .load_store_register_pair = .{ - .rt = .x29, - .rt2 = .x30, - .rn = .sp, - .offset = Instruction.LoadStorePairOffset.pre_index(-16), - } }, - }); - - // - const backpatch_save_registers = try self.addNop(); - - // mov fp, sp - _ = try self.addInst(.{ - .tag = .mov_to_from_sp, - .data = .{ .rr = .{ .rd = .x29, .rn = .sp } }, - }); - - // sub sp, sp, #reloc - const backpatch_reloc = try self.addNop(); - - if (self.ret_mcv == .stack_offset) { - // The address of where to store the return value is in x0 - // (or w0 when pointer size is 32 bits). As this register - // might get overwritten along the way, save the address - // to the stack. - const ret_ptr_reg = self.registerAlias(.x0, Type.usize); - - const stack_offset = try self.allocMem(8, .@"8", null); - - try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg }); - self.ret_mcv = MCValue{ .stack_offset = stack_offset }; - } - - for (self.args, 0..) |*arg, arg_index| { - // Copy register arguments to the stack - switch (arg.*) { - .register => |reg| { - // The first AIR instructions of the main body are guaranteed - // to be the functions arguments - const inst = self.air.getMainBody()[arg_index]; - assert(self.air.instructions.items(.tag)[@intFromEnum(inst)] == .arg); - - const ty = self.typeOfIndex(inst); - - const abi_size = @as(u32, @intCast(ty.abiSize(zcu))); - const abi_align = ty.abiAlignment(zcu); - const stack_offset = try self.allocMem(abi_size, abi_align, inst); - try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - - arg.* = MCValue{ .stack_offset = stack_offset }; - }, - else => {}, - } - } - - _ = try self.addInst(.{ - .tag = .dbg_prologue_end, - .data = .{ .nop = {} }, - }); - - try self.genBody(self.air.getMainBody()); - - // Backpatch push callee saved regs - var saved_regs: u32 = 0; - self.saved_regs_stack_space = 16; - inline for (callee_preserved_regs) |reg| { - if (self.register_manager.isRegAllocated(reg)) { - saved_regs |= @as(u32, 1) << @as(u5, @intCast(reg.id())); - self.saved_regs_stack_space += 8; - } - } - - // Emit.mirPopPushRegs automatically adds extra empty space so - // that sp is always aligned to 16 - if (!std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)) { - self.saved_regs_stack_space += 8; - } - assert(std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)); - - self.mir_instructions.set(backpatch_save_registers, .{ - .tag = .push_regs, - .data = .{ .reg_list = saved_regs }, - }); - - // Backpatch stack offset - const total_stack_size = self.max_end_stack + self.saved_regs_stack_space; - const aligned_total_stack_end = mem.alignForward(u32, total_stack_size, self.stack_align); - const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; - self.max_end_stack = stack_size; - if (math.cast(u12, stack_size)) |size| { - self.mir_instructions.set(backpatch_reloc, .{ - .tag = .sub_immediate, - .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, - }); - } else { - @panic("TODO AArch64: allow larger stacks"); - } - - _ = try self.addInst(.{ - .tag = .dbg_epilogue_begin, - .data = .{ .nop = {} }, - }); - - // exitlude jumps - if (self.exitlude_jump_relocs.items.len > 0 and - self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) - { - // If the last Mir instruction (apart from the - // dbg_epilogue_begin) is the last exitlude jump - // relocation (which would just jump one instruction - // further), it can be safely removed - self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop().?); - } - - for (self.exitlude_jump_relocs.items) |jmp_reloc| { - self.mir_instructions.set(jmp_reloc, .{ - .tag = .b, - .data = .{ .inst = @as(u32, @intCast(self.mir_instructions.len)) }, - }); - } - - // add sp, sp, #stack_size - _ = try self.addInst(.{ - .tag = .add_immediate, - .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = @as(u12, @intCast(stack_size)) } }, - }); - - // - _ = try self.addInst(.{ - .tag = .pop_regs, - .data = .{ .reg_list = saved_regs }, - }); - - // ldp fp, lr, [sp], #16 - _ = try self.addInst(.{ - .tag = .ldp, - .data = .{ .load_store_register_pair = .{ - .rt = .x29, - .rt2 = .x30, - .rn = .sp, - .offset = Instruction.LoadStorePairOffset.post_index(16), - } }, - }); - - // ret lr - _ = try self.addInst(.{ - .tag = .ret, - .data = .{ .reg = .x30 }, - }); - } else { - _ = try self.addInst(.{ - .tag = .dbg_prologue_end, - .data = .{ .nop = {} }, - }); - - try self.genBody(self.air.getMainBody()); - - _ = try self.addInst(.{ - .tag = .dbg_epilogue_begin, - .data = .{ .nop = {} }, - }); - } - - // Drop them off at the rbrace. - _ = try self.addInst(.{ - .tag = .dbg_line, - .data = .{ .dbg_line_column = .{ - .line = self.end_di_line, - .column = self.end_di_column, - } }, - }); -} - -fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const air_tags = self.air.instructions.items(.tag); - - for (body) |inst| { - // TODO: remove now-redundant isUnused calls from AIR handler functions - if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) - continue; - - const old_air_bookkeeping = self.air_bookkeeping; - try self.ensureProcessDeathCapacity(Air.Liveness.bpi); - - self.reused_operands = @TypeOf(self.reused_operands).initEmpty(); - switch (air_tags[@intFromEnum(inst)]) { - // zig fmt: off - .add => try self.airBinOp(inst, .add), - .add_wrap => try self.airBinOp(inst, .add_wrap), - .sub => try self.airBinOp(inst, .sub), - .sub_wrap => try self.airBinOp(inst, .sub_wrap), - .mul => try self.airBinOp(inst, .mul), - .mul_wrap => try self.airBinOp(inst, .mul_wrap), - .shl => try self.airBinOp(inst, .shl), - .shl_exact => try self.airBinOp(inst, .shl_exact), - .bool_and => try self.airBinOp(inst, .bool_and), - .bool_or => try self.airBinOp(inst, .bool_or), - .bit_and => try self.airBinOp(inst, .bit_and), - .bit_or => try self.airBinOp(inst, .bit_or), - .xor => try self.airBinOp(inst, .xor), - .shr => try self.airBinOp(inst, .shr), - .shr_exact => try self.airBinOp(inst, .shr_exact), - .div_float => try self.airBinOp(inst, .div_float), - .div_trunc => try self.airBinOp(inst, .div_trunc), - .div_floor => try self.airBinOp(inst, .div_floor), - .div_exact => try self.airBinOp(inst, .div_exact), - .rem => try self.airBinOp(inst, .rem), - .mod => try self.airBinOp(inst, .mod), - - .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), - .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), - - .min => try self.airMinMax(inst), - .max => try self.airMinMax(inst), - - .add_sat => try self.airAddSat(inst), - .sub_sat => try self.airSubSat(inst), - .mul_sat => try self.airMulSat(inst), - .shl_sat => try self.airShlSat(inst), - .slice => try self.airSlice(inst), - - .sqrt, - .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .floor, - .ceil, - .round, - .trunc_float, - .neg, - => try self.airUnaryMath(inst), - - .add_with_overflow => try self.airOverflow(inst), - .sub_with_overflow => try self.airOverflow(inst), - .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), - - .cmp_lt => try self.airCmp(inst, .lt), - .cmp_lte => try self.airCmp(inst, .lte), - .cmp_eq => try self.airCmp(inst, .eq), - .cmp_gte => try self.airCmp(inst, .gte), - .cmp_gt => try self.airCmp(inst, .gt), - .cmp_neq => try self.airCmp(inst, .neq), - - .cmp_vector => try self.airCmpVector(inst), - .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - - .alloc => try self.airAlloc(inst), - .ret_ptr => try self.airRetPtr(inst), - .arg => try self.airArg(inst), - .assembly => try self.airAsm(inst), - .bitcast => try self.airBitCast(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .repeat => return self.fail("TODO implement `repeat`", .{}), - .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}), - .trap => try self.airTrap(), - .breakpoint => try self.airBreakpoint(), - .ret_addr => try self.airRetAddr(inst), - .frame_addr => try self.airFrameAddress(inst), - .cond_br => try self.airCondBr(inst), - .fptrunc => try self.airFptrunc(inst), - .fpext => try self.airFpext(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .is_non_null => try self.airIsNonNull(inst), - .is_non_null_ptr => try self.airIsNonNullPtr(inst), - .is_null => try self.airIsNull(inst), - .is_null_ptr => try self.airIsNullPtr(inst), - .is_non_err => try self.airIsNonErr(inst), - .is_non_err_ptr => try self.airIsNonErrPtr(inst), - .is_err => try self.airIsErr(inst), - .is_err_ptr => try self.airIsErrPtr(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ret => try self.airRet(inst), - .ret_safe => try self.airRet(inst), // TODO - .ret_load => try self.airRetLoad(inst), - .store => try self.airStore(inst, false), - .store_safe => try self.airStore(inst, true), - .struct_field_ptr=> try self.airStructFieldPtr(inst), - .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => try self.airArrayToSlice(inst), - .float_from_int => try self.airFloatFromInt(inst), - .int_from_float => try self.airIntFromFloat(inst), - .cmpxchg_strong => try self.airCmpxchg(inst), - .cmpxchg_weak => try self.airCmpxchg(inst), - .atomic_rmw => try self.airAtomicRmw(inst), - .atomic_load => try self.airAtomicLoad(inst), - .memcpy => try self.airMemcpy(inst), - .memmove => try self.airMemmove(inst), - .memset => try self.airMemset(inst, false), - .memset_safe => try self.airMemset(inst, true), - .set_union_tag => try self.airSetUnionTag(inst), - .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClz(inst), - .ctz => try self.airCtz(inst), - .popcount => try self.airPopcount(inst), - .abs => try self.airAbs(inst), - .byte_swap => try self.airByteSwap(inst), - .bit_reverse => try self.airBitReverse(inst), - .tag_name => try self.airTagName(inst), - .error_name => try self.airErrorName(inst), - .splat => try self.airSplat(inst), - .select => try self.airSelect(inst), - .shuffle_one => try self.airShuffleOne(inst), - .shuffle_two => try self.airShuffleTwo(inst), - .reduce => try self.airReduce(inst), - .aggregate_init => try self.airAggregateInit(inst), - .union_init => try self.airUnionInit(inst), - .prefetch => try self.airPrefetch(inst), - .mul_add => try self.airMulAdd(inst), - .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}), - - .@"try" => try self.airTry(inst), - .try_cold => try self.airTry(inst), - .try_ptr => try self.airTryPtr(inst), - .try_ptr_cold => try self.airTryPtr(inst), - - .dbg_stmt => try self.airDbgStmt(inst), - .dbg_empty_stmt => self.finishAirBookkeeping(), - .dbg_inline_block => try self.airDbgInlineBlock(inst), - .dbg_var_ptr, - .dbg_var_val, - .dbg_arg_inline, - => try self.airDbgVar(inst), - - .call => try self.airCall(inst, .auto), - .call_always_tail => try self.airCall(inst, .always_tail), - .call_never_tail => try self.airCall(inst, .never_tail), - .call_never_inline => try self.airCall(inst, .never_inline), - - .atomic_store_unordered => try self.airAtomicStore(inst, .unordered), - .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic), - .atomic_store_release => try self.airAtomicStore(inst, .release), - .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst), - - .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), - .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), - .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), - .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), - - .field_parent_ptr => try self.airFieldParentPtr(inst), - - .switch_br => try self.airSwitch(inst), - .loop_switch_br => return self.fail("TODO implement `loop_switch_br`", .{}), - .slice_ptr => try self.airSlicePtr(inst), - .slice_len => try self.airSliceLen(inst), - - .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), - .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), - - .array_elem_val => try self.airArrayElemVal(inst), - .slice_elem_val => try self.airSliceElemVal(inst), - .slice_elem_ptr => try self.airSliceElemPtr(inst), - .ptr_elem_val => try self.airPtrElemVal(inst), - .ptr_elem_ptr => try self.airPtrElemPtr(inst), - - .inferred_alloc, .inferred_alloc_comptime => unreachable, - .unreach => self.finishAirBookkeeping(), - - .optional_payload => try self.airOptionalPayload(inst), - .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), - .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), - .unwrap_errunion_err => try self.airUnwrapErrErr(inst), - .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), - .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), - .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), - .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), - .err_return_trace => try self.airErrReturnTrace(inst), - .set_err_return_trace => try self.airSetErrReturnTrace(inst), - .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst), - - .wrap_optional => try self.airWrapOptional(inst), - .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), - .wrap_errunion_err => try self.airWrapErrUnionErr(inst), - - .add_optimized, - .sub_optimized, - .mul_optimized, - .div_float_optimized, - .div_trunc_optimized, - .div_floor_optimized, - .div_exact_optimized, - .rem_optimized, - .mod_optimized, - .neg_optimized, - .cmp_lt_optimized, - .cmp_lte_optimized, - .cmp_eq_optimized, - .cmp_gte_optimized, - .cmp_gt_optimized, - .cmp_neq_optimized, - .cmp_vector_optimized, - .reduce_optimized, - .int_from_float_optimized, - => return self.fail("TODO implement optimized float mode", .{}), - - .add_safe, - .sub_safe, - .mul_safe, - .intcast_safe, - .int_from_float_safe, - .int_from_float_optimized_safe, - => return self.fail("TODO implement safety_checked_instructions", .{}), - - .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), - .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), - .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), - .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}), - - .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), - .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), - .c_va_end => return self.fail("TODO implement c_va_end", .{}), - .c_va_start => return self.fail("TODO implement c_va_start", .{}), - - .wasm_memory_size => unreachable, - .wasm_memory_grow => unreachable, - - .work_item_id => unreachable, - .work_group_size => unreachable, - .work_group_id => unreachable, - // zig fmt: on - } - - assert(!self.register_manager.lockedRegsExist()); - - if (std.debug.runtime_safety) { - if (self.air_bookkeeping < old_air_bookkeeping + 1) { - 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)] }); - } - } - } -} - -/// Asserts there is already capacity to insert into top branch inst_table. -fn processDeath(self: *Self, inst: Air.Inst.Index) void { - // When editing this function, note that the logic must synchronize with `reuseOperand`. - const prev_value = self.getResolvedInstValue(inst); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacity(inst, .dead); - switch (prev_value) { - .register => |reg| { - self.register_manager.freeReg(reg); - }, - .register_with_overflow => |rwo| { - self.register_manager.freeReg(rwo.reg); - self.compare_flags_inst = null; - }, - .compare_flags => { - self.compare_flags_inst = null; - }, - else => {}, // TODO process stack allocation death - } -} - -/// Called when there are no operands, and the instruction is always unreferenced. -fn finishAirBookkeeping(self: *Self) void { - if (std.debug.runtime_safety) { - self.air_bookkeeping += 1; - } -} - -fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Air.Liveness.bpi - 1]Air.Inst.Ref) void { - const tomb_bits = self.liveness.getTombBits(inst); - for (0.., operands) |op_index, op| { - if (tomb_bits & @as(Air.Liveness.Bpi, 1) << @intCast(op_index) == 0) continue; - if (self.reused_operands.isSet(op_index)) continue; - self.processDeath(op.toIndexAllowNone() orelse continue); - } - if (tomb_bits & 1 << (Air.Liveness.bpi - 1) == 0) { - log.debug("%{d} => {}", .{ inst, result }); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacityNoClobber(inst, result); - - switch (result) { - .register => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } - }, - .register_with_overflow => |rwo| { - if (self.register_manager.isRegFree(rwo.reg)) { - self.register_manager.getRegAssumeFree(rwo.reg, inst); - } - self.compare_flags_inst = inst; - }, - .compare_flags => |_| { - self.compare_flags_inst = inst; - }, - else => {}, - } - } - self.finishAirBookkeeping(); -} - -fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { - const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; - try table.ensureUnusedCapacity(self.gpa, additional_count); -} - -fn allocMem( - self: *Self, - abi_size: u32, - abi_align: Alignment, - maybe_inst: ?Air.Inst.Index, -) !u32 { - assert(abi_size > 0); - assert(abi_align != .none); - - // In order to efficiently load and store stack items that fit - // into registers, we bump up the alignment to the next power of - // two. - const adjusted_align = if (abi_size > 8) - abi_align - else - Alignment.fromNonzeroByteUnits(std.math.ceilPowerOfTwoAssert(u64, abi_size)); - - // TODO find a free slot instead of always appending - const offset: u32 = @intCast(adjusted_align.forward(self.next_stack_offset) + abi_size); - self.next_stack_offset = offset; - self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); - - if (maybe_inst) |inst| { - try self.stack.putNoClobber(self.gpa, offset, .{ - .inst = inst, - .size = abi_size, - }); - } - - return offset; -} - -/// Use a pointer instruction as the basis for allocating stack memory. -fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = self.typeOfIndex(inst).childType(zcu); - - if (!elem_ty.hasRuntimeBits(zcu)) { - // return the stack offset 0. Stack offset 0 will be where all - // zero-sized stack allocations live as non-zero-sized - // allocations will always have an offset > 0. - return @as(u32, 0); - } - - const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse { - return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)}); - }; - // TODO swap this for inst.ty.ptrAlign - const abi_align = elem_ty.abiAlignment(zcu); - - return self.allocMem(abi_size, abi_align, inst); -} - -fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { - const pt = self.pt; - const abi_size = math.cast(u32, elem_ty.abiSize(pt.zcu)) orelse { - return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)}); - }; - const abi_align = elem_ty.abiAlignment(pt.zcu); - - if (reg_ok) { - // Make sure the type can fit in a register before we try to allocate one. - if (abi_size <= 8) { - if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { - return MCValue{ .register = self.registerAlias(reg, elem_ty) }; - } - } - } - - const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst); - return MCValue{ .stack_offset = stack_offset }; -} - -pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { - const stack_mcv = try self.allocRegOrMem(self.typeOfIndex(inst), false, inst); - log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); - - const reg_mcv = self.getResolvedInstValue(inst); - switch (reg_mcv) { - .register => |r| assert(reg.id() == r.id()), - .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()), - else => unreachable, // not a register - } - - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - try branch.inst_table.put(self.gpa, inst, stack_mcv); - try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); -} - -/// Save the current instruction stored in the compare flags if -/// occupied -fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.compare_flags_inst) |inst_to_save| { - const ty = self.typeOfIndex(inst_to_save); - const mcv = self.getResolvedInstValue(inst_to_save); - const new_mcv = switch (mcv) { - .compare_flags => try self.allocRegOrMem(ty, true, inst_to_save), - .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save), - else => unreachable, // mcv doesn't occupy the compare flags - }; - - try self.setRegOrMem(self.typeOfIndex(inst_to_save), new_mcv, mcv); - log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); - - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - - self.compare_flags_inst = null; - - // TODO consolidate with register manager and spillInstruction - // this call should really belong in the register manager! - switch (mcv) { - .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg), - else => {}, - } - } -} - -/// Copies a value to a register without tracking the register. The register is not considered -/// allocated. A second call to `copyToTmpRegister` may return the same register. -/// This can have a side effect of spilling instructions to the stack to free up a register. -fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) InnerError!Register { - const raw_reg = try self.register_manager.allocReg(null, gp); - const reg = self.registerAlias(raw_reg, ty); - try self.genSetReg(ty, reg, mcv); - return reg; -} - -/// Allocates a new register and copies `mcv` into it. -/// `reg_owner` is the instruction that gets associated with the register in the register table. -/// This can have a side effect of spilling instructions to the stack to free up a register. -fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const raw_reg = try self.register_manager.allocReg(reg_owner, gp); - const ty = self.typeOfIndex(reg_owner); - const reg = self.registerAlias(raw_reg, ty); - try self.genSetReg(self.typeOfIndex(reg_owner), reg, mcv); - return MCValue{ .register = reg }; -} - -fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void { - const stack_offset = try self.allocMemPtr(inst); - return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); -} - -fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = switch (self.ret_mcv) { - .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, - .stack_offset => blk: { - // self.ret_mcv is an address to where this function - // should store its result into - const ret_ty = self.fn_type.fnReturnType(zcu); - const ptr_ty = try pt.singleMutPtrType(ret_ty); - - // addr_reg will contain the address of where to store the - // result into - const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv); - break :blk .{ .register = addr_reg }; - }, - else => unreachable, // invalid return result - }; - - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airIntCast(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - if (self.liveness.isUnused(inst)) - return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - - const pt = self.pt; - const zcu = pt.zcu; - const operand = ty_op.operand; - const operand_mcv = try self.resolveInst(operand); - const operand_ty = self.typeOf(operand); - const operand_info = operand_ty.intInfo(zcu); - - const dest_ty = self.typeOfIndex(inst); - const dest_info = dest_ty.intInfo(zcu); - - const result: MCValue = result: { - const operand_lock: ?RegisterLock = switch (operand_mcv) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - - const truncated: MCValue = switch (operand_mcv) { - .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) }, - else => operand_mcv, - }; - - if (dest_info.bits > operand_info.bits) { - const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); - try self.setRegOrMem(self.typeOfIndex(inst), dest_mcv, truncated); - break :result dest_mcv; - } else { - if (self.reuseOperand(inst, operand, 0, truncated)) { - break :result truncated; - } else { - const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); - try self.setRegOrMem(self.typeOfIndex(inst), dest_mcv, truncated); - break :result dest_mcv; - } - } - }; - - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn truncRegister( - self: *Self, - operand_reg: Register, - dest_reg: Register, - int_signedness: std.builtin.Signedness, - int_bits: u16, -) !void { - switch (int_bits) { - 1...31, 33...63 => { - _ = try self.addInst(.{ - .tag = switch (int_signedness) { - .signed => .sbfx, - .unsigned => .ubfx, - }, - .data = .{ .rr_lsb_width = .{ - .rd = dest_reg, - .rn = operand_reg, - .lsb = 0, - .width = @as(u6, @intCast(int_bits)), - } }, - }); - }, - 32, 64 => { - _ = try self.addInst(.{ - .tag = .mov_register, - .data = .{ .rr = .{ - .rd = if (int_bits == 32) dest_reg.toW() else dest_reg.toX(), - .rn = if (int_bits == 32) operand_reg.toW() else operand_reg.toX(), - } }, - }); - }, - else => unreachable, - } -} - -fn trunc( - self: *Self, - maybe_inst: ?Air.Inst.Index, - operand: MCValue, - operand_ty: Type, - dest_ty: Type, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const info_a = operand_ty.intInfo(zcu); - const info_b = dest_ty.intInfo(zcu); - - if (info_b.bits <= 64) { - const operand_reg = switch (operand) { - .register => |r| r, - else => operand_reg: { - if (info_a.bits <= 64) { - const raw_reg = try self.copyToTmpRegister(operand_ty, operand); - break :operand_reg self.registerAlias(raw_reg, operand_ty); - } else { - return self.fail("TODO load least significant word into register", .{}); - } - }, - }; - const lock = self.register_manager.lockReg(operand_reg); - defer if (lock) |reg| self.register_manager.unlockReg(reg); - - const dest_reg = if (maybe_inst) |inst| blk: { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - - if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { - break :blk self.registerAlias(operand_reg, dest_ty); - } else { - const raw_reg = try self.register_manager.allocReg(inst, gp); - break :blk self.registerAlias(raw_reg, dest_ty); - } - } else blk: { - const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk self.registerAlias(raw_reg, dest_ty); - }; - - try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); - - return MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO: truncate to ints > 64 bits", .{}); - } -} - -fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.typeOf(ty_op.operand); - const dest_ty = self.typeOfIndex(inst); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { - break :blk try self.trunc(inst, operand, operand_ty, dest_ty); - }; - - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.typeOf(ty_op.operand); - switch (operand) { - .dead => unreachable, - .unreach => unreachable, - .compare_flags => |cond| break :result MCValue{ .compare_flags = cond.negate() }, - else => { - switch (operand_ty.zigTypeTag(zcu)) { - .bool => { - // TODO convert this to mvn + and - const op_reg = switch (operand) { - .register => |r| r, - else => try self.copyToTmpRegister(operand_ty, operand), - }; - const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); - defer self.register_manager.unlockReg(reg_lock); - - const dest_reg = blk: { - if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { - break :blk op_reg; - } - - const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk self.registerAlias(raw_reg, operand_ty); - }; - - _ = try self.addInst(.{ - .tag = .eor_immediate, - .data = .{ .rr_bitmask = .{ - .rd = dest_reg, - .rn = op_reg, - .imms = 0b000000, - .immr = 0b000000, - .n = 0b0, - } }, - }); - - break :result MCValue{ .register = dest_reg }; - }, - .vector => return self.fail("TODO bitwise not for vectors", .{}), - .int => { - const int_info = operand_ty.intInfo(zcu); - if (int_info.bits <= 64) { - const op_reg = switch (operand) { - .register => |r| r, - else => try self.copyToTmpRegister(operand_ty, operand), - }; - const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); - defer self.register_manager.unlockReg(reg_lock); - - const dest_reg = blk: { - if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { - break :blk op_reg; - } - - const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk self.registerAlias(raw_reg, operand_ty); - }; - - _ = try self.addInst(.{ - .tag = .mvn, - .data = .{ .rr_imm6_logical_shift = .{ - .rd = dest_reg, - .rm = op_reg, - .imm6 = 0, - .shift = .lsl, - } }, - }); - - try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); - - break :result MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO AArch64 not on integers > u64/i64", .{}); - } - }, - else => unreachable, - } - }, - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn minMax( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM min/max on floats", .{}), - .vector => return self.fail("TODO ARM min/max on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{ 0, 1 }, - } else null, - ); - - // lhs == reg should have been checked by airMinMax - assert(lhs_reg != rhs_reg); // see note above - - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = lhs_reg, - .rm = rhs_reg, - .imm6 = 0, - .shift = .lsl, - } }, - }); - - const cond_choose_lhs: Condition = switch (tag) { - .max => switch (int_info.signedness) { - .signed => Condition.gt, - .unsigned => Condition.hi, - }, - .min => switch (int_info.signedness) { - .signed => Condition.lt, - .unsigned => Condition.cc, - }, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = .csel, - .data = .{ .rrr_cond = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - .cond = cond_choose_lhs, - } }, - }); - - return MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO ARM min/max on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn airMinMax(self: *Self, inst: Air.Inst.Index) InnerError!void { - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - const lhs = try self.resolveInst(bin_op.lhs); - if (bin_op.lhs == bin_op.rhs) break :result lhs; - - break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr = try self.resolveInst(bin_op.lhs); - const ptr_ty = self.typeOf(bin_op.lhs); - const len = try self.resolveInst(bin_op.rhs); - const len_ty = self.typeOf(bin_op.rhs); - - const stack_offset = try self.allocMem(16, .@"8", inst); - try self.genSetStack(ptr_ty, stack_offset, ptr); - try self.genSetStack(len_ty, stack_offset - 8, len); - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -/// An argument to a Mir instruction which is read (and possibly also -/// written to) by the respective instruction -const ReadArg = struct { - ty: Type, - bind: Bind, - class: RegisterManager.RegisterBitSet, - reg: *Register, - - const Bind = union(enum) { - inst: Air.Inst.Ref, - mcv: MCValue, - - fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue { - return switch (bind) { - .inst => |inst| try function.resolveInst(inst), - .mcv => |mcv| mcv, - }; - } - - fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u64 { - switch (bind) { - .inst => |inst| { - // TODO resolve independently of inst_table - const mcv = try function.resolveInst(inst); - switch (mcv) { - .immediate => |imm| return imm, - else => return null, - } - }, - .mcv => |mcv| { - switch (mcv) { - .immediate => |imm| return imm, - else => return null, - } - }, - } - } - }; -}; - -/// An argument to a Mir instruction which is written to (but not read -/// from) by the respective instruction -const WriteArg = struct { - ty: Type, - bind: Bind, - class: RegisterManager.RegisterBitSet, - reg: *Register, - - const Bind = union(enum) { - reg: Register, - none: void, - }; -}; - -/// Holds all data necessary for enabling the potential reuse of -/// operand registers as destinations -const ReuseMetadata = struct { - corresponding_inst: Air.Inst.Index, - - /// Maps every element index of read_args to the corresponding - /// index in the Air instruction - /// - /// When the order of read_args corresponds exactly to the order - /// of the inputs of the Air instruction, this would be e.g. - /// &.{ 0, 1 }. However, when the order is not the same or some - /// inputs to the Air instruction are omitted (e.g. when they can - /// be represented as immediates to the Mir instruction), - /// operand_mapping should reflect that fact. - operand_mapping: []const Air.Liveness.OperandInt, -}; - -/// Allocate a set of registers for use as arguments for a Mir -/// instruction -/// -/// If the Mir instruction these registers are allocated for -/// corresponds exactly to a single Air instruction, populate -/// reuse_metadata in order to enable potential reuse of an operand as -/// the destination (provided that that operand dies in this -/// instruction). -/// -/// Reusing an operand register as destination is the only time two -/// arguments may share the same register. In all other cases, -/// allocRegs guarantees that a register will never be allocated to -/// more than one argument. -/// -/// Furthermore, allocReg guarantees that all arguments which are -/// already bound to registers before calling allocRegs will not -/// change their register binding. This is done by locking these -/// registers. -fn allocRegs( - self: *Self, - read_args: []const ReadArg, - write_args: []const WriteArg, - reuse_metadata: ?ReuseMetadata, -) InnerError!void { - // Air instructions have exactly one output - assert(!(reuse_metadata != null and write_args.len != 1)); // see note above - - // The operand mapping is a 1:1 mapping of read args to their - // corresponding operand index in the Air instruction - assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above - - const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len); - defer self.gpa.free(locks); - const read_locks = locks[0..read_args.len]; - const write_locks = locks[read_args.len..]; - - @memset(locks, null); - defer for (locks) |lock| { - if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg); - }; - - // When we reuse a read_arg as a destination, the corresponding - // MCValue of the read_arg will be set to .dead. In that case, we - // skip allocating this read_arg. - var reused_read_arg: ?usize = null; - - // Lock all args which are already allocated to registers - for (read_args, 0..) |arg, i| { - const mcv = try arg.bind.resolveToMcv(self); - if (mcv == .register) { - read_locks[i] = self.register_manager.lockReg(mcv.register); - } - } - - for (write_args, 0..) |arg, i| { - if (arg.bind == .reg) { - write_locks[i] = self.register_manager.lockReg(arg.bind.reg); - } - } - - // Allocate registers for all args which aren't allocated to - // registers yet - for (read_args, 0..) |arg, i| { - const mcv = try arg.bind.resolveToMcv(self); - if (mcv == .register) { - const raw_reg = mcv.register; - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - } else { - const track_inst: ?Air.Inst.Index = switch (arg.bind) { - .inst => |inst| inst.toIndex().?, - else => null, - }; - const raw_reg = try self.register_manager.allocReg(track_inst, gp); - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - read_locks[i] = self.register_manager.lockRegAssumeUnused(arg.reg.*); - } - } - - if (reuse_metadata != null) { - const inst = reuse_metadata.?.corresponding_inst; - const operand_mapping = reuse_metadata.?.operand_mapping; - const arg = write_args[0]; - if (arg.bind == .reg) { - const raw_reg = arg.bind.reg; - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - } else { - reuse_operand: for (read_args, 0..) |read_arg, i| { - if (read_arg.bind == .inst) { - const operand = read_arg.bind.inst; - const mcv = try self.resolveInst(operand); - if (mcv == .register and - std.meta.eql(arg.class, read_arg.class) and - self.reuseOperand(inst, operand, operand_mapping[i], mcv)) - { - const raw_reg = mcv.register; - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - write_locks[0] = null; - reused_read_arg = i; - break :reuse_operand; - } - } - } else { - const raw_reg = try self.register_manager.allocReg(inst, arg.class); - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - write_locks[0] = self.register_manager.lockReg(arg.reg.*); - } - } - } else { - for (write_args, 0..) |arg, i| { - if (arg.bind == .reg) { - const raw_reg = arg.bind.reg; - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - } else { - const raw_reg = try self.register_manager.allocReg(null, arg.class); - arg.reg.* = self.registerAlias(raw_reg, arg.ty); - write_locks[i] = self.register_manager.lockReg(arg.reg.*); - } - } - } - - // For all read_args which need to be moved from non-register to - // register, perform the move - for (read_args, 0..) |arg, i| { - if (reused_read_arg) |j| { - // Check whether this read_arg was reused - if (i == j) continue; - } - - const mcv = try arg.bind.resolveToMcv(self); - if (mcv != .register) { - if (arg.bind == .inst) { - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - const inst = arg.bind.inst.toIndex().?; - - // Overwrite the MCValue associated with this inst - branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); - - // If the previous MCValue occupied some space we track, we - // need to make sure it is marked as free now. - switch (mcv) { - .compare_flags => { - assert(self.compare_flags_inst.? == inst); - self.compare_flags_inst = null; - }, - .register => |prev_reg| { - assert(!self.register_manager.isRegFree(prev_reg)); - self.register_manager.freeReg(prev_reg); - }, - else => {}, - } - } - - try self.genSetReg(arg.ty, arg.reg.*, mcv); - } - } -} - -/// Wrapper around allocRegs and addInst tailored for specific Mir -/// instructions which are binary operations acting on two registers -/// -/// Returns the destination register -fn binOpRegister( - self: *Self, - mir_tag: Mir.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{ 0, 1 }, - } else null, - ); - - const mir_data: Mir.Inst.Data = switch (mir_tag) { - .add_shifted_register, - .adds_shifted_register, - .sub_shifted_register, - .subs_shifted_register, - => .{ .rrr_imm6_shift = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - .imm6 = 0, - .shift = .lsl, - } }, - .mul, - .lsl_register, - .asr_register, - .lsr_register, - .sdiv, - .udiv, - => .{ .rrr = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - .smull, - .umull, - => .{ .rrr = .{ - .rd = dest_reg.toX(), - .rn = lhs_reg, - .rm = rhs_reg, - } }, - .and_shifted_register, - .orr_shifted_register, - .eor_shifted_register, - => .{ .rrr_imm6_logical_shift = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - .imm6 = 0, - .shift = .lsl, - } }, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = mir_tag, - .data = mir_data, - }); - - return MCValue{ .register = dest_reg }; -} - -/// Wrapper around allocRegs and addInst tailored for specific Mir -/// instructions which are binary operations acting on a register and -/// an immediate -/// -/// Returns the destination register -fn binOpImmediate( - self: *Self, - mir_tag: Mir.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_immediate: u64, - lhs_ty: Type, - lhs_and_rhs_swapped: bool, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - var lhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - const operand_mapping: []const Air.Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = operand_mapping, - } else null, - ); - - const mir_data: Mir.Inst.Data = switch (mir_tag) { - .add_immediate, - .adds_immediate, - .sub_immediate, - .subs_immediate, - => .{ .rr_imm12_sh = .{ - .rd = dest_reg, - .rn = lhs_reg, - .imm12 = @as(u12, @intCast(rhs_immediate)), - } }, - .lsl_immediate, - .asr_immediate, - .lsr_immediate, - => .{ .rr_shift = .{ - .rd = dest_reg, - .rn = lhs_reg, - .shift = @as(u6, @intCast(rhs_immediate)), - } }, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = mir_tag, - .data = mir_data, - }); - - return MCValue{ .register = dest_reg }; -} - -fn addSub( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO binary operations on floats", .{}), - .vector => return self.fail("TODO binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - const lhs_immediate = try lhs_bind.resolveToImmediate(self); - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - // Only say yes if the operation is - // commutative, i.e. we can swap both of the - // operands - const lhs_immediate_ok = switch (tag) { - .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, - .sub => false, - else => unreachable, - }; - const rhs_immediate_ok = switch (tag) { - .add, - .sub, - => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, - else => unreachable, - }; - - const mir_tag_register: Mir.Inst.Tag = switch (tag) { - .add => .add_shifted_register, - .sub => .sub_shifted_register, - else => unreachable, - }; - const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { - .add => .add_immediate, - .sub => .sub_immediate, - else => unreachable, - }; - - if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); - } else if (lhs_immediate_ok) { - // swap lhs and rhs - return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); - } else { - return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } - } else { - return self.fail("TODO binary operations on int with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn mul( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - // TODO add optimisations for multiplication - // with immediates, for example a * 2 can be - // lowered to a << 1 - return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } else { - return self.fail("TODO binary operations on int with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn divFloat( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - _ = lhs_bind; - _ = rhs_bind; - _ = rhs_ty; - _ = maybe_inst; - - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO div_float", .{}), - .vector => return self.fail("TODO div_float on vectors", .{}), - else => unreachable, - } -} - -fn divTrunc( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO div on floats", .{}), - .vector => return self.fail("TODO div on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - switch (int_info.signedness) { - .signed => { - // TODO optimize integer division by constants - return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - }, - .unsigned => { - // TODO optimize integer division by constants - return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - }, - } - } else { - return self.fail("TODO integer division for ints with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn divFloor( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO div on floats", .{}), - .vector => return self.fail("TODO div on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - switch (int_info.signedness) { - .signed => { - return self.fail("TODO div_floor on signed integers", .{}); - }, - .unsigned => { - // TODO optimize integer division by constants - return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - }, - } - } else { - return self.fail("TODO integer division for ints with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn divExact( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO div on floats", .{}), - .vector => return self.fail("TODO div on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - switch (int_info.signedness) { - .signed => { - // TODO optimize integer division by constants - return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - }, - .unsigned => { - // TODO optimize integer division by constants - return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - }, - } - } else { - return self.fail("TODO integer division for ints with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn rem( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - _ = maybe_inst; - - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO rem/zcu on floats", .{}), - .vector => return self.fail("TODO rem/zcu on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var quotient_reg: Register = undefined; - var remainder_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = "ient_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &remainder_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - _ = try self.addInst(.{ - .tag = switch (int_info.signedness) { - .signed => .sdiv, - .unsigned => .udiv, - }, - .data = .{ .rrr = .{ - .rd = quotient_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - _ = try self.addInst(.{ - .tag = .msub, - .data = .{ .rrrr = .{ - .rd = remainder_reg, - .rn = quotient_reg, - .rm = rhs_reg, - .ra = lhs_reg, - } }, - }); - - return MCValue{ .register = remainder_reg }; - } else { - return self.fail("TODO rem/zcu for integers with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn modulo( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - _ = lhs_bind; - _ = rhs_bind; - _ = rhs_ty; - _ = maybe_inst; - - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO zcu on floats", .{}), - .vector => return self.fail("TODO zcu on vectors", .{}), - .int => return self.fail("TODO zcu on ints", .{}), - else => unreachable, - } -} - -fn wrappingArithmetic( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO binary operations on vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - // Generate an add/sub/mul - const result: MCValue = switch (tag) { - .add_wrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - .sub_wrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - .mul_wrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - else => unreachable, - }; - - // Truncate if necessary - const result_reg = result.register; - try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); - return result; - } else { - return self.fail("TODO binary operations on integers > u64/i64", .{}); - } - }, - else => unreachable, - } -} - -fn bitwise( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - // TODO implement bitwise operations with immediates - const mir_tag: Mir.Inst.Tag = switch (tag) { - .bit_and => .and_shifted_register, - .bit_or => .orr_shifted_register, - .xor => .eor_shifted_register, - else => unreachable, - }; - - return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } else { - return self.fail("TODO binary operations on int with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn shiftExact( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => if (!rhs_ty.isVector(zcu)) - return self.fail("TODO vector shift with scalar rhs", .{}) - else - return self.fail("TODO binary operations on vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - const mir_tag_register: Mir.Inst.Tag = switch (tag) { - .shl_exact => .lsl_register, - .shr_exact => switch (int_info.signedness) { - .signed => Mir.Inst.Tag.asr_register, - .unsigned => Mir.Inst.Tag.lsr_register, - }, - else => unreachable, - }; - const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { - .shl_exact => .lsl_immediate, - .shr_exact => switch (int_info.signedness) { - .signed => Mir.Inst.Tag.asr_immediate, - .unsigned => Mir.Inst.Tag.lsr_immediate, - }, - else => unreachable, - }; - - if (rhs_immediate) |imm| { - return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); - } else { - // We intentionally pass lhs_ty here in order to - // prevent using the 32-bit register alias when - // lhs_ty is > 32 bits. - return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); - } - } else { - return self.fail("TODO binary operations on int with bits > 64", .{}); - } - }, - else => unreachable, - } -} - -fn shiftNormal( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => if (!rhs_ty.isVector(zcu)) - return self.fail("TODO vector shift with scalar rhs", .{}) - else - return self.fail("TODO binary operations on vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - // Generate a shl_exact/shr_exact - const result: MCValue = switch (tag) { - .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - else => unreachable, - }; - - // Truncate if necessary - switch (tag) { - .shr => return result, - .shl => { - const result_reg = result.register; - try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); - return result; - }, - else => unreachable, - } - } else { - return self.fail("TODO binary operations on integers > u64/i64", .{}); - } - }, - else => unreachable, - } -} - -fn booleanOp( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .bool => { - assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema - assert((try rhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema - - const mir_tag_register: Mir.Inst.Tag = switch (tag) { - .bool_and => .and_shifted_register, - .bool_or => .orr_shifted_register, - else => unreachable, - }; - - return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - }, - else => unreachable, - } -} - -fn ptrArithmetic( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .pointer => { - assert(rhs_ty.eql(Type.usize, zcu)); - - const ptr_ty = lhs_ty; - const elem_ty = switch (ptr_ty.ptrSize(zcu)) { - .one => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type - else => ptr_ty.childType(zcu), - }; - const elem_size = elem_ty.abiSize(zcu); - - const base_tag: Air.Inst.Tag = switch (tag) { - .ptr_add => .add, - .ptr_sub => .sub, - else => unreachable, - }; - - if (elem_size == 1) { - return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst); - } else { - // convert the offset into a byte offset by - // multiplying it with elem_size - const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } }; - - const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null); - const offset_bind = ReadArg.Bind{ .mcv = offset }; - - const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null); - return addr; - } - }, - else => unreachable, - } -} - -fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result switch (tag) { - .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .add_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .sub_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .mul_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - else => unreachable, - }; - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airAddSat(self: *Self, inst: Air.Inst.Index) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airSubSat(self: *Self, inst: Air.Inst.Index) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airMulSat(self: *Self, inst: Air.Inst.Index) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void { - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - const lhs_ty = self.typeOf(extra.lhs); - const rhs_ty = self.typeOf(extra.rhs); - - const tuple_ty = self.typeOfIndex(inst); - const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu)); - const tuple_align = tuple_ty.abiAlignment(zcu); - const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); - - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - switch (int_info.bits) { - 1...31, 33...63 => { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; - - const base_tag: Air.Inst.Tag = switch (tag) { - .add_with_overflow => .add, - .sub_with_overflow => .sub, - else => unreachable, - }; - const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); - const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); - defer self.register_manager.unlockReg(dest_reg_lock); - - const raw_truncated_reg = try self.register_manager.allocReg(null, gp); - const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty); - const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); - defer self.register_manager.unlockReg(truncated_reg_lock); - - // sbfx/ubfx truncated, dest, #0, #bits - try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); - - // cmp dest, truncated - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = dest_reg, - .rm = truncated_reg, - .imm6 = 0, - .shift = .lsl, - } }, - }); - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - }, - 32, 64 => { - const lhs_immediate = try lhs_bind.resolveToImmediate(self); - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - // Only say yes if the operation is - // commutative, i.e. we can swap both of the - // operands - const lhs_immediate_ok = switch (tag) { - .add_with_overflow => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, - .sub_with_overflow => false, - else => unreachable, - }; - const rhs_immediate_ok = switch (tag) { - .add_with_overflow, - .sub_with_overflow, - => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, - else => unreachable, - }; - - const mir_tag_register: Mir.Inst.Tag = switch (tag) { - .add_with_overflow => .adds_shifted_register, - .sub_with_overflow => .subs_shifted_register, - else => unreachable, - }; - const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { - .add_with_overflow => .adds_immediate, - .sub_with_overflow => .subs_immediate, - else => unreachable, - }; - - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; - - const dest = blk: { - if (rhs_immediate_ok) { - break :blk try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null); - } else if (lhs_immediate_ok) { - // swap lhs and rhs - break :blk try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null); - } else { - break :blk try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); - } - }; - - const flag: bits.Instruction.Condition = switch (int_info.signedness) { - .unsigned => switch (tag) { - .add_with_overflow => bits.Instruction.Condition.cs, - .sub_with_overflow => bits.Instruction.Condition.cc, - else => unreachable, - }, - .signed => .vs, - }; - break :result MCValue{ .register_with_overflow = .{ - .reg = dest.register, - .flag = flag, - } }; - }, - else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), - } - }, - else => unreachable, - } - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const zcu = self.pt.zcu; - const result: MCValue = result: { - const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - const lhs_ty = self.typeOf(extra.lhs); - const rhs_ty = self.typeOf(extra.rhs); - - const tuple_ty = self.typeOfIndex(inst); - const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu))); - const tuple_align = tuple_ty.abiAlignment(zcu); - const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu))); - - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { - .signed => .smull, - .unsigned => .umull, - }; - - const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); - const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); - defer self.register_manager.unlockReg(dest_reg_lock); - - const truncated_reg = try self.register_manager.allocReg(null, gp); - const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); - defer self.register_manager.unlockReg(truncated_reg_lock); - - try self.truncRegister( - dest_reg.toW(), - truncated_reg.toW(), - int_info.signedness, - int_info.bits, - ); - - switch (int_info.signedness) { - .signed => { - _ = try self.addInst(.{ - .tag = .cmp_extended_register, - .data = .{ .rr_extend_shift = .{ - .rn = dest_reg.toX(), - .rm = truncated_reg.toW(), - .ext_type = .sxtw, - .imm3 = 0, - } }, - }); - }, - .unsigned => { - _ = try self.addInst(.{ - .tag = .cmp_extended_register, - .data = .{ .rr_extend_shift = .{ - .rn = dest_reg.toX(), - .rm = truncated_reg.toW(), - .ext_type = .uxtw, - .imm3 = 0, - } }, - }); - }, - } - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else if (int_info.bits <= 64) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - var dest_high_reg: Register = undefined; - var truncated_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_high_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - switch (int_info.signedness) { - .signed => { - // mul dest, lhs, rhs - _ = try self.addInst(.{ - .tag = .mul, - .data = .{ .rrr = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - // smulh dest_high, lhs, rhs - _ = try self.addInst(.{ - .tag = .smulh, - .data = .{ .rrr = .{ - .rd = dest_high_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - // cmp dest_high, dest, asr #63 - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = dest_high_reg, - .rm = dest_reg, - .imm6 = 63, - .shift = .asr, - } }, - }); - - const shift: u6 = @as(u6, @intCast(@as(u7, 64) - @as(u7, @intCast(int_info.bits)))); - if (shift > 0) { - // lsl dest_high, dest, #shift - _ = try self.addInst(.{ - .tag = .lsl_immediate, - .data = .{ .rr_shift = .{ - .rd = dest_high_reg, - .rn = dest_reg, - .shift = shift, - } }, - }); - - // cmp dest, dest_high, #shift - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = dest_reg, - .rm = dest_high_reg, - .imm6 = shift, - .shift = .asr, - } }, - }); - } - }, - .unsigned => { - // umulh dest_high, lhs, rhs - _ = try self.addInst(.{ - .tag = .umulh, - .data = .{ .rrr = .{ - .rd = dest_high_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - // mul dest, lhs, rhs - _ = try self.addInst(.{ - .tag = .mul, - .data = .{ .rrr = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - _ = try self.addInst(.{ - .tag = .cmp_immediate, - .data = .{ .r_imm12_sh = .{ - .rn = dest_high_reg, - .imm12 = 0, - } }, - }); - - if (int_info.bits < 64) { - // lsr dest_high, dest, #shift - _ = try self.addInst(.{ - .tag = .lsr_immediate, - .data = .{ .rr_shift = .{ - .rd = dest_high_reg, - .rn = dest_reg, - .shift = @as(u6, @intCast(int_info.bits)), - } }, - }); - - _ = try self.addInst(.{ - .tag = .cmp_immediate, - .data = .{ .r_imm12_sh = .{ - .rn = dest_high_reg, - .imm12 = 0, - } }, - }); - } - }, - } - - try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); - }, - else => unreachable, - } - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = result: { - const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - const lhs_ty = self.typeOf(extra.lhs); - const rhs_ty = self.typeOf(extra.rhs); - - const tuple_ty = self.typeOfIndex(inst); - const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu))); - const tuple_align = tuple_ty.abiAlignment(zcu); - const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu))); - - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => if (!rhs_ty.isVector(zcu)) - return self.fail("TODO implement vector shl_with_overflow with scalar rhs", .{}) - else - return self.fail("TODO implement shl_with_overflow for vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 64) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - var reconstructed_reg: Register = undefined; - - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - if (rhs_immediate) |imm| { - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - // lsl dest, lhs, rhs - _ = try self.addInst(.{ - .tag = .lsl_immediate, - .data = .{ .rr_shift = .{ - .rd = dest_reg, - .rn = lhs_reg, - .shift = @as(u6, @intCast(imm)), - } }, - }); - - try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); - - // asr/lsr reconstructed, dest, rhs - _ = try self.addInst(.{ - .tag = switch (int_info.signedness) { - .signed => Mir.Inst.Tag.asr_immediate, - .unsigned => Mir.Inst.Tag.lsr_immediate, - }, - .data = .{ .rr_shift = .{ - .rd = reconstructed_reg, - .rn = dest_reg, - .shift = @as(u6, @intCast(imm)), - } }, - }); - } else { - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - // lsl dest, lhs, rhs - _ = try self.addInst(.{ - .tag = .lsl_register, - .data = .{ .rrr = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); - - // asr/lsr reconstructed, dest, rhs - _ = try self.addInst(.{ - .tag = switch (int_info.signedness) { - .signed => Mir.Inst.Tag.asr_register, - .unsigned => Mir.Inst.Tag.lsr_register, - }, - .data = .{ .rrr = .{ - .rd = reconstructed_reg, - .rn = dest_reg, - .rm = rhs_reg, - } }, - }); - } - - // cmp lhs, reconstructed - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = lhs_reg, - .rm = reconstructed_reg, - .imm6 = 0, - .shift = .lsl, - } }, - }); - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else { - return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void { - const zcu = self.pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else if (self.typeOf(bin_op.lhs).isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu)) - return self.fail("TODO implement vector shl_sat with scalar rhs for {}", .{self.target.cpu.arch}) - else - return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const optional_ty = self.typeOf(ty_op.operand); - const mcv = try self.resolveInst(ty_op.operand); - break :result try self.optionalPayload(inst, mcv, optional_ty); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: Type) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const payload_ty = optional_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBits(zcu)) return MCValue.none; - if (optional_ty.isPtrLikeOptional(zcu)) { - // TODO should we reuse the operand here? - const raw_reg = try self.register_manager.allocReg(inst, gp); - const reg = self.registerAlias(raw_reg, payload_ty); - try self.genSetReg(payload_ty, reg, mcv); - return MCValue{ .register = reg }; - } - - switch (mcv) { - .register => { - // TODO should we reuse the operand here? - const raw_reg = try self.register_manager.allocReg(inst, gp); - const dest_reg = raw_reg.toX(); - - try self.genSetReg(payload_ty, dest_reg, mcv); - return MCValue{ .register = self.registerAlias(dest_reg, payload_ty) }; - }, - .stack_argument_offset, .stack_offset, .memory => return mcv, - else => unreachable, // invalid MCValue for an error union - } -} - -fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// Given an error union, returns the error -fn errUnionErr( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const err_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - if (err_ty.errorSetIsEmpty(zcu)) { - return MCValue{ .immediate = 0 }; - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return try error_union_bind.resolveToMcv(self); - } - - const err_offset: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu)); - switch (try error_union_bind.resolveToMcv(self)) { - .register => { - var operand_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - } else null, - ); - - const err_bit_offset = err_offset * 8; - const err_bit_size = @as(u32, @intCast(err_ty.abiSize(zcu))) * 8; - - _ = try self.addInst(.{ - .tag = .ubfx, // errors are unsigned integers - .data = .{ - .rr_lsb_width = .{ - // Set both registers to the X variant to get the full width - .rd = dest_reg.toX(), - .rn = operand_reg.toX(), - .lsb = @as(u6, @intCast(err_bit_offset)), - .width = @as(u7, @intCast(err_bit_size)), - }, - }, - }); - - return MCValue{ .register = dest_reg }; - }, - .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off + err_offset }; - }, - .stack_offset => |off| { - return MCValue{ .stack_offset = off - err_offset }; - }, - .memory => |addr| { - return MCValue{ .memory = addr + err_offset }; - }, - else => unreachable, // invalid MCValue for an error union - } -} - -fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; - const error_union_ty = self.typeOf(ty_op.operand); - - break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// Given an error union, returns the payload -fn errUnionPayload( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const err_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - if (err_ty.errorSetIsEmpty(zcu)) { - return try error_union_bind.resolveToMcv(self); - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return MCValue.none; - } - - const payload_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))); - switch (try error_union_bind.resolveToMcv(self)) { - .register => { - var operand_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - } else null, - ); - - const payload_bit_offset = payload_offset * 8; - const payload_bit_size = @as(u32, @intCast(payload_ty.abiSize(zcu))) * 8; - - _ = try self.addInst(.{ - .tag = if (payload_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx, - .data = .{ - .rr_lsb_width = .{ - // Set both registers to the X variant to get the full width - .rd = dest_reg.toX(), - .rn = operand_reg.toX(), - .lsb = @as(u5, @intCast(payload_bit_offset)), - .width = @as(u6, @intCast(payload_bit_size)), - }, - }, - }); - - return MCValue{ .register = dest_reg }; - }, - .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off + payload_offset }; - }, - .stack_offset => |off| { - return MCValue{ .stack_offset = off - payload_offset }; - }, - .memory => |addr| { - return MCValue{ .memory = addr + payload_offset }; - }, - else => unreachable, // invalid MCValue for an error union - } -} - -fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; - const error_union_ty = self.typeOf(ty_op.operand); - - break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -// *(E!T) -> E -fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - 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}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -// *(E!T) -> *T -fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - 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}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void { - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); -} - -fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch}); -} - -fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - } - - const result: MCValue = result: { - const payload_ty = self.typeOf(ty_op.operand); - if (!payload_ty.hasRuntimeBits(zcu)) { - break :result MCValue{ .immediate = 1 }; - } - - const optional_ty = self.typeOfIndex(inst); - const operand = try self.resolveInst(ty_op.operand); - const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - - if (optional_ty.isPtrLikeOptional(zcu)) { - // TODO should we check if we can reuse the operand? - const raw_reg = try self.register_manager.allocReg(inst, gp); - const reg = self.registerAlias(raw_reg, payload_ty); - try self.genSetReg(payload_ty, raw_reg, operand); - break :result MCValue{ .register = reg }; - } - - const optional_abi_size: u32 = @intCast(optional_ty.abiSize(zcu)); - const optional_abi_align = optional_ty.abiAlignment(zcu); - const offset: u32 = @intCast(payload_ty.abiSize(zcu)); - - const stack_offset = try self.allocMem(optional_abi_size, optional_abi_align, inst); - try self.genSetStack(payload_ty, stack_offset, operand); - try self.genSetStack(Type.bool, stack_offset - offset, .{ .immediate = 1 }); - - break :result MCValue{ .stack_offset = stack_offset }; - }; - - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// T to E!T -fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_ty = ty_op.ty.toType(); - const error_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - const operand = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand; - - const abi_size = @as(u32, @intCast(error_union_ty.abiSize(zcu))); - const abi_align = error_union_ty.abiAlignment(zcu); - const stack_offset = try self.allocMem(abi_size, abi_align, inst); - const payload_off = errUnionPayloadOffset(payload_ty, zcu); - const err_off = errUnionErrorOffset(payload_ty, zcu); - try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), operand); - try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), .{ .immediate = 0 }); - - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// E to E!T -fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const pt = self.pt; - const zcu = pt.zcu; - const error_union_ty = ty_op.ty.toType(); - const error_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - const operand = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand; - - const abi_size = @as(u32, @intCast(error_union_ty.abiSize(zcu))); - const abi_align = error_union_ty.abiAlignment(zcu); - const stack_offset = try self.allocMem(abi_size, abi_align, inst); - const payload_off = errUnionPayloadOffset(payload_ty, zcu); - const err_off = errUnionErrorOffset(payload_ty, zcu); - try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), operand); - try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), .undef); - - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn slicePtr(mcv: MCValue) MCValue { - switch (mcv) { - .dead, .unreach, .none => unreachable, - .register => unreachable, // a slice doesn't fit in one register - .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off }; - }, - .stack_offset => |off| { - return MCValue{ .stack_offset = off }; - }, - .memory => |addr| { - return MCValue{ .memory = addr }; - }, - else => unreachable, // invalid MCValue for a slice - } -} - -fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(ty_op.operand); - break :result slicePtr(mcv); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr_bits = 64; - const ptr_bytes = @divExact(ptr_bits, 8); - const mcv = try self.resolveInst(ty_op.operand); - switch (mcv) { - .dead, .unreach, .none => unreachable, - .register => unreachable, // a slice doesn't fit in one register - .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + ptr_bytes }; - }, - .stack_offset => |off| { - break :result MCValue{ .stack_offset = off - ptr_bytes }; - }, - .memory => |addr| { - break :result MCValue{ .memory = addr + ptr_bytes }; - }, - else => return self.fail("TODO implement slice_len for {}", .{mcv}), - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr_bits = 64; - const ptr_bytes = @divExact(ptr_bits, 8); - const mcv = try self.resolveInst(ty_op.operand); - switch (mcv) { - .dead, .unreach, .none => unreachable, - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off - ptr_bytes }; - }, - else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(ty_op.operand); - switch (mcv) { - .dead, .unreach, .none => unreachable, - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off }; - }, - else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const slice_ty = self.typeOf(bin_op.lhs); - const result: MCValue = if (!slice_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: { - const ptr_ty = slice_ty.slicePtrFieldType(zcu); - - const slice_mcv = try self.resolveInst(bin_op.lhs); - const base_mcv = slicePtr(slice_mcv); - - const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; - const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn ptrElemVal( - self: *Self, - ptr_bind: ReadArg.Bind, - index_bind: ReadArg.Bind, - ptr_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = ptr_ty.childType(zcu); - const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); - - // TODO optimize for elem_sizes of 1, 2, 4, 8 - switch (elem_size) { - else => { - const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null); - - const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst); - try self.load(dest, addr, ptr_ty); - return dest; - }, - } -} - -fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const slice_mcv = try self.resolveInst(extra.lhs); - const base_mcv = slicePtr(slice_mcv); - - const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; - const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - - const slice_ty = self.typeOf(extra.lhs); - const index_ty = self.typeOf(extra.rhs); - - const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null); - break :result addr; - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const ptr_ty = self.typeOf(bin_op.lhs); - const result: MCValue = if (!ptr_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: { - const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - - const ptr_ty = self.typeOf(extra.lhs); - const index_ty = self.typeOf(extra.rhs); - - const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null); - break :result addr; - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - _ = bin_op; - return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); -} - -fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airAbs(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airBitReverse(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airUnaryMath(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn reuseOperand( - self: *Self, - inst: Air.Inst.Index, - operand: Air.Inst.Ref, - op_index: Air.Liveness.OperandInt, - mcv: MCValue, -) bool { - if (!self.liveness.operandDies(inst, op_index)) - return false; - - switch (mcv) { - .register => |reg| { - // If it's in the registers table, need to associate the register with the - // new instruction. - if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { - if (!self.register_manager.isRegFree(reg)) { - self.register_manager.registers[index] = inst; - } - } - log.debug("%{d} => {} (reused)", .{ inst, reg }); - }, - .stack_offset => |off| { - log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); - }, - else => return false, - } - - // Prevent the operand deaths processing code from deallocating it. - self.reused_operands.set(op_index); - - // That makes us responsible for doing the rest of the stuff that processDeath would have done. - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead); - - return true; -} - -fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = ptr_ty.childType(zcu); - const elem_size = elem_ty.abiSize(zcu); - - switch (ptr) { - .none => unreachable, - .undef => unreachable, - .unreach => unreachable, - .dead => unreachable, - .compare_flags, - .register_with_overflow, - => unreachable, // cannot hold an address - .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), - .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), - .register => |addr_reg| { - const addr_reg_lock = self.register_manager.lockReg(addr_reg); - defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); - - switch (dst_mcv) { - .dead => unreachable, - .undef => unreachable, - .compare_flags => unreachable, - .register => |dst_reg| { - try self.genLdrRegister(dst_reg, addr_reg, elem_ty); - }, - .stack_offset => |off| { - if (elem_size <= 8) { - const raw_tmp_reg = try self.register_manager.allocReg(null, gp); - const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty); - const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_reg_lock); - - try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); - try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); - } else { - // TODO optimize the register allocation - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); - const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); - defer for (regs_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const src_reg = addr_reg; - const dst_reg = regs[0]; - const len_reg = regs[1]; - const count_reg = regs[2]; - const tmp_reg = regs[3]; - - // sub dst_reg, fp, #off - try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); - - // mov len, #elem_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - else => return self.fail("TODO load from register into {}", .{dst_mcv}), - } - }, - .memory, - .stack_offset, - .stack_argument_offset, - .linker_load, - => { - const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); - try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty); - }, - } -} - -fn genInlineMemcpy( - self: *Self, - src: Register, - dst: Register, - len: Register, - count: Register, - tmp: Register, -) !void { - // movz count, #0 - _ = try self.addInst(.{ - .tag = .movz, - .data = .{ .r_imm16_sh = .{ - .rd = count, - .imm16 = 0, - } }, - }); - - // loop: - // cmp count, len - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = count, - .rm = len, - .imm6 = 0, - .shift = .lsl, - } }, - }); - - // bge end - _ = try self.addInst(.{ - .tag = .b_cond, - .data = .{ .inst_cond = .{ - .inst = @as(u32, @intCast(self.mir_instructions.len + 5)), - .cond = .ge, - } }, - }); - - // ldrb tmp, [src, count] - _ = try self.addInst(.{ - .tag = .ldrb_register, - .data = .{ .load_store_register_register = .{ - .rt = tmp, - .rn = src, - .offset = Instruction.LoadStoreOffset.reg(count).register, - } }, - }); - - // strb tmp, [dest, count] - _ = try self.addInst(.{ - .tag = .strb_register, - .data = .{ .load_store_register_register = .{ - .rt = tmp, - .rn = dst, - .offset = Instruction.LoadStoreOffset.reg(count).register, - } }, - }); - - // add count, count, #1 - _ = try self.addInst(.{ - .tag = .add_immediate, - .data = .{ .rr_imm12_sh = .{ - .rd = count, - .rn = count, - .imm12 = 1, - } }, - }); - - // b loop - _ = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = @as(u32, @intCast(self.mir_instructions.len - 5)) }, - }); - - // end: -} - -fn genInlineMemset( - self: *Self, - dst: MCValue, - val: MCValue, - len: MCValue, -) !void { - const dst_reg = switch (dst) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.manyptr_u8, dst), - }; - const dst_reg_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); - - const val_reg = switch (val) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.u8, val), - }; - const val_reg_lock = self.register_manager.lockReg(val_reg); - defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock); - - const len_reg = switch (len) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.usize, len), - }; - const len_reg_lock = self.register_manager.lockReg(len_reg); - defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock); - - const count_reg = try self.register_manager.allocReg(null, gp); - - try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg); -} - -fn genInlineMemsetCode( - self: *Self, - dst: Register, - val: Register, - len: Register, - count: Register, -) !void { - // mov count, #0 - _ = try self.addInst(.{ - .tag = .movz, - .data = .{ .r_imm16_sh = .{ - .rd = count, - .imm16 = 0, - } }, - }); - - // loop: - // cmp count, len - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = count, - .rm = len, - .imm6 = 0, - .shift = .lsl, - } }, - }); - - // bge end - _ = try self.addInst(.{ - .tag = .b_cond, - .data = .{ .inst_cond = .{ - .inst = @as(u32, @intCast(self.mir_instructions.len + 4)), - .cond = .ge, - } }, - }); - - // strb val, [src, count] - _ = try self.addInst(.{ - .tag = .strb_register, - .data = .{ .load_store_register_register = .{ - .rt = val, - .rn = dst, - .offset = Instruction.LoadStoreOffset.reg(count).register, - } }, - }); - - // add count, count, #1 - _ = try self.addInst(.{ - .tag = .add_immediate, - .data = .{ .rr_imm12_sh = .{ - .rd = count, - .rn = count, - .imm12 = 1, - } }, - }); - - // b loop - _ = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = @as(u32, @intCast(self.mir_instructions.len - 4)) }, - }); - - // end: -} - -fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const elem_ty = self.typeOfIndex(inst); - const elem_size = elem_ty.abiSize(zcu); - const result: MCValue = result: { - if (!elem_ty.hasRuntimeBits(zcu)) - break :result MCValue.none; - - const ptr = try self.resolveInst(ty_op.operand); - const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(zcu); - if (self.liveness.isUnused(inst) and !is_volatile) - break :result MCValue.dead; - - const dst_mcv: MCValue = blk: { - if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk switch (ptr) { - .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) }, - else => ptr, - }; - } else { - break :blk try self.allocRegOrMem(elem_ty, true, inst); - } - }; - try self.load(dst_mcv, ptr, self.typeOf(ty_op.operand)); - break :result dst_mcv; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { - const pt = self.pt; - const zcu = pt.zcu; - const abi_size = ty.abiSize(zcu); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate, - 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate, - 4 => .ldr_immediate, - 8 => .ldr_immediate, - 3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}), - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg, - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); -} - -fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { - const pt = self.pt; - const abi_size = ty.abiSize(pt.zcu); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .strb_immediate, - 2 => .strh_immediate, - 4, 8 => .str_immediate, - 3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}), - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg, - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); -} - -fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - const pt = self.pt; - log.debug("store: storing {} to {}", .{ value, ptr }); - const abi_size = value_ty.abiSize(pt.zcu); - - switch (ptr) { - .none => unreachable, - .undef => unreachable, - .unreach => unreachable, - .dead => unreachable, - .compare_flags, - .register_with_overflow, - => unreachable, // cannot hold an address - .immediate => |imm| { - try self.setRegOrMem(value_ty, .{ .memory = imm }, value); - }, - .ptr_stack_offset => |off| { - try self.genSetStack(value_ty, off, value); - }, - .register => |addr_reg| { - const addr_reg_lock = self.register_manager.lockReg(addr_reg); - defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); - - switch (value) { - .dead => unreachable, - .undef => { - try self.genSetReg(value_ty, addr_reg, value); - }, - .register => |value_reg| { - log.debug("store: register {} to {}", .{ value_reg, addr_reg }); - try self.genStrRegister(value_reg, addr_reg, value_ty); - }, - else => { - if (abi_size <= 8) { - const raw_tmp_reg = try self.register_manager.allocReg(null, gp); - const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty); - const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_reg_lock); - - try self.genSetReg(value_ty, tmp_reg, value); - try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); - } else { - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); - const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); - defer for (regs_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const src_reg = regs[0]; - const dst_reg = addr_reg; - const len_reg = regs[1]; - const count_reg = regs[2]; - const tmp_reg = regs[3]; - - switch (value) { - .stack_offset => |off| { - // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); - }, - .stack_argument_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .load_store_stack = .{ - .rt = src_reg, - .offset = off, - } }, - }); - }, - .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @as(u32, @intCast(addr)) }), - .linker_load => |load_struct| { - const tag: Mir.Inst.Tag = switch (load_struct.type) { - .got => .load_memory_ptr_got, - .direct => .load_memory_ptr_direct, - .import => unreachable, - }; - const atom_index = switch (self.bin_file.tag) { - .macho => { - // const macho_file = self.bin_file.cast(link.File.MachO).?; - // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - // break :blk macho_file.getAtom(atom).getSymbolIndex().?; - @panic("TODO store"); - }, - .coff => blk: { - const coff_file = self.bin_file.cast(.coff).?; - const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav); - break :blk coff_file.getAtom(atom).getSymbolIndex().?; - }, - else => unreachable, // unsupported target format - }; - _ = try self.addInst(.{ - .tag = tag, - .data = .{ - .payload = try self.addExtra(Mir.LoadMemoryPie{ - .register = @intFromEnum(src_reg), - .atom_index = atom_index, - .sym_index = load_struct.sym_index, - }), - }, - }); - }, - else => return self.fail("TODO store {} to register", .{value}), - } - - // mov len, #abi_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - } - }, - .memory, - .stack_offset, - .stack_argument_offset, - .linker_load, - => { - const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); - try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); - }, - } -} - -fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void { - if (safety) { - // TODO if the value is undef, write 0xaa bytes to dest - } else { - // TODO if the value is undef, don't lower this instruction - } - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const ptr = try self.resolveInst(bin_op.lhs); - const value = try self.resolveInst(bin_op.rhs); - const ptr_ty = self.typeOf(bin_op.lhs); - const value_ty = self.typeOf(bin_op.rhs); - - try self.store(ptr, value, ptr_ty, value_ty); - - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); - return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); -} - -fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result = try self.structFieldPtr(inst, ty_op.operand, index); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { - return if (self.liveness.isUnused(inst)) .dead else result: { - const pt = self.pt; - const zcu = pt.zcu; - const mcv = try self.resolveInst(operand); - const ptr_ty = self.typeOf(operand); - const struct_ty = ptr_ty.childType(zcu); - const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(index, zcu))); - switch (mcv) { - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; - }, - else => { - const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; - const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; - - break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); - }, - } - }; -} - -fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - const operand = extra.struct_operand; - const index = extra.field_index; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const pt = self.pt; - const zcu = pt.zcu; - const mcv = try self.resolveInst(operand); - const struct_ty = self.typeOf(operand); - const struct_field_ty = struct_ty.fieldType(index, zcu); - const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(index, zcu))); - - switch (mcv) { - .dead, .unreach => unreachable, - .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + struct_field_offset }; - }, - .stack_offset => |off| { - break :result MCValue{ .stack_offset = off - struct_field_offset }; - }, - .memory => |addr| { - break :result MCValue{ .memory = addr + struct_field_offset }; - }, - .register_with_overflow => |rwo| { - const reg_lock = self.register_manager.lockRegAssumeUnused(rwo.reg); - defer self.register_manager.unlockReg(reg_lock); - - const field: MCValue = switch (index) { - // get wrapped value: return register - 0 => MCValue{ .register = rwo.reg }, - - // get overflow bit: return C or V flag - 1 => MCValue{ .compare_flags = rwo.flag }, - - else => unreachable, - }; - - if (self.reuseOperand(inst, operand, 0, field)) { - break :result field; - } else { - // Copy to new register - const raw_dest_reg = try self.register_manager.allocReg(null, gp); - const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty); - try self.genSetReg(struct_field_ty, dest_reg, field); - - break :result MCValue{ .register = dest_reg }; - } - }, - else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), - } - }; - - return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); -} - -fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const field_ptr = try self.resolveInst(extra.field_ptr); - const struct_ty = ty_pl.ty.toType().childType(zcu); - const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(extra.field_index, zcu))); - switch (field_ptr) { - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off + struct_field_offset }; - }, - else => { - const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr }; - const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; - - break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null); - }, - } - }; - return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); -} - -fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void { - // skip zero-bit arguments as they don't have a corresponding arg instruction - var arg_index = self.arg_index; - while (self.args[arg_index] == .none) arg_index += 1; - self.arg_index = arg_index + 1; - - const zcu = self.pt.zcu; - const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?; - const file = zcu.fileByIndex(func_zir.file); - if (!file.mod.?.strip) { - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; - const ty = self.typeOfIndex(inst); - const zir = &file.zir.?; - const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name, - .mcv = self.args[arg_index], - }); - } - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airTrap(self: *Self) InnerError!void { - _ = try self.addInst(.{ - .tag = .brk, - .data = .{ .imm16 = 0x0001 }, - }); - return self.finishAirBookkeeping(); -} - -fn airBreakpoint(self: *Self) InnerError!void { - _ = try self.addInst(.{ - .tag = .brk, - .data = .{ .imm16 = 0xf000 }, - }); - return self.finishAirBookkeeping(); -} - -fn airRetAddr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{}); - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airFrameAddress(self: *Self, inst: Air.Inst.Index) InnerError!void { - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{}); - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { - if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{}); - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const callee = pl_op.operand; - const extra = self.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]); - const ty = self.typeOf(callee); - const pt = self.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - const fn_ty = switch (ty.zigTypeTag(zcu)) { - .@"fn" => ty, - .pointer => ty.childType(zcu), - else => unreachable, - }; - - var info = try self.resolveCallingConventionValues(fn_ty); - defer info.deinit(self); - - // According to the Procedure Call Standard for the ARM - // Architecture, compare flags are not preserved across - // calls. Therefore, if some value is currently stored there, we - // need to save it. - // - // TODO once caller-saved registers are implemented, save them - // here too, but crucially *after* we save the compare flags as - // saving compare flags may require a new caller-saved register - try self.spillCompareFlagsIfOccupied(); - - if (info.return_value == .stack_offset) { - log.debug("airCall: return by reference", .{}); - const ret_ty = fn_ty.fnReturnType(zcu); - const ret_abi_size: u32 = @intCast(ret_ty.abiSize(zcu)); - const ret_abi_align = ret_ty.abiAlignment(zcu); - const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst); - - const ret_ptr_reg = self.registerAlias(.x0, Type.usize); - - const ptr_ty = try pt.singleMutPtrType(ret_ty); - try self.register_manager.getReg(ret_ptr_reg, null); - try self.genSetReg(ptr_ty, ret_ptr_reg, .{ .ptr_stack_offset = stack_offset }); - - info.return_value = .{ .stack_offset = stack_offset }; - } - - // Make space for the arguments passed via the stack - self.max_end_stack += info.stack_byte_count; - - for (info.args, 0..) |mc_arg, arg_i| { - const arg = args[arg_i]; - const arg_ty = self.typeOf(arg); - const arg_mcv = try self.resolveInst(args[arg_i]); - - switch (mc_arg) { - .none => continue, - .register => |reg| { - try self.register_manager.getReg(reg, null); - try self.genSetReg(arg_ty, reg, arg_mcv); - }, - .stack_offset => unreachable, - .stack_argument_offset => |offset| try self.genSetStackArgument( - arg_ty, - offset, - arg_mcv, - ), - else => unreachable, - } - } - - // Due to incremental compilation, how function calls are generated depends - // on linking. - if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { - .func => |func| { - if (self.bin_file.cast(.elf)) |_| { - return self.fail("TODO implement calling functions for Elf", .{}); - } else if (self.bin_file.cast(.macho)) |_| { - return self.fail("TODO implement calling functions for MachO", .{}); - } else if (self.bin_file.cast(.coff)) |coff_file| { - const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); - const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; - try self.genSetReg(Type.u64, .x30, .{ - .linker_load = .{ - .type = .got, - .sym_index = sym_index, - }, - }); - } else if (self.bin_file.cast(.plan9)) |p9| { - const atom_index = try p9.seeNav(pt, func.owner_nav); - const atom = p9.getAtom(atom_index); - try self.genSetReg(Type.usize, .x30, .{ .memory = atom.getOffsetTableAddress(p9) }); - } else unreachable; - - _ = try self.addInst(.{ - .tag = .blr, - .data = .{ .reg = .x30 }, - }); - }, - .@"extern" => |@"extern"| { - const nav_name = ip.getNav(@"extern".owner_nav).name.toSlice(ip); - const lib_name = @"extern".lib_name.toSlice(ip); - if (self.bin_file.cast(.macho)) |_| { - return self.fail("TODO implement calling extern functions for MachO", .{}); - } else if (self.bin_file.cast(.coff)) |coff_file| { - const sym_index = try coff_file.getGlobalSymbol(nav_name, lib_name); - try self.genSetReg(Type.u64, .x30, .{ - .linker_load = .{ - .type = .import, - .sym_index = sym_index, - }, - }); - _ = try self.addInst(.{ - .tag = .blr, - .data = .{ .reg = .x30 }, - }); - } else { - return self.fail("TODO implement calling extern functions", .{}); - } - }, - else => return self.fail("TODO implement calling bitcasted functions", .{}), - } else { - assert(ty.zigTypeTag(zcu) == .pointer); - const mcv = try self.resolveInst(callee); - try self.genSetReg(ty, .x30, mcv); - - _ = try self.addInst(.{ - .tag = .blr, - .data = .{ .reg = .x30 }, - }); - } - - const result: MCValue = result: { - switch (info.return_value) { - .register => |reg| { - if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { - // Save function return value in a callee saved register - break :result try self.copyToNewRegister(inst, info.return_value); - } - }, - else => {}, - } - break :result info.return_value; - }; - - if (args.len + 1 <= Air.Liveness.bpi - 1) { - var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - buf[0] = callee; - @memcpy(buf[1..][0..args.len], args); - return self.finishAir(inst, result, buf); - } - var bt = try self.iterateBigTomb(inst, 1 + args.len); - bt.feed(callee); - for (args) |arg| { - bt.feed(arg); - } - return bt.finishAir(result); -} - -fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - const ret_ty = self.fn_type.fnReturnType(zcu); - - switch (self.ret_mcv) { - .none => {}, - .immediate => { - assert(ret_ty.isError(zcu)); - }, - .register => |reg| { - // Return result by value - try self.genSetReg(ret_ty, reg, operand); - }, - .stack_offset => { - // Return result by reference - // - // self.ret_mcv is an address to where this function - // should store its result into - const ptr_ty = try pt.singleMutPtrType(ret_ty); - try self.store(self.ret_mcv, operand, ptr_ty, ret_ty); - }, - else => unreachable, - } - - // Just add space for an instruction, patch this later - try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); - - return self.finishAir(inst, .dead, .{ un_op, .none, .none }); -} - -fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const ret_ty = self.fn_type.fnReturnType(zcu); - - switch (self.ret_mcv) { - .none => {}, - .register => { - // Return result by value - try self.load(self.ret_mcv, ptr, ptr_ty); - }, - .stack_offset => { - // Return result by reference - // - // self.ret_mcv is an address to where this function - // should store its result into - // - // If the operand is a ret_ptr instruction, we are done - // here. Else we need to load the result from the location - // pointed to by the operand and store it to the result - // location. - const op_inst = un_op.toIndex().?; - if (self.air.instructions.items(.tag)[@intFromEnum(op_inst)] != .ret_ptr) { - const abi_size = @as(u32, @intCast(ret_ty.abiSize(zcu))); - const abi_align = ret_ty.abiAlignment(zcu); - - const offset = try self.allocMem(abi_size, abi_align, null); - - const tmp_mcv = MCValue{ .stack_offset = offset }; - try self.load(tmp_mcv, ptr, ptr_ty); - try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty); - } - }, - else => unreachable, // invalid return result - } - - try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); - - return self.finishAir(inst, .dead, .{ un_op, .none, .none }); -} - -fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) InnerError!void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs_ty = self.typeOf(bin_op.lhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { - break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op); - }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn cmp( - self: *Self, - lhs: ReadArg.Bind, - rhs: ReadArg.Bind, - lhs_ty: Type, - op: math.CompareOperator, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { - .optional => blk: { - const payload_ty = lhs_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - break :blk Type.u1; - } else if (lhs_ty.isPtrLikeOptional(zcu)) { - break :blk Type.usize; - } else { - return self.fail("TODO ARM cmp non-pointer optionals", .{}); - } - }, - .float => return self.fail("TODO ARM cmp floats", .{}), - .@"enum" => lhs_ty.intTagType(zcu), - .int => lhs_ty, - .bool => Type.u1, - .pointer => Type.usize, - .error_set => Type.u16, - else => unreachable, - }; - - const int_info = int_ty.intInfo(zcu); - if (int_info.bits <= 64) { - try self.spillCompareFlagsIfOccupied(); - - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - - const rhs_immediate = try rhs.resolveToImmediate(self); - const rhs_immediate_ok = if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false; - - if (rhs_immediate_ok) { - const read_args = [_]ReadArg{ - .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, - }; - try self.allocRegs( - &read_args, - &.{}, - null, // we won't be able to reuse a register as there are no write_regs - ); - - _ = try self.addInst(.{ - .tag = .cmp_immediate, - .data = .{ .r_imm12_sh = .{ - .rn = lhs_reg, - .imm12 = @as(u12, @intCast(rhs_immediate.?)), - } }, - }); - } else { - const read_args = [_]ReadArg{ - .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, - .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg }, - }; - try self.allocRegs( - &read_args, - &.{}, - null, // we won't be able to reuse a register as there are no write_regs - ); - - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rr_imm6_shift = .{ - .rn = lhs_reg, - .rm = rhs_reg, - .imm6 = 0, - .shift = .lsl, - } }, - }); - } - - return switch (int_info.signedness) { - .signed => MCValue{ .compare_flags = Condition.fromCompareOperatorSigned(op) }, - .unsigned => MCValue{ .compare_flags = Condition.fromCompareOperatorUnsigned(op) }, - }; - } else { - return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); - } -} - -fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); -} - -fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - _ = operand; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airDbgStmt(self: *Self, inst: Air.Inst.Index) InnerError!void { - const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; - - _ = try self.addInst(.{ - .tag = .dbg_line, - .data = .{ .dbg_line_column = .{ - .line = dbg_stmt.line, - .column = dbg_stmt.column, - } }, - }); - - return self.finishAirBookkeeping(); -} - -fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); - const func = zcu.funcInfo(extra.data.func); - // TODO emit debug info for function change - _ = func; - try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); -} - -fn airDbgVar(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const operand = pl_op.operand; - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const ty = self.typeOf(operand); - const mcv = try self.resolveInst(operand); - const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - - log.debug("airDbgVar: %{f}: {f}, {}", .{ inst, ty.fmtDebug(), mcv }); - - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name.toSlice(self.air), - .mcv = mcv, - }); - - return self.finishAir(inst, .dead, .{ operand, .none, .none }); -} - -fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { - switch (condition) { - .compare_flags => |cond| return try self.addInst(.{ - .tag = .b_cond, - .data = .{ - .inst_cond = .{ - .inst = undefined, // populated later through performReloc - // Here we map to the opposite condition because the jump is to the false branch. - .cond = cond.negate(), - }, - }, - }), - else => { - const reg = switch (condition) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, condition), - }; - - return try self.addInst(.{ - .tag = .cbz, - .data = .{ - .r_inst = .{ - .rt = reg, - .inst = undefined, // populated later through performReloc - }, - }, - }); - }, - } -} - -fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); - const liveness_condbr = self.liveness.getCondBr(inst); - - const reloc = try self.condBr(cond); - - // If the condition dies here in this condbr instruction, process - // that death now instead of later as this has an effect on - // whether it needs to be spilled in the branches - if (self.liveness.operandDies(inst, 0)) { - if (pl_op.operand.toIndex()) |op_index| { - self.processDeath(op_index); - } - } - - // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; - const parent_compare_flags_inst = self.compare_flags_inst; - - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop().?; - } - - try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); - for (liveness_condbr.then_deaths) |operand| { - self.processDeath(operand); - } - try self.genBody(then_body); - - // Revert to the previous register and stack allocation state. - - var saved_then_branch = self.branch_stack.pop().?; - defer saved_then_branch.deinit(self.gpa); - - self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; - - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; - - try self.performReloc(reloc); - const else_branch = self.branch_stack.addOneAssumeCapacity(); - else_branch.* = .{}; - - try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); - for (liveness_condbr.else_deaths) |operand| { - self.processDeath(operand); - } - try self.genBody(else_body); - - // At this point, each branch will possibly have conflicting values for where - // each instruction is stored. They agree, however, on which instructions are alive/dead. - // We use the first ("then") branch as canonical, and here emit - // instructions into the second ("else") branch to make it conform. - // We continue respect the data structure semantic guarantees of the else_branch so - // that we can use all the code emitting abstractions. This is why at the bottom we - // assert that parent_branch.free_registers equals the saved_then_branch.free_registers - // rather than assigning it. - const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; - try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); - - const else_slice = else_branch.inst_table.entries.slice(); - const else_keys = else_slice.items(.key); - const else_values = else_slice.items(.value); - for (else_keys, 0..) |else_key, else_idx| { - const else_value = else_values[else_idx]; - const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { - // The instruction's MCValue is overridden in both branches. - parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); - if (else_value == .dead) { - assert(then_entry.value == .dead); - continue; - } - break :blk then_entry.value; - } else blk: { - if (else_value == .dead) - continue; - // The instruction is only overridden in the else branch. - var i: usize = self.branch_stack.items.len - 1; - while (true) { - i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? - if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { - assert(mcv != .dead); - break :blk mcv; - } - } - }; - log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); - // TODO make sure the destination stack offset / register does not already have something - // going on there. - try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value); - // TODO track the new register / stack allocation - } - try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); - const then_slice = saved_then_branch.inst_table.entries.slice(); - const then_keys = then_slice.items(.key); - const then_values = then_slice.items(.value); - for (then_keys, 0..) |then_key, then_idx| { - const then_value = then_values[then_idx]; - // We already deleted the items from this table that matched the else_branch. - // So these are all instructions that are only overridden in the then branch. - parent_branch.inst_table.putAssumeCapacity(then_key, then_value); - if (then_value == .dead) - continue; - const parent_mcv = blk: { - var i: usize = self.branch_stack.items.len - 1; - while (true) { - i -= 1; - if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { - assert(mcv != .dead); - break :blk mcv; - } - } - }; - log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); - // TODO make sure the destination stack offset / register does not already have something - // going on there. - try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value); - // TODO track the new register / stack allocation - } - - { - var item = self.branch_stack.pop().?; - item.deinit(self.gpa); - } - - // We already took care of pl_op.operand earlier, so we're going - // to pass .none here - return self.finishAir(inst, .unreach, .{ .none, .none, .none }); -} - -fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const sentinel: struct { ty: Type, bind: ReadArg.Bind } = if (!operand_ty.isPtrLikeOptional(zcu)) blk: { - const payload_ty = operand_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) - break :blk .{ .ty = operand_ty, .bind = operand_bind }; - - const offset = @as(u32, @intCast(payload_ty.abiSize(zcu))); - const operand_mcv = try operand_bind.resolveToMcv(self); - const new_mcv: MCValue = switch (operand_mcv) { - .register => |source_reg| new: { - // TODO should we reuse the operand here? - const raw_reg = try self.register_manager.allocReg(null, gp); - const dest_reg = raw_reg.toX(); - - const shift = @as(u6, @intCast(offset * 8)); - if (shift == 0) { - try self.genSetReg(payload_ty, dest_reg, operand_mcv); - } else { - _ = try self.addInst(.{ - .tag = if (payload_ty.isSignedInt(zcu)) - Mir.Inst.Tag.asr_immediate - else - Mir.Inst.Tag.lsr_immediate, - .data = .{ .rr_shift = .{ - .rd = dest_reg, - .rn = source_reg.toX(), - .shift = shift, - } }, - }); - } - - break :new .{ .register = self.registerAlias(dest_reg, payload_ty) }; - }, - .stack_argument_offset => |off| .{ .stack_argument_offset = off + offset }, - .stack_offset => |off| .{ .stack_offset = off - offset }, - .memory => |addr| .{ .memory = addr + offset }, - else => unreachable, // invalid MCValue for an optional - }; - - break :blk .{ .ty = Type.bool, .bind = .{ .mcv = new_mcv } }; - } else .{ .ty = operand_ty, .bind = operand_bind }; - const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } }; - return self.cmp(sentinel.bind, imm_bind, sentinel.ty, .eq); -} - -fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { - const is_null_res = try self.isNull(operand_bind, operand_ty); - assert(is_null_res.compare_flags == .eq); - return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; -} - -fn isErr( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const error_type = error_union_ty.errorUnionSet(zcu); - - if (error_type.errorSetIsEmpty(zcu)) { - return MCValue{ .immediate = 0 }; // always false - } - - const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); - return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); -} - -fn isNonErr( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, -) !MCValue { - const is_err_result = try self.isErr(error_union_bind, error_union_ty); - switch (is_err_result) { - .compare_flags => |cond| { - assert(cond == .hi); - return MCValue{ .compare_flags = cond.negate() }; - }, - .immediate => |imm| { - assert(imm == 0); - return MCValue{ .immediate = 1 }; - }, - else => unreachable, - } -} - -fn airIsNull(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(un_op); - const operand_ty = self.typeOf(un_op); - - break :result try self.isNull(.{ .mcv = operand }, operand_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isNull(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonNull(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(un_op); - const operand_ty = self.typeOf(un_op); - - break :result try self.isNonNull(.{ .mcv = operand }, operand_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isNonNull(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsErr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; - const error_union_ty = self.typeOf(un_op); - - break :result try self.isErr(error_union_bind, error_union_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isErr(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonErr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; - const error_union_ty = self.typeOf(un_op); - - break :result try self.isNonErr(error_union_bind, error_union_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void { - // A loop is a setup to be able to jump back to the beginning. - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = self.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[loop.end..][0..loop.data.body_len]); - const start_index = @as(u32, @intCast(self.mir_instructions.len)); - - try self.genBody(body); - try self.jump(start_index); - - return self.finishAirBookkeeping(); -} - -/// Send control flow to `inst`. -fn jump(self: *Self, inst: Mir.Inst.Index) !void { - _ = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = inst }, - }); -} - -fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); -} - -fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { - try self.blocks.putNoClobber(self.gpa, inst, .{ - // A block is a setup to be able to jump to the end. - .relocs = .{}, - // It also acts as a receptacle for break operands. - // Here we use `MCValue.none` to represent a null value so that the first - // break instruction will choose a MCValue for the block result and overwrite - // this field. Following break instructions will use that MCValue to put their - // block results. - .mcv = MCValue{ .none = {} }, - }); - defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); - - // TODO emit debug info lexical block - try self.genBody(body); - - // relocations for `br` instructions - const relocs = &self.blocks.getPtr(inst).?.relocs; - if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { - // If the last Mir instruction is the last relocation (which - // would just jump one instruction further), it can be safely - // removed - self.mir_instructions.orderedRemove(relocs.pop().?); - } - for (relocs.items) |reloc| { - try self.performReloc(reloc); - } - - const result = self.blocks.getPtr(inst).?.mcv; - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airSwitch(self: *Self, inst: Air.Inst.Index) InnerError!void { - const switch_br = self.air.unwrapSwitch(inst); - const condition_ty = self.typeOf(switch_br.operand); - const liveness = try self.liveness.getSwitchBr( - self.gpa, - inst, - switch_br.cases_len + 1, - ); - defer self.gpa.free(liveness.deaths); - - var it = switch_br.iterateCases(); - while (it.next()) |case| { - if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{}); - - // For every item, we compare it to condition and branch into - // the prong if they are equal. After we compared to all - // items, we branch into the next prong (or if no other prongs - // exist out of the switch statement). - // - // cmp condition, item1 - // beq prong - // cmp condition, item2 - // beq prong - // cmp condition, item3 - // beq prong - // b out - // prong: ... - // ... - // out: ... - const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len); - defer self.gpa.free(branch_into_prong_relocs); - - for (case.items, 0..) |item, idx| { - const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq); - branch_into_prong_relocs[idx] = try self.condBr(cmp_result); - } - - const branch_away_from_prong_reloc = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = undefined }, // populated later through performReloc - }); - - for (branch_into_prong_relocs) |reloc| { - try self.performReloc(reloc); - } - - // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - const parent_compare_flags_inst = self.compare_flags_inst; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; - - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop().?; - } - - try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len); - for (liveness.deaths[case.idx]) |operand| { - self.processDeath(operand); - } - try self.genBody(case.body); - - // Revert to the previous register and stack allocation state. - var saved_case_branch = self.branch_stack.pop().?; - defer saved_case_branch.deinit(self.gpa); - - self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; - - try self.performReloc(branch_away_from_prong_reloc); - } - - if (switch_br.else_body_len > 0) { - const else_body = it.elseBody(); - - // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - const parent_compare_flags_inst = self.compare_flags_inst; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; - - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop().?; - } - - const else_deaths = liveness.deaths.len - 1; - try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); - for (liveness.deaths[else_deaths]) |operand| { - self.processDeath(operand); - } - try self.genBody(else_body); - - // Revert to the previous register and stack allocation state. - var saved_case_branch = self.branch_stack.pop().?; - defer saved_case_branch.deinit(self.gpa); - - self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; - - // TODO consolidate returned MCValues between prongs and else branch like we do - // in airCondBr. - } - - return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none }); -} - -fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { - const tag = self.mir_instructions.items(.tag)[inst]; - switch (tag) { - .cbz => self.mir_instructions.items(.data)[inst].r_inst.inst = @intCast(self.mir_instructions.len), - .b_cond => self.mir_instructions.items(.data)[inst].inst_cond.inst = @intCast(self.mir_instructions.len), - .b => self.mir_instructions.items(.data)[inst].inst = @intCast(self.mir_instructions.len), - else => unreachable, - } -} - -fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; - try self.br(branch.block_inst, branch.operand); - return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); -} - -fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { - const pt = self.pt; - const zcu = pt.zcu; - const block_data = self.blocks.getPtr(block).?; - - if (self.typeOf(operand).hasRuntimeBits(zcu)) { - const operand_mcv = try self.resolveInst(operand); - const block_mcv = block_data.mcv; - if (block_mcv == .none) { - block_data.mcv = switch (operand_mcv) { - .none, .dead, .unreach => unreachable, - .register, .stack_offset, .memory => operand_mcv, - .immediate, .stack_argument_offset, .compare_flags => blk: { - const new_mcv = try self.allocRegOrMem(self.typeOfIndex(block), true, block); - try self.setRegOrMem(self.typeOfIndex(block), new_mcv, operand_mcv); - break :blk new_mcv; - }, - else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), - }; - } else { - try self.setRegOrMem(self.typeOfIndex(block), block_mcv, operand_mcv); - } - } - return self.brVoid(block); -} - -fn brVoid(self: *Self, block: Air.Inst.Index) !void { - const block_data = self.blocks.getPtr(block).?; - - // Emit a jump with a relocation. It will be patched up after the block ends. - try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); - - block_data.relocs.appendAssumeCapacity(try self.addInst(.{ - .tag = .b, - .data = .{ .inst = undefined }, // populated later through performReloc - })); -} - -fn airAsm(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len = @as(u31, @truncate(extra.data.flags)); - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; - - const dead = !is_volatile and self.liveness.isUnused(inst); - const result: MCValue = if (dead) .dead else result: { - if (outputs.len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - - const output_constraint: ?[]const u8 = for (outputs) |output| { - if (output != .none) { - return self.fail("TODO implement codegen for non-expr asm", .{}); - } - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - break constraint; - } else null; - - for (inputs) |input| { - const input_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { - return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); - } - const reg_name = constraint[1 .. constraint.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail("unrecognized register: '{s}'", .{reg_name}); - - const arg_mcv = try self.resolveInst(input); - try self.register_manager.getReg(reg, null); - try self.genSetReg(self.typeOf(input), reg, arg_mcv); - } - - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - // TODO honor these - } - } - - const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; - - if (mem.eql(u8, asm_source, "svc #0")) { - _ = try self.addInst(.{ - .tag = .svc, - .data = .{ .imm16 = 0x0 }, - }); - } else if (mem.eql(u8, asm_source, "svc #0x80")) { - _ = try self.addInst(.{ - .tag = .svc, - .data = .{ .imm16 = 0x80 }, - }); - } else { - return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); - } - - if (output_constraint) |output| { - if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail("unrecognized asm output constraint: '{s}'", .{output}); - } - const reg_name = output[2 .. output.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail("unrecognized register: '{s}'", .{reg_name}); - break :result MCValue{ .register = reg }; - } else { - break :result MCValue{ .none = {} }; - } - }; - - simple: { - var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - var buf_index: usize = 0; - for (outputs) |output| { - if (output == .none) continue; - - if (buf_index >= buf.len) break :simple; - buf[buf_index] = output; - buf_index += 1; - } - if (buf_index + inputs.len > buf.len) break :simple; - @memcpy(buf[buf_index..][0..inputs.len], inputs); - return self.finishAir(inst, result, buf); - } - var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); - for (outputs) |output| { - if (output == .none) continue; - - bt.feed(output); - } - for (inputs) |input| { - bt.feed(input); - } - return bt.finishAir(result); -} - -fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { - try self.ensureProcessDeathCapacity(operand_count + 1); - return BigTomb{ - .function = self, - .inst = inst, - .lbt = self.liveness.iterateBigTomb(inst), - }; -} - -/// Sets the value without any modifications to register allocation metadata or stack allocation metadata. -fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { - switch (loc) { - .none => return, - .register => |reg| return self.genSetReg(ty, reg, val), - .stack_offset => |off| return self.genSetStack(ty, off, val), - .memory => { - return self.fail("TODO implement setRegOrMem for memory", .{}); - }, - else => unreachable, - } -} - -fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const abi_size = @as(u32, @intCast(ty.abiSize(zcu))); - switch (mcv) { - .dead => unreachable, - .unreach, .none => return, // Nothing to do. - .undef => { - if (!self.wantSafety()) - return; // The already existing value will do just fine. - // TODO Upgrade this to a memset call when we have that available. - switch (abi_size) { - 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), - 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), - 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), - 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), - else => try self.genInlineMemset( - .{ .ptr_stack_offset = stack_offset }, - .{ .immediate = 0xaa }, - .{ .immediate = abi_size }, - ), - } - }, - .compare_flags, - .immediate, - .ptr_stack_offset, - => { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - }, - .register => |reg| { - switch (abi_size) { - 1, 2, 4, 8 => { - assert(std.mem.isAlignedGeneric(u32, stack_offset, abi_size)); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .strb_stack, - 2 => .strh_stack, - 4, 8 => .str_stack, - else => unreachable, // unexpected abi size - }; - const rt = self.registerAlias(reg, ty); - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .load_store_stack = .{ - .rt = rt, - .offset = stack_offset, - } }, - }); - }, - else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), - } - }, - .register_with_overflow => |rwo| { - const reg_lock = self.register_manager.lockReg(rwo.reg); - defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); - - const wrapped_ty = ty.fieldType(0, zcu); - try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg }); - - const overflow_bit_ty = ty.fieldType(1, zcu); - const overflow_bit_offset = @as(u32, @intCast(ty.structFieldOffset(1, zcu))); - const raw_cond_reg = try self.register_manager.allocReg(null, gp); - const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty); - - _ = try self.addInst(.{ - .tag = .cset, - .data = .{ .r_cond = .{ - .rd = cond_reg, - .cond = rwo.flag, - } }, - }); - - try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ - .register = cond_reg, - }); - }, - .linker_load, - .memory, - .stack_argument_offset, - .stack_offset, - => { - switch (mcv) { - .stack_offset => |off| { - if (stack_offset == off) - return; // Copy stack variable to itself; nothing to do. - }, - else => {}, - } - - if (abi_size <= 8) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - } else { - const ptr_ty = try pt.singleMutPtrType(ty); - - // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); - const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); - defer for (regs_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const src_reg = regs[0]; - const dst_reg = regs[1]; - const len_reg = regs[2]; - const count_reg = regs[3]; - const tmp_reg = regs[4]; - - switch (mcv) { - .stack_offset => |off| { - // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); - }, - .stack_argument_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .load_store_stack = .{ - .rt = src_reg, - .offset = off, - } }, - }); - }, - .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), - .linker_load => |load_struct| { - const tag: Mir.Inst.Tag = switch (load_struct.type) { - .got => .load_memory_ptr_got, - .direct => .load_memory_ptr_direct, - .import => unreachable, - }; - const atom_index = switch (self.bin_file.tag) { - .macho => { - // const macho_file = self.bin_file.cast(link.File.MachO).?; - // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - // break :blk macho_file.getAtom(atom).getSymbolIndex().?; - @panic("TODO genSetStack"); - }, - .coff => blk: { - const coff_file = self.bin_file.cast(.coff).?; - const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav); - break :blk coff_file.getAtom(atom).getSymbolIndex().?; - }, - else => unreachable, // unsupported target format - }; - _ = try self.addInst(.{ - .tag = tag, - .data = .{ - .payload = try self.addExtra(Mir.LoadMemoryPie{ - .register = @intFromEnum(src_reg), - .atom_index = atom_index, - .sym_index = load_struct.sym_index, - }), - }, - }); - }, - else => unreachable, - } - - // sub dst_reg, fp, #stack_offset - try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); - - // mov len, #abi_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - } -} - -fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - switch (mcv) { - .dead => unreachable, - .unreach, .none => return, // Nothing to do. - .undef => { - if (!self.wantSafety()) - return; // The already existing value will do just fine. - // Write the debug undefined value. - switch (reg.size()) { - 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), - 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), - else => unreachable, // unexpected register size - } - }, - .ptr_stack_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack, - .data = .{ .load_store_stack = .{ - .rt = reg, - .offset = @intCast(off), - } }, - }); - }, - .compare_flags => |condition| { - _ = try self.addInst(.{ - .tag = .cset, - .data = .{ .r_cond = .{ - .rd = reg, - .cond = condition, - } }, - }); - }, - .immediate => |x| { - _ = try self.addInst(.{ - .tag = .movz, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x) } }, - }); - - if (x & 0x0000_0000_ffff_0000 != 0) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 16), .hw = 1 } }, - }); - } - - if (reg.size() == 64) { - if (x & 0x0000_ffff_0000_0000 != 0) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 32), .hw = 2 } }, - }); - } - if (x & 0xffff_0000_0000_0000 != 0) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 48), .hw = 3 } }, - }); - } - } - }, - .register => |src_reg| { - assert(src_reg.size() == reg.size()); - - // If the registers are the same, nothing to do. - if (src_reg.id() == reg.id()) - return; - - // mov reg, src_reg - _ = try self.addInst(.{ - .tag = .mov_register, - .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, - }); - }, - .register_with_overflow => unreachable, // doesn't fit into a register - .linker_load => |load_struct| { - const tag: Mir.Inst.Tag = switch (load_struct.type) { - .got => .load_memory_got, - .direct => .load_memory_direct, - .import => .load_memory_import, - }; - const atom_index = switch (self.bin_file.tag) { - .macho => { - @panic("TODO genSetReg"); - // const macho_file = self.bin_file.cast(link.File.MachO).?; - // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - // break :blk macho_file.getAtom(atom).getSymbolIndex().?; - }, - .coff => blk: { - const coff_file = self.bin_file.cast(.coff).?; - const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav); - break :blk coff_file.getAtom(atom).getSymbolIndex().?; - }, - else => unreachable, // unsupported target format - }; - _ = try self.addInst(.{ - .tag = tag, - .data = .{ - .payload = try self.addExtra(Mir.LoadMemoryPie{ - .register = @intFromEnum(reg), - .atom_index = atom_index, - .sym_index = load_struct.sym_index, - }), - }, - }); - }, - .memory => |addr| { - // The value is in memory at a hard-coded address. - // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(ty, reg.toX(), .{ .immediate = addr }); - try self.genLdrRegister(reg, reg.toX(), ty); - }, - .stack_offset => |off| { - const abi_size = ty.abiSize(zcu); - - switch (abi_size) { - 1, 2, 4, 8 => { - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack, - 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack, - 4, 8 => .ldr_stack, - else => unreachable, // unexpected abi size - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .load_store_stack = .{ - .rt = reg, - .offset = @intCast(off), - } }, - }); - }, - 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}), - else => unreachable, - } - }, - .stack_argument_offset => |off| { - const abi_size = ty.abiSize(zcu); - - switch (abi_size) { - 1, 2, 4, 8 => { - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument, - 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument, - 4, 8 => .ldr_stack_argument, - else => unreachable, // unexpected abi size - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .load_store_stack = .{ - .rt = reg, - .offset = @intCast(off), - } }, - }); - }, - 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}), - else => unreachable, - } - }, - } -} - -fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const abi_size = @as(u32, @intCast(ty.abiSize(zcu))); - switch (mcv) { - .dead => unreachable, - .none, .unreach => return, - .undef => { - if (!self.wantSafety()) - return; // The already existing value will do just fine. - // TODO Upgrade this to a memset call when we have that available. - switch (ty.abiSize(pt.zcu)) { - 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), - 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), - 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), - 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), - else => return self.fail("TODO implement memset", .{}), - } - }, - .register => |reg| { - switch (abi_size) { - 1, 2, 4, 8 => { - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .strb_immediate, - 2 => .strh_immediate, - 4, 8 => .str_immediate, - else => unreachable, // unexpected abi size - }; - const rt = self.registerAlias(reg, ty); - const offset = switch (abi_size) { - 1 => blk: { - if (math.cast(u12, stack_offset)) |imm| { - break :blk Instruction.LoadStoreOffset.imm(imm); - } else { - return self.fail("TODO genSetStackArgument byte with larger offset", .{}); - } - }, - 2 => blk: { - assert(std.mem.isAlignedGeneric(u32, stack_offset, 2)); // misaligned stack entry - if (math.cast(u12, @divExact(stack_offset, 2))) |imm| { - break :blk Instruction.LoadStoreOffset.imm(imm); - } else { - return self.fail("TODO getSetStackArgument halfword with larger offset", .{}); - } - }, - 4, 8 => blk: { - const alignment = abi_size; - assert(std.mem.isAlignedGeneric(u32, stack_offset, alignment)); // misaligned stack entry - if (math.cast(u12, @divExact(stack_offset, alignment))) |imm| { - break :blk Instruction.LoadStoreOffset.imm(imm); - } else { - return self.fail("TODO genSetStackArgument with larger offset", .{}); - } - }, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .load_store_register_immediate = .{ - .rt = rt, - .rn = .sp, - .offset = offset.immediate, - } }, - }); - }, - else => return self.fail("TODO genSetStackArgument other types abi_size={}", .{abi_size}), - } - }, - .register_with_overflow => { - return self.fail("TODO implement genSetStackArgument {}", .{mcv}); - }, - .linker_load, - .memory, - .stack_argument_offset, - .stack_offset, - => { - if (abi_size <= 4) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); - } else { - const ptr_ty = try pt.singleMutPtrType(ty); - - // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); - const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); - defer for (regs_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const src_reg = regs[0]; - const dst_reg = regs[1]; - const len_reg = regs[2]; - const count_reg = regs[3]; - const tmp_reg = regs[4]; - - switch (mcv) { - .stack_offset => |off| { - // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); - }, - .stack_argument_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .load_store_stack = .{ - .rt = src_reg, - .offset = off, - } }, - }); - }, - .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @as(u32, @intCast(addr)) }), - .linker_load => |load_struct| { - const tag: Mir.Inst.Tag = switch (load_struct.type) { - .got => .load_memory_ptr_got, - .direct => .load_memory_ptr_direct, - .import => unreachable, - }; - const atom_index = switch (self.bin_file.tag) { - .macho => { - @panic("TODO genSetStackArgument"); - // const macho_file = self.bin_file.cast(link.File.MachO).?; - // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - // break :blk macho_file.getAtom(atom).getSymbolIndex().?; - }, - .coff => blk: { - const coff_file = self.bin_file.cast(.coff).?; - const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav); - break :blk coff_file.getAtom(atom).getSymbolIndex().?; - }, - else => unreachable, // unsupported target format - }; - _ = try self.addInst(.{ - .tag = tag, - .data = .{ - .payload = try self.addExtra(Mir.LoadMemoryPie{ - .register = @intFromEnum(src_reg), - .atom_index = atom_index, - .sym_index = load_struct.sym_index, - }), - }, - }); - }, - else => unreachable, - } - - // add dst_reg, sp, #stack_offset - _ = try self.addInst(.{ - .tag = .add_immediate, - .data = .{ .rr_imm12_sh = .{ - .rd = dst_reg, - .rn = .sp, - .imm12 = math.cast(u12, stack_offset) orelse { - return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); - }, - } }, - }); - - // mov len, #abi_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - .compare_flags, - .immediate, - .ptr_stack_offset, - => { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); - }, - } -} - -fn airBitCast(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(ty_op.operand); - if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; - - const operand_lock = switch (operand) { - .register => |reg| self.register_manager.lockReg(reg), - .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - - const dest_ty = self.typeOfIndex(inst); - const dest = try self.allocRegOrMem(dest_ty, true, inst); - try self.setRegOrMem(dest_ty, dest, operand); - break :result dest; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr_ty = self.typeOf(ty_op.operand); - const ptr = try self.resolveInst(ty_op.operand); - const array_ty = ptr_ty.childType(zcu); - const array_len = @as(u32, @intCast(array_ty.arrayLen(zcu))); - const ptr_bytes = 8; - const stack_offset = try self.allocMem(ptr_bytes * 2, .@"8", inst); - try self.genSetStack(ptr_ty, stack_offset, ptr); - try self.genSetStack(Type.usize, stack_offset - ptr_bytes, .{ .immediate = array_len }); - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{ - self.target.cpu.arch, - }); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{ - self.target.cpu.arch, - }); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airCmpxchg(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - _ = extra; - - return self.fail("TODO implement airCmpxchg for {}", .{ - self.target.cpu.arch, - }); -} - -fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); -} - -fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); -} - -fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) InnerError!void { - _ = inst; - _ = order; - return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); -} - -fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void { - _ = inst; - if (safety) { - // TODO if the value is undef, write 0xaa bytes to dest - } else { - // TODO if the value is undef, don't lower this instruction - } - return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); -} - -fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); -} - -fn airMemmove(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch}); -} - -fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { - _ = operand; - return self.fail("TODO implement airTagName for aarch64", .{}); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { - _ = operand; - return self.fail("TODO implement airErrorName for aarch64", .{}); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); -} - -fn airShuffleOne(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airShuffleOne for {}", .{self.target.cpu.arch}); -} - -fn airShuffleTwo(self: *Self, inst: Air.Inst.Index) InnerError!void { - _ = inst; - return self.fail("TODO implement airShuffleTwo for {}", .{self.target.cpu.arch}); -} - -fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void { - const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{}); - return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); -} - -fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const vector_ty = self.typeOfIndex(inst); - const len = vector_ty.vectorLen(zcu); - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]); - const result: MCValue = res: { - if (self.liveness.isUnused(inst)) break :res MCValue.dead; - return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch}); - }; - - if (elements.len <= Air.Liveness.bpi - 1) { - var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - @memcpy(buf[0..elements.len], elements); - return self.finishAir(inst, result, buf); - } - var bt = try self.iterateBigTomb(inst, elements.len); - for (elements) |elem| { - bt.feed(elem); - } - return bt.finishAir(result); -} - -fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; - _ = extra; - return self.fail("TODO implement airUnionInit for aarch64", .{}); -} - -fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void { - const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; - return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); -} - -fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { - return self.fail("TODO implement airMulAdd for aarch64", .{}); - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); -} - -fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void { - const pt = self.pt; - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); - const result: MCValue = result: { - const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; - const error_union_ty = self.typeOf(pl_op.operand); - const error_union_size = @as(u32, @intCast(error_union_ty.abiSize(pt.zcu))); - const error_union_align = error_union_ty.abiAlignment(pt.zcu); - - // The error union will die in the body. However, we need the - // error union after the body in order to extract the payload - // of the error union, so we create a copy of it - const error_union_copy = try self.allocMem(error_union_size, error_union_align, null); - try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self)); - - const is_err_result = try self.isErr(error_union_bind, error_union_ty); - const reloc = try self.condBr(is_err_result); - - try self.genBody(body); - try self.performReloc(reloc); - - break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null); - }; - return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); -} - -fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); - const body = self.air.extra.items[extra.end..][0..extra.data.body_len]; - _ = body; - return self.fail("TODO implement airTryPtr for arm", .{}); - // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); -} - -fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - - // If the type has no codegen bits, no need to store it. - const inst_ty = self.typeOf(inst); - if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !inst_ty.isError(zcu)) - return MCValue{ .none = {} }; - - const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, pt)).?); - - return self.getResolvedInstValue(inst_index); -} - -fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { - // Treat each stack item as a "layer" on top of the previous one. - var i: usize = self.branch_stack.items.len; - while (true) { - i -= 1; - if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { - assert(mcv != .dead); - return mcv; - } - } -} - -fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { - const mcv: MCValue = switch (try codegen.genTypedValue( - self.bin_file, - self.pt, - self.src_loc, - val, - self.target, - )) { - .mcv => |mcv| switch (mcv) { - .none => .none, - .undef => .undef, - .immediate => |imm| .{ .immediate = imm }, - .memory => |addr| .{ .memory = addr }, - .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } }, - .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, - .load_symbol, .lea_symbol, .lea_direct => unreachable, // TODO - }, - .fail => |msg| return self.failMsg(msg), - }; - return mcv; -} - -const CallMCValues = struct { - args: []MCValue, - return_value: MCValue, - stack_byte_count: u32, - stack_align: u32, - - fn deinit(self: *CallMCValues, func: *Self) void { - func.gpa.free(self.args); - self.* = undefined; - } -}; - -/// Caller must call `CallMCValues.deinit`. -fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { - const pt = self.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const fn_info = zcu.typeToFunc(fn_ty).?; - const cc = fn_info.cc; - var result: CallMCValues = .{ - .args = try self.gpa.alloc(MCValue, fn_info.param_types.len), - // These undefined values must be populated before returning from this function. - .return_value = undefined, - .stack_byte_count = undefined, - .stack_align = undefined, - }; - errdefer self.gpa.free(result.args); - - const ret_ty = fn_ty.fnReturnType(zcu); - - switch (cc) { - .naked => { - assert(result.args.len == 0); - result.return_value = .{ .unreach = {} }; - result.stack_byte_count = 0; - result.stack_align = 1; - return result; - }, - .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => { - // ARM64 Procedure Call Standard - var ncrn: usize = 0; // Next Core Register Number - var nsaa: u32 = 0; // Next stacked argument address - - if (ret_ty.zigTypeTag(zcu) == .noreturn) { - result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { - result.return_value = .{ .none = {} }; - } else { - const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu)); - if (ret_ty_size == 0) { - assert(ret_ty.isError(zcu)); - result.return_value = .{ .immediate = 0 }; - } else if (ret_ty_size <= 8) { - result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) }; - } else { - return self.fail("TODO support more return types for ARM backend", .{}); - } - } - - for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { - const param_size = @as(u32, @intCast(Type.fromInterned(ty).abiSize(zcu))); - if (param_size == 0) { - result_arg.* = .{ .none = {} }; - continue; - } - - // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned - // values to spread across odd-numbered registers. - if (Type.fromInterned(ty).abiAlignment(zcu) == .@"16" and cc != .aarch64_aapcs_darwin) { - // Round up NCRN to the next even number - ncrn += ncrn % 2; - } - - if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { - if (param_size <= 8) { - result_arg.* = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], Type.fromInterned(ty)) }; - ncrn += 1; - } else { - return self.fail("TODO MCValues with multiple registers", .{}); - } - } else if (ncrn < 8 and nsaa == 0) { - return self.fail("TODO MCValues split between registers and stack", .{}); - } else { - ncrn = 8; - // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided - // that the entire stack space consumed by the arguments is 8-byte aligned. - if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8") { - if (nsaa % 8 != 0) { - nsaa += 8 - (nsaa % 8); - } - } - - result_arg.* = .{ .stack_argument_offset = nsaa }; - nsaa += param_size; - } - } - - result.stack_byte_count = nsaa; - result.stack_align = 16; - }, - .auto => { - if (ret_ty.zigTypeTag(zcu) == .noreturn) { - result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { - result.return_value = .{ .none = {} }; - } else { - const ret_ty_size = @as(u32, @intCast(ret_ty.abiSize(zcu))); - if (ret_ty_size == 0) { - assert(ret_ty.isError(zcu)); - result.return_value = .{ .immediate = 0 }; - } else if (ret_ty_size <= 8) { - result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) }; - } else { - // The result is returned by reference, not by - // value. This means that x0 (or w0 when pointer - // size is 32 bits) will contain the address of - // where this function should write the result - // into. - result.return_value = .{ .stack_offset = 0 }; - } - } - - var stack_offset: u32 = 0; - - for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { - if (Type.fromInterned(ty).abiSize(zcu) > 0) { - const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu)); - const param_alignment = Type.fromInterned(ty).abiAlignment(zcu); - - stack_offset = @intCast(param_alignment.forward(stack_offset)); - result_arg.* = .{ .stack_argument_offset = stack_offset }; - stack_offset += param_size; - } else { - result_arg.* = .{ .none = {} }; - } - } - - result.stack_byte_count = stack_offset; - result.stack_align = 16; - }, - else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}), - } - - return result; -} - -/// TODO support scope overrides. Also note this logic is duplicated with `Zcu.wantSafety`. -fn wantSafety(self: *Self) bool { - return switch (self.bin_file.comp.root_mod.optimize_mode) { - .Debug => true, - .ReleaseSafe => true, - .ReleaseFast => false, - .ReleaseSmall => false, - }; -} - -fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { - @branchHint(.cold); - return self.pt.zcu.codegenFail(self.owner_nav, format, args); -} - -fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { - @branchHint(.cold); - return self.pt.zcu.codegenFailMsg(self.owner_nav, msg); -} - -fn parseRegName(name: []const u8) ?Register { - if (@hasDecl(Register, "parseRegName")) { - return Register.parseRegName(name); - } - return std.meta.stringToEnum(Register, name); -} - -fn registerAlias(self: *Self, reg: Register, ty: Type) Register { - const abi_size = ty.abiSize(self.pt.zcu); - - switch (reg.class()) { - .general_purpose => { - if (abi_size == 0) { - unreachable; // should be comptime-known - } else if (abi_size <= 4) { - return reg.toW(); - } else if (abi_size <= 8) { - return reg.toX(); - } else unreachable; - }, - .stack_pointer => unreachable, // we can't store/load the sp - .floating_point => { - return switch (ty.floatBits(self.target)) { - 16 => reg.toH(), - 32 => reg.toS(), - 64 => reg.toD(), - 128 => reg.toQ(), - - 80 => unreachable, // f80 registers don't exist - else => unreachable, - }; - }, - } -} - -fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - return self.air.typeOf(inst, &self.pt.zcu.intern_pool); -} - -fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - return self.air.typeOfIndex(inst, &self.pt.zcu.intern_pool); -} diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig deleted file mode 100644 index e46bfb856ad06b4efff7520b6a28e1db427cd41d..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/Emit.zig +++ /dev/null @@ -1,1349 +0,0 @@ -//! This file contains the functionality for lowering AArch64 MIR into -//! machine code - -const Emit = @This(); -const std = @import("std"); -const math = std.math; -const Mir = @import("Mir.zig"); -const bits = @import("bits.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); -const ErrorMsg = Zcu.ErrorMsg; -const assert = std.debug.assert; -const Instruction = bits.Instruction; -const Register = bits.Register; -const log = std.log.scoped(.aarch64_emit); - -mir: Mir, -bin_file: *link.File, -debug_output: link.File.DebugInfoOutput, -target: *const std.Target, -err_msg: ?*ErrorMsg = null, -src_loc: Zcu.LazySrcLoc, -code: *std.ArrayListUnmanaged(u8), - -prev_di_line: u32, -prev_di_column: u32, - -/// Relative to the beginning of `code`. -prev_di_pc: usize, - -/// The amount of stack space consumed by the saved callee-saved -/// registers in bytes -saved_regs_stack_space: u32, - -/// The branch type of every branch -branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .empty, - -/// For every forward branch, maps the target instruction to a list of -/// branches which branch to this target instruction -branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .empty, - -/// For backward branches: stores the code offset of the target -/// instruction -/// -/// For forward branches: stores the code offset of the branch -/// instruction -code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, - -/// The final stack frame size of the function (already aligned to the -/// respective stack alignment). Does not include prologue stack space. -stack_size: u32, - -const InnerError = error{ - OutOfMemory, - EmitFail, -}; - -const BranchType = enum { - cbz, - b_cond, - unconditional_branch_immediate, - - fn default(tag: Mir.Inst.Tag) BranchType { - return switch (tag) { - .cbz => .cbz, - .b, .bl => .unconditional_branch_immediate, - .b_cond => .b_cond, - else => unreachable, - }; - } -}; - -pub fn emitMir(emit: *Emit) InnerError!void { - const mir_tags = emit.mir.instructions.items(.tag); - - // Find smallest lowerings for branch instructions - try emit.lowerBranches(); - - // Emit machine code - for (mir_tags, 0..) |tag, index| { - const inst = @as(u32, @intCast(index)); - switch (tag) { - .add_immediate => try emit.mirAddSubtractImmediate(inst), - .adds_immediate => try emit.mirAddSubtractImmediate(inst), - .cmp_immediate => try emit.mirAddSubtractImmediate(inst), - .sub_immediate => try emit.mirAddSubtractImmediate(inst), - .subs_immediate => try emit.mirAddSubtractImmediate(inst), - - .asr_register => try emit.mirDataProcessing2Source(inst), - .lsl_register => try emit.mirDataProcessing2Source(inst), - .lsr_register => try emit.mirDataProcessing2Source(inst), - .sdiv => try emit.mirDataProcessing2Source(inst), - .udiv => try emit.mirDataProcessing2Source(inst), - - .asr_immediate => try emit.mirShiftImmediate(inst), - .lsl_immediate => try emit.mirShiftImmediate(inst), - .lsr_immediate => try emit.mirShiftImmediate(inst), - - .b_cond => try emit.mirConditionalBranchImmediate(inst), - - .b => try emit.mirBranch(inst), - .bl => try emit.mirBranch(inst), - - .cbz => try emit.mirCompareAndBranch(inst), - - .blr => try emit.mirUnconditionalBranchRegister(inst), - .ret => try emit.mirUnconditionalBranchRegister(inst), - - .brk => try emit.mirExceptionGeneration(inst), - .svc => try emit.mirExceptionGeneration(inst), - - .call_extern => try emit.mirCallExtern(inst), - - .eor_immediate => try emit.mirLogicalImmediate(inst), - .tst_immediate => try emit.mirLogicalImmediate(inst), - - .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), - .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), - .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), - .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), - .subs_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), - - .add_extended_register => try emit.mirAddSubtractExtendedRegister(inst), - .adds_extended_register => try emit.mirAddSubtractExtendedRegister(inst), - .sub_extended_register => try emit.mirAddSubtractExtendedRegister(inst), - .subs_extended_register => try emit.mirAddSubtractExtendedRegister(inst), - .cmp_extended_register => try emit.mirAddSubtractExtendedRegister(inst), - - .csel => try emit.mirConditionalSelect(inst), - .cset => try emit.mirConditionalSelect(inst), - - .dbg_line => try emit.mirDbgLine(inst), - - .dbg_prologue_end => try emit.mirDebugPrologueEnd(), - .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), - - .and_shifted_register => try emit.mirLogicalShiftedRegister(inst), - .eor_shifted_register => try emit.mirLogicalShiftedRegister(inst), - .orr_shifted_register => try emit.mirLogicalShiftedRegister(inst), - - .load_memory_got => try emit.mirLoadMemoryPie(inst), - .load_memory_direct => try emit.mirLoadMemoryPie(inst), - .load_memory_import => try emit.mirLoadMemoryPie(inst), - .load_memory_ptr_got => try emit.mirLoadMemoryPie(inst), - .load_memory_ptr_direct => try emit.mirLoadMemoryPie(inst), - - .ldp => try emit.mirLoadStoreRegisterPair(inst), - .stp => try emit.mirLoadStoreRegisterPair(inst), - - .ldr_ptr_stack => try emit.mirLoadStoreStack(inst), - .ldr_stack => try emit.mirLoadStoreStack(inst), - .ldrb_stack => try emit.mirLoadStoreStack(inst), - .ldrh_stack => try emit.mirLoadStoreStack(inst), - .ldrsb_stack => try emit.mirLoadStoreStack(inst), - .ldrsh_stack => try emit.mirLoadStoreStack(inst), - .str_stack => try emit.mirLoadStoreStack(inst), - .strb_stack => try emit.mirLoadStoreStack(inst), - .strh_stack => try emit.mirLoadStoreStack(inst), - - .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst), - .ldr_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrb_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrh_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst), - - .ldr_register => try emit.mirLoadStoreRegisterRegister(inst), - .ldrb_register => try emit.mirLoadStoreRegisterRegister(inst), - .ldrh_register => try emit.mirLoadStoreRegisterRegister(inst), - .str_register => try emit.mirLoadStoreRegisterRegister(inst), - .strb_register => try emit.mirLoadStoreRegisterRegister(inst), - .strh_register => try emit.mirLoadStoreRegisterRegister(inst), - - .ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .ldrh_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .ldrsb_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .ldrsh_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .ldrsw_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .str_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - .strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst), - - .mov_register => try emit.mirMoveRegister(inst), - .mov_to_from_sp => try emit.mirMoveRegister(inst), - .mvn => try emit.mirMoveRegister(inst), - - .movk => try emit.mirMoveWideImmediate(inst), - .movz => try emit.mirMoveWideImmediate(inst), - - .msub => try emit.mirDataProcessing3Source(inst), - .mul => try emit.mirDataProcessing3Source(inst), - .smulh => try emit.mirDataProcessing3Source(inst), - .smull => try emit.mirDataProcessing3Source(inst), - .umulh => try emit.mirDataProcessing3Source(inst), - .umull => try emit.mirDataProcessing3Source(inst), - - .nop => try emit.mirNop(), - - .push_regs => try emit.mirPushPopRegs(inst), - .pop_regs => try emit.mirPushPopRegs(inst), - - .sbfx, - .ubfx, - => try emit.mirBitfieldExtract(inst), - - .sxtb, - .sxth, - .sxtw, - .uxtb, - .uxth, - => try emit.mirExtend(inst), - } - } -} - -pub fn deinit(emit: *Emit) void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - var iter = emit.branch_forward_origins.valueIterator(); - while (iter.next()) |origin_list| { - origin_list.deinit(gpa); - } - - emit.branch_types.deinit(gpa); - emit.branch_forward_origins.deinit(gpa); - emit.code_offset_mapping.deinit(gpa); - emit.* = undefined; -} - -fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { - assert(offset & 0b11 == 0); - - switch (tag) { - .cbz => { - if (std.math.cast(i19, @shrExact(offset, 2))) |_| { - return BranchType.cbz; - } else { - return emit.fail("TODO support cbz branches larger than +-1 MiB", .{}); - } - }, - .b, .bl => { - if (std.math.cast(i26, @shrExact(offset, 2))) |_| { - return BranchType.unconditional_branch_immediate; - } else { - return emit.fail("TODO support unconditional branches larger than +-128 MiB", .{}); - } - }, - .b_cond => { - if (std.math.cast(i19, @shrExact(offset, 2))) |_| { - return BranchType.b_cond; - } else { - return emit.fail("TODO support conditional branches larger than +-1 MiB", .{}); - } - }, - else => unreachable, - } -} - -fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { - const tag = emit.mir.instructions.items(.tag)[inst]; - - if (isBranch(tag)) { - switch (emit.branch_types.get(inst).?) { - .cbz, - .unconditional_branch_immediate, - .b_cond, - => return 4, - } - } - - switch (tag) { - .load_memory_direct => return 3 * 4, - .load_memory_got, - .load_memory_ptr_got, - .load_memory_ptr_direct, - => return 2 * 4, - .pop_regs, .push_regs => { - const reg_list = emit.mir.instructions.items(.data)[inst].reg_list; - const number_of_regs = @popCount(reg_list); - const number_of_insts = std.math.divCeil(u6, number_of_regs, 2) catch unreachable; - return number_of_insts * 4; - }, - .call_extern => return 4, - .dbg_line, - .dbg_epilogue_begin, - .dbg_prologue_end, - => return 0, - else => return 4, - } -} - -fn isBranch(tag: Mir.Inst.Tag) bool { - return switch (tag) { - .cbz, - .b, - .bl, - .b_cond, - => true, - else => false, - }; -} - -fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { - const tag = emit.mir.instructions.items(.tag)[inst]; - - switch (tag) { - .cbz => return emit.mir.instructions.items(.data)[inst].r_inst.inst, - .b, .bl => return emit.mir.instructions.items(.data)[inst].inst, - .b_cond => return emit.mir.instructions.items(.data)[inst].inst_cond.inst, - else => unreachable, - } -} - -fn lowerBranches(emit: *Emit) !void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - const mir_tags = emit.mir.instructions.items(.tag); - - // First pass: Note down all branches and their target - // instructions, i.e. populate branch_types, - // branch_forward_origins, and code_offset_mapping - // - // TODO optimization opportunity: do this in codegen while - // generating MIR - for (mir_tags, 0..) |tag, index| { - const inst = @as(u32, @intCast(index)); - if (isBranch(tag)) { - const target_inst = emit.branchTarget(inst); - - // Remember this branch instruction - try emit.branch_types.put(gpa, inst, BranchType.default(tag)); - - // Forward branches require some extra stuff: We only - // know their offset once we arrive at the target - // instruction. Therefore, we need to be able to - // access the branch instruction when we visit the - // target instruction in order to manipulate its type - // etc. - if (target_inst > inst) { - // Remember the branch instruction index - try emit.code_offset_mapping.put(gpa, inst, 0); - - if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| { - try origin_list.append(gpa, inst); - } else { - var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty; - try origin_list.append(gpa, inst); - try emit.branch_forward_origins.put(gpa, target_inst, origin_list); - } - } - - // Remember the target instruction index so that we - // update the real code offset in all future passes - // - // putNoClobber may not be used as the put operation - // may clobber the entry when multiple branches branch - // to the same target instruction - try emit.code_offset_mapping.put(gpa, target_inst, 0); - } - } - - // Further passes: Until all branches are lowered, interate - // through all instructions and calculate new offsets and - // potentially new branch types - var all_branches_lowered = false; - while (!all_branches_lowered) { - all_branches_lowered = true; - var current_code_offset: usize = 0; - - for (mir_tags, 0..) |tag, index| { - const inst = @as(u32, @intCast(index)); - - // If this instruction contained in the code offset - // mapping (when it is a target of a branch or if it is a - // forward branch), update the code offset - if (emit.code_offset_mapping.getPtr(inst)) |offset| { - offset.* = current_code_offset; - } - - // If this instruction is a backward branch, calculate the - // offset, which may potentially update the branch type - if (isBranch(tag)) { - const target_inst = emit.branchTarget(inst); - if (target_inst < inst) { - const target_offset = emit.code_offset_mapping.get(target_inst).?; - const offset = @as(i64, @intCast(target_offset)) - @as(i64, @intCast(current_code_offset)); - const branch_type = emit.branch_types.getPtr(inst).?; - const optimal_branch_type = try emit.optimalBranchType(tag, offset); - if (branch_type.* != optimal_branch_type) { - branch_type.* = optimal_branch_type; - all_branches_lowered = false; - } - - log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset }); - } - } - - // If this instruction is the target of one or more - // forward branches, calculate the offset, which may - // potentially update the branch type - if (emit.branch_forward_origins.get(inst)) |origin_list| { - for (origin_list.items) |forward_branch_inst| { - const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst]; - const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?; - const offset = @as(i64, @intCast(current_code_offset)) - @as(i64, @intCast(forward_branch_inst_offset)); - const branch_type = emit.branch_types.getPtr(forward_branch_inst).?; - const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset); - if (branch_type.* != optimal_branch_type) { - branch_type.* = optimal_branch_type; - all_branches_lowered = false; - } - - log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset }); - } - } - - // Increment code offset - current_code_offset += emit.instructionSize(inst); - } - } -} - -fn writeInstruction(emit: *Emit, instruction: Instruction) !void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - const endian = emit.target.cpu.arch.endian(); - std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian); -} - -fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { - @branchHint(.cold); - assert(emit.err_msg == null); - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args); - return error.EmitFail; -} - -fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { - const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line); - const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; - log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); - switch (emit.debug_output) { - .dwarf => |dw| { - if (column != emit.prev_di_column) try dw.setColumn(column); - try dw.advancePCAndLine(delta_line, delta_pc); - emit.prev_di_line = line; - emit.prev_di_column = column; - emit.prev_di_pc = emit.code.items.len; - }, - .plan9 => |dbg_out| { - if (delta_pc <= 0) return; // only do this when the pc changes - - var aw: std.io.Writer.Allocating = .fromArrayList(emit.bin_file.comp.gpa, &dbg_out.dbg_line); - const bw = &aw.interface; - defer dbg_out.dbg_line = aw.toArrayList(); - - // increasing the line number - try link.File.Plan9.changeLine(bw, @intCast(delta_line)); - // increasing the pc - const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta; - if (d_pc_p9 > 0) { - // minus one because if its the last one, we want to leave space to change the line which is one pc quanta - try bw.writeByte(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta); - const dbg_line = aw.getWritten(); - if (dbg_out.pcop_change_index) |pci| dbg_line[pci] += 1; - dbg_out.pcop_change_index = @intCast(dbg_line.len - 1); - } else if (d_pc_p9 == 0) { - // we don't need to do anything, because adding the pc quanta does it for us - } else unreachable; - if (dbg_out.start_line == null) - dbg_out.start_line = emit.prev_di_line; - dbg_out.end_line = line; - // only do this if the pc changed - emit.prev_di_line = line; - emit.prev_di_column = column; - emit.prev_di_pc = emit.code.items.len; - }, - .none => {}, - } -} - -fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - switch (tag) { - .add_immediate, - .adds_immediate, - .sub_immediate, - .subs_immediate, - => { - const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh; - const rd = rr_imm12_sh.rd; - const rn = rr_imm12_sh.rn; - const imm12 = rr_imm12_sh.imm12; - const sh = rr_imm12_sh.sh == 1; - - switch (tag) { - .add_immediate => try emit.writeInstruction(Instruction.add(rd, rn, imm12, sh)), - .adds_immediate => try emit.writeInstruction(Instruction.adds(rd, rn, imm12, sh)), - .sub_immediate => try emit.writeInstruction(Instruction.sub(rd, rn, imm12, sh)), - .subs_immediate => try emit.writeInstruction(Instruction.subs(rd, rn, imm12, sh)), - else => unreachable, - } - }, - .cmp_immediate => { - const r_imm12_sh = emit.mir.instructions.items(.data)[inst].r_imm12_sh; - const rn = r_imm12_sh.rn; - const imm12 = r_imm12_sh.imm12; - const sh = r_imm12_sh.sh == 1; - const zr: Register = switch (rn.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - - try emit.writeInstruction(Instruction.subs(zr, rn, imm12, sh)); - }, - else => unreachable, - } -} - -fn mirDataProcessing2Source(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const rrr = emit.mir.instructions.items(.data)[inst].rrr; - const rd = rrr.rd; - const rn = rrr.rn; - const rm = rrr.rm; - - switch (tag) { - .asr_register => try emit.writeInstruction(Instruction.asrRegister(rd, rn, rm)), - .lsl_register => try emit.writeInstruction(Instruction.lslRegister(rd, rn, rm)), - .lsr_register => try emit.writeInstruction(Instruction.lsrRegister(rd, rn, rm)), - .sdiv => try emit.writeInstruction(Instruction.sdiv(rd, rn, rm)), - .udiv => try emit.writeInstruction(Instruction.udiv(rd, rn, rm)), - else => unreachable, - } -} - -fn mirShiftImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const rr_shift = emit.mir.instructions.items(.data)[inst].rr_shift; - const rd = rr_shift.rd; - const rn = rr_shift.rn; - const shift = rr_shift.shift; - - switch (tag) { - .asr_immediate => try emit.writeInstruction(Instruction.asrImmediate(rd, rn, shift)), - .lsl_immediate => try emit.writeInstruction(Instruction.lslImmediate(rd, rn, shift)), - .lsr_immediate => try emit.writeInstruction(Instruction.lsrImmediate(rd, rn, shift)), - else => unreachable, - } -} - -fn mirConditionalBranchImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const inst_cond = emit.mir.instructions.items(.data)[inst].inst_cond; - - const offset = @as(i64, @intCast(emit.code_offset_mapping.get(inst_cond.inst).?)) - @as(i64, @intCast(emit.code.items.len)); - const branch_type = emit.branch_types.get(inst).?; - log.debug("mirConditionalBranchImmediate: {} offset={}", .{ inst, offset }); - - switch (branch_type) { - .b_cond => switch (tag) { - .b_cond => try emit.writeInstruction(Instruction.bCond(inst_cond.cond, @as(i21, @intCast(offset)))), - else => unreachable, - }, - else => unreachable, - } -} - -fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const target_inst = emit.mir.instructions.items(.data)[inst].inst; - - log.debug("branch {}(tag: {}) -> {}(tag: {})", .{ - inst, - tag, - target_inst, - emit.mir.instructions.items(.tag)[target_inst], - }); - - const offset = @as(i64, @intCast(emit.code_offset_mapping.get(target_inst).?)) - @as(i64, @intCast(emit.code.items.len)); - const branch_type = emit.branch_types.get(inst).?; - log.debug("mirBranch: {} offset={}", .{ inst, offset }); - - switch (branch_type) { - .unconditional_branch_immediate => switch (tag) { - .b => try emit.writeInstruction(Instruction.b(@as(i28, @intCast(offset)))), - .bl => try emit.writeInstruction(Instruction.bl(@as(i28, @intCast(offset)))), - else => unreachable, - }, - else => unreachable, - } -} - -fn mirCompareAndBranch(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const r_inst = emit.mir.instructions.items(.data)[inst].r_inst; - - const offset = @as(i64, @intCast(emit.code_offset_mapping.get(r_inst.inst).?)) - @as(i64, @intCast(emit.code.items.len)); - const branch_type = emit.branch_types.get(inst).?; - log.debug("mirCompareAndBranch: {} offset={}", .{ inst, offset }); - - switch (branch_type) { - .cbz => switch (tag) { - .cbz => try emit.writeInstruction(Instruction.cbz(r_inst.rt, @as(i21, @intCast(offset)))), - else => unreachable, - }, - else => unreachable, - } -} - -fn mirUnconditionalBranchRegister(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const reg = emit.mir.instructions.items(.data)[inst].reg; - - switch (tag) { - .blr => try emit.writeInstruction(Instruction.blr(reg)), - .ret => try emit.writeInstruction(Instruction.ret(reg)), - else => unreachable, - } -} - -fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const imm16 = emit.mir.instructions.items(.data)[inst].imm16; - - switch (tag) { - .brk => try emit.writeInstruction(Instruction.brk(imm16)), - .svc => try emit.writeInstruction(Instruction.svc(imm16)), - else => unreachable, - } -} - -fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; - - switch (tag) { - .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), - else => unreachable, - } -} - -fn mirDebugPrologueEnd(emit: *Emit) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setPrologueEnd(); - log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ - emit.prev_di_line, emit.prev_di_column, - }); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} - -fn mirDebugEpilogueBegin(emit: *Emit) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setEpilogueBegin(); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} - -fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { - assert(emit.mir.instructions.items(.tag)[inst] == .call_extern); - const relocation = emit.mir.instructions.items(.data)[inst].relocation; - _ = relocation; - - const offset = blk: { - const offset = @as(u32, @intCast(emit.code.items.len)); - // bl - try emit.writeInstruction(Instruction.bl(0)); - break :blk offset; - }; - _ = offset; - - if (emit.bin_file.cast(.macho)) |macho_file| { - _ = macho_file; - @panic("TODO mirCallExtern"); - // // Add relocation to the decl. - // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?; - // const target = macho_file.getGlobalByIndex(relocation.sym_index); - // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - // .type = .branch, - // .target = target, - // .offset = offset, - // .addend = 0, - // .pcrel = true, - // .length = 2, - // }); - } else if (emit.bin_file.cast(.coff)) |_| { - unreachable; // Calling imports is handled via `.load_memory_import` - } else { - return emit.fail("Implement call_extern for linking backends != {{ COFF, MachO }}", .{}); - } -} - -fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const rr_bitmask = emit.mir.instructions.items(.data)[inst].rr_bitmask; - const rd = rr_bitmask.rd; - const rn = rr_bitmask.rn; - const imms = rr_bitmask.imms; - const immr = rr_bitmask.immr; - const n = rr_bitmask.n; - - switch (tag) { - .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)), - .tst_immediate => { - const zr: Register = switch (rd.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - try emit.writeInstruction(Instruction.andsImmediate(zr, rn, imms, immr, n)); - }, - else => unreachable, - } -} - -fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - switch (tag) { - .add_shifted_register, - .adds_shifted_register, - .sub_shifted_register, - .subs_shifted_register, - => { - const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift; - const rd = rrr_imm6_shift.rd; - const rn = rrr_imm6_shift.rn; - const rm = rrr_imm6_shift.rm; - const shift = rrr_imm6_shift.shift; - const imm6 = rrr_imm6_shift.imm6; - - switch (tag) { - .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)), - .adds_shifted_register => try emit.writeInstruction(Instruction.addsShiftedRegister(rd, rn, rm, shift, imm6)), - .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)), - .subs_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)), - else => unreachable, - } - }, - .cmp_shifted_register => { - const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift; - const rn = rr_imm6_shift.rn; - const rm = rr_imm6_shift.rm; - const shift = rr_imm6_shift.shift; - const imm6 = rr_imm6_shift.imm6; - const zr: Register = switch (rn.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - - try emit.writeInstruction(Instruction.subsShiftedRegister(zr, rn, rm, shift, imm6)); - }, - else => unreachable, - } -} - -fn mirAddSubtractExtendedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - switch (tag) { - .add_extended_register, - .adds_extended_register, - .sub_extended_register, - .subs_extended_register, - => { - const rrr_extend_shift = emit.mir.instructions.items(.data)[inst].rrr_extend_shift; - const rd = rrr_extend_shift.rd; - const rn = rrr_extend_shift.rn; - const rm = rrr_extend_shift.rm; - const ext_type = rrr_extend_shift.ext_type; - const imm3 = rrr_extend_shift.imm3; - - switch (tag) { - .add_extended_register => try emit.writeInstruction(Instruction.addExtendedRegister(rd, rn, rm, ext_type, imm3)), - .adds_extended_register => try emit.writeInstruction(Instruction.addsExtendedRegister(rd, rn, rm, ext_type, imm3)), - .sub_extended_register => try emit.writeInstruction(Instruction.subExtendedRegister(rd, rn, rm, ext_type, imm3)), - .subs_extended_register => try emit.writeInstruction(Instruction.subsExtendedRegister(rd, rn, rm, ext_type, imm3)), - else => unreachable, - } - }, - .cmp_extended_register => { - const rr_extend_shift = emit.mir.instructions.items(.data)[inst].rr_extend_shift; - const rn = rr_extend_shift.rn; - const rm = rr_extend_shift.rm; - const ext_type = rr_extend_shift.ext_type; - const imm3 = rr_extend_shift.imm3; - const zr: Register = switch (rn.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - - try emit.writeInstruction(Instruction.subsExtendedRegister(zr, rn, rm, ext_type, imm3)); - }, - else => unreachable, - } -} - -fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - switch (tag) { - .csel => { - const rrr_cond = emit.mir.instructions.items(.data)[inst].rrr_cond; - const rd = rrr_cond.rd; - const rn = rrr_cond.rn; - const rm = rrr_cond.rm; - const cond = rrr_cond.cond; - try emit.writeInstruction(Instruction.csel(rd, rn, rm, cond)); - }, - .cset => { - const r_cond = emit.mir.instructions.items(.data)[inst].r_cond; - const zr: Register = switch (r_cond.rd.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond.negate())); - }, - else => unreachable, - } -} - -fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const rrr_imm6_logical_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_logical_shift; - const rd = rrr_imm6_logical_shift.rd; - const rn = rrr_imm6_logical_shift.rn; - const rm = rrr_imm6_logical_shift.rm; - const shift = rrr_imm6_logical_shift.shift; - const imm6 = rrr_imm6_logical_shift.imm6; - - switch (tag) { - .and_shifted_register => try emit.writeInstruction(Instruction.andShiftedRegister(rd, rn, rm, shift, imm6)), - .eor_shifted_register => try emit.writeInstruction(Instruction.eorShiftedRegister(rd, rn, rm, shift, imm6)), - .orr_shifted_register => try emit.writeInstruction(Instruction.orrShiftedRegister(rd, rn, rm, shift, imm6)), - else => unreachable, - } -} - -fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const payload = emit.mir.instructions.items(.data)[inst].payload; - const data = emit.mir.extraData(Mir.LoadMemoryPie, payload).data; - const reg = @as(Register, @enumFromInt(data.register)); - - // PC-relative displacement to the entry in memory. - // adrp - const offset = @as(u32, @intCast(emit.code.items.len)); - try emit.writeInstruction(Instruction.adrp(reg.toX(), 0)); - - switch (tag) { - .load_memory_got, - .load_memory_import, - => { - // ldr reg, reg, offset - try emit.writeInstruction(Instruction.ldr( - reg, - reg.toX(), - Instruction.LoadStoreOffset.imm(0), - )); - }, - .load_memory_direct => { - // We cannot load the offset directly as it may not be aligned properly. - // For example, load for 64bit register will require the target address offset - // to be 8-byte aligned, while the value might have non-8-byte natural alignment, - // meaning the linker might have put it at a non-8-byte aligned address. To circumvent - // this, we use `adrp, add` to form the address value which we then dereference with - // `ldr`. - // Note that this can potentially be optimised out by the codegen/linker if the - // target address is appropriately aligned. - // add reg, reg, offset - try emit.writeInstruction(Instruction.add(reg.toX(), reg.toX(), 0, false)); - // ldr reg, reg, offset - try emit.writeInstruction(Instruction.ldr( - reg, - reg.toX(), - Instruction.LoadStoreOffset.imm(0), - )); - }, - .load_memory_ptr_direct, - .load_memory_ptr_got, - => { - // add reg, reg, offset - try emit.writeInstruction(Instruction.add(reg, reg, 0, false)); - }, - else => unreachable, - } - - if (emit.bin_file.cast(.macho)) |macho_file| { - _ = macho_file; - @panic("TODO mirLoadMemoryPie"); - // const Atom = link.File.MachO.Atom; - // const Relocation = Atom.Relocation; - // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?; - // try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{ - // .target = .{ .sym_index = data.sym_index }, - // .offset = offset, - // .addend = 0, - // .pcrel = true, - // .length = 2, - // .type = switch (tag) { - // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_page, - // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.page, - // else => unreachable, - // }, - // }, .{ - // .target = .{ .sym_index = data.sym_index }, - // .offset = offset + 4, - // .addend = 0, - // .pcrel = false, - // .length = 2, - // .type = switch (tag) { - // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_pageoff, - // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.pageoff, - // else => unreachable, - // }, - // } }); - } else if (emit.bin_file.cast(.coff)) |coff_file| { - const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?; - const target = switch (tag) { - .load_memory_got, - .load_memory_ptr_got, - .load_memory_direct, - .load_memory_ptr_direct, - => link.File.Coff.SymbolWithLoc{ .sym_index = data.sym_index, .file = null }, - .load_memory_import => coff_file.getGlobalByIndex(data.sym_index), - else => unreachable, - }; - try coff_file.addRelocation(atom_index, .{ - .target = target, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - .type = switch (tag) { - .load_memory_got, - .load_memory_ptr_got, - => .got_page, - .load_memory_direct, - .load_memory_ptr_direct, - => .page, - .load_memory_import => .import_page, - else => unreachable, - }, - }); - try coff_file.addRelocation(atom_index, .{ - .target = target, - .offset = offset + 4, - .addend = 0, - .pcrel = false, - .length = 2, - .type = switch (tag) { - .load_memory_got, - .load_memory_ptr_got, - => .got_pageoff, - .load_memory_direct, - .load_memory_ptr_direct, - => .pageoff, - .load_memory_import => .import_pageoff, - else => unreachable, - }, - }); - } else { - return emit.fail("TODO implement load_memory for PIE GOT indirection on this platform", .{}); - } -} - -fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair; - const rt = load_store_register_pair.rt; - const rt2 = load_store_register_pair.rt2; - const rn = load_store_register_pair.rn; - const offset = load_store_register_pair.offset; - - switch (tag) { - .stp => try emit.writeInstruction(Instruction.stp(rt, rt2, rn, offset)), - .ldp => try emit.writeInstruction(Instruction.ldp(rt, rt2, rn, offset)), - else => unreachable, - } -} - -fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const load_store_stack = emit.mir.instructions.items(.data)[inst].load_store_stack; - const rt = load_store_stack.rt; - - const raw_offset = emit.stack_size + emit.saved_regs_stack_space + load_store_stack.offset; - switch (tag) { - .ldr_ptr_stack_argument => { - const offset = if (math.cast(u12, raw_offset)) |imm| imm else { - return emit.fail("TODO load stack argument ptr with larger offset", .{}); - }; - - switch (tag) { - .ldr_ptr_stack_argument => try emit.writeInstruction(Instruction.add(rt, .sp, offset, false)), - else => unreachable, - } - }, - .ldrb_stack_argument, .ldrsb_stack_argument => { - const offset = if (math.cast(u12, raw_offset)) |imm| Instruction.LoadStoreOffset.imm(imm) else { - return emit.fail("TODO load stack argument byte with larger offset", .{}); - }; - - switch (tag) { - .ldrb_stack_argument => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)), - .ldrsb_stack_argument => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)), - else => unreachable, - } - }, - .ldrh_stack_argument, .ldrsh_stack_argument => { - assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry - const offset = if (math.cast(u12, @divExact(raw_offset, 2))) |imm| Instruction.LoadStoreOffset.imm(imm) else { - return emit.fail("TODO load stack argument halfword with larger offset", .{}); - }; - - switch (tag) { - .ldrh_stack_argument => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)), - .ldrsh_stack_argument => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)), - else => unreachable, - } - }, - .ldr_stack_argument => { - const alignment: u32 = switch (rt.size()) { - 32 => 4, - 64 => 8, - else => unreachable, - }; - - assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry - const offset = if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| Instruction.LoadStoreOffset.imm(imm) else { - return emit.fail("TODO load stack argument with larger offset", .{}); - }; - - switch (tag) { - .ldr_stack_argument => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)), - else => unreachable, - } - }, - else => unreachable, - } -} - -fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const load_store_stack = emit.mir.instructions.items(.data)[inst].load_store_stack; - const rt = load_store_stack.rt; - - const raw_offset = emit.stack_size - load_store_stack.offset; - switch (tag) { - .ldr_ptr_stack => { - const offset = if (math.cast(u12, raw_offset)) |imm| imm else { - return emit.fail("TODO load stack argument ptr with larger offset", .{}); - }; - - switch (tag) { - .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(rt, .sp, offset, false)), - else => unreachable, - } - }, - .ldrb_stack, .ldrsb_stack, .strb_stack => { - const offset = if (math.cast(u12, raw_offset)) |imm| Instruction.LoadStoreOffset.imm(imm) else { - return emit.fail("TODO load/store stack byte with larger offset", .{}); - }; - - switch (tag) { - .ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)), - .ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)), - .strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)), - else => unreachable, - } - }, - .ldrh_stack, .ldrsh_stack, .strh_stack => { - assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry - const offset = if (math.cast(u12, @divExact(raw_offset, 2))) |imm| Instruction.LoadStoreOffset.imm(imm) else { - return emit.fail("TODO load/store stack halfword with larger offset", .{}); - }; - - switch (tag) { - .ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)), - .ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)), - .strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)), - else => unreachable, - } - }, - .ldr_stack, .str_stack => { - const alignment: u32 = switch (rt.size()) { - 32 => 4, - 64 => 8, - else => unreachable, - }; - - assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry - const offset = if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| Instruction.LoadStoreOffset.imm(imm) else { - return emit.fail("TODO load/store stack with larger offset", .{}); - }; - - switch (tag) { - .ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)), - .str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)), - else => unreachable, - } - }, - else => unreachable, - } -} - -fn mirLoadStoreRegisterImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const load_store_register_immediate = emit.mir.instructions.items(.data)[inst].load_store_register_immediate; - const rt = load_store_register_immediate.rt; - const rn = load_store_register_immediate.rn; - const offset = Instruction.LoadStoreOffset{ .immediate = load_store_register_immediate.offset }; - - switch (tag) { - .ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)), - .ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)), - .ldrh_immediate => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)), - .ldrsb_immediate => try emit.writeInstruction(Instruction.ldrsb(rt, rn, offset)), - .ldrsh_immediate => try emit.writeInstruction(Instruction.ldrsh(rt, rn, offset)), - .ldrsw_immediate => try emit.writeInstruction(Instruction.ldrsw(rt, rn, offset)), - .str_immediate => try emit.writeInstruction(Instruction.str(rt, rn, offset)), - .strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)), - .strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)), - else => unreachable, - } -} - -fn mirLoadStoreRegisterRegister(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const load_store_register_register = emit.mir.instructions.items(.data)[inst].load_store_register_register; - const rt = load_store_register_register.rt; - const rn = load_store_register_register.rn; - const offset = Instruction.LoadStoreOffset{ .register = load_store_register_register.offset }; - - switch (tag) { - .ldr_register => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)), - .ldrb_register => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)), - .ldrh_register => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)), - .str_register => try emit.writeInstruction(Instruction.str(rt, rn, offset)), - .strb_register => try emit.writeInstruction(Instruction.strb(rt, rn, offset)), - .strh_register => try emit.writeInstruction(Instruction.strh(rt, rn, offset)), - else => unreachable, - } -} - -fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - switch (tag) { - .mov_register => { - const rr = emit.mir.instructions.items(.data)[inst].rr; - const zr: Register = switch (rr.rd.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - - try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, zr, rr.rn, .lsl, 0)); - }, - .mov_to_from_sp => { - const rr = emit.mir.instructions.items(.data)[inst].rr; - try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false)); - }, - .mvn => { - const rr_imm6_logical_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_logical_shift; - const rd = rr_imm6_logical_shift.rd; - const rm = rr_imm6_logical_shift.rm; - const shift = rr_imm6_logical_shift.shift; - const imm6 = rr_imm6_logical_shift.imm6; - const zr: Register = switch (rd.size()) { - 32 => .wzr, - 64 => .xzr, - else => unreachable, - }; - - try emit.writeInstruction(Instruction.ornShiftedRegister(rd, zr, rm, shift, imm6)); - }, - else => unreachable, - } -} - -fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const r_imm16_sh = emit.mir.instructions.items(.data)[inst].r_imm16_sh; - - switch (tag) { - .movz => try emit.writeInstruction(Instruction.movz(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)), - .movk => try emit.writeInstruction(Instruction.movk(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)), - else => unreachable, - } -} - -fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - - switch (tag) { - .mul, - .smulh, - .smull, - .umulh, - .umull, - => { - const rrr = emit.mir.instructions.items(.data)[inst].rrr; - switch (tag) { - .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)), - .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)), - .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)), - .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)), - .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)), - else => unreachable, - } - }, - .msub => { - const rrrr = emit.mir.instructions.items(.data)[inst].rrrr; - switch (tag) { - .msub => try emit.writeInstruction(Instruction.msub(rrrr.rd, rrrr.rn, rrrr.rm, rrrr.ra)), - else => unreachable, - } - }, - else => unreachable, - } -} - -fn mirNop(emit: *Emit) !void { - try emit.writeInstruction(Instruction.nop()); -} - -fn regListIsSet(reg_list: u32, reg: Register) bool { - return reg_list & @as(u32, 1) << @as(u5, @intCast(reg.id())) != 0; -} - -fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const reg_list = emit.mir.instructions.items(.data)[inst].reg_list; - - if (regListIsSet(reg_list, .xzr)) return emit.fail("xzr is not a valid register for {}", .{tag}); - - // sp must be aligned at all times, so we only use stp and ldp - // instructions for minimal instruction count. - // - // However, if we have an odd number of registers, for pop_regs we - // use one ldr instruction followed by zero or more ldp - // instructions; for push_regs we use zero or more stp - // instructions followed by one str instruction. - const number_of_regs = @popCount(reg_list); - const odd_number_of_regs = number_of_regs % 2 != 0; - - switch (tag) { - .pop_regs => { - var i: u6 = 32; - var count: u6 = 0; - var other_reg: ?Register = null; - while (i > 0) : (i -= 1) { - const reg = @as(Register, @enumFromInt(i - 1)); - if (regListIsSet(reg_list, reg)) { - if (count == 0 and odd_number_of_regs) { - try emit.writeInstruction(Instruction.ldr( - reg, - .sp, - Instruction.LoadStoreOffset.imm_post_index(16), - )); - } else if (other_reg) |r| { - try emit.writeInstruction(Instruction.ldp( - reg, - r, - .sp, - Instruction.LoadStorePairOffset.post_index(16), - )); - other_reg = null; - } else { - other_reg = reg; - } - count += 1; - } - } - assert(count == number_of_regs); - }, - .push_regs => { - var i: u6 = 0; - var count: u6 = 0; - var other_reg: ?Register = null; - while (i < 32) : (i += 1) { - const reg = @as(Register, @enumFromInt(i)); - if (regListIsSet(reg_list, reg)) { - if (count == number_of_regs - 1 and odd_number_of_regs) { - try emit.writeInstruction(Instruction.str( - reg, - .sp, - Instruction.LoadStoreOffset.imm_pre_index(-16), - )); - } else if (other_reg) |r| { - try emit.writeInstruction(Instruction.stp( - r, - reg, - .sp, - Instruction.LoadStorePairOffset.pre_index(-16), - )); - other_reg = null; - } else { - other_reg = reg; - } - count += 1; - } - } - assert(count == number_of_regs); - }, - else => unreachable, - } -} - -fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; - const rd = rr_lsb_width.rd; - const rn = rr_lsb_width.rn; - const lsb = rr_lsb_width.lsb; - const width = rr_lsb_width.width; - - switch (tag) { - .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)), - .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)), - else => unreachable, - } -} - -fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const rr = emit.mir.instructions.items(.data)[inst].rr; - - switch (tag) { - .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)), - .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)), - .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)), - .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)), - .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)), - else => unreachable, - } -} diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig deleted file mode 100644 index 88089c84883bf4ebe1cfae5510a92c387da89913..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/Mir.zig +++ /dev/null @@ -1,568 +0,0 @@ -//! Machine Intermediate Representation. -//! This data is produced by AArch64 Codegen or AArch64 assembly parsing -//! These instructions have a 1:1 correspondence with machine code instructions -//! for the target. MIR can be lowered to source-annotated textual assembly code -//! instructions, or it can be lowered to machine code. -//! The main purpose of MIR is to postpone the assignment of offsets until Isel, -//! so that, for example, the smaller encodings of jump instructions can be used. - -const Mir = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; - -const bits = @import("bits.zig"); -const Register = bits.Register; -const InternPool = @import("../../InternPool.zig"); -const Emit = @import("Emit.zig"); -const codegen = @import("../../codegen.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); - -max_end_stack: u32, -saved_regs_stack_space: u32, - -instructions: std.MultiArrayList(Inst).Slice, -/// The meaning of this data is determined by `Inst.Tag` value. -extra: []const u32, - -pub const Inst = struct { - tag: Tag, - /// The meaning of this depends on `tag`. - data: Data, - - pub const Tag = enum(u16) { - /// Add (immediate) - add_immediate, - /// Add, update condition flags (immediate) - adds_immediate, - /// Add (shifted register) - add_shifted_register, - /// Add, update condition flags (shifted register) - adds_shifted_register, - /// Add (extended register) - add_extended_register, - /// Add, update condition flags (extended register) - adds_extended_register, - /// Bitwise AND (shifted register) - and_shifted_register, - /// Arithmetic Shift Right (immediate) - asr_immediate, - /// Arithmetic Shift Right (register) - asr_register, - /// Branch conditionally - b_cond, - /// Branch - b, - /// Branch with Link - bl, - /// Branch with Link to Register - blr, - /// Breakpoint - brk, - /// Pseudo-instruction: Call extern - call_extern, - /// Compare and Branch on Zero - cbz, - /// Compare (immediate) - cmp_immediate, - /// Compare (shifted register) - cmp_shifted_register, - /// Compare (extended register) - cmp_extended_register, - /// Conditional Select - csel, - /// Conditional set - cset, - /// Pseudo-instruction: End of prologue - dbg_prologue_end, - /// Pseudo-instruction: Beginning of epilogue - dbg_epilogue_begin, - /// Pseudo-instruction: Update debug line - dbg_line, - /// Bitwise Exclusive OR (immediate) - eor_immediate, - /// Bitwise Exclusive OR (shifted register) - eor_shifted_register, - /// Loads the contents into a register - /// - /// Payload is `LoadMemoryPie` - load_memory_got, - /// Loads the contents into a register - /// - /// Payload is `LoadMemoryPie` - load_memory_direct, - /// Loads the contents into a register - /// - /// Payload is `LoadMemoryPie` - load_memory_import, - /// Loads the address into a register - /// - /// Payload is `LoadMemoryPie` - load_memory_ptr_got, - /// Loads the address into a register - /// - /// Payload is `LoadMemoryPie` - load_memory_ptr_direct, - /// Load Pair of Registers - ldp, - /// Pseudo-instruction: Load pointer to stack item - ldr_ptr_stack, - /// Pseudo-instruction: Load pointer to stack argument - ldr_ptr_stack_argument, - /// Pseudo-instruction: Load from stack - ldr_stack, - /// Pseudo-instruction: Load from stack argument - ldr_stack_argument, - /// Load Register (immediate) - ldr_immediate, - /// Load Register (register) - ldr_register, - /// Pseudo-instruction: Load byte from stack - ldrb_stack, - /// Pseudo-instruction: Load byte from stack argument - ldrb_stack_argument, - /// Load Register Byte (immediate) - ldrb_immediate, - /// Load Register Byte (register) - ldrb_register, - /// Pseudo-instruction: Load halfword from stack - ldrh_stack, - /// Pseudo-instruction: Load halfword from stack argument - ldrh_stack_argument, - /// Load Register Halfword (immediate) - ldrh_immediate, - /// Load Register Halfword (register) - ldrh_register, - /// Load Register Signed Byte (immediate) - ldrsb_immediate, - /// Pseudo-instruction: Load signed byte from stack - ldrsb_stack, - /// Pseudo-instruction: Load signed byte from stack argument - ldrsb_stack_argument, - /// Load Register Signed Halfword (immediate) - ldrsh_immediate, - /// Pseudo-instruction: Load signed halfword from stack - ldrsh_stack, - /// Pseudo-instruction: Load signed halfword from stack argument - ldrsh_stack_argument, - /// Load Register Signed Word (immediate) - ldrsw_immediate, - /// Logical Shift Left (immediate) - lsl_immediate, - /// Logical Shift Left (register) - lsl_register, - /// Logical Shift Right (immediate) - lsr_immediate, - /// Logical Shift Right (register) - lsr_register, - /// Move (to/from SP) - mov_to_from_sp, - /// Move (register) - mov_register, - /// Move wide with keep - movk, - /// Move wide with zero - movz, - /// Multiply-subtract - msub, - /// Multiply - mul, - /// Bitwise NOT - mvn, - /// No Operation - nop, - /// Bitwise inclusive OR (shifted register) - orr_shifted_register, - /// Pseudo-instruction: Pop multiple registers - pop_regs, - /// Pseudo-instruction: Push multiple registers - push_regs, - /// Return from subroutine - ret, - /// Signed bitfield extract - sbfx, - /// Signed divide - sdiv, - /// Signed multiply high - smulh, - /// Signed multiply long - smull, - /// Signed extend byte - sxtb, - /// Signed extend halfword - sxth, - /// Signed extend word - sxtw, - /// Store Pair of Registers - stp, - /// Pseudo-instruction: Store to stack - str_stack, - /// Store Register (immediate) - str_immediate, - /// Store Register (register) - str_register, - /// Pseudo-instruction: Store byte to stack - strb_stack, - /// Store Register Byte (immediate) - strb_immediate, - /// Store Register Byte (register) - strb_register, - /// Pseudo-instruction: Store halfword to stack - strh_stack, - /// Store Register Halfword (immediate) - strh_immediate, - /// Store Register Halfword (register) - strh_register, - /// Subtract (immediate) - sub_immediate, - /// Subtract, update condition flags (immediate) - subs_immediate, - /// Subtract (shifted register) - sub_shifted_register, - /// Subtract, update condition flags (shifted register) - subs_shifted_register, - /// Subtract (extended register) - sub_extended_register, - /// Subtract, update condition flags (extended register) - subs_extended_register, - /// Supervisor Call - svc, - /// Test bits (immediate) - tst_immediate, - /// Unsigned bitfield extract - ubfx, - /// Unsigned divide - udiv, - /// Unsigned multiply high - umulh, - /// Unsigned multiply long - umull, - /// Unsigned extend byte - uxtb, - /// Unsigned extend halfword - uxth, - }; - - /// The position of an MIR instruction within the `Mir` instructions array. - pub const Index = u32; - - /// All instructions have a 4-byte payload, which is contained within - /// this union. `Tag` determines which union field is active, as well as - /// how to interpret the data within. - pub const Data = union { - /// No additional data - /// - /// Used by e.g. nop - nop: void, - /// Another instruction - /// - /// Used by e.g. b - inst: Index, - /// Relocation for the linker where: - /// * `atom_index` is the index of the source - /// * `sym_index` is the index of the target - /// - /// Used by e.g. call_extern - relocation: struct { - /// Index of the containing atom. - atom_index: u32, - /// Index into the linker's string table. - sym_index: u32, - }, - /// A 16-bit immediate value. - /// - /// Used by e.g. svc - imm16: u16, - /// Index into `extra`. Meaning of what can be found there is context-dependent. - payload: u32, - /// A register - /// - /// Used by e.g. blr - reg: Register, - /// Multiple registers - /// - /// Used by e.g. pop_regs - reg_list: u32, - /// Another instruction and a condition - /// - /// Used by e.g. b_cond - inst_cond: struct { - inst: Index, - cond: bits.Instruction.Condition, - }, - /// A register, an unsigned 16-bit immediate, and an optional shift - /// - /// Used by e.g. movz - r_imm16_sh: struct { - rd: Register, - imm16: u16, - hw: u2 = 0, - }, - /// A register and a condition - /// - /// Used by e.g. cset - r_cond: struct { - rd: Register, - cond: bits.Instruction.Condition, - }, - /// A register and another instruction - /// - /// Used by e.g. cbz - r_inst: struct { - rt: Register, - inst: Index, - }, - /// A register, an unsigned 12-bit immediate, and an optional shift - /// - /// Used by e.g. cmp_immediate - r_imm12_sh: struct { - rn: Register, - imm12: u12, - sh: u1 = 0, - }, - /// Two registers - /// - /// Used by e.g. mov_register - rr: struct { - rd: Register, - rn: Register, - }, - /// Two registers, an unsigned 12-bit immediate, and an optional shift - /// - /// Used by e.g. sub_immediate - rr_imm12_sh: struct { - rd: Register, - rn: Register, - imm12: u12, - sh: u1 = 0, - }, - /// Two registers and a shift (shift type and 6-bit amount) - /// - /// Used by e.g. cmp_shifted_register - rr_imm6_shift: struct { - rn: Register, - rm: Register, - imm6: u6, - shift: bits.Instruction.AddSubtractShiftedRegisterShift, - }, - /// Two registers with sign-extension (extension type and 3-bit shift amount) - /// - /// Used by e.g. cmp_extended_register - rr_extend_shift: struct { - rn: Register, - rm: Register, - ext_type: bits.Instruction.AddSubtractExtendedRegisterOption, - imm3: u3, - }, - /// Two registers and a shift (logical instruction version) - /// (shift type and 6-bit amount) - /// - /// Used by e.g. mvn - rr_imm6_logical_shift: struct { - rd: Register, - rm: Register, - imm6: u6, - shift: bits.Instruction.LogicalShiftedRegisterShift, - }, - /// Two registers and a lsb (range 0-63) and a width (range - /// 1-64) - /// - /// Used by e.g. ubfx - rr_lsb_width: struct { - rd: Register, - rn: Register, - lsb: u6, - width: u7, - }, - /// Two registers and a bitmask immediate - /// - /// Used by e.g. eor_immediate - rr_bitmask: struct { - rd: Register, - rn: Register, - imms: u6, - immr: u6, - n: u1, - }, - /// Two registers and a 6-bit unsigned shift - /// - /// Used by e.g. lsl_immediate - rr_shift: struct { - rd: Register, - rn: Register, - shift: u6, - }, - /// Three registers - /// - /// Used by e.g. mul - rrr: struct { - rd: Register, - rn: Register, - rm: Register, - }, - /// Three registers and a condition - /// - /// Used by e.g. csel - rrr_cond: struct { - rd: Register, - rn: Register, - rm: Register, - cond: bits.Instruction.Condition, - }, - /// Three registers and a shift (shift type and 6-bit amount) - /// - /// Used by e.g. add_shifted_register - rrr_imm6_shift: struct { - rd: Register, - rn: Register, - rm: Register, - imm6: u6, - shift: bits.Instruction.AddSubtractShiftedRegisterShift, - }, - /// Three registers with sign-extension (extension type and 3-bit shift amount) - /// - /// Used by e.g. add_extended_register - rrr_extend_shift: struct { - rd: Register, - rn: Register, - rm: Register, - ext_type: bits.Instruction.AddSubtractExtendedRegisterOption, - imm3: u3, - }, - /// Three registers and a shift (logical instruction version) - /// (shift type and 6-bit amount) - /// - /// Used by e.g. eor_shifted_register - rrr_imm6_logical_shift: struct { - rd: Register, - rn: Register, - rm: Register, - imm6: u6, - shift: bits.Instruction.LogicalShiftedRegisterShift, - }, - /// Two registers and a LoadStoreOffsetImmediate - /// - /// Used by e.g. str_immediate - load_store_register_immediate: struct { - rt: Register, - rn: Register, - offset: bits.Instruction.LoadStoreOffsetImmediate, - }, - /// Two registers and a LoadStoreOffsetRegister - /// - /// Used by e.g. str_register - load_store_register_register: struct { - rt: Register, - rn: Register, - offset: bits.Instruction.LoadStoreOffsetRegister, - }, - /// A register and a stack offset - /// - /// Used by e.g. str_stack - load_store_stack: struct { - rt: Register, - offset: u32, - }, - /// Three registers and a LoadStorePairOffset - /// - /// Used by e.g. stp - load_store_register_pair: struct { - rt: Register, - rt2: Register, - rn: Register, - offset: bits.Instruction.LoadStorePairOffset, - }, - /// Four registers - /// - /// Used by e.g. msub - rrrr: struct { - rd: Register, - rn: Register, - rm: Register, - ra: Register, - }, - /// Debug info: line and column - /// - /// Used by e.g. dbg_line - dbg_line_column: struct { - line: u32, - column: u32, - }, - }; - - // Make sure we don't accidentally make instructions bigger than expected. - // Note that in safety builds, Zig is allowed to insert a secret field for safety checks. - comptime { - if (!std.debug.runtime_safety) { - assert(@sizeOf(Data) == 8); - } - } -}; - -pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { - mir.instructions.deinit(gpa); - gpa.free(mir.extra); - mir.* = undefined; -} - -pub fn emit( - mir: Mir, - lf: *link.File, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - func_index: InternPool.Index, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { - const zcu = pt.zcu; - const func = zcu.funcInfo(func_index); - const nav = func.owner_nav; - const mod = zcu.navFileScope(nav).mod.?; - var e: Emit = .{ - .mir = mir, - .bin_file = lf, - .debug_output = debug_output, - .target = &mod.resolved_target.result, - .src_loc = src_loc, - .code = code, - .prev_di_pc = 0, - .prev_di_line = func.lbrace_line, - .prev_di_column = func.lbrace_column, - .stack_size = mir.max_end_stack, - .saved_regs_stack_space = mir.saved_regs_stack_space, - }; - defer e.deinit(); - e.emitMir() catch |err| switch (err) { - error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), - else => |e1| return e1, - }; -} - -/// Returns the requested data, as well as the new index which is at the start of the -/// trailers for the object. -pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { - const fields = std.meta.fields(T); - var i: usize = index; - var result: T = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => mir.extra[i], - i32 => @as(i32, @bitCast(mir.extra[i])), - else => @compileError("bad field type"), - }; - i += 1; - } - return .{ - .data = result, - .end = i, - }; -} - -pub const LoadMemoryPie = struct { - register: u32, - /// Index of the containing atom. - atom_index: u32, - /// Index into the linker's symbol table. - sym_index: u32, -}; diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig deleted file mode 100644 index 99c67d676186d8c1c7b48a1769837925a6ab5faf..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/abi.zig +++ /dev/null @@ -1,165 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const bits = @import("bits.zig"); -const Register = bits.Register; -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const Type = @import("../../Type.zig"); -const Zcu = @import("../../Zcu.zig"); - -pub const Class = union(enum) { - memory, - byval, - integer, - double_integer, - float_array: u8, -}; - -/// For `float_array` the second element will be the amount of floats. -pub fn classifyType(ty: Type, zcu: *Zcu) Class { - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); - - var maybe_float_bits: ?u16 = null; - switch (ty.zigTypeTag(zcu)) { - .@"struct" => { - if (ty.containerLayout(zcu) == .@"packed") return .byval; - const float_count = countFloats(ty, zcu, &maybe_float_bits); - if (float_count <= sret_float_count) return .{ .float_array = float_count }; - - const bit_size = ty.bitSize(zcu); - if (bit_size > 128) return .memory; - if (bit_size > 64) return .double_integer; - return .integer; - }, - .@"union" => { - if (ty.containerLayout(zcu) == .@"packed") return .byval; - const float_count = countFloats(ty, zcu, &maybe_float_bits); - if (float_count <= sret_float_count) return .{ .float_array = float_count }; - - const bit_size = ty.bitSize(zcu); - if (bit_size > 128) return .memory; - if (bit_size > 64) return .double_integer; - return .integer; - }, - .int, .@"enum", .error_set, .float, .bool => return .byval, - .vector => { - const bit_size = ty.bitSize(zcu); - // TODO is this controlled by a cpu feature? - if (bit_size > 128) return .memory; - return .byval; - }, - .optional => { - std.debug.assert(ty.isPtrLikeOptional(zcu)); - return .byval; - }, - .pointer => { - std.debug.assert(!ty.isSlice(zcu)); - return .byval; - }, - .error_union, - .frame, - .@"anyframe", - .noreturn, - .void, - .type, - .comptime_float, - .comptime_int, - .undefined, - .null, - .@"fn", - .@"opaque", - .enum_literal, - .array, - => unreachable, - } -} - -const sret_float_count = 4; -fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u8 { - const ip = &zcu.intern_pool; - const target = zcu.getTarget(); - const invalid = std.math.maxInt(u8); - switch (ty.zigTypeTag(zcu)) { - .@"union" => { - const union_obj = zcu.typeToUnion(ty).?; - var max_count: u8 = 0; - for (union_obj.field_types.get(ip)) |field_ty| { - const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits); - if (field_count == invalid) return invalid; - if (field_count > max_count) max_count = field_count; - if (max_count > sret_float_count) return invalid; - } - return max_count; - }, - .@"struct" => { - const fields_len = ty.structFieldCount(zcu); - var count: u8 = 0; - var i: u32 = 0; - while (i < fields_len) : (i += 1) { - const field_ty = ty.fieldType(i, zcu); - const field_count = countFloats(field_ty, zcu, maybe_float_bits); - if (field_count == invalid) return invalid; - count += field_count; - if (count > sret_float_count) return invalid; - } - return count; - }, - .float => { - const float_bits = maybe_float_bits.* orelse { - maybe_float_bits.* = ty.floatBits(target); - return 1; - }; - if (ty.floatBits(target) == float_bits) return 1; - return invalid; - }, - .void => return 0, - else => return invalid, - } -} - -pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type { - const ip = &zcu.intern_pool; - switch (ty.zigTypeTag(zcu)) { - .@"union" => { - const union_obj = zcu.typeToUnion(ty).?; - for (union_obj.field_types.get(ip)) |field_ty| { - if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some; - } - return null; - }, - .@"struct" => { - const fields_len = ty.structFieldCount(zcu); - var i: u32 = 0; - while (i < fields_len) : (i += 1) { - const field_ty = ty.fieldType(i, zcu); - if (getFloatArrayType(field_ty, zcu)) |some| return some; - } - return null; - }, - .float => return ty, - else => return null, - } -} - -pub const callee_preserved_regs = [_]Register{ - .x19, .x20, .x21, .x22, .x23, - .x24, .x25, .x26, .x27, .x28, -}; - -pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; -pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; - -const allocatable_registers = callee_preserved_regs; -pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); - -// Register classes -const RegisterBitSet = RegisterManager.RegisterBitSet; -pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - for (callee_preserved_regs) |reg| { - const index = RegisterManager.indexOfRegIntoTracked(reg).?; - set.set(index); - } - break :blk set; - }; -}; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig deleted file mode 100644 index 12eb0c79a76b0322f346e8b1cb07ec028e2790b0..0000000000000000000000000000000000000000 --- a/src/arch/arm/CodeGen.zig +++ /dev/null @@ -1,6340 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const mem = std.mem; -const math = std.math; -const assert = std.debug.assert; -const codegen = @import("../../codegen.zig"); -const Air = @import("../../Air.zig"); -const Mir = @import("Mir.zig"); -const Emit = @import("Emit.zig"); -const Type = @import("../../Type.zig"); -const Value = @import("../../Value.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); -const InternPool = @import("../../InternPool.zig"); -const Compilation = @import("../../Compilation.zig"); -const ErrorMsg = Zcu.ErrorMsg; -const Target = std.Target; -const Allocator = mem.Allocator; -const trace = @import("../../tracy.zig").trace; -const leb128 = std.leb; -const log = std.log.scoped(.codegen); -const build_options = @import("build_options"); -const Alignment = InternPool.Alignment; - -const CodeGenError = codegen.CodeGenError; - -const bits = @import("bits.zig"); -const abi = @import("abi.zig"); -const errUnionPayloadOffset = codegen.errUnionPayloadOffset; -const errUnionErrorOffset = codegen.errUnionErrorOffset; -const RegisterManager = abi.RegisterManager; -const RegisterLock = RegisterManager.RegisterLock; -const Register = bits.Register; -const Instruction = bits.Instruction; -const Condition = bits.Condition; -const callee_preserved_regs = abi.callee_preserved_regs; -const caller_preserved_regs = abi.caller_preserved_regs; -const c_abi_int_param_regs = abi.c_abi_int_param_regs; -const c_abi_int_return_regs = abi.c_abi_int_return_regs; -const gp = abi.RegisterClass.gp; - -const InnerError = CodeGenError || error{OutOfRegisters}; - -pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { - return null; -} - -gpa: Allocator, -pt: Zcu.PerThread, -air: Air, -liveness: Air.Liveness, -bin_file: *link.File, -target: *const std.Target, -func_index: InternPool.Index, -err_msg: ?*ErrorMsg, -args: []MCValue, -ret_mcv: MCValue, -fn_type: Type, -arg_index: u32, -src_loc: Zcu.LazySrcLoc, -stack_align: u32, - -/// MIR Instructions -mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, -/// MIR extra data -mir_extra: std.ArrayListUnmanaged(u32) = .empty, - -/// Byte offset within the source file of the ending curly. -end_di_line: u32, -end_di_column: u32, - -/// The value is an offset into the `Function` `code` from the beginning. -/// To perform the reloc, write 32-bit signed little-endian integer -/// which is a relative jump, based on the address following the reloc. -exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty, - -reused_operands: std.StaticBitSet(Air.Liveness.bpi - 1) = undefined, - -/// We postpone the creation of debug info for function args and locals -/// until after all Mir instructions have been generated. Only then we -/// will know saved_regs_stack_space which is necessary in order to -/// calculate the right stack offsest with respect to the `.fp` register. -dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .empty, - -/// Whenever there is a runtime branch, we push a Branch onto this stack, -/// and pop it off when the runtime branch joins. This provides an "overlay" -/// of the table of mappings from instructions to `MCValue` from within the branch. -/// This way we can modify the `MCValue` for an instruction in different ways -/// within different branches. Special consideration is needed when a branch -/// joins with its parent, to make sure all instructions have the same MCValue -/// across each runtime branch upon joining. -branch_stack: *std.ArrayList(Branch), - -// Key is the block instruction -blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, - -register_manager: RegisterManager = .{}, -/// Maps offset to what is stored there. -stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .empty, -/// Tracks the current instruction allocated to the compare flags -cpsr_flags_inst: ?Air.Inst.Index = null, - -/// Offset from the stack base, representing the end of the stack frame. -max_end_stack: u32 = 0, -/// Represents the current end stack offset. If there is no existing slot -/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. -next_stack_offset: u32 = 0, - -saved_regs_stack_space: u32 = 0, - -/// Debug field, used to find bugs in the compiler. -air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, - -const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; - -const MCValue = union(enum) { - /// No runtime bits. `void` types, empty structs, u0, enums with 1 - /// tag, etc. - /// - /// TODO Look into deleting this tag and using `dead` instead, - /// since every use of MCValue.none should be instead looking at - /// the type and noticing it is 0 bits. - none, - /// Control flow will not allow this value to be observed. - unreach, - /// No more references to this value remain. - dead, - /// The value is undefined. - undef, - /// A pointer-sized integer that fits in a register. - /// - /// If the type is a pointer, this is the pointer address in - /// virtual address space. - immediate: u32, - /// The value is in a target-specific register. - register: Register, - /// The value is a tuple { wrapped: u32, overflow: u1 } where - /// wrapped is stored in the register and the overflow bit is - /// stored in the C flag of the CPSR. - /// - /// This MCValue is only generated by a add_with_overflow or - /// sub_with_overflow instruction operating on u32. - register_c_flag: Register, - /// The value is a tuple { wrapped: i32, overflow: u1 } where - /// wrapped is stored in the register and the overflow bit is - /// stored in the V flag of the CPSR. - /// - /// This MCValue is only generated by a add_with_overflow or - /// sub_with_overflow instruction operating on i32. - register_v_flag: Register, - /// The value is in memory at a hard-coded address. - /// - /// If the type is a pointer, it means the pointer address is at - /// this memory location. - memory: u64, - /// The value is one of the stack variables. - /// - /// If the type is a pointer, it means the pointer address is in - /// the stack at this offset. - stack_offset: u32, - /// The value is a pointer to one of the stack variables (payload - /// is stack offset). - ptr_stack_offset: u32, - /// The value resides in the N, Z, C, V flags of the Current - /// Program Status Register (CPSR). The value is 1 (if the type is - /// u1) or true (if the type in bool) iff the specified condition - /// is true. - cpsr_flags: Condition, - /// The value is a function argument passed via the stack. - stack_argument_offset: u32, -}; - -const Branch = struct { - inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .empty, - - fn deinit(self: *Branch, gpa: Allocator) void { - self.inst_table.deinit(gpa); - self.* = undefined; - } -}; - -const StackAllocation = struct { - inst: Air.Inst.Index, - /// TODO do we need size? should be determined by inst.ty.abiSize() - size: u32, -}; - -const BlockData = struct { - relocs: std.ArrayListUnmanaged(Mir.Inst.Index), - /// The first break instruction encounters `null` here and chooses a - /// machine code value for the block result, populating this field. - /// Following break instructions encounter that value and use it for - /// the location to store their block results. - mcv: MCValue, -}; - -const BigTomb = struct { - function: *Self, - inst: Air.Inst.Index, - lbt: Air.Liveness.BigTomb, - - fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const dies = bt.lbt.feed(); - const op_index = op_ref.toIndex() orelse return; - if (!dies) return; - bt.function.processDeath(op_index); - } - - fn finishAir(bt: *BigTomb, result: MCValue) void { - const is_used = !bt.function.liveness.isUnused(bt.inst); - if (is_used) { - log.debug("%{d} => {}", .{ bt.inst, result }); - const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); - - switch (result) { - .register => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (bt.function.register_manager.isRegFree(reg)) { - bt.function.register_manager.getRegAssumeFree(reg, bt.inst); - } - }, - .register_c_flag, - .register_v_flag, - => |reg| { - if (bt.function.register_manager.isRegFree(reg)) { - bt.function.register_manager.getRegAssumeFree(reg, bt.inst); - } - bt.function.cpsr_flags_inst = bt.inst; - }, - .cpsr_flags => { - bt.function.cpsr_flags_inst = bt.inst; - }, - else => {}, - } - } - bt.function.finishAirBookkeeping(); - } -}; - -const DbgInfoReloc = struct { - tag: Air.Inst.Tag, - ty: Type, - name: [:0]const u8, - mcv: MCValue, - - fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - switch (reloc.tag) { - .arg, - .dbg_arg_inline, - => try reloc.genArgDbgInfo(function), - - .dbg_var_ptr, - .dbg_var_val, - => try reloc.genVarDbgInfo(function), - - else => unreachable, - } - } - - fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - // TODO: Add a pseudo-instruction or something to defer this work until Emit. - // We aren't allowed to interact with linker state here. - if (true) return; - switch (function.debug_output) { - .dwarf => |dw| { - const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { - .register => |reg| .{ .reg = reg.dwarfNum() }, - .stack_offset, - .stack_argument_offset, - => blk: { - const adjusted_stack_offset = switch (reloc.mcv) { - .stack_offset => |offset| -@as(i32, @intCast(offset)), - .stack_argument_offset => |offset| @as(i32, @intCast(function.saved_regs_stack_space + offset)), - else => unreachable, - }; - break :blk .{ .plus = .{ - &.{ .reg = 11 }, - &.{ .consts = adjusted_stack_offset }, - } }; - }, - else => unreachable, // not a possible argument - }; - - try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc); - }, - .plan9 => {}, - .none => {}, - } - } - - fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - // TODO: Add a pseudo-instruction or something to defer this work until Emit. - // We aren't allowed to interact with linker state here. - if (true) return; - switch (function.debug_output) { - .dwarf => |dw| { - const loc: link.File.Dwarf.Loc = switch (reloc.mcv) { - .register => |reg| .{ .reg = reg.dwarfNum() }, - .ptr_stack_offset, - .stack_offset, - .stack_argument_offset, - => |offset| blk: { - const adjusted_offset = switch (reloc.mcv) { - .ptr_stack_offset, - .stack_offset, - => -@as(i32, @intCast(offset)), - .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)), - else => unreachable, - }; - break :blk .{ .plus = .{ - &.{ .reg = 11 }, - &.{ .consts = adjusted_offset }, - } }; - }, - .memory => |address| .{ .constu = address }, - .immediate => |x| .{ .constu = x }, - .none => .empty, - else => blk: { - log.debug("TODO generate debug info for {}", .{reloc.mcv}); - break :blk .empty; - }, - }; - try dw.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc); - }, - .plan9 => {}, - .none => {}, - } - } -}; - -const Self = @This(); - -pub fn generate( - lf: *link.File, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - func_index: InternPool.Index, - air: *const Air, - liveness: *const Air.Liveness, -) CodeGenError!Mir { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const func = zcu.funcInfo(func_index); - const func_ty = Type.fromInterned(func.ty); - const file_scope = zcu.navFileScope(func.owner_nav); - const target = &file_scope.mod.?.resolved_target.result; - - var branch_stack = std.ArrayList(Branch).init(gpa); - defer { - assert(branch_stack.items.len == 1); - branch_stack.items[0].deinit(gpa); - branch_stack.deinit(); - } - try branch_stack.append(.{}); - - var function: Self = .{ - .gpa = gpa, - .pt = pt, - .air = air.*, - .liveness = liveness.*, - .target = target, - .bin_file = lf, - .func_index = func_index, - .err_msg = null, - .args = undefined, // populated after `resolveCallingConventionValues` - .ret_mcv = undefined, // populated after `resolveCallingConventionValues` - .fn_type = func_ty, - .arg_index = 0, - .branch_stack = &branch_stack, - .src_loc = src_loc, - .stack_align = undefined, - .end_di_line = func.rbrace_line, - .end_di_column = func.rbrace_column, - }; - defer function.stack.deinit(gpa); - defer function.blocks.deinit(gpa); - defer function.exitlude_jump_relocs.deinit(gpa); - defer function.dbg_info_relocs.deinit(gpa); - - var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - else => |e| return e, - }; - defer call_info.deinit(&function); - - function.args = call_info.args; - function.ret_mcv = call_info.return_value; - function.stack_align = call_info.stack_align; - function.max_end_stack = call_info.stack_byte_count; - - function.gen() catch |err| switch (err) { - error.CodegenFail => return error.CodegenFail, - error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), - else => |e| return e, - }; - - for (function.dbg_info_relocs.items) |reloc| { - reloc.genDbgInfo(function) catch |err| - return function.fail("failed to generate debug info: {s}", .{@errorName(err)}); - } - - var mir: Mir = .{ - .instructions = function.mir_instructions.toOwnedSlice(), - .extra = &.{}, // fallible, so assign after errdefer - .max_end_stack = function.max_end_stack, - .saved_regs_stack_space = function.saved_regs_stack_space, - }; - errdefer mir.deinit(gpa); - mir.extra = try function.mir_extra.toOwnedSlice(gpa); - return mir; -} - -fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { - const gpa = self.gpa; - - try self.mir_instructions.ensureUnusedCapacity(gpa, 1); - - const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len); - self.mir_instructions.appendAssumeCapacity(inst); - return result_index; -} - -fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { - return try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); -} - -pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { - const fields = std.meta.fields(@TypeOf(extra)); - try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); - return self.addExtraAssumeCapacity(extra); -} - -pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { - const fields = std.meta.fields(@TypeOf(extra)); - const result: u32 = @intCast(self.mir_extra.items.len); - inline for (fields) |field| { - self.mir_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - i32 => @bitCast(@field(extra, field.name)), - else => @compileError("bad field type"), - }); - } - return result; -} - -fn gen(self: *Self) !void { - const pt = self.pt; - const zcu = pt.zcu; - const cc = self.fn_type.fnCallingConvention(zcu); - if (cc != .naked) { - // push {fp, lr} - const push_reloc = try self.addNop(); - - // mov fp, sp - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = .fp, - .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none), - } }, - }); - - // sub sp, sp, #reloc - const sub_reloc = try self.addNop(); - - // The sub_sp_scratch_r4 instruction may use r4, so we mark r4 - // as allocated by this function. - const index = RegisterManager.indexOfRegIntoTracked(.r4).?; - self.register_manager.allocated_registers.set(index); - - if (self.ret_mcv == .stack_offset) { - // The address of where to store the return value is in - // r0. As this register might get overwritten along the - // way, save the address to the stack. - const stack_offset = try self.allocMem(4, .@"4", null); - - try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 }); - self.ret_mcv = MCValue{ .stack_offset = stack_offset }; - } - - for (self.args, 0..) |*arg, arg_index| { - // Copy register arguments to the stack - switch (arg.*) { - .register => |reg| { - // The first AIR instructions of the main body are guaranteed - // to be the functions arguments - const inst = self.air.getMainBody()[arg_index]; - assert(self.air.instructions.items(.tag)[@intFromEnum(inst)] == .arg); - - const ty = self.typeOfIndex(inst); - - const abi_size: u32 = @intCast(ty.abiSize(zcu)); - const abi_align = ty.abiAlignment(zcu); - const stack_offset = try self.allocMem(abi_size, abi_align, inst); - try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - - arg.* = MCValue{ .stack_offset = stack_offset }; - }, - else => {}, - } - } - - _ = try self.addInst(.{ - .tag = .dbg_prologue_end, - .cond = undefined, - .data = .{ .nop = {} }, - }); - - try self.genBody(self.air.getMainBody()); - - // Backpatch push callee saved regs - var saved_regs = Instruction.RegisterList{ - .r11 = true, // fp - .r14 = true, // lr - }; - self.saved_regs_stack_space = 8; - inline for (callee_preserved_regs) |reg| { - if (self.register_manager.isRegAllocated(reg)) { - @field(saved_regs, @tagName(reg)) = true; - self.saved_regs_stack_space += 4; - } - } - self.mir_instructions.set(push_reloc, .{ - .tag = .push, - .data = .{ .register_list = saved_regs }, - }); - - // Backpatch stack offset - const total_stack_size = self.max_end_stack + self.saved_regs_stack_space; - const aligned_total_stack_end = mem.alignForward(u32, total_stack_size, self.stack_align); - const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; - self.max_end_stack = stack_size; - self.mir_instructions.set(sub_reloc, .{ - .tag = .sub_sp_scratch_r4, - .data = .{ .imm32 = stack_size }, - }); - - _ = try self.addInst(.{ - .tag = .dbg_epilogue_begin, - .cond = undefined, - .data = .{ .nop = {} }, - }); - - // exitlude jumps - if (self.exitlude_jump_relocs.items.len > 0 and - self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) - { - // If the last Mir instruction (apart from the - // dbg_epilogue_begin) is the last exitlude jump - // relocation (which would just jump one instruction - // further), it can be safely removed - self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop().?); - } - - for (self.exitlude_jump_relocs.items) |jmp_reloc| { - self.mir_instructions.set(jmp_reloc, .{ - .tag = .b, - .data = .{ .inst = @intCast(self.mir_instructions.len) }, - }); - } - - // Epilogue: pop callee saved registers (swap lr with pc in saved_regs) - saved_regs.r14 = false; // lr - saved_regs.r15 = true; // pc - - // mov sp, fp - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = .sp, - .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none), - } }, - }); - - // pop {fp, pc} - _ = try self.addInst(.{ - .tag = .pop, - .data = .{ .register_list = saved_regs }, - }); - } else { - _ = try self.addInst(.{ - .tag = .dbg_prologue_end, - .cond = undefined, - .data = .{ .nop = {} }, - }); - - try self.genBody(self.air.getMainBody()); - - _ = try self.addInst(.{ - .tag = .dbg_epilogue_begin, - .cond = undefined, - .data = .{ .nop = {} }, - }); - } - - // Drop them off at the rbrace. - _ = try self.addInst(.{ - .tag = .dbg_line, - .cond = undefined, - .data = .{ .dbg_line_column = .{ - .line = self.end_di_line, - .column = self.end_di_column, - } }, - }); -} - -fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const air_tags = self.air.instructions.items(.tag); - - for (body) |inst| { - // TODO: remove now-redundant isUnused calls from AIR handler functions - if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) - continue; - - const old_air_bookkeeping = self.air_bookkeeping; - try self.ensureProcessDeathCapacity(Air.Liveness.bpi); - - self.reused_operands = @TypeOf(self.reused_operands).initEmpty(); - switch (air_tags[@intFromEnum(inst)]) { - // zig fmt: off - .add, => try self.airBinOp(inst, .add), - .add_wrap => try self.airBinOp(inst, .add_wrap), - .sub, => try self.airBinOp(inst, .sub), - .sub_wrap => try self.airBinOp(inst, .sub_wrap), - .mul => try self.airBinOp(inst, .mul), - .mul_wrap => try self.airBinOp(inst, .mul_wrap), - .shl => try self.airBinOp(inst, .shl), - .shl_exact => try self.airBinOp(inst, .shl_exact), - .bool_and => try self.airBinOp(inst, .bool_and), - .bool_or => try self.airBinOp(inst, .bool_or), - .bit_and => try self.airBinOp(inst, .bit_and), - .bit_or => try self.airBinOp(inst, .bit_or), - .xor => try self.airBinOp(inst, .xor), - .shr => try self.airBinOp(inst, .shr), - .shr_exact => try self.airBinOp(inst, .shr_exact), - .div_float => try self.airBinOp(inst, .div_float), - .div_trunc => try self.airBinOp(inst, .div_trunc), - .div_floor => try self.airBinOp(inst, .div_floor), - .div_exact => try self.airBinOp(inst, .div_exact), - .rem => try self.airBinOp(inst, .rem), - .mod => try self.airBinOp(inst, .mod), - - .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), - .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), - - .min => try self.airMinMax(inst), - .max => try self.airMinMax(inst), - - .add_sat => try self.airAddSat(inst), - .sub_sat => try self.airSubSat(inst), - .mul_sat => try self.airMulSat(inst), - .shl_sat => try self.airShlSat(inst), - .slice => try self.airSlice(inst), - - .sqrt, - .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .floor, - .ceil, - .round, - .trunc_float, - .neg, - => try self.airUnaryMath(inst), - - .add_with_overflow => try self.airOverflow(inst), - .sub_with_overflow => try self.airOverflow(inst), - .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), - - .cmp_lt => try self.airCmp(inst, .lt), - .cmp_lte => try self.airCmp(inst, .lte), - .cmp_eq => try self.airCmp(inst, .eq), - .cmp_gte => try self.airCmp(inst, .gte), - .cmp_gt => try self.airCmp(inst, .gt), - .cmp_neq => try self.airCmp(inst, .neq), - - .cmp_vector => try self.airCmpVector(inst), - .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - - .alloc => try self.airAlloc(inst), - .ret_ptr => try self.airRetPtr(inst), - .arg => try self.airArg(inst), - .assembly => try self.airAsm(inst), - .bitcast => try self.airBitCast(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .repeat => return self.fail("TODO implement `repeat`", .{}), - .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}), - .trap => try self.airTrap(), - .breakpoint => try self.airBreakpoint(), - .ret_addr => try self.airRetAddr(inst), - .frame_addr => try self.airFrameAddress(inst), - .cond_br => try self.airCondBr(inst), - .fptrunc => try self.airFptrunc(inst), - .fpext => try self.airFpext(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .is_non_null => try self.airIsNonNull(inst), - .is_non_null_ptr => try self.airIsNonNullPtr(inst), - .is_null => try self.airIsNull(inst), - .is_null_ptr => try self.airIsNullPtr(inst), - .is_non_err => try self.airIsNonErr(inst), - .is_non_err_ptr => try self.airIsNonErrPtr(inst), - .is_err => try self.airIsErr(inst), - .is_err_ptr => try self.airIsErrPtr(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ret => try self.airRet(inst), - .ret_safe => try self.airRet(inst), // TODO - .ret_load => try self.airRetLoad(inst), - .store => try self.airStore(inst, false), - .store_safe => try self.airStore(inst, true), - .struct_field_ptr=> try self.airStructFieldPtr(inst), - .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => try self.airArrayToSlice(inst), - .float_from_int => try self.airFloatFromInt(inst), - .int_from_float => try self.airIntFromFloat(inst), - .cmpxchg_strong => try self.airCmpxchg(inst), - .cmpxchg_weak => try self.airCmpxchg(inst), - .atomic_rmw => try self.airAtomicRmw(inst), - .atomic_load => try self.airAtomicLoad(inst), - .memcpy => try self.airMemcpy(inst), - .memmove => try self.airMemmove(inst), - .memset => try self.airMemset(inst, false), - .memset_safe => try self.airMemset(inst, true), - .set_union_tag => try self.airSetUnionTag(inst), - .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClz(inst), - .ctz => try self.airCtz(inst), - .popcount => try self.airPopcount(inst), - .abs => try self.airAbs(inst), - .byte_swap => try self.airByteSwap(inst), - .bit_reverse => try self.airBitReverse(inst), - .tag_name => try self.airTagName(inst), - .error_name => try self.airErrorName(inst), - .splat => try self.airSplat(inst), - .select => try self.airSelect(inst), - .shuffle_one => try self.airShuffleOne(inst), - .shuffle_two => try self.airShuffleTwo(inst), - .reduce => try self.airReduce(inst), - .aggregate_init => try self.airAggregateInit(inst), - .union_init => try self.airUnionInit(inst), - .prefetch => try self.airPrefetch(inst), - .mul_add => try self.airMulAdd(inst), - .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}), - - .@"try" => try self.airTry(inst), - .try_cold => try self.airTry(inst), - .try_ptr => try self.airTryPtr(inst), - .try_ptr_cold => try self.airTryPtr(inst), - - .dbg_stmt => try self.airDbgStmt(inst), - .dbg_empty_stmt => self.finishAirBookkeeping(), - .dbg_inline_block => try self.airDbgInlineBlock(inst), - .dbg_var_ptr, - .dbg_var_val, - .dbg_arg_inline, - => try self.airDbgVar(inst), - - .call => try self.airCall(inst, .auto), - .call_always_tail => try self.airCall(inst, .always_tail), - .call_never_tail => try self.airCall(inst, .never_tail), - .call_never_inline => try self.airCall(inst, .never_inline), - - .atomic_store_unordered => try self.airAtomicStore(inst, .unordered), - .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic), - .atomic_store_release => try self.airAtomicStore(inst, .release), - .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst), - - .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), - .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), - .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), - .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), - - .field_parent_ptr => try self.airFieldParentPtr(inst), - - .switch_br => try self.airSwitch(inst), - .loop_switch_br => return self.fail("TODO implement `loop_switch_br`", .{}), - .slice_ptr => try self.airSlicePtr(inst), - .slice_len => try self.airSliceLen(inst), - - .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), - .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), - - .array_elem_val => try self.airArrayElemVal(inst), - .slice_elem_val => try self.airSliceElemVal(inst), - .slice_elem_ptr => try self.airSliceElemPtr(inst), - .ptr_elem_val => try self.airPtrElemVal(inst), - .ptr_elem_ptr => try self.airPtrElemPtr(inst), - - .inferred_alloc, .inferred_alloc_comptime => unreachable, - .unreach => self.finishAirBookkeeping(), - - .optional_payload => try self.airOptionalPayload(inst), - .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), - .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), - .unwrap_errunion_err => try self.airUnwrapErrErr(inst), - .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), - .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), - .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), - .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), - .err_return_trace => try self.airErrReturnTrace(inst), - .set_err_return_trace => try self.airSetErrReturnTrace(inst), - .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst), - - .wrap_optional => try self.airWrapOptional(inst), - .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), - .wrap_errunion_err => try self.airWrapErrUnionErr(inst), - - .add_optimized, - .sub_optimized, - .mul_optimized, - .div_float_optimized, - .div_trunc_optimized, - .div_floor_optimized, - .div_exact_optimized, - .rem_optimized, - .mod_optimized, - .neg_optimized, - .cmp_lt_optimized, - .cmp_lte_optimized, - .cmp_eq_optimized, - .cmp_gte_optimized, - .cmp_gt_optimized, - .cmp_neq_optimized, - .cmp_vector_optimized, - .reduce_optimized, - .int_from_float_optimized, - => return self.fail("TODO implement optimized float mode", .{}), - - .add_safe, - .sub_safe, - .mul_safe, - .intcast_safe, - .int_from_float_safe, - .int_from_float_optimized_safe, - => return self.fail("TODO implement safety_checked_instructions", .{}), - - .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), - .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), - .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), - .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}), - - .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), - .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), - .c_va_end => return self.fail("TODO implement c_va_end", .{}), - .c_va_start => return self.fail("TODO implement c_va_start", .{}), - - .wasm_memory_size => unreachable, - .wasm_memory_grow => unreachable, - - .work_item_id => unreachable, - .work_group_size => unreachable, - .work_group_id => unreachable, - // zig fmt: on - } - - assert(!self.register_manager.lockedRegsExist()); - - if (std.debug.runtime_safety) { - if (self.air_bookkeeping < old_air_bookkeeping + 1) { - 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)] }); - } - } - } -} - -/// Asserts there is already capacity to insert into top branch inst_table. -fn processDeath(self: *Self, inst: Air.Inst.Index) void { - // When editing this function, note that the logic must synchronize with `reuseOperand`. - const prev_value = self.getResolvedInstValue(inst); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacity(inst, .dead); - switch (prev_value) { - .register => |reg| { - self.register_manager.freeReg(reg); - }, - .register_c_flag, - .register_v_flag, - => |reg| { - self.register_manager.freeReg(reg); - self.cpsr_flags_inst = null; - }, - .cpsr_flags => { - self.cpsr_flags_inst = null; - }, - else => {}, // TODO process stack allocation death - } -} - -/// Called when there are no operands, and the instruction is always unreferenced. -fn finishAirBookkeeping(self: *Self) void { - if (std.debug.runtime_safety) { - self.air_bookkeeping += 1; - } -} - -fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Air.Liveness.bpi - 1]Air.Inst.Ref) void { - const tomb_bits = self.liveness.getTombBits(inst); - for (0.., operands) |op_index, op| { - if (tomb_bits & @as(Air.Liveness.Bpi, 1) << @intCast(op_index) == 0) continue; - if (self.reused_operands.isSet(op_index)) continue; - self.processDeath(op.toIndexAllowNone() orelse continue); - } - if (tomb_bits & 1 << (Air.Liveness.bpi - 1) == 0) { - log.debug("%{d} => {}", .{ inst, result }); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacityNoClobber(inst, result); - - switch (result) { - .register => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } - }, - .register_c_flag, - .register_v_flag, - => |reg| { - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } - self.cpsr_flags_inst = inst; - }, - .cpsr_flags => { - self.cpsr_flags_inst = inst; - }, - else => {}, - } - } - self.finishAirBookkeeping(); -} - -fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { - const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; - try table.ensureUnusedCapacity(self.gpa, additional_count); -} - -fn allocMem( - self: *Self, - abi_size: u32, - abi_align: Alignment, - maybe_inst: ?Air.Inst.Index, -) !u32 { - assert(abi_size > 0); - assert(abi_align != .none); - - // TODO find a free slot instead of always appending - const offset: u32 = @intCast(abi_align.forward(self.next_stack_offset) + abi_size); - self.next_stack_offset = offset; - self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); - - if (maybe_inst) |inst| { - try self.stack.putNoClobber(self.gpa, offset, .{ - .inst = inst, - .size = abi_size, - }); - } - - return offset; -} - -/// Use a pointer instruction as the basis for allocating stack memory. -fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = self.typeOfIndex(inst).childType(zcu); - - if (!elem_ty.hasRuntimeBits(zcu)) { - // As this stack item will never be dereferenced at runtime, - // return the stack offset 0. Stack offset 0 will be where all - // zero-sized stack allocations live as non-zero-sized - // allocations will always have an offset > 0. - return 0; - } - - const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse { - return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)}); - }; - // TODO swap this for inst.ty.ptrAlign - const abi_align = elem_ty.abiAlignment(zcu); - - return self.allocMem(abi_size, abi_align, inst); -} - -fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { - const pt = self.pt; - const abi_size = math.cast(u32, elem_ty.abiSize(pt.zcu)) orelse { - return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)}); - }; - const abi_align = elem_ty.abiAlignment(pt.zcu); - - if (reg_ok) { - // Make sure the type can fit in a register before we try to allocate one. - const ptr_bits = self.target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { - return MCValue{ .register = reg }; - } - } - } - - const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst); - return MCValue{ .stack_offset = stack_offset }; -} - -pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { - const stack_mcv = try self.allocRegOrMem(self.typeOfIndex(inst), false, inst); - log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); - - const reg_mcv = self.getResolvedInstValue(inst); - switch (reg_mcv) { - .register, - .register_c_flag, - .register_v_flag, - => |r| assert(r == reg), - else => unreachable, // not a register - } - - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - try branch.inst_table.put(self.gpa, inst, stack_mcv); - try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); -} - -/// Save the current instruction stored in the compare flags if -/// occupied -fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.cpsr_flags_inst) |inst_to_save| { - const ty = self.typeOfIndex(inst_to_save); - const mcv = self.getResolvedInstValue(inst_to_save); - const new_mcv = switch (mcv) { - .cpsr_flags => try self.allocRegOrMem(ty, true, inst_to_save), - .register_c_flag, - .register_v_flag, - => try self.allocRegOrMem(ty, false, inst_to_save), - else => unreachable, // mcv doesn't occupy the compare flags - }; - - try self.setRegOrMem(self.typeOfIndex(inst_to_save), new_mcv, mcv); - log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); - - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - - self.cpsr_flags_inst = null; - - // TODO consolidate with register manager and spillInstruction - // this call should really belong in the register manager! - switch (mcv) { - .register_c_flag, - .register_v_flag, - => |reg| self.register_manager.freeReg(reg), - else => {}, - } - } -} - -/// Copies a value to a register without tracking the register. The register is not considered -/// allocated. A second call to `copyToTmpRegister` may return the same register. -/// This can have a side effect of spilling instructions to the stack to free up a register. -fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null, gp); - try self.genSetReg(ty, reg, mcv); - return reg; -} - -fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { - const stack_offset = try self.allocMemPtr(inst); - return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); -} - -fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = switch (self.ret_mcv) { - .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, - .stack_offset => blk: { - // self.ret_mcv is an address to where this function - // should store its result into - const ret_ty = self.fn_type.fnReturnType(zcu); - const ptr_ty = try pt.singleMutPtrType(ret_ty); - - // addr_reg will contain the address of where to store the - // result into - const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv); - break :blk .{ .register = addr_reg }; - }, - else => unreachable, // invalid return result - }; - - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airFpext(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - if (self.liveness.isUnused(inst)) - return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - - const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.typeOf(ty_op.operand); - const dest_ty = self.typeOfIndex(inst); - - const operand_abi_size = operand_ty.abiSize(zcu); - const dest_abi_size = dest_ty.abiSize(zcu); - const info_a = operand_ty.intInfo(zcu); - const info_b = dest_ty.intInfo(zcu); - - const dst_mcv: MCValue = blk: { - if (info_a.bits == info_b.bits) { - break :blk operand; - } - if (operand_abi_size > 4 or dest_abi_size > 4) { - return self.fail("TODO implement intCast for abi sizes larger than 4", .{}); - } - - const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - - const reg = try self.register_manager.allocReg(inst, gp); - try self.genSetReg(dest_ty, reg, operand); - break :blk MCValue{ .register = reg }; - }; - - return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); -} - -fn truncRegister( - self: *Self, - operand_reg: Register, - dest_reg: Register, - int_signedness: std.builtin.Signedness, - int_bits: u16, -) !void { - // TODO check if sxtb/uxtb/sxth/uxth are more efficient - _ = try self.addInst(.{ - .tag = switch (int_signedness) { - .signed => .sbfx, - .unsigned => .ubfx, - }, - .data = .{ .rr_lsb_width = .{ - .rd = dest_reg, - .rn = operand_reg, - .lsb = 0, - .width = @intCast(int_bits), - } }, - }); -} - -/// Asserts that both operand_ty and dest_ty are integer types -fn trunc( - self: *Self, - maybe_inst: ?Air.Inst.Index, - operand_bind: ReadArg.Bind, - operand_ty: Type, - dest_ty: Type, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const info_a = operand_ty.intInfo(zcu); - const info_b = dest_ty.intInfo(zcu); - - if (info_b.bits <= 32) { - if (info_a.bits > 32) { - return self.fail("TODO load least significant word into register", .{}); - } - - var operand_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - } else null, - ); - - switch (info_b.bits) { - 32 => { - try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); - }, - else => { - try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); - }, - } - - return MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO: truncate to ints > 32 bits", .{}); - } -} - -fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; - const operand_ty = self.typeOf(ty_op.operand); - const dest_ty = self.typeOfIndex(inst); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { - break :blk try self.trunc(inst, operand_bind, operand_ty, dest_ty); - }; - - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airNot(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; - const operand_ty = self.typeOf(ty_op.operand); - switch (try operand_bind.resolveToMcv(self)) { - .dead => unreachable, - .unreach => unreachable, - .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() }, - else => { - switch (operand_ty.zigTypeTag(zcu)) { - .bool => { - var op_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - ReuseMetadata{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - }, - ); - - _ = try self.addInst(.{ - .tag = .eor, - .data = .{ .rr_op = .{ - .rd = dest_reg, - .rn = op_reg, - .op = Instruction.Operand.fromU32(1).?, - } }, - }); - - break :result MCValue{ .register = dest_reg }; - }, - .vector => return self.fail("TODO bitwise not for vectors", .{}), - .int => { - const int_info = operand_ty.intInfo(zcu); - if (int_info.bits <= 32) { - var op_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - ReuseMetadata{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - }, - ); - - _ = try self.addInst(.{ - .tag = .mvn, - .data = .{ .r_op_mov = .{ - .rd = dest_reg, - .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none), - } }, - }); - - if (int_info.bits < 32) { - try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); - } - - break :result MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO ARM not on integers > u32/i32", .{}); - } - }, - else => unreachable, - } - }, - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn minMax( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM min/max on floats", .{}), - .vector => return self.fail("TODO ARM min/max on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{ 0, 1 }, - } else null, - ); - - // lhs == reg should have been checked by airMinMax - // - // By guaranteeing lhs != rhs, we guarantee (dst != - // lhs) or (dst != rhs), which is a property we use to - // omit generating one instruction when we reuse a - // register. - assert(lhs_reg != rhs_reg); // see note above - - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = lhs_reg, - .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), - } }, - }); - - const cond_choose_lhs: Condition = switch (tag) { - .max => switch (int_info.signedness) { - .signed => Condition.gt, - .unsigned => Condition.hi, - }, - .min => switch (int_info.signedness) { - .signed => Condition.lt, - .unsigned => Condition.cc, - }, - else => unreachable, - }; - const cond_choose_rhs = cond_choose_lhs.negate(); - - if (dest_reg != lhs_reg) { - _ = try self.addInst(.{ - .tag = .mov, - .cond = cond_choose_lhs, - .data = .{ .r_op_mov = .{ - .rd = dest_reg, - .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none), - } }, - }); - } - if (dest_reg != rhs_reg) { - _ = try self.addInst(.{ - .tag = .mov, - .cond = cond_choose_rhs, - .data = .{ .r_op_mov = .{ - .rd = dest_reg, - .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), - } }, - }); - } - - return MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO ARM min/max on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - const lhs = try self.resolveInst(bin_op.lhs); - if (bin_op.lhs == bin_op.rhs) break :result lhs; - - break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airSlice(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr = try self.resolveInst(bin_op.lhs); - const ptr_ty = self.typeOf(bin_op.lhs); - const len = try self.resolveInst(bin_op.rhs); - const len_ty = self.typeOf(bin_op.rhs); - - const stack_offset = try self.allocMem(8, .@"4", inst); - try self.genSetStack(ptr_ty, stack_offset, ptr); - try self.genSetStack(len_ty, stack_offset - 4, len); - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result switch (tag) { - .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .add_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .sub_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .mul_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), - - else => unreachable, - }; - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const lhs_ty = self.typeOf(bin_op.lhs); - const rhs_ty = self.typeOf(bin_op.rhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - const lhs_ty = self.typeOf(extra.lhs); - const rhs_ty = self.typeOf(extra.rhs); - - const tuple_ty = self.typeOfIndex(inst); - const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu)); - const tuple_align = tuple_ty.abiAlignment(zcu); - const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); - - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits < 32) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - const base_tag: Air.Inst.Tag = switch (tag) { - .add_with_overflow => .add, - .sub_with_overflow => .sub, - else => unreachable, - }; - const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); - const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); - defer self.register_manager.unlockReg(dest_reg_lock); - - const truncated_reg = try self.register_manager.allocReg(null, gp); - const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); - defer self.register_manager.unlockReg(truncated_reg_lock); - - // sbfx/ubfx truncated, dest, #0, #bits - try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); - - // cmp dest, truncated - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = dest_reg, - .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none), - } }, - }); - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else if (int_info.bits == 32) { - const lhs_immediate = try lhs_bind.resolveToImmediate(self); - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - // Only say yes if the operation is - // commutative, i.e. we can swap both of the - // operands - const lhs_immediate_ok = switch (tag) { - .add_with_overflow => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, - .sub_with_overflow => false, - else => unreachable, - }; - const rhs_immediate_ok = switch (tag) { - .add_with_overflow, - .sub_with_overflow, - => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, - else => unreachable, - }; - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add_with_overflow => .adds, - .sub_with_overflow => .subs, - else => unreachable, - }; - - try self.spillCompareFlagsIfOccupied(); - self.cpsr_flags_inst = inst; - - const dest = blk: { - if (rhs_immediate_ok) { - break :blk try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, null); - } else if (lhs_immediate_ok) { - // swap lhs and rhs - break :blk try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, null); - } else { - break :blk try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); - } - }; - - if (tag == .sub_with_overflow) { - break :result MCValue{ .register_v_flag = dest.register }; - } - - switch (int_info.signedness) { - .unsigned => break :result MCValue{ .register_c_flag = dest.register }, - .signed => break :result MCValue{ .register_v_flag = dest.register }, - } - } else { - return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = result: { - const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - const lhs_ty = self.typeOf(extra.lhs); - const rhs_ty = self.typeOf(extra.rhs); - - const tuple_ty = self.typeOfIndex(inst); - const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu)); - const tuple_align = tuple_ty.abiAlignment(zcu); - const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); - - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 16) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { - .signed => .smulbb, - .unsigned => .mul, - }; - - const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); - const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); - defer self.register_manager.unlockReg(dest_reg_lock); - - const truncated_reg = try self.register_manager.allocReg(null, gp); - const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); - defer self.register_manager.unlockReg(truncated_reg_lock); - - // sbfx/ubfx truncated, dest, #0, #bits - try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); - - // cmp dest, truncated - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = dest_reg, - .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none), - } }, - }); - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else if (int_info.bits <= 32) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { - .signed => .smull, - .unsigned => .umull, - }; - - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var rdhi: Register = undefined; - var rdlo: Register = undefined; - var truncated_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdhi }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdlo }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - _ = try self.addInst(.{ - .tag = base_tag, - .data = .{ .rrrr = .{ - .rdlo = rdlo, - .rdhi = rdhi, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - }); - - // sbfx/ubfx truncated, rdlo, #0, #bits - try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits); - - // str truncated, [...] - try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - - // cmp truncated, rdlo - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = truncated_reg, - .op = Instruction.Operand.reg(rdlo, Instruction.Operand.Shift.none), - } }, - }); - - // mov rdlo, #0 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = rdlo, - .op = Instruction.Operand.fromU32(0).?, - } }, - }); - - // movne rdlo, #1 - _ = try self.addInst(.{ - .tag = .mov, - .cond = .ne, - .data = .{ .r_op_mov = .{ - .rd = rdlo, - .op = Instruction.Operand.fromU32(1).?, - } }, - }); - - // cmp rdhi, #0 - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = rdhi, - .op = Instruction.Operand.fromU32(0).?, - } }, - }); - - // movne rdlo, #1 - _ = try self.addInst(.{ - .tag = .mov, - .cond = .ne, - .data = .{ .r_op_mov = .{ - .rd = rdlo, - .op = Instruction.Operand.fromU32(1).?, - } }, - }); - - // strb rdlo, [...] - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .register = rdlo }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else { - return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = result: { - const lhs_ty = self.typeOf(extra.lhs); - const rhs_ty = self.typeOf(extra.rhs); - - const tuple_ty = self.typeOfIndex(inst); - const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu)); - const tuple_align = tuple_ty.abiAlignment(zcu); - const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); - - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => if (!rhs_ty.isVector(zcu)) - return self.fail("TODO implement vector shl_with_overflow with scalar rhs", .{}) - else - return self.fail("TODO implement shl_with_overflow for vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); - - try self.spillCompareFlagsIfOccupied(); - - const shr_mir_tag: Mir.Inst.Tag = switch (int_info.signedness) { - .signed => Mir.Inst.Tag.asr, - .unsigned => Mir.Inst.Tag.lsr, - }; - - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - var reconstructed_reg: Register = undefined; - - const rhs_mcv = try self.resolveInst(extra.rhs); - const rhs_immediate_ok = rhs_mcv == .immediate and Instruction.Operand.fromU32(rhs_mcv.immediate) != null; - - const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - - if (rhs_immediate_ok) { - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - // lsl dest, lhs, rhs - _ = try self.addInst(.{ - .tag = .lsl, - .data = .{ .rr_shift = .{ - .rd = dest_reg, - .rm = lhs_reg, - .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_mcv.immediate)), - } }, - }); - - try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); - - // asr/lsr reconstructed, dest, rhs - _ = try self.addInst(.{ - .tag = shr_mir_tag, - .data = .{ .rr_shift = .{ - .rd = reconstructed_reg, - .rm = dest_reg, - .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_mcv.immediate)), - } }, - }); - } else { - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - null, - ); - - // lsl dest, lhs, rhs - _ = try self.addInst(.{ - .tag = .lsl, - .data = .{ .rr_shift = .{ - .rd = dest_reg, - .rm = lhs_reg, - .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), - } }, - }); - - try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); - - // asr/lsr reconstructed, dest, rhs - _ = try self.addInst(.{ - .tag = shr_mir_tag, - .data = .{ .rr_shift = .{ - .rd = reconstructed_reg, - .rm = dest_reg, - .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), - } }, - }); - } - - // cmp lhs, reconstructed - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = lhs_reg, - .op = Instruction.Operand.reg(reconstructed_reg, Instruction.Operand.Shift.none), - } }, - }); - - try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg }); - try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); - - break :result MCValue{ .stack_offset = stack_offset }; - } else { - return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { - const zcu = self.pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else if (self.typeOf(bin_op.lhs).isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu)) - return self.fail("TODO implement vector shl_sat with scalar rhs for {}", .{self.target.cpu.arch}) - else - return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const optional_ty = self.typeOfIndex(inst); - const abi_size: u32 = @intCast(optional_ty.abiSize(pt.zcu)); - - // Optional with a zero-bit payload type is just a boolean true - if (abi_size == 1) { - break :result MCValue{ .immediate = 1 }; - } else { - return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// Given an error union, returns the error -fn errUnionErr( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const err_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - if (err_ty.errorSetIsEmpty(zcu)) { - return MCValue{ .immediate = 0 }; - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return try error_union_bind.resolveToMcv(self); - } - - const err_offset: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu)); - switch (try error_union_bind.resolveToMcv(self)) { - .register => { - var operand_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - } else null, - ); - - const err_bit_offset = err_offset * 8; - const err_bit_size: u32 = @intCast(err_ty.abiSize(zcu) * 8); - - _ = try self.addInst(.{ - .tag = .ubfx, // errors are unsigned integers - .data = .{ .rr_lsb_width = .{ - .rd = dest_reg, - .rn = operand_reg, - .lsb = @intCast(err_bit_offset), - .width = @intCast(err_bit_size), - } }, - }); - - return MCValue{ .register = dest_reg }; - }, - .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off + err_offset }; - }, - .stack_offset => |off| { - return MCValue{ .stack_offset = off - err_offset }; - }, - .memory => |addr| { - return MCValue{ .memory = addr + err_offset }; - }, - else => unreachable, // invalid MCValue for an error union - } -} - -fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; - const error_union_ty = self.typeOf(ty_op.operand); - - break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// Given an error union, returns the payload -fn errUnionPayload( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const err_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - if (err_ty.errorSetIsEmpty(zcu)) { - return try error_union_bind.resolveToMcv(self); - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return MCValue.none; - } - - const payload_offset: u32 = @intCast(errUnionPayloadOffset(payload_ty, zcu)); - switch (try error_union_bind.resolveToMcv(self)) { - .register => { - var operand_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - } else null, - ); - - const payload_bit_offset = payload_offset * 8; - const payload_bit_size: u32 = @intCast(payload_ty.abiSize(zcu) * 8); - - _ = try self.addInst(.{ - .tag = if (payload_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx, - .data = .{ .rr_lsb_width = .{ - .rd = dest_reg, - .rn = operand_reg, - .lsb = @intCast(payload_bit_offset), - .width = @intCast(payload_bit_size), - } }, - }); - - return MCValue{ .register = dest_reg }; - }, - .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off + payload_offset }; - }, - .stack_offset => |off| { - return MCValue{ .stack_offset = off - payload_offset }; - }, - .memory => |addr| { - return MCValue{ .memory = addr + payload_offset }; - }, - else => unreachable, // invalid MCValue for an error union - } -} - -fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; - const error_union_ty = self.typeOf(ty_op.operand); - - break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -// *(E!T) -> E -fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - 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}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -// *(E!T) -> *T -fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - 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}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); -} - -fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch}); -} - -/// T to E!T -fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_ty = ty_op.ty.toType(); - const error_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - const operand = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand; - - const abi_size: u32 = @intCast(error_union_ty.abiSize(zcu)); - const abi_align = error_union_ty.abiAlignment(zcu); - const stack_offset: u32 = @intCast(try self.allocMem(abi_size, abi_align, inst)); - const payload_off = errUnionPayloadOffset(payload_ty, zcu); - const err_off = errUnionErrorOffset(payload_ty, zcu); - try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), operand); - try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), .{ .immediate = 0 }); - - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// E to E!T -fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_ty = ty_op.ty.toType(); - const error_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); - const operand = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand; - - const abi_size: u32 = @intCast(error_union_ty.abiSize(zcu)); - const abi_align = error_union_ty.abiAlignment(zcu); - const stack_offset: u32 = @intCast(try self.allocMem(abi_size, abi_align, inst)); - const payload_off = errUnionPayloadOffset(payload_ty, zcu); - const err_off = errUnionErrorOffset(payload_ty, zcu); - try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), operand); - try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), .undef); - - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -/// Given a slice, returns the length -fn slicePtr(mcv: MCValue) MCValue { - switch (mcv) { - .register => unreachable, // a slice doesn't fit in one register - .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off }; - }, - .stack_offset => |off| { - return MCValue{ .stack_offset = off }; - }, - .memory => |addr| { - return MCValue{ .memory = addr }; - }, - else => unreachable, // invalid MCValue for a slice - } -} - -fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(ty_op.operand); - break :result slicePtr(mcv); - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(ty_op.operand); - switch (mcv) { - .register => unreachable, // a slice doesn't fit in one register - .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + 4 }; - }, - .stack_offset => |off| { - break :result MCValue{ .stack_offset = off - 4 }; - }, - .memory => |addr| { - break :result MCValue{ .memory = addr + 4 }; - }, - else => unreachable, // invalid MCValue for a slice - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(ty_op.operand); - switch (mcv) { - .dead, .unreach => unreachable, - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off - 4 }; - }, - else => { - const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; - const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 4 } }; - - break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); - }, - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(ty_op.operand); - switch (mcv) { - .dead, .unreach => unreachable, - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off }; - }, - else => { - if (self.reuseOperand(inst, ty_op.operand, 0, mcv)) { - break :result mcv; - } else { - break :result MCValue{ .register = try self.copyToTmpRegister(Type.usize, mcv) }; - } - }, - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn ptrElemVal( - self: *Self, - ptr_bind: ReadArg.Bind, - index_bind: ReadArg.Bind, - ptr_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = ptr_ty.childType(zcu); - const elem_size: u32 = @intCast(elem_ty.abiSize(zcu)); - - switch (elem_size) { - 1, 4 => { - var base_reg: Register = undefined; - var index_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = ptr_ty, .bind = ptr_bind, .class = gp, .reg = &base_reg }, - .{ .ty = Type.usize, .bind = index_bind, .class = gp, .reg = &index_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = elem_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{ 0, 1 }, - } else null, - ); - - const tag: Mir.Inst.Tag = switch (elem_size) { - 1 => .ldrb, - 4 => .ldr, - else => unreachable, - }; - const shift: u5 = switch (elem_size) { - 1 => 0, - 4 => 2, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .rr_offset = .{ - .rt = dest_reg, - .rn = base_reg, - .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) }, - } }, - }); - - return MCValue{ .register = dest_reg }; - }, - else => { - const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null); - - const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst); - try self.load(dest, addr, ptr_ty); - return dest; - }, - } -} - -fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const slice_ty = self.typeOf(bin_op.lhs); - const result: MCValue = if (!slice_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: { - const ptr_ty = slice_ty.slicePtrFieldType(zcu); - - const slice_mcv = try self.resolveInst(bin_op.lhs); - const base_mcv = slicePtr(slice_mcv); - - const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; - const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const slice_mcv = try self.resolveInst(extra.lhs); - const base_mcv = slicePtr(slice_mcv); - - const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; - const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - - const slice_ty = self.typeOf(extra.lhs); - const index_ty = self.typeOf(extra.rhs); - - const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null); - break :result addr; - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn arrayElemVal( - self: *Self, - array_bind: ReadArg.Bind, - index_bind: ReadArg.Bind, - array_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = array_ty.childType(zcu); - - const mcv = try array_bind.resolveToMcv(self); - switch (mcv) { - .stack_offset, - .memory, - .stack_argument_offset, - => { - const ptr_to_mcv = switch (mcv) { - .stack_offset => |off| MCValue{ .ptr_stack_offset = off }, - .memory => |addr| MCValue{ .immediate = @intCast(addr) }, - .stack_argument_offset => |off| blk: { - const reg = try self.register_manager.allocReg(null, gp); - - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .r_stack_offset = .{ - .rt = reg, - .stack_offset = off, - } }, - }); - - break :blk MCValue{ .register = reg }; - }, - else => unreachable, - }; - const ptr_to_mcv_lock: ?RegisterLock = switch (ptr_to_mcv) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (ptr_to_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - const base_bind: ReadArg.Bind = .{ .mcv = ptr_to_mcv }; - - const ptr_ty = try pt.singleMutPtrType(elem_ty); - - return try self.ptrElemVal(base_bind, index_bind, ptr_ty, maybe_inst); - }, - else => return self.fail("TODO implement array_elem_val for {}", .{mcv}), - } -} - -fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const array_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - const array_ty = self.typeOf(bin_op.lhs); - - break :result try self.arrayElemVal(array_bind, index_bind, array_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const ptr_ty = self.typeOf(bin_op.lhs); - const result: MCValue = if (!ptr_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: { - const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; - const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; - - break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs }; - const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; - - const ptr_ty = self.typeOf(extra.lhs); - const index_ty = self.typeOf(extra.rhs); - - const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null); - break :result addr; - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); -} - -fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - _ = bin_op; - return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airClz(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airCtz(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airAbs(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - _ = ty_op; - return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn reuseOperand( - self: *Self, - inst: Air.Inst.Index, - operand: Air.Inst.Ref, - op_index: Air.Liveness.OperandInt, - mcv: MCValue, -) bool { - if (!self.liveness.operandDies(inst, op_index)) - return false; - - switch (mcv) { - .register => |reg| { - // We assert that this register is allocatable by asking - // for its index - const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above - if (!self.register_manager.isRegFree(reg)) { - self.register_manager.registers[index] = inst; - } - - log.debug("%{d} => {} (reused)", .{ inst, reg }); - }, - .stack_offset => |off| { - log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); - }, - .cpsr_flags => { - log.debug("%{d} => cpsr_flags (reused)", .{inst}); - }, - else => return false, - } - - // Prevent the operand deaths processing code from deallocating it. - self.reused_operands.set(op_index); - - // That makes us responsible for doing the rest of the stuff that processDeath would have done. - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead); - - return true; -} - -fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const elem_ty = ptr_ty.childType(zcu); - const elem_size: u32 = @intCast(elem_ty.abiSize(zcu)); - - switch (ptr) { - .none => unreachable, - .undef => unreachable, - .unreach => unreachable, - .dead => unreachable, - .cpsr_flags, - .register_c_flag, - .register_v_flag, - => unreachable, // cannot hold an address - .immediate => |imm| { - try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); - }, - .ptr_stack_offset => |off| { - try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }); - }, - .register => |reg| { - const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); - - switch (dst_mcv) { - .register => |dst_reg| { - try self.genLdrRegister(dst_reg, reg, elem_ty); - }, - .stack_offset => |off| { - if (elem_size <= 4) { - const tmp_reg = try self.register_manager.allocReg(null, gp); - const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_reg_lock); - - try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); - try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); - } else { - // TODO optimize the register allocation - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); - const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); - defer for (regs_locks) |reg_locked| { - self.register_manager.unlockReg(reg_locked); - }; - - const src_reg = reg; - const dst_reg = regs[0]; - const len_reg = regs[1]; - const count_reg = regs[2]; - const tmp_reg = regs[3]; - - // sub dst_reg, fp, #off - try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); - - // mov len, #elem_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - else => unreachable, // attempting to load into non-register or non-stack MCValue - } - }, - .memory, - .stack_offset, - .stack_argument_offset, - => { - const reg = try self.register_manager.allocReg(null, gp); - const reg_lock = self.register_manager.lockRegAssumeUnused(reg); - defer self.register_manager.unlockReg(reg_lock); - - try self.genSetReg(ptr_ty, reg, ptr); - try self.load(dst_mcv, .{ .register = reg }, ptr_ty); - }, - } -} - -fn airLoad(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const elem_ty = self.typeOfIndex(inst); - const result: MCValue = result: { - if (!elem_ty.hasRuntimeBits(zcu)) - break :result MCValue.none; - - const ptr = try self.resolveInst(ty_op.operand); - const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(zcu); - if (self.liveness.isUnused(inst) and !is_volatile) - break :result MCValue.dead; - - const dest_mcv: MCValue = blk: { - const ptr_fits_dest = elem_ty.abiSize(zcu) <= 4; - if (ptr_fits_dest and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk ptr; - } else { - break :blk try self.allocRegOrMem(elem_ty, true, inst); - } - }; - try self.load(dest_mcv, ptr, self.typeOf(ty_op.operand)); - - break :result dest_mcv; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - const pt = self.pt; - const elem_size: u32 = @intCast(value_ty.abiSize(pt.zcu)); - - switch (ptr) { - .none => unreachable, - .undef => unreachable, - .unreach => unreachable, - .dead => unreachable, - .cpsr_flags, - .register_c_flag, - .register_v_flag, - => unreachable, // cannot hold an address - .immediate => |imm| { - try self.setRegOrMem(value_ty, .{ .memory = imm }, value); - }, - .ptr_stack_offset => |off| { - try self.genSetStack(value_ty, off, value); - }, - .register => |addr_reg| { - const addr_reg_lock = self.register_manager.lockReg(addr_reg); - defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); - - switch (value) { - .dead => unreachable, - .undef => { - try self.genSetReg(value_ty, addr_reg, value); - }, - .register => |value_reg| { - try self.genStrRegister(value_reg, addr_reg, value_ty); - }, - else => { - if (elem_size <= 4) { - const tmp_reg = try self.register_manager.allocReg(null, gp); - const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_reg_lock); - - try self.genSetReg(value_ty, tmp_reg, value); - try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); - } else { - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); - const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); - defer for (regs_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const src_reg = regs[0]; - const dst_reg = addr_reg; - const len_reg = regs[1]; - const count_reg = regs[2]; - const tmp_reg = regs[3]; - - switch (value) { - .stack_offset => |off| { - // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); - }, - .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }), - .stack_argument_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .r_stack_offset = .{ - .rt = src_reg, - .stack_offset = off, - } }, - }); - }, - else => return self.fail("TODO store {} to register", .{value}), - } - - // mov len, #elem_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - } - }, - .memory, - .stack_offset, - .stack_argument_offset, - => { - const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); - try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); - }, - } -} - -fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { - if (safety) { - // TODO if the value is undef, write 0xaa bytes to dest - } else { - // TODO if the value is undef, don't lower this instruction - } - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const ptr = try self.resolveInst(bin_op.lhs); - const value = try self.resolveInst(bin_op.rhs); - const ptr_ty = self.typeOf(bin_op.lhs); - const value_ty = self.typeOf(bin_op.rhs); - - try self.store(ptr, value, ptr_ty, value_ty); - - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); - return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); -} - -fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result = try self.structFieldPtr(inst, ty_op.operand, index); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { - return if (self.liveness.isUnused(inst)) .dead else result: { - const pt = self.pt; - const zcu = pt.zcu; - const mcv = try self.resolveInst(operand); - const ptr_ty = self.typeOf(operand); - const struct_ty = ptr_ty.childType(zcu); - const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(index, zcu)); - switch (mcv) { - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; - }, - else => { - const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; - const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; - - break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); - }, - } - }; -} - -fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - const operand = extra.struct_operand; - const index = extra.field_index; - const pt = self.pt; - const zcu = pt.zcu; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(operand); - const struct_ty = self.typeOf(operand); - const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(index, zcu)); - const struct_field_ty = struct_ty.fieldType(index, zcu); - - switch (mcv) { - .dead, .unreach => unreachable, - .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + struct_field_offset }; - }, - .stack_offset => |off| { - break :result MCValue{ .stack_offset = off - struct_field_offset }; - }, - .memory => |addr| { - break :result MCValue{ .memory = addr + struct_field_offset }; - }, - .register_c_flag, - .register_v_flag, - => |reg| { - const reg_lock = self.register_manager.lockRegAssumeUnused(reg); - defer self.register_manager.unlockReg(reg_lock); - - const field: MCValue = switch (index) { - // get wrapped value: return register - 0 => MCValue{ .register = reg }, - - // get overflow bit: return C or V flag - 1 => MCValue{ .cpsr_flags = switch (mcv) { - .register_c_flag => .cs, - .register_v_flag => .vs, - else => unreachable, - } }, - - else => unreachable, - }; - - if (self.reuseOperand(inst, operand, 0, field)) { - break :result field; - } else { - // Copy to new register - const dest_reg = try self.register_manager.allocReg(null, gp); - try self.genSetReg(struct_field_ty, dest_reg, field); - - break :result MCValue{ .register = dest_reg }; - } - }, - .register => { - var operand_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = struct_ty, .bind = .{ .mcv = mcv }, .class = gp, .reg = &operand_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = struct_field_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - ReuseMetadata{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - }, - ); - - const field_bit_offset = struct_field_offset * 8; - const field_bit_size: u32 = @intCast(struct_field_ty.abiSize(zcu) * 8); - - _ = try self.addInst(.{ - .tag = if (struct_field_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx, - .data = .{ .rr_lsb_width = .{ - .rd = dest_reg, - .rn = operand_reg, - .lsb = @intCast(field_bit_offset), - .width = @intCast(field_bit_size), - } }, - }); - - break :result MCValue{ .register = dest_reg }; - }, - else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), - } - }; - - return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); -} - -fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const field_ptr = try self.resolveInst(extra.field_ptr); - const struct_ty = ty_pl.ty.toType().childType(zcu); - - if (struct_ty.zigTypeTag(zcu) == .@"union") { - return self.fail("TODO implement @fieldParentPtr codegen for unions", .{}); - } - - const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(extra.field_index, zcu)); - switch (field_ptr) { - .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off + struct_field_offset }; - }, - else => { - const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr }; - const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; - - break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null); - }, - } - }; - return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); -} - -/// An argument to a Mir instruction which is read (and possibly also -/// written to) by the respective instruction -const ReadArg = struct { - ty: Type, - bind: Bind, - class: RegisterManager.RegisterBitSet, - reg: *Register, - - const Bind = union(enum) { - inst: Air.Inst.Ref, - mcv: MCValue, - - fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue { - return switch (bind) { - .inst => |inst| try function.resolveInst(inst), - .mcv => |mcv| mcv, - }; - } - - fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u32 { - switch (bind) { - .inst => |inst| { - // TODO resolve independently of inst_table - const mcv = try function.resolveInst(inst); - switch (mcv) { - .immediate => |imm| return imm, - else => return null, - } - }, - .mcv => |mcv| { - switch (mcv) { - .immediate => |imm| return imm, - else => return null, - } - }, - } - } - }; -}; - -/// An argument to a Mir instruction which is written to (but not read -/// from) by the respective instruction -const WriteArg = struct { - ty: Type, - bind: Bind, - class: RegisterManager.RegisterBitSet, - reg: *Register, - - const Bind = union(enum) { - reg: Register, - none: void, - }; -}; - -/// Holds all data necessary for enabling the potential reuse of -/// operand registers as destinations -const ReuseMetadata = struct { - corresponding_inst: Air.Inst.Index, - - /// Maps every element index of read_args to the corresponding - /// index in the Air instruction - /// - /// When the order of read_args corresponds exactly to the order - /// of the inputs of the Air instruction, this would be e.g. - /// &.{ 0, 1 }. However, when the order is not the same or some - /// inputs to the Air instruction are omitted (e.g. when they can - /// be represented as immediates to the Mir instruction), - /// operand_mapping should reflect that fact. - operand_mapping: []const Air.Liveness.OperandInt, -}; - -/// Allocate a set of registers for use as arguments for a Mir -/// instruction -/// -/// If the Mir instruction these registers are allocated for -/// corresponds exactly to a single Air instruction, populate -/// reuse_metadata in order to enable potential reuse of an operand as -/// the destination (provided that that operand dies in this -/// instruction). -/// -/// Reusing an operand register as destination is the only time two -/// arguments may share the same register. In all other cases, -/// allocRegs guarantees that a register will never be allocated to -/// more than one argument. -/// -/// Furthermore, allocReg guarantees that all arguments which are -/// already bound to registers before calling allocRegs will not -/// change their register binding. This is done by locking these -/// registers. -fn allocRegs( - self: *Self, - read_args: []const ReadArg, - write_args: []const WriteArg, - reuse_metadata: ?ReuseMetadata, -) InnerError!void { - // Air instructions have exactly one output - assert(!(reuse_metadata != null and write_args.len != 1)); // see note above - - // The operand mapping is a 1:1 mapping of read args to their - // corresponding operand index in the Air instruction - assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above - - const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len); - defer self.gpa.free(locks); - const read_locks = locks[0..read_args.len]; - const write_locks = locks[read_args.len..]; - - @memset(locks, null); - defer for (locks) |lock| { - if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg); - }; - - // When we reuse a read_arg as a destination, the corresponding - // MCValue of the read_arg will be set to .dead. In that case, we - // skip allocating this read_arg. - var reused_read_arg: ?usize = null; - - // Lock all args which are already allocated to registers - for (read_args, 0..) |arg, i| { - const mcv = try arg.bind.resolveToMcv(self); - if (mcv == .register) { - read_locks[i] = self.register_manager.lockReg(mcv.register); - } - } - - for (write_args, 0..) |arg, i| { - if (arg.bind == .reg) { - write_locks[i] = self.register_manager.lockReg(arg.bind.reg); - } - } - - // Allocate registers for all args which aren't allocated to - // registers yet - for (read_args, 0..) |arg, i| { - const mcv = try arg.bind.resolveToMcv(self); - if (mcv == .register) { - arg.reg.* = mcv.register; - } else { - const track_inst: ?Air.Inst.Index = switch (arg.bind) { - .inst => |inst| inst.toIndex().?, - else => null, - }; - arg.reg.* = try self.register_manager.allocReg(track_inst, arg.class); - read_locks[i] = self.register_manager.lockReg(arg.reg.*); - } - } - - if (reuse_metadata != null) { - const inst = reuse_metadata.?.corresponding_inst; - const operand_mapping = reuse_metadata.?.operand_mapping; - const arg = write_args[0]; - if (arg.bind == .reg) { - arg.reg.* = arg.bind.reg; - } else { - reuse_operand: for (read_args, 0..) |read_arg, i| { - if (read_arg.bind == .inst) { - const operand = read_arg.bind.inst; - const mcv = try self.resolveInst(operand); - if (mcv == .register and - std.meta.eql(arg.class, read_arg.class) and - self.reuseOperand(inst, operand, operand_mapping[i], mcv)) - { - arg.reg.* = mcv.register; - write_locks[0] = null; - reused_read_arg = i; - break :reuse_operand; - } - } - } else { - arg.reg.* = try self.register_manager.allocReg(inst, arg.class); - write_locks[0] = self.register_manager.lockReg(arg.reg.*); - } - } - } else { - for (write_args, 0..) |arg, i| { - if (arg.bind == .reg) { - arg.reg.* = arg.bind.reg; - } else { - arg.reg.* = try self.register_manager.allocReg(null, arg.class); - write_locks[i] = self.register_manager.lockReg(arg.reg.*); - } - } - } - - // For all read_args which need to be moved from non-register to - // register, perform the move - for (read_args, 0..) |arg, i| { - if (reused_read_arg) |j| { - // Check whether this read_arg was reused - if (i == j) continue; - } - - const mcv = try arg.bind.resolveToMcv(self); - if (mcv != .register) { - if (arg.bind == .inst) { - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - const inst = arg.bind.inst.toIndex().?; - - // Overwrite the MCValue associated with this inst - branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); - - // If the previous MCValue occupied some space we track, we - // need to make sure it is marked as free now. - switch (mcv) { - .cpsr_flags => { - assert(self.cpsr_flags_inst.? == inst); - self.cpsr_flags_inst = null; - }, - .register => |prev_reg| { - assert(!self.register_manager.isRegFree(prev_reg)); - self.register_manager.freeReg(prev_reg); - }, - else => {}, - } - } - - try self.genSetReg(arg.ty, arg.reg.*, mcv); - } - } -} - -/// Wrapper around allocRegs and addInst tailored for specific Mir -/// instructions which are binary operations acting on two registers -/// -/// Returns the destination register -fn binOpRegister( - self: *Self, - mir_tag: Mir.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{ 0, 1 }, - } else null, - ); - - const mir_data: Mir.Inst.Data = switch (mir_tag) { - .add, - .adds, - .sub, - .subs, - .@"and", - .orr, - .eor, - => .{ .rr_op = .{ - .rd = dest_reg, - .rn = lhs_reg, - .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), - } }, - .lsl, - .asr, - .lsr, - => .{ .rr_shift = .{ - .rd = dest_reg, - .rm = lhs_reg, - .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), - } }, - .mul, - .smulbb, - => .{ .rrr = .{ - .rd = dest_reg, - .rn = lhs_reg, - .rm = rhs_reg, - } }, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = mir_tag, - .data = mir_data, - }); - - return MCValue{ .register = dest_reg }; -} - -/// Wrapper around allocRegs and addInst tailored for specific Mir -/// instructions which are binary operations acting on a register and -/// an immediate -/// -/// Returns the destination register -fn binOpImmediate( - self: *Self, - mir_tag: Mir.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_immediate: u32, - lhs_ty: Type, - lhs_and_rhs_swapped: bool, - maybe_inst: ?Air.Inst.Index, -) !MCValue { - var lhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - const operand_mapping: []const Air.Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = operand_mapping, - } else null, - ); - - const mir_data: Mir.Inst.Data = switch (mir_tag) { - .add, - .adds, - .sub, - .subs, - .@"and", - .orr, - .eor, - => .{ .rr_op = .{ - .rd = dest_reg, - .rn = lhs_reg, - .op = Instruction.Operand.fromU32(rhs_immediate).?, - } }, - .lsl, - .asr, - .lsr, - => .{ .rr_shift = .{ - .rd = dest_reg, - .rm = lhs_reg, - .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_immediate)), - } }, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = mir_tag, - .data = mir_data, - }); - - return MCValue{ .register = dest_reg }; -} - -fn addSub( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - const lhs_immediate = try lhs_bind.resolveToImmediate(self); - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - // Only say yes if the operation is - // commutative, i.e. we can swap both of the - // operands - const lhs_immediate_ok = switch (tag) { - .add => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, - .sub => false, - else => unreachable, - }; - const rhs_immediate_ok = switch (tag) { - .add, - .sub, - => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false, - else => unreachable, - }; - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add => .add, - .sub => .sub, - else => unreachable, - }; - - if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); - } else if (lhs_immediate_ok) { - // swap lhs and rhs - return try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); - } else { - return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } - } else { - return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn mul( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - // TODO add optimisations for multiplication - // with immediates, for example a * 2 can be - // lowered to a << 1 - return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } else { - return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn divFloat( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - _ = lhs_bind; - _ = rhs_bind; - _ = rhs_ty; - _ = maybe_inst; - - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - else => unreachable, - } -} - -fn divTrunc( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - switch (int_info.signedness) { - .signed => { - return self.fail("TODO ARM signed integer division", .{}); - }, - .unsigned => { - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - if (rhs_immediate) |imm| { - if (std.math.isPowerOfTwo(imm)) { - const shift = std.math.log2_int(u32, imm); - return try self.binOpImmediate(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst); - } else { - return self.fail("TODO ARM integer division by constants", .{}); - } - } else { - return self.fail("TODO ARM integer division", .{}); - } - }, - } - } else { - return self.fail("TODO ARM integer division for integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn divFloor( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - switch (int_info.signedness) { - .signed => { - return self.fail("TODO ARM signed integer division", .{}); - }, - .unsigned => { - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - if (rhs_immediate) |imm| { - if (std.math.isPowerOfTwo(imm)) { - const shift = std.math.log2_int(u32, imm); - return try self.binOpImmediate(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst); - } else { - return self.fail("TODO ARM integer division by constants", .{}); - } - } else { - return self.fail("TODO ARM integer division", .{}); - } - }, - } - } else { - return self.fail("TODO ARM integer division for integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn divExact( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - _ = lhs_bind; - _ = rhs_bind; - _ = rhs_ty; - _ = maybe_inst; - - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => return self.fail("TODO ARM div_exact", .{}), - else => unreachable, - } -} - -fn rem( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - switch (int_info.signedness) { - .signed => { - return self.fail("TODO ARM signed integer zcu", .{}); - }, - .unsigned => { - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - if (rhs_immediate) |imm| { - if (std.math.isPowerOfTwo(imm)) { - const log2 = std.math.log2_int(u32, imm); - - var lhs_reg: Register = undefined; - var dest_reg: Register = undefined; - - const read_args = [_]ReadArg{ - .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, - }; - const write_args = [_]WriteArg{ - .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, - }; - try self.allocRegs( - &read_args, - &write_args, - if (maybe_inst) |inst| .{ - .corresponding_inst = inst, - .operand_mapping = &.{0}, - } else null, - ); - - try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2); - - return MCValue{ .register = dest_reg }; - } else { - return self.fail("TODO ARM integer zcu by constants", .{}); - } - } else { - return self.fail("TODO ARM integer zcu", .{}); - } - }, - } - } else { - return self.fail("TODO ARM integer division for integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn modulo( - self: *Self, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - _ = lhs_bind; - _ = rhs_bind; - _ = rhs_ty; - _ = maybe_inst; - - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .float => return self.fail("TODO ARM binary operations on floats", .{}), - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => return self.fail("TODO ARM zcu", .{}), - else => unreachable, - } -} - -fn wrappingArithmetic( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - // Generate an add/sub/mul - const result: MCValue = switch (tag) { - .add_wrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - .sub_wrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - .mul_wrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - else => unreachable, - }; - - // Truncate if necessary - const result_reg = result.register; - if (int_info.bits < 32) { - try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); - } - - return result; - } else { - return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn bitwise( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - assert(lhs_ty.eql(rhs_ty, zcu)); - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - const lhs_immediate = try lhs_bind.resolveToImmediate(self); - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - const lhs_immediate_ok = if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false; - const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false; - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .bit_and => .@"and", - .bit_or => .orr, - .xor => .eor, - else => unreachable, - }; - - if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); - } else if (lhs_immediate_ok) { - // swap lhs and rhs - return try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); - } else { - return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } - } else { - return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn shiftExact( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => if (!rhs_ty.isVector(zcu)) - return self.fail("TODO ARM vector shift with scalar rhs", .{}) - else - return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .shl_exact => .lsl, - .shr_exact => switch (lhs_ty.intInfo(zcu).signedness) { - .signed => Mir.Inst.Tag.asr, - .unsigned => Mir.Inst.Tag.lsr, - }, - else => unreachable, - }; - - if (rhs_immediate) |imm| { - return try self.binOpImmediate(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst); - } else { - return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } - } else { - return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn shiftNormal( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .vector => if (!rhs_ty.isVector(zcu)) - return self.fail("TODO ARM vector shift with scalar rhs", .{}) - else - return self.fail("TODO ARM binary operations on vectors", .{}), - .int => { - const int_info = lhs_ty.intInfo(zcu); - if (int_info.bits <= 32) { - // Generate a shl_exact/shr_exact - const result: MCValue = switch (tag) { - .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), - else => unreachable, - }; - - // Truncate if necessary - switch (tag) { - .shr => return result, - .shl => { - const result_reg = result.register; - if (int_info.bits < 32) { - try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); - } - - return result; - }, - else => unreachable, - } - } else { - return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); - } - }, - else => unreachable, - } -} - -fn booleanOp( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .bool => { - const lhs_immediate = try lhs_bind.resolveToImmediate(self); - const rhs_immediate = try rhs_bind.resolveToImmediate(self); - - const mir_tag: Mir.Inst.Tag = switch (tag) { - .bool_and => .@"and", - .bool_or => .orr, - else => unreachable, - }; - - if (rhs_immediate) |imm| { - return try self.binOpImmediate(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst); - } else if (lhs_immediate) |imm| { - // swap lhs and rhs - return try self.binOpImmediate(mir_tag, rhs_bind, imm, rhs_ty, true, maybe_inst); - } else { - return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); - } - }, - else => unreachable, - } -} - -fn ptrArithmetic( - self: *Self, - tag: Air.Inst.Tag, - lhs_bind: ReadArg.Bind, - rhs_bind: ReadArg.Bind, - lhs_ty: Type, - rhs_ty: Type, - maybe_inst: ?Air.Inst.Index, -) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - switch (lhs_ty.zigTypeTag(zcu)) { - .pointer => { - assert(rhs_ty.eql(Type.usize, zcu)); - - const ptr_ty = lhs_ty; - const elem_ty = switch (ptr_ty.ptrSize(zcu)) { - .one => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type - else => ptr_ty.childType(zcu), - }; - const elem_size: u32 = @intCast(elem_ty.abiSize(zcu)); - - const base_tag: Air.Inst.Tag = switch (tag) { - .ptr_add => .add, - .ptr_sub => .sub, - else => unreachable, - }; - - if (elem_size == 1) { - return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst); - } else { - // convert the offset into a byte offset by - // multiplying it with elem_size - const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } }; - - const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null); - const offset_bind = ReadArg.Bind{ .mcv = offset }; - - const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null); - return addr; - } - }, - else => unreachable, - } -} - -fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void { - const pt = self.pt; - const zcu = pt.zcu; - const abi_size = ty.abiSize(zcu); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb else .ldrb, - 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh else .ldrh, - 3, 4 => .ldr, - else => unreachable, - }; - - const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{ - .rt = dest_reg, - .rn = addr_reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }; - const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{ - .rt = dest_reg, - .rn = addr_reg, - .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, - } }; - - const data: Mir.Inst.Data = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) rr_extra_offset else rr_offset, - 2 => rr_extra_offset, - 3, 4 => rr_offset, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = data, - }); -} - -fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void { - const pt = self.pt; - const abi_size = ty.abiSize(pt.zcu); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .strb, - 2 => .strh, - 4 => .str, - 3 => return self.fail("TODO: genStrRegister for abi_size={}", .{abi_size}), - else => unreachable, - }; - - const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{ - .rt = source_reg, - .rn = addr_reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }; - const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{ - .rt = source_reg, - .rn = addr_reg, - .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, - } }; - - const data: Mir.Inst.Data = switch (abi_size) { - 1, 4 => rr_offset, - 2 => rr_extra_offset, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = data, - }); -} - -fn genInlineMemcpy( - self: *Self, - src: Register, - dst: Register, - len: Register, - count: Register, - tmp: Register, -) !void { - // mov count, #0 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = count, - .op = Instruction.Operand.imm(0, 0), - } }, - }); - - // loop: - // cmp count, len - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = count, - .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none), - } }, - }); - - // bge end - _ = try self.addInst(.{ - .tag = .b, - .cond = .ge, - .data = .{ .inst = @intCast(self.mir_instructions.len + 5) }, - }); - - // ldrb tmp, [src, count] - _ = try self.addInst(.{ - .tag = .ldrb, - .data = .{ .rr_offset = .{ - .rt = tmp, - .rn = src, - .offset = .{ .offset = Instruction.Offset.reg(count, .none) }, - } }, - }); - - // strb tmp, [src, count] - _ = try self.addInst(.{ - .tag = .strb, - .data = .{ .rr_offset = .{ - .rt = tmp, - .rn = dst, - .offset = .{ .offset = Instruction.Offset.reg(count, .none) }, - } }, - }); - - // add count, count, #1 - _ = try self.addInst(.{ - .tag = .add, - .data = .{ .rr_op = .{ - .rd = count, - .rn = count, - .op = Instruction.Operand.imm(1, 0), - } }, - }); - - // b loop - _ = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = @intCast(self.mir_instructions.len - 5) }, - }); - - // end: -} - -fn genInlineMemset( - self: *Self, - dst: MCValue, - val: MCValue, - len: MCValue, -) !void { - const dst_reg = switch (dst) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.manyptr_u8, dst), - }; - const dst_reg_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); - - const val_reg = switch (val) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.u8, val), - }; - const val_reg_lock = self.register_manager.lockReg(val_reg); - defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock); - - const len_reg = switch (len) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.usize, len), - }; - const len_reg_lock = self.register_manager.lockReg(len_reg); - defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock); - - const count_reg = try self.register_manager.allocReg(null, gp); - - try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg); -} - -fn genInlineMemsetCode( - self: *Self, - dst: Register, - val: Register, - len: Register, - count: Register, -) !void { - // mov count, #0 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = count, - .op = Instruction.Operand.imm(0, 0), - } }, - }); - - // loop: - // cmp count, len - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = count, - .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none), - } }, - }); - - // bge end - _ = try self.addInst(.{ - .tag = .b, - .cond = .ge, - .data = .{ .inst = @intCast(self.mir_instructions.len + 4) }, - }); - - // strb val, [src, count] - _ = try self.addInst(.{ - .tag = .strb, - .data = .{ .rr_offset = .{ - .rt = val, - .rn = dst, - .offset = .{ .offset = Instruction.Offset.reg(count, .none) }, - } }, - }); - - // add count, count, #1 - _ = try self.addInst(.{ - .tag = .add, - .data = .{ .rr_op = .{ - .rd = count, - .rn = count, - .op = Instruction.Operand.imm(1, 0), - } }, - }); - - // b loop - _ = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = @intCast(self.mir_instructions.len - 4) }, - }); - - // end: -} - -fn airArg(self: *Self, inst: Air.Inst.Index) !void { - // skip zero-bit arguments as they don't have a corresponding arg instruction - var arg_index = self.arg_index; - while (self.args[arg_index] == .none) arg_index += 1; - self.arg_index = arg_index + 1; - - const zcu = self.pt.zcu; - const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?; - const file = zcu.fileByIndex(func_zir.file); - if (!file.mod.?.strip) { - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; - const ty = self.typeOfIndex(inst); - const zir = &file.zir.?; - const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?); - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name, - .mcv = self.args[arg_index], - }); - } - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airTrap(self: *Self) !void { - _ = try self.addInst(.{ - .tag = .undefined_instruction, - .data = .{ .nop = {} }, - }); - return self.finishAirBookkeeping(); -} - -fn airBreakpoint(self: *Self) !void { - _ = try self.addInst(.{ - .tag = .bkpt, - .data = .{ .imm16 = 0 }, - }); - return self.finishAirBookkeeping(); -} - -fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for arm", .{}); - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for arm", .{}); - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { - if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{}); - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const callee = pl_op.operand; - const extra = self.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]); - const ty = self.typeOf(callee); - const pt = self.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - const fn_ty = switch (ty.zigTypeTag(zcu)) { - .@"fn" => ty, - .pointer => ty.childType(zcu), - else => unreachable, - }; - - var info = try self.resolveCallingConventionValues(fn_ty); - defer info.deinit(self); - - // According to the Procedure Call Standard for the ARM - // Architecture, compare flags are not preserved across - // calls. Therefore, if some value is currently stored there, we - // need to save it. - try self.spillCompareFlagsIfOccupied(); - - // Save caller-saved registers, but crucially *after* we save the - // compare flags as saving compare flags may require a new - // caller-saved register - for (caller_preserved_regs) |reg| { - try self.register_manager.getReg(reg, null); - } - - // If returning by reference, r0 will contain the address of where - // to put the result into. In that case, make sure that r0 remains - // untouched by the parameter passing code - const r0_lock: ?RegisterLock = if (info.return_value == .stack_offset) blk: { - log.debug("airCall: return by reference", .{}); - const ret_ty = fn_ty.fnReturnType(zcu); - const ret_abi_size: u32 = @intCast(ret_ty.abiSize(zcu)); - const ret_abi_align = ret_ty.abiAlignment(zcu); - const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst); - - const ptr_ty = try pt.singleMutPtrType(ret_ty); - try self.register_manager.getReg(.r0, null); - try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); - - info.return_value = .{ .stack_offset = stack_offset }; - - break :blk self.register_manager.lockRegAssumeUnused(.r0); - } else null; - defer if (r0_lock) |reg| self.register_manager.unlockReg(reg); - - // Make space for the arguments passed via the stack - self.max_end_stack += info.stack_byte_count; - - for (info.args, 0..) |mc_arg, arg_i| { - const arg = args[arg_i]; - const arg_ty = self.typeOf(arg); - const arg_mcv = try self.resolveInst(args[arg_i]); - - switch (mc_arg) { - .none => continue, - .register => |reg| { - try self.register_manager.getReg(reg, null); - try self.genSetReg(arg_ty, reg, arg_mcv); - }, - .stack_offset => unreachable, - .stack_argument_offset => |offset| try self.genSetStackArgument( - arg_ty, - offset, - arg_mcv, - ), - else => unreachable, - } - } - - // Due to incremental compilation, how function calls are generated depends - // on linking. - if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { - .func => { - return self.fail("TODO implement calling functions", .{}); - }, - .@"extern" => { - return self.fail("TODO implement calling extern functions", .{}); - }, - else => { - return self.fail("TODO implement calling bitcasted functions", .{}); - }, - } else { - assert(ty.zigTypeTag(zcu) == .pointer); - const mcv = try self.resolveInst(callee); - - try self.genSetReg(Type.usize, .lr, mcv); - } - - // TODO: add Instruction.supportedOn - // function for ARM - if (self.target.cpu.has(.arm, .has_v5t)) { - _ = try self.addInst(.{ - .tag = .blx, - .data = .{ .reg = .lr }, - }); - } else { - return self.fail("TODO fix blx emulation for ARM |reg| { - if (RegisterManager.indexOfRegIntoTracked(reg) == null) { - // Save function return value into a tracked register - log.debug("airCall: copying {} as it is not tracked", .{reg}); - const new_reg = try self.copyToTmpRegister(fn_ty.fnReturnType(zcu), info.return_value); - break :result MCValue{ .register = new_reg }; - } - }, - else => {}, - } - break :result info.return_value; - }; - - if (args.len <= Air.Liveness.bpi - 2) { - var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - buf[0] = callee; - @memcpy(buf[1..][0..args.len], args); - return self.finishAir(inst, result, buf); - } - var bt = try self.iterateBigTomb(inst, 1 + args.len); - bt.feed(callee); - for (args) |arg| { - bt.feed(arg); - } - return bt.finishAir(result); -} - -fn airRet(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - const ret_ty = self.fn_type.fnReturnType(zcu); - - switch (self.ret_mcv) { - .none => {}, - .immediate => { - assert(ret_ty.isError(zcu)); - }, - .register => |reg| { - // Return result by value - try self.genSetReg(ret_ty, reg, operand); - }, - .stack_offset => { - // Return result by reference - // - // self.ret_mcv is an address to where this function - // should store its result into - const ptr_ty = try pt.singleMutPtrType(ret_ty); - try self.store(self.ret_mcv, operand, ptr_ty, ret_ty); - }, - else => unreachable, // invalid return result - } - - // Just add space for an instruction, patch this later - try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); - - return self.finishAir(inst, .dead, .{ un_op, .none, .none }); -} - -fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const ret_ty = self.fn_type.fnReturnType(zcu); - - switch (self.ret_mcv) { - .none => {}, - .register => { - // Return result by value - try self.load(self.ret_mcv, ptr, ptr_ty); - }, - .stack_offset => { - // Return result by reference - // - // self.ret_mcv is an address to where this function - // should store its result into - // - // If the operand is a ret_ptr instruction, we are done - // here. Else we need to load the result from the location - // pointed to by the operand and store it to the result - // location. - const op_inst = un_op.toIndex().?; - if (self.air.instructions.items(.tag)[@intFromEnum(op_inst)] != .ret_ptr) { - const abi_size: u32 = @intCast(ret_ty.abiSize(zcu)); - const abi_align = ret_ty.abiAlignment(zcu); - - const offset = try self.allocMem(abi_size, abi_align, null); - - const tmp_mcv = MCValue{ .stack_offset = offset }; - try self.load(tmp_mcv, ptr, ptr_ty); - try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty); - } - }, - else => unreachable, // invalid return result - } - - // Just add space for an instruction, patch this later - try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); - - return self.finishAir(inst, .dead, .{ un_op, .none, .none }); -} - -fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const lhs_ty = self.typeOf(bin_op.lhs); - - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { - break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op); - }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn cmp( - self: *Self, - lhs: ReadArg.Bind, - rhs: ReadArg.Bind, - lhs_ty: Type, - op: math.CompareOperator, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { - .optional => blk: { - const payload_ty = lhs_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - break :blk Type.u1; - } else if (lhs_ty.isPtrLikeOptional(zcu)) { - break :blk Type.usize; - } else { - return self.fail("TODO ARM cmp non-pointer optionals", .{}); - } - }, - .float => return self.fail("TODO ARM cmp floats", .{}), - .@"enum" => lhs_ty.intTagType(zcu), - .int => lhs_ty, - .bool => Type.u1, - .pointer => Type.usize, - .error_set => Type.u16, - else => unreachable, - }; - - const int_info = int_ty.intInfo(zcu); - if (int_info.bits <= 32) { - try self.spillCompareFlagsIfOccupied(); - - var lhs_reg: Register = undefined; - var rhs_reg: Register = undefined; - - const rhs_immediate = try rhs.resolveToImmediate(self); - const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false; - - if (rhs_immediate_ok) { - const read_args = [_]ReadArg{ - .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, - }; - try self.allocRegs( - &read_args, - &.{}, - null, // we won't be able to reuse a register as there are no write_regs - ); - - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = lhs_reg, - .op = Instruction.Operand.fromU32(rhs_immediate.?).?, - } }, - }); - } else { - const read_args = [_]ReadArg{ - .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, - .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg }, - }; - try self.allocRegs( - &read_args, - &.{}, - null, // we won't be able to reuse a register as there are no write_regs - ); - - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = lhs_reg, - .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), - } }, - }); - } - - return switch (int_info.signedness) { - .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) }, - .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) }, - }; - } else { - return self.fail("TODO ARM cmp for ints > 32 bits", .{}); - } -} - -fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); -} - -fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - _ = operand; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { - const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; - - _ = try self.addInst(.{ - .tag = .dbg_line, - .cond = undefined, - .data = .{ .dbg_line_column = .{ - .line = dbg_stmt.line, - .column = dbg_stmt.column, - } }, - }); - - return self.finishAirBookkeeping(); -} - -fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); - const func = zcu.funcInfo(extra.data.func); - // TODO emit debug info for function change - _ = func; - try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); -} - -fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const operand = pl_op.operand; - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const ty = self.typeOf(operand); - const mcv = try self.resolveInst(operand); - const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - - log.debug("airDbgVar: %{f}: {f}, {}", .{ inst, ty.fmtDebug(), mcv }); - - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name.toSlice(self.air), - .mcv = mcv, - }); - - return self.finishAir(inst, .dead, .{ operand, .none, .none }); -} - -/// Given a boolean condition, emit a jump that is taken when that -/// condition is false. -fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { - const condition_code: Condition = switch (condition) { - .cpsr_flags => |cond| cond.negate(), - else => blk: { - const reg = switch (condition) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, condition), - }; - - try self.spillCompareFlagsIfOccupied(); - - // cmp reg, 1 - // bne ... - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .r_op_cmp = .{ - .rn = reg, - .op = Instruction.Operand.imm(1, 0), - } }, - }); - - break :blk .ne; - }, - }; - - return try self.addInst(.{ - .tag = .b, - .cond = condition_code, - .data = .{ .inst = undefined }, // populated later through performReloc - }); -} - -fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond_inst = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); - const liveness_condbr = self.liveness.getCondBr(inst); - - const reloc: Mir.Inst.Index = try self.condBr(cond_inst); - - // If the condition dies here in this condbr instruction, process - // that death now instead of later as this has an effect on - // whether it needs to be spilled in the branches - if (self.liveness.operandDies(inst, 0)) { - if (pl_op.operand.toIndex()) |op_index| { - self.processDeath(op_index); - } - } - - // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; - const parent_cpsr_flags_inst = self.cpsr_flags_inst; - - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop().?; - } - - try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); - for (liveness_condbr.then_deaths) |operand| { - self.processDeath(operand); - } - try self.genBody(then_body); - - // Revert to the previous register and stack allocation state. - - var saved_then_branch = self.branch_stack.pop().?; - defer saved_then_branch.deinit(self.gpa); - - self.register_manager.registers = parent_registers; - self.cpsr_flags_inst = parent_cpsr_flags_inst; - - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; - - try self.performReloc(reloc); - const else_branch = self.branch_stack.addOneAssumeCapacity(); - else_branch.* = .{}; - - try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); - for (liveness_condbr.else_deaths) |operand| { - self.processDeath(operand); - } - try self.genBody(else_body); - - // At this point, each branch will possibly have conflicting values for where - // each instruction is stored. They agree, however, on which instructions are alive/dead. - // We use the first ("then") branch as canonical, and here emit - // instructions into the second ("else") branch to make it conform. - // We continue respect the data structure semantic guarantees of the else_branch so - // that we can use all the code emitting abstractions. This is why at the bottom we - // assert that parent_branch.free_registers equals the saved_then_branch.free_registers - // rather than assigning it. - const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; - try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); - - const else_slice = else_branch.inst_table.entries.slice(); - const else_keys = else_slice.items(.key); - const else_values = else_slice.items(.value); - for (else_keys, 0..) |else_key, else_idx| { - const else_value = else_values[else_idx]; - const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { - // The instruction's MCValue is overridden in both branches. - parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); - if (else_value == .dead) { - assert(then_entry.value == .dead); - continue; - } - break :blk then_entry.value; - } else blk: { - if (else_value == .dead) - continue; - // The instruction is only overridden in the else branch. - var i: usize = self.branch_stack.items.len - 1; - while (true) { - i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? - if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { - assert(mcv != .dead); - break :blk mcv; - } - } - }; - log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); - // TODO make sure the destination stack offset / register does not already have something - // going on there. - try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value); - // TODO track the new register / stack allocation - } - try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); - const then_slice = saved_then_branch.inst_table.entries.slice(); - const then_keys = then_slice.items(.key); - const then_values = then_slice.items(.value); - for (then_keys, 0..) |then_key, then_idx| { - const then_value = then_values[then_idx]; - // We already deleted the items from this table that matched the else_branch. - // So these are all instructions that are only overridden in the then branch. - parent_branch.inst_table.putAssumeCapacity(then_key, then_value); - if (then_value == .dead) - continue; - const parent_mcv = blk: { - var i: usize = self.branch_stack.items.len - 1; - while (true) { - i -= 1; - if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { - assert(mcv != .dead); - break :blk mcv; - } - } - }; - log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); - // TODO make sure the destination stack offset / register does not already have something - // going on there. - try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value); - // TODO track the new register / stack allocation - } - - { - var item = self.branch_stack.pop().?; - item.deinit(self.gpa); - } - - // We already took care of pl_op.operand earlier, so we're going - // to pass .none here - return self.finishAir(inst, .unreach, .{ .none, .none, .none }); -} - -fn isNull( - self: *Self, - operand_bind: ReadArg.Bind, - operand_ty: Type, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - if (operand_ty.isPtrLikeOptional(zcu)) { - assert(operand_ty.abiSize(zcu) == 4); - - const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } }; - return self.cmp(operand_bind, imm_bind, Type.usize, .eq); - } else { - return self.fail("TODO implement non-pointer optionals", .{}); - } -} - -fn isNonNull( - self: *Self, - operand_bind: ReadArg.Bind, - operand_ty: Type, -) !MCValue { - const is_null_result = try self.isNull(operand_bind, operand_ty); - assert(is_null_result.cpsr_flags == .eq); - - return MCValue{ .cpsr_flags = .ne }; -} - -fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_bind: ReadArg.Bind = .{ .inst = un_op }; - const operand_ty = self.typeOf(un_op); - - break :result try self.isNull(operand_bind, operand_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isNull(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_bind: ReadArg.Bind = .{ .inst = un_op }; - const operand_ty = self.typeOf(un_op); - - break :result try self.isNonNull(operand_bind, operand_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isNonNull(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn isErr( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, -) !MCValue { - const pt = self.pt; - const zcu = pt.zcu; - const error_type = error_union_ty.errorUnionSet(zcu); - - if (error_type.errorSetIsEmpty(zcu)) { - return MCValue{ .immediate = 0 }; // always false - } - - const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); - return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); -} - -fn isNonErr( - self: *Self, - error_union_bind: ReadArg.Bind, - error_union_ty: Type, -) !MCValue { - const is_err_result = try self.isErr(error_union_bind, error_union_ty); - switch (is_err_result) { - .cpsr_flags => |cond| { - assert(cond == .hi); - return MCValue{ .cpsr_flags = cond.negate() }; - }, - .immediate => |imm| { - assert(imm == 0); - return MCValue{ .immediate = 1 }; - }, - else => unreachable, - } -} - -fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; - const error_union_ty = self.typeOf(un_op); - - break :result try self.isErr(error_union_bind, error_union_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isErr(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; - const error_union_ty = self.typeOf(un_op); - - break :result try self.isNonErr(error_union_bind, error_union_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const ptr_ty = self.typeOf(un_op); - const elem_ty = ptr_ty.childType(zcu); - - const operand = try self.allocRegOrMem(elem_ty, true, null); - try self.load(operand, operand_ptr, ptr_ty); - - break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airLoop(self: *Self, inst: Air.Inst.Index) !void { - // A loop is a setup to be able to jump back to the beginning. - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = self.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[loop.end..][0..loop.data.body_len]); - const start_index: Mir.Inst.Index = @intCast(self.mir_instructions.len); - - try self.genBody(body); - try self.jump(start_index); - - return self.finishAirBookkeeping(); -} - -/// Send control flow to `inst`. -fn jump(self: *Self, inst: Mir.Inst.Index) !void { - _ = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = inst }, - }); -} - -fn airBlock(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); -} - -fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { - try self.blocks.putNoClobber(self.gpa, inst, .{ - // A block is a setup to be able to jump to the end. - .relocs = .{}, - // It also acts as a receptacle for break operands. - // Here we use `MCValue.none` to represent a null value so that the first - // break instruction will choose a MCValue for the block result and overwrite - // this field. Following break instructions will use that MCValue to put their - // block results. - .mcv = MCValue{ .none = {} }, - }); - defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); - - // TODO emit debug info lexical block - try self.genBody(body); - - // relocations for `br` instructions - const relocs = &self.blocks.getPtr(inst).?.relocs; - if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { - // If the last Mir instruction is the last relocation (which - // would just jump one instruction further), it can be safely - // removed - self.mir_instructions.orderedRemove(relocs.pop().?); - } - for (relocs.items) |reloc| { - try self.performReloc(reloc); - } - - const result = self.blocks.getPtr(inst).?.mcv; - return self.finishAir(inst, result, .{ .none, .none, .none }); -} - -fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { - const switch_br = self.air.unwrapSwitch(inst); - const condition_ty = self.typeOf(switch_br.operand); - const liveness = try self.liveness.getSwitchBr( - self.gpa, - inst, - switch_br.cases_len + 1, - ); - defer self.gpa.free(liveness.deaths); - - var it = switch_br.iterateCases(); - while (it.next()) |case| { - if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{}); - // For every item, we compare it to condition and branch into - // the prong if they are equal. After we compared to all - // items, we branch into the next prong (or if no other prongs - // exist out of the switch statement). - // - // cmp condition, item1 - // beq prong - // cmp condition, item2 - // beq prong - // cmp condition, item3 - // beq prong - // b out - // prong: ... - // ... - // out: ... - const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len); - defer self.gpa.free(branch_into_prong_relocs); - - for (case.items, 0..) |item, idx| { - const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq); - branch_into_prong_relocs[idx] = try self.condBr(cmp_result); - } - - const branch_away_from_prong_reloc = try self.addInst(.{ - .tag = .b, - .data = .{ .inst = undefined }, // populated later through performReloc - }); - - for (branch_into_prong_relocs) |reloc| { - try self.performReloc(reloc); - } - - // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - const parent_cpsr_flags_inst = self.cpsr_flags_inst; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; - - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop().?; - } - - try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len); - for (liveness.deaths[case.idx]) |operand| { - self.processDeath(operand); - } - try self.genBody(case.body); - - // Revert to the previous register and stack allocation state. - var saved_case_branch = self.branch_stack.pop().?; - defer saved_case_branch.deinit(self.gpa); - - self.register_manager.registers = parent_registers; - self.cpsr_flags_inst = parent_cpsr_flags_inst; - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; - - try self.performReloc(branch_away_from_prong_reloc); - } - - if (switch_br.else_body_len > 0) { - const else_body = it.elseBody(); - - // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - const parent_cpsr_flags_inst = self.cpsr_flags_inst; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; - - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop().?; - } - - const else_deaths = liveness.deaths.len - 1; - try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); - for (liveness.deaths[else_deaths]) |operand| { - self.processDeath(operand); - } - try self.genBody(else_body); - - // Revert to the previous register and stack allocation state. - var saved_case_branch = self.branch_stack.pop().?; - defer saved_case_branch.deinit(self.gpa); - - self.register_manager.registers = parent_registers; - self.cpsr_flags_inst = parent_cpsr_flags_inst; - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; - - // TODO consolidate returned MCValues between prongs and else branch like we do - // in airCondBr. - } - - return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none }); -} - -fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { - const tag = self.mir_instructions.items(.tag)[inst]; - switch (tag) { - .b => self.mir_instructions.items(.data)[inst].inst = @intCast(self.mir_instructions.len), - else => unreachable, - } -} - -fn airBr(self: *Self, inst: Air.Inst.Index) !void { - const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; - try self.br(branch.block_inst, branch.operand); - return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); -} - -fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { - const zcu = self.pt.zcu; - const block_data = self.blocks.getPtr(block).?; - - if (self.typeOf(operand).hasRuntimeBits(zcu)) { - const operand_mcv = try self.resolveInst(operand); - const block_mcv = block_data.mcv; - if (block_mcv == .none) { - block_data.mcv = switch (operand_mcv) { - .none, .dead, .unreach => unreachable, - .register, .stack_offset, .memory => operand_mcv, - .immediate, .stack_argument_offset, .cpsr_flags => blk: { - const new_mcv = try self.allocRegOrMem(self.typeOfIndex(block), true, block); - try self.setRegOrMem(self.typeOfIndex(block), new_mcv, operand_mcv); - break :blk new_mcv; - }, - else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), - }; - } else { - try self.setRegOrMem(self.typeOfIndex(block), block_mcv, operand_mcv); - } - } - return self.brVoid(block); -} - -fn brVoid(self: *Self, block: Air.Inst.Index) !void { - const block_data = self.blocks.getPtr(block).?; - - // Emit a jump with a relocation. It will be patched up after the block ends. - try block_data.relocs.append(self.gpa, try self.addInst(.{ - .tag = .b, - .data = .{ .inst = undefined }, // populated later through performReloc - })); -} - -fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; - - const dead = !is_volatile and self.liveness.isUnused(inst); - const result: MCValue = if (dead) .dead else result: { - if (outputs.len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - - const output_constraint: ?[]const u8 = for (outputs) |output| { - if (output != .none) { - return self.fail("TODO implement codegen for non-expr asm", .{}); - } - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - break constraint; - } else null; - - for (inputs) |input| { - const input_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { - return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); - } - const reg_name = constraint[1 .. constraint.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail("unrecognized register: '{s}'", .{reg_name}); - - const arg_mcv = try self.resolveInst(input); - try self.register_manager.getReg(reg, null); - try self.genSetReg(self.typeOf(input), reg, arg_mcv); - } - - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - // TODO honor these - } - } - - const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; - - if (mem.eql(u8, asm_source, "svc #0")) { - _ = try self.addInst(.{ - .tag = .svc, - .data = .{ .imm24 = 0 }, - }); - } else { - return self.fail("TODO implement support for more arm assembly instructions", .{}); - } - - if (output_constraint) |output| { - if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail("unrecognized asm output constraint: '{s}'", .{output}); - } - const reg_name = output[2 .. output.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail("unrecognized register: '{s}'", .{reg_name}); - - break :result MCValue{ .register = reg }; - } else { - break :result MCValue{ .none = {} }; - } - }; - - simple: { - var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - var buf_index: usize = 0; - for (outputs) |output| { - if (output == .none) continue; - - if (buf_index >= buf.len) break :simple; - buf[buf_index] = output; - buf_index += 1; - } - if (buf_index + inputs.len > buf.len) break :simple; - @memcpy(buf[buf_index..][0..inputs.len], inputs); - return self.finishAir(inst, result, buf); - } - var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); - for (outputs) |output| { - if (output == .none) continue; - - bt.feed(output); - } - for (inputs) |input| { - bt.feed(input); - } - return bt.finishAir(result); -} - -fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { - try self.ensureProcessDeathCapacity(operand_count + 1); - return BigTomb{ - .function = self, - .inst = inst, - .lbt = self.liveness.iterateBigTomb(inst), - }; -} - -/// Sets the value without any modifications to register allocation metadata or stack allocation metadata. -fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { - switch (loc) { - .none => return, - .register => |reg| return self.genSetReg(ty, reg, val), - .stack_offset => |off| return self.genSetStack(ty, off, val), - .memory => { - return self.fail("TODO implement setRegOrMem for memory", .{}); - }, - else => unreachable, - } -} - -fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const abi_size: u32 = @intCast(ty.abiSize(zcu)); - switch (mcv) { - .dead => unreachable, - .unreach, .none => return, // Nothing to do. - .undef => { - if (!self.wantSafety()) - return; // The already existing value will do just fine. - // TODO Upgrade this to a memset call when we have that available. - switch (abi_size) { - 1 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), - 2 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), - 4 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), - else => try self.genInlineMemset( - .{ .ptr_stack_offset = stack_offset }, - .{ .immediate = 0xaa }, - .{ .immediate = abi_size }, - ), - } - }, - .cpsr_flags, - .immediate, - .ptr_stack_offset, - => { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - }, - .register => |reg| { - switch (abi_size) { - 1, 4 => { - const offset = if (math.cast(u12, stack_offset)) |imm| blk: { - break :blk Instruction.Offset.imm(imm); - } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }), .none); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .strb, - 4 => .str, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .rr_offset = .{ - .rt = reg, - .rn = .fp, - .offset = .{ - .offset = offset, - .positive = false, - }, - } }, - }); - }, - 2 => { - const offset = if (stack_offset <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(stack_offset)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset })); - - _ = try self.addInst(.{ - .tag = .strh, - .data = .{ .rr_extra_offset = .{ - .rt = reg, - .rn = .fp, - .offset = .{ - .offset = offset, - .positive = false, - }, - } }, - }); - }, - else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), - } - }, - .register_c_flag, - .register_v_flag, - => |reg| { - const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); - - const wrapped_ty = ty.fieldType(0, zcu); - try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }); - - const overflow_bit_ty = ty.fieldType(1, zcu); - const overflow_bit_offset: u32 = @intCast(ty.structFieldOffset(1, zcu)); - const cond_reg = try self.register_manager.allocReg(null, gp); - - // C flag: movcs reg, #1 - // V flag: movvs reg, #1 - _ = try self.addInst(.{ - .tag = .mov, - .cond = switch (mcv) { - .register_c_flag => .cs, - .register_v_flag => .vs, - else => unreachable, - }, - .data = .{ .r_op_mov = .{ - .rd = cond_reg, - .op = Instruction.Operand.fromU32(1).?, - } }, - }); - - try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ - .register = cond_reg, - }); - }, - .memory, - .stack_argument_offset, - .stack_offset, - => { - switch (mcv) { - .stack_offset => |off| { - if (stack_offset == off) - return; // Copy stack variable to itself; nothing to do. - }, - else => {}, - } - - if (abi_size <= 4) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - } else { - const ptr_ty = try pt.singleMutPtrType(ty); - - // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); - const src_reg = regs[0]; - const dst_reg = regs[1]; - const len_reg = regs[2]; - const count_reg = regs[3]; - const tmp_reg = regs[4]; - - switch (mcv) { - .stack_offset => |off| { - // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); - }, - .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }), - .stack_argument_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .r_stack_offset = .{ - .rt = src_reg, - .stack_offset = off, - } }, - }); - }, - else => unreachable, - } - - // sub dst_reg, fp, #stack_offset - try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); - - // mov len, #abi_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - } -} - -fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - switch (mcv) { - .dead => unreachable, - .unreach, .none => return, // Nothing to do. - .undef => { - if (!self.wantSafety()) - return; // The already existing value will do just fine. - // Write the debug undefined value. - return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }); - }, - .ptr_stack_offset => |off| { - // TODO: maybe addressing from sp instead of fp - const op = Instruction.Operand.fromU32(off) orelse - return self.fail("TODO larger stack offsets", .{}); - - _ = try self.addInst(.{ - .tag = .sub, - .data = .{ .rr_op = .{ - .rd = reg, - .rn = .fp, - .op = op, - } }, - }); - }, - .cpsr_flags => |condition| { - const zero = Instruction.Operand.imm(0, 0); - const one = Instruction.Operand.imm(1, 0); - - // mov reg, 0 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = zero, - } }, - }); - - // moveq reg, 1 - _ = try self.addInst(.{ - .tag = .mov, - .cond = condition, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = one, - } }, - }); - }, - .immediate => |x| { - if (Instruction.Operand.fromU32(x)) |op| { - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = op, - } }, - }); - } else if (Instruction.Operand.fromU32(~x)) |op| { - _ = try self.addInst(.{ - .tag = .mvn, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = op, - } }, - }); - } else if (x <= math.maxInt(u16)) { - if (self.target.cpu.has(.arm, .has_v7)) { - _ = try self.addInst(.{ - .tag = .movw, - .data = .{ .r_imm16 = .{ - .rd = reg, - .imm16 = @intCast(x), - } }, - }); - } else { - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = Instruction.Operand.imm(@truncate(x), 0), - } }, - }); - _ = try self.addInst(.{ - .tag = .orr, - .data = .{ .rr_op = .{ - .rd = reg, - .rn = reg, - .op = Instruction.Operand.imm(@truncate(x >> 8), 12), - } }, - }); - } - } else { - // TODO write constant to code and load - // relative to pc - if (self.target.cpu.has(.arm, .has_v7)) { - // immediate: 0xaaaabbbb - // movw reg, #0xbbbb - // movt reg, #0xaaaa - _ = try self.addInst(.{ - .tag = .movw, - .data = .{ .r_imm16 = .{ - .rd = reg, - .imm16 = @truncate(x), - } }, - }); - _ = try self.addInst(.{ - .tag = .movt, - .data = .{ .r_imm16 = .{ - .rd = reg, - .imm16 = @truncate(x >> 16), - } }, - }); - } else { - // immediate: 0xaabbccdd - // mov reg, #0xaa - // orr reg, reg, #0xbb, 24 - // orr reg, reg, #0xcc, 16 - // orr reg, reg, #0xdd, 8 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = Instruction.Operand.imm(@truncate(x), 0), - } }, - }); - _ = try self.addInst(.{ - .tag = .orr, - .data = .{ .rr_op = .{ - .rd = reg, - .rn = reg, - .op = Instruction.Operand.imm(@truncate(x >> 8), 12), - } }, - }); - _ = try self.addInst(.{ - .tag = .orr, - .data = .{ .rr_op = .{ - .rd = reg, - .rn = reg, - .op = Instruction.Operand.imm(@truncate(x >> 16), 8), - } }, - }); - _ = try self.addInst(.{ - .tag = .orr, - .data = .{ .rr_op = .{ - .rd = reg, - .rn = reg, - .op = Instruction.Operand.imm(@truncate(x >> 24), 4), - } }, - }); - } - } - }, - .register => |src_reg| { - // If the registers are the same, nothing to do. - if (src_reg.id() == reg.id()) - return; - - // mov reg, src_reg - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .r_op_mov = .{ - .rd = reg, - .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none), - } }, - }); - }, - .register_c_flag => unreachable, // doesn't fit into a register - .register_v_flag => unreachable, // doesn't fit into a register - .memory => |addr| { - // The value is in memory at a hard-coded address. - // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(ty, reg, .{ .immediate = @intCast(addr) }); - try self.genLdrRegister(reg, reg, ty); - }, - .stack_offset => |off| { - // TODO: maybe addressing from sp instead of fp - const abi_size: u32 = @intCast(ty.abiSize(zcu)); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb else .ldrb, - 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh else .ldrh, - 3, 4 => .ldr, - else => unreachable, - }; - - const extra_offset = switch (abi_size) { - 1 => ty.isSignedInt(zcu), - 2 => true, - 3, 4 => false, - else => unreachable, - }; - - if (extra_offset) { - const offset = if (off <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.usize, MCValue{ .immediate = off })); - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .rr_extra_offset = .{ - .rt = reg, - .rn = .fp, - .offset = .{ - .offset = offset, - .positive = false, - }, - } }, - }); - } else { - const offset = if (off <= math.maxInt(u12)) blk: { - break :blk Instruction.Offset.imm(@intCast(off)); - } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.usize, MCValue{ .immediate = off }), .none); - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .rr_offset = .{ - .rt = reg, - .rn = .fp, - .offset = .{ - .offset = offset, - .positive = false, - }, - } }, - }); - } - }, - .stack_argument_offset => |off| { - const abi_size = ty.abiSize(zcu); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument, - 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument, - 3, 4 => .ldr_stack_argument, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .r_stack_offset = .{ - .rt = reg, - .stack_offset = off, - } }, - }); - }, - } -} - -fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const pt = self.pt; - const abi_size: u32 = @intCast(ty.abiSize(pt.zcu)); - switch (mcv) { - .dead => unreachable, - .none, .unreach => return, - .undef => { - if (!self.wantSafety()) - return; // The already existing value will do just fine. - // TODO Upgrade this to a memset call when we have that available. - switch (abi_size) { - 1 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }), - 2 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }), - 4 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), - else => return self.fail("TODO implement memset", .{}), - } - }, - .register => |reg| { - switch (abi_size) { - 1, 4 => { - const offset = if (math.cast(u12, stack_offset)) |imm| blk: { - break :blk Instruction.Offset.imm(imm); - } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }), .none); - - const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .strb, - 4 => .str, - else => unreachable, - }; - - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .rr_offset = .{ - .rt = reg, - .rn = .sp, - .offset = .{ .offset = offset }, - } }, - }); - }, - 2 => { - const offset = if (stack_offset <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(stack_offset)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset })); - - _ = try self.addInst(.{ - .tag = .strh, - .data = .{ .rr_extra_offset = .{ - .rt = reg, - .rn = .sp, - .offset = .{ .offset = offset }, - } }, - }); - }, - else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), - } - }, - .register_c_flag, - .register_v_flag, - => { - return self.fail("TODO implement genSetStack {}", .{mcv}); - }, - .stack_offset, - .memory, - .stack_argument_offset, - => { - if (abi_size <= 4) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); - } else { - const ptr_ty = try pt.singleMutPtrType(ty); - - // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); - const src_reg = regs[0]; - const dst_reg = regs[1]; - const len_reg = regs[2]; - const count_reg = regs[3]; - const tmp_reg = regs[4]; - - switch (mcv) { - .stack_offset => |off| { - // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); - }, - .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }), - .stack_argument_offset => |off| { - _ = try self.addInst(.{ - .tag = .ldr_ptr_stack_argument, - .data = .{ .r_stack_offset = .{ - .rt = src_reg, - .stack_offset = off, - } }, - }); - }, - else => unreachable, - } - - // add dst_reg, sp, #stack_offset - const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(stack_offset)) |x| x else { - return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); - }; - _ = try self.addInst(.{ - .tag = .add, - .data = .{ .rr_op = .{ - .rd = dst_reg, - .rn = .sp, - .op = dst_offset_op, - } }, - }); - - // mov len, #abi_size - try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); - - // memcpy(src, dst, len) - try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); - } - }, - .cpsr_flags, - .immediate, - .ptr_stack_offset, - => { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); - }, - } -} - -fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(ty_op.operand); - if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; - - const operand_lock = switch (operand) { - .register, - .register_c_flag, - .register_v_flag, - => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - - const dest_ty = self.typeOfIndex(inst); - const dest = try self.allocRegOrMem(dest_ty, true, inst); - try self.setRegOrMem(dest_ty, dest, operand); - break :result dest; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const ptr_ty = self.typeOf(ty_op.operand); - const ptr = try self.resolveInst(ty_op.operand); - const array_ty = ptr_ty.childType(zcu); - const array_len: u32 = @intCast(array_ty.arrayLen(zcu)); - - const stack_offset = try self.allocMem(8, .@"8", inst); - try self.genSetStack(ptr_ty, stack_offset, ptr); - try self.genSetStack(Type.usize, stack_offset - 4, .{ .immediate = array_len }); - break :result MCValue{ .stack_offset = stack_offset }; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{ - self.target.cpu.arch, - }); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{ - self.target.cpu.arch, - }); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - _ = extra; - - return self.fail("TODO implement airCmpxchg for {}", .{ - self.target.cpu.arch, - }); -} - -fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); -} - -fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); -} - -fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { - _ = inst; - _ = order; - return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); -} - -fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { - if (safety) { - // TODO if the value is undef, write 0xaa bytes to dest - } else { - // TODO if the value is undef, don't lower this instruction - } - _ = inst; - return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); -} - -fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); -} - -fn airMemmove(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch}); -} - -fn airTagName(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { - _ = operand; - return self.fail("TODO implement airTagName for arm", .{}); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const operand = try self.resolveInst(un_op); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { - _ = operand; - return self.fail("TODO implement airErrorName for arm", .{}); - }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); -} - -fn airSplat(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for arm", .{}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn airSelect(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for arm", .{}); - return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); -} - -fn airShuffleOne(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airShuffleOne for arm", .{}); -} - -fn airShuffleTwo(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airShuffleTwo for arm", .{}); -} - -fn airReduce(self: *Self, inst: Air.Inst.Index) !void { - const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for arm", .{}); - return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); -} - -fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const vector_ty = self.typeOfIndex(inst); - const len = vector_ty.vectorLen(zcu); - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]); - const result: MCValue = res: { - if (self.liveness.isUnused(inst)) break :res MCValue.dead; - return self.fail("TODO implement airAggregateInit for arm", .{}); - }; - - if (elements.len <= Air.Liveness.bpi - 1) { - var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - @memcpy(buf[0..elements.len], elements); - return self.finishAir(inst, result, buf); - } - var bt = try self.iterateBigTomb(inst, elements.len); - for (elements) |elem| { - bt.feed(elem); - } - return bt.finishAir(result); -} - -fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; - _ = extra; - - return self.fail("TODO implement airUnionInit for arm", .{}); -} - -fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { - const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; - return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); -} - -fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { - return self.fail("TODO implement airMulAdd for arm", .{}); - }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); -} - -fn airTry(self: *Self, inst: Air.Inst.Index) !void { - const pt = self.pt; - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); - const result: MCValue = result: { - const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; - const error_union_ty = self.typeOf(pl_op.operand); - const error_union_size: u32 = @intCast(error_union_ty.abiSize(pt.zcu)); - const error_union_align = error_union_ty.abiAlignment(pt.zcu); - - // The error union will die in the body. However, we need the - // error union after the body in order to extract the payload - // of the error union, so we create a copy of it - const error_union_copy = try self.allocMem(error_union_size, error_union_align, null); - try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self)); - - const is_err_result = try self.isErr(error_union_bind, error_union_ty); - const reloc = try self.condBr(is_err_result); - - try self.genBody(body); - try self.performReloc(reloc); - - break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null); - }; - return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); -} - -fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); - const body = self.air.extra.items[extra.end..][0..extra.data.body_len]; - _ = body; - return self.fail("TODO implement airTryPtr for arm", .{}); - // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); -} - -fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { - const pt = self.pt; - const zcu = pt.zcu; - - // If the type has no codegen bits, no need to store it. - const inst_ty = self.typeOf(inst); - if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !inst_ty.isError(zcu)) - return MCValue{ .none = {} }; - - const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, pt)).?); - - return self.getResolvedInstValue(inst_index); -} - -fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { - // Treat each stack item as a "layer" on top of the previous one. - var i: usize = self.branch_stack.items.len; - while (true) { - i -= 1; - if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { - assert(mcv != .dead); - return mcv; - } - } -} - -fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { - const pt = self.pt; - const mcv: MCValue = switch (try codegen.genTypedValue( - self.bin_file, - pt, - self.src_loc, - val, - self.target, - )) { - .mcv => |mcv| switch (mcv) { - .none => .none, - .undef => .undef, - .load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO - .immediate => |imm| .{ .immediate = @truncate(imm) }, - .memory => |addr| .{ .memory = addr }, - }, - .fail => |msg| { - self.err_msg = msg; - return error.CodegenFail; - }, - }; - return mcv; -} - -const CallMCValues = struct { - args: []MCValue, - return_value: MCValue, - stack_byte_count: u32, - stack_align: u32, - - fn deinit(self: *CallMCValues, func: *Self) void { - func.gpa.free(self.args); - self.* = undefined; - } -}; - -/// Caller must call `CallMCValues.deinit`. -fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { - const pt = self.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const fn_info = zcu.typeToFunc(fn_ty).?; - const cc = fn_info.cc; - var result: CallMCValues = .{ - .args = try self.gpa.alloc(MCValue, fn_info.param_types.len), - // These undefined values must be populated before returning from this function. - .return_value = undefined, - .stack_byte_count = undefined, - .stack_align = undefined, - }; - errdefer self.gpa.free(result.args); - - const ret_ty = fn_ty.fnReturnType(zcu); - - switch (cc) { - .naked => { - assert(result.args.len == 0); - result.return_value = .{ .unreach = {} }; - result.stack_byte_count = 0; - result.stack_align = 1; - return result; - }, - .arm_aapcs => { - // ARM Procedure Call Standard, Chapter 6.5 - var ncrn: usize = 0; // Next Core Register Number - var nsaa: u32 = 0; // Next stacked argument address - - if (ret_ty.zigTypeTag(zcu) == .noreturn) { - result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - result.return_value = .{ .none = {} }; - } else { - const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu)); - // TODO handle cases where multiple registers are used - if (ret_ty_size <= 4) { - result.return_value = .{ .register = c_abi_int_return_regs[0] }; - } else { - // The result is returned by reference, not by - // value. This means that r0 will contain the - // address of where this function should write the - // result into. - result.return_value = .{ .stack_offset = 0 }; - ncrn = 1; - } - } - - for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { - if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8") - ncrn = std.mem.alignForward(usize, ncrn, 2); - - const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu)); - if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) { - if (param_size <= 4) { - result_arg.* = .{ .register = c_abi_int_param_regs[ncrn] }; - ncrn += 1; - } else { - return self.fail("TODO MCValues with multiple registers", .{}); - } - } else if (ncrn < 4 and nsaa == 0) { - return self.fail("TODO MCValues split between registers and stack", .{}); - } else { - ncrn = 4; - if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8") - nsaa = std.mem.alignForward(u32, nsaa, 8); - - result_arg.* = .{ .stack_argument_offset = nsaa }; - nsaa += param_size; - } - } - - result.stack_byte_count = nsaa; - result.stack_align = 8; - }, - .auto => { - if (ret_ty.zigTypeTag(zcu) == .noreturn) { - result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { - result.return_value = .{ .none = {} }; - } else { - const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu)); - if (ret_ty_size == 0) { - assert(ret_ty.isError(zcu)); - result.return_value = .{ .immediate = 0 }; - } else if (ret_ty_size <= 4) { - result.return_value = .{ .register = .r0 }; - } else { - // The result is returned by reference, not by - // value. This means that r0 will contain the - // address of where this function should write the - // result into. - result.return_value = .{ .stack_offset = 0 }; - } - } - - var stack_offset: u32 = 0; - - for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { - if (Type.fromInterned(ty).abiSize(zcu) > 0) { - const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu)); - const param_alignment = Type.fromInterned(ty).abiAlignment(zcu); - - stack_offset = @intCast(param_alignment.forward(stack_offset)); - result_arg.* = .{ .stack_argument_offset = stack_offset }; - stack_offset += param_size; - } else { - result_arg.* = .{ .none = {} }; - } - } - - result.stack_byte_count = stack_offset; - result.stack_align = 8; - }, - else => return self.fail("TODO implement function parameters for {} on arm", .{cc}), - } - - return result; -} - -/// TODO support scope overrides. Also note this logic is duplicated with `Zcu.wantSafety`. -fn wantSafety(self: *Self) bool { - return switch (self.bin_file.comp.root_mod.optimize_mode) { - .Debug => true, - .ReleaseSafe => true, - .ReleaseFast => false, - .ReleaseSmall => false, - }; -} - -fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { - @branchHint(.cold); - const zcu = self.pt.zcu; - const func = zcu.funcInfo(self.func_index); - const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args); - return zcu.codegenFailMsg(func.owner_nav, msg); -} - -fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { - @branchHint(.cold); - const zcu = self.pt.zcu; - const func = zcu.funcInfo(self.func_index); - return zcu.codegenFailMsg(func.owner_nav, msg); -} - -fn parseRegName(name: []const u8) ?Register { - if (@hasDecl(Register, "parseRegName")) { - return Register.parseRegName(name); - } - return std.meta.stringToEnum(Register, name); -} - -fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - return self.air.typeOf(inst, &self.pt.zcu.intern_pool); -} - -fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - return self.air.typeOfIndex(inst, &self.pt.zcu.intern_pool); -} diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig deleted file mode 100644 index 9872d9f4c8e64efa5f848c68ee1cccc7c10b56d8..0000000000000000000000000000000000000000 --- a/src/arch/arm/Emit.zig +++ /dev/null @@ -1,714 +0,0 @@ -//! This file contains the functionality for lowering AArch32 MIR into -//! machine code - -const Emit = @This(); -const builtin = @import("builtin"); -const std = @import("std"); -const math = std.math; -const Mir = @import("Mir.zig"); -const bits = @import("bits.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); -const Type = @import("../../Type.zig"); -const ErrorMsg = Zcu.ErrorMsg; -const Target = std.Target; -const assert = std.debug.assert; -const Instruction = bits.Instruction; -const Register = bits.Register; -const log = std.log.scoped(.aarch32_emit); -const CodeGen = @import("CodeGen.zig"); - -mir: Mir, -bin_file: *link.File, -debug_output: link.File.DebugInfoOutput, -target: *const std.Target, -err_msg: ?*ErrorMsg = null, -src_loc: Zcu.LazySrcLoc, -code: *std.ArrayListUnmanaged(u8), - -prev_di_line: u32, -prev_di_column: u32, -/// Relative to the beginning of `code`. -prev_di_pc: usize, - -/// The amount of stack space consumed by the saved callee-saved -/// registers in bytes -saved_regs_stack_space: u32, - -/// The final stack frame size of the function (already aligned to the -/// respective stack alignment). Does not include prologue stack space. -stack_size: u32, - -/// The branch type of every branch -branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .empty, -/// For every forward branch, maps the target instruction to a list of -/// branches which branch to this target instruction -branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .empty, -/// For backward branches: stores the code offset of the target -/// instruction -/// -/// For forward branches: stores the code offset of the branch -/// instruction -code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, - -const InnerError = error{ - OutOfMemory, - EmitFail, -}; - -const BranchType = enum { - b, - - fn default(tag: Mir.Inst.Tag) BranchType { - return switch (tag) { - .b => .b, - else => unreachable, - }; - } -}; - -pub fn emitMir(emit: *Emit) InnerError!void { - const mir_tags = emit.mir.instructions.items(.tag); - - // Find smallest lowerings for branch instructions - try emit.lowerBranches(); - - // Emit machine code - for (mir_tags, 0..) |tag, index| { - const inst = @as(u32, @intCast(index)); - switch (tag) { - .add => try emit.mirDataProcessing(inst), - .adds => try emit.mirDataProcessing(inst), - .@"and" => try emit.mirDataProcessing(inst), - .cmp => try emit.mirDataProcessing(inst), - .eor => try emit.mirDataProcessing(inst), - .mov => try emit.mirDataProcessing(inst), - .mvn => try emit.mirDataProcessing(inst), - .orr => try emit.mirDataProcessing(inst), - .rsb => try emit.mirDataProcessing(inst), - .sub => try emit.mirDataProcessing(inst), - .subs => try emit.mirDataProcessing(inst), - - .sub_sp_scratch_r4 => try emit.mirSubStackPointer(inst), - - .asr => try emit.mirShift(inst), - .lsl => try emit.mirShift(inst), - .lsr => try emit.mirShift(inst), - - .b => try emit.mirBranch(inst), - - .undefined_instruction => try emit.mirUndefinedInstruction(), - .bkpt => try emit.mirExceptionGeneration(inst), - - .blx => try emit.mirBranchExchange(inst), - .bx => try emit.mirBranchExchange(inst), - - .dbg_line => try emit.mirDbgLine(inst), - - .dbg_prologue_end => try emit.mirDebugPrologueEnd(), - - .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), - - .ldr => try emit.mirLoadStore(inst), - .ldrb => try emit.mirLoadStore(inst), - .str => try emit.mirLoadStore(inst), - .strb => try emit.mirLoadStore(inst), - - .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst), - .ldr_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrb_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrh_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst), - .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst), - - .ldrh => try emit.mirLoadStoreExtra(inst), - .ldrsb => try emit.mirLoadStoreExtra(inst), - .ldrsh => try emit.mirLoadStoreExtra(inst), - .strh => try emit.mirLoadStoreExtra(inst), - - .movw => try emit.mirSpecialMove(inst), - .movt => try emit.mirSpecialMove(inst), - - .mul => try emit.mirMultiply(inst), - .smulbb => try emit.mirMultiply(inst), - - .smull => try emit.mirMultiplyLong(inst), - .umull => try emit.mirMultiplyLong(inst), - - .nop => try emit.mirNop(), - - .pop => try emit.mirBlockDataTransfer(inst), - .push => try emit.mirBlockDataTransfer(inst), - - .svc => try emit.mirSupervisorCall(inst), - - .sbfx => try emit.mirBitFieldExtract(inst), - .ubfx => try emit.mirBitFieldExtract(inst), - } - } -} - -pub fn deinit(emit: *Emit) void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - - var iter = emit.branch_forward_origins.valueIterator(); - while (iter.next()) |origin_list| { - origin_list.deinit(gpa); - } - - emit.branch_types.deinit(gpa); - emit.branch_forward_origins.deinit(gpa); - emit.code_offset_mapping.deinit(gpa); - emit.* = undefined; -} - -fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { - assert(std.mem.isAlignedGeneric(i64, offset, 4)); // misaligned offset - - switch (tag) { - .b => { - if (std.math.cast(i24, @divExact(offset, 4))) |_| { - return BranchType.b; - } else { - return emit.fail("TODO support larger branches", .{}); - } - }, - else => unreachable, - } -} - -fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { - const tag = emit.mir.instructions.items(.tag)[inst]; - - if (isBranch(tag)) { - switch (emit.branch_types.get(inst).?) { - .b => return 4, - } - } - - switch (tag) { - .dbg_line, - .dbg_epilogue_begin, - .dbg_prologue_end, - => return 0, - - .sub_sp_scratch_r4 => { - const imm32 = emit.mir.instructions.items(.data)[inst].imm32; - - if (imm32 == 0) { - return 0 * 4; - } else if (Instruction.Operand.fromU32(imm32) != null) { - // sub - return 1 * 4; - } else if (emit.target.cpu.has(.arm, .has_v7)) { - // movw; movt; sub - return 3 * 4; - } else { - // mov; orr; orr; orr; sub - return 5 * 4; - } - }, - - else => return 4, - } -} - -fn isBranch(tag: Mir.Inst.Tag) bool { - return switch (tag) { - .b => true, - else => false, - }; -} - -fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { - const tag = emit.mir.instructions.items(.tag)[inst]; - - switch (tag) { - .b => return emit.mir.instructions.items(.data)[inst].inst, - else => unreachable, - } -} - -fn lowerBranches(emit: *Emit) !void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - const mir_tags = emit.mir.instructions.items(.tag); - - // First pass: Note down all branches and their target - // instructions, i.e. populate branch_types, - // branch_forward_origins, and code_offset_mapping - // - // TODO optimization opportunity: do this in codegen while - // generating MIR - for (mir_tags, 0..) |tag, index| { - const inst = @as(u32, @intCast(index)); - if (isBranch(tag)) { - const target_inst = emit.branchTarget(inst); - - // Remember this branch instruction - try emit.branch_types.put(gpa, inst, BranchType.default(tag)); - - // Forward branches require some extra stuff: We only - // know their offset once we arrive at the target - // instruction. Therefore, we need to be able to - // access the branch instruction when we visit the - // target instruction in order to manipulate its type - // etc. - if (target_inst > inst) { - // Remember the branch instruction index - try emit.code_offset_mapping.put(gpa, inst, 0); - - if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| { - try origin_list.append(gpa, inst); - } else { - var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty; - try origin_list.append(gpa, inst); - try emit.branch_forward_origins.put(gpa, target_inst, origin_list); - } - } - - // Remember the target instruction index so that we - // update the real code offset in all future passes - // - // putNoClobber may not be used as the put operation - // may clobber the entry when multiple branches branch - // to the same target instruction - try emit.code_offset_mapping.put(gpa, target_inst, 0); - } - } - - // Further passes: Until all branches are lowered, interate - // through all instructions and calculate new offsets and - // potentially new branch types - var all_branches_lowered = false; - while (!all_branches_lowered) { - all_branches_lowered = true; - var current_code_offset: usize = 0; - - for (mir_tags, 0..) |tag, index| { - const inst = @as(u32, @intCast(index)); - - // If this instruction contained in the code offset - // mapping (when it is a target of a branch or if it is a - // forward branch), update the code offset - if (emit.code_offset_mapping.getPtr(inst)) |offset| { - offset.* = current_code_offset; - } - - // If this instruction is a backward branch, calculate the - // offset, which may potentially update the branch type - if (isBranch(tag)) { - const target_inst = emit.branchTarget(inst); - if (target_inst < inst) { - const target_offset = emit.code_offset_mapping.get(target_inst).?; - const offset = @as(i64, @intCast(target_offset)) - @as(i64, @intCast(current_code_offset + 8)); - const branch_type = emit.branch_types.getPtr(inst).?; - const optimal_branch_type = try emit.optimalBranchType(tag, offset); - if (branch_type.* != optimal_branch_type) { - branch_type.* = optimal_branch_type; - all_branches_lowered = false; - } - - log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset }); - } - } - - // If this instruction is the target of one or more - // forward branches, calculate the offset, which may - // potentially update the branch type - if (emit.branch_forward_origins.get(inst)) |origin_list| { - for (origin_list.items) |forward_branch_inst| { - const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst]; - const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?; - const offset = @as(i64, @intCast(current_code_offset)) - @as(i64, @intCast(forward_branch_inst_offset + 8)); - const branch_type = emit.branch_types.getPtr(forward_branch_inst).?; - const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset); - if (branch_type.* != optimal_branch_type) { - branch_type.* = optimal_branch_type; - all_branches_lowered = false; - } - - log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset }); - } - } - - // Increment code offset - current_code_offset += emit.instructionSize(inst); - } - } -} - -fn writeInstruction(emit: *Emit, instruction: Instruction) !void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - const endian = emit.target.cpu.arch.endian(); - std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian); -} - -fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { - @branchHint(.cold); - assert(emit.err_msg == null); - const comp = emit.bin_file.comp; - const gpa = comp.gpa; - emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args); - return error.EmitFail; -} - -fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { - const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(self.prev_di_line)); - const delta_pc: usize = self.code.items.len - self.prev_di_pc; - switch (self.debug_output) { - .dwarf => |dw| { - try dw.advancePCAndLine(delta_line, delta_pc); - self.prev_di_line = line; - self.prev_di_column = column; - self.prev_di_pc = self.code.items.len; - }, - .plan9 => |dbg_out| { - if (delta_pc <= 0) return; // only do this when the pc changes - - var aw: std.io.Writer.Allocating = .fromArrayList(self.bin_file.comp.gpa, &dbg_out.dbg_line); - const bw = &aw.interface; - defer dbg_out.dbg_line = aw.toArrayList(); - - // increasing the line number - try link.File.Plan9.changeLine(bw, delta_line); - // increasing the pc - const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta; - if (d_pc_p9 > 0) { - // minus one because if its the last one, we want to leave space to change the line which is one pc quanta - try bw.writeByte(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta); - const dbg_line = aw.getWritten(); - if (dbg_out.pcop_change_index) |pci| dbg_line[pci] += 1; - dbg_out.pcop_change_index = @intCast(dbg_line.len - 1); - } else if (d_pc_p9 == 0) { - // we don't need to do anything, because adding the pc quanta does it for us - } else unreachable; - if (dbg_out.start_line == null) - dbg_out.start_line = self.prev_di_line; - dbg_out.end_line = line; - // only do this if the pc changed - self.prev_di_line = line; - self.prev_di_column = column; - self.prev_di_pc = self.code.items.len; - }, - .none => {}, - } -} - -fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - - switch (tag) { - .add, - .adds, - .@"and", - .eor, - .orr, - .rsb, - .sub, - .subs, - => { - const rr_op = emit.mir.instructions.items(.data)[inst].rr_op; - switch (tag) { - .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)), - else => unreachable, - } - }, - .cmp => { - const r_op_cmp = emit.mir.instructions.items(.data)[inst].r_op_cmp; - try emit.writeInstruction(Instruction.cmp(cond, r_op_cmp.rn, r_op_cmp.op)); - }, - .mov, - .mvn, - => { - const r_op_mov = emit.mir.instructions.items(.data)[inst].r_op_mov; - switch (tag) { - .mov => try emit.writeInstruction(Instruction.mov(cond, r_op_mov.rd, r_op_mov.op)), - .mvn => try emit.writeInstruction(Instruction.mvn(cond, r_op_mov.rd, r_op_mov.op)), - else => unreachable, - } - }, - else => unreachable, - } -} - -fn mirSubStackPointer(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const imm32 = emit.mir.instructions.items(.data)[inst].imm32; - - switch (tag) { - .sub_sp_scratch_r4 => { - if (imm32 == 0) return; - - const operand = Instruction.Operand.fromU32(imm32) orelse blk: { - const scratch: Register = .r4; - - if (emit.target.cpu.has(.arm, .has_v7)) { - try emit.writeInstruction(Instruction.movw(cond, scratch, @as(u16, @truncate(imm32)))); - try emit.writeInstruction(Instruction.movt(cond, scratch, @as(u16, @truncate(imm32 >> 16)))); - } else { - try emit.writeInstruction(Instruction.mov(cond, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32)), 0))); - try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32 >> 8)), 12))); - try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32 >> 16)), 8))); - try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32 >> 24)), 4))); - } - - break :blk Instruction.Operand.reg(scratch, Instruction.Operand.Shift.none); - }; - - try emit.writeInstruction(Instruction.sub(cond, .sp, .sp, operand)); - }, - else => unreachable, - } -} - -fn mirShift(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const rr_shift = emit.mir.instructions.items(.data)[inst].rr_shift; - - switch (tag) { - .asr => try emit.writeInstruction(Instruction.asr(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)), - .lsl => try emit.writeInstruction(Instruction.lsl(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)), - .lsr => try emit.writeInstruction(Instruction.lsr(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)), - else => unreachable, - } -} - -fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const target_inst = emit.mir.instructions.items(.data)[inst].inst; - - const offset = @as(i64, @intCast(emit.code_offset_mapping.get(target_inst).?)) - @as(i64, @intCast(emit.code.items.len + 8)); - const branch_type = emit.branch_types.get(inst).?; - - switch (branch_type) { - .b => switch (tag) { - .b => try emit.writeInstruction(Instruction.b(cond, @as(i26, @intCast(offset)))), - else => unreachable, - }, - } -} - -fn mirUndefinedInstruction(emit: *Emit) !void { - try emit.writeInstruction(Instruction.undefinedInstruction()); -} - -fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const imm16 = emit.mir.instructions.items(.data)[inst].imm16; - - switch (tag) { - .bkpt => try emit.writeInstruction(Instruction.bkpt(imm16)), - else => unreachable, - } -} - -fn mirBranchExchange(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const reg = emit.mir.instructions.items(.data)[inst].reg; - - switch (tag) { - .blx => try emit.writeInstruction(Instruction.blx(cond, reg)), - .bx => try emit.writeInstruction(Instruction.bx(cond, reg)), - else => unreachable, - } -} - -fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; - - switch (tag) { - .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), - else => unreachable, - } -} - -fn mirDebugPrologueEnd(emit: *Emit) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setPrologueEnd(); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} - -fn mirDebugEpilogueBegin(emit: *Emit) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setEpilogueBegin(); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} - -fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const rr_offset = emit.mir.instructions.items(.data)[inst].rr_offset; - - switch (tag) { - .ldr => try emit.writeInstruction(Instruction.ldr(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)), - .ldrb => try emit.writeInstruction(Instruction.ldrb(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)), - .str => try emit.writeInstruction(Instruction.str(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)), - .strb => try emit.writeInstruction(Instruction.strb(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)), - else => unreachable, - } -} - -fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const r_stack_offset = emit.mir.instructions.items(.data)[inst].r_stack_offset; - const rt = r_stack_offset.rt; - - const raw_offset = emit.stack_size + emit.saved_regs_stack_space + r_stack_offset.stack_offset; - switch (tag) { - .ldr_ptr_stack_argument => { - const operand = Instruction.Operand.fromU32(raw_offset) orelse - return emit.fail("TODO mirLoadStack larger offsets", .{}); - - try emit.writeInstruction(Instruction.add(cond, rt, .sp, operand)); - }, - .ldr_stack_argument, - .ldrb_stack_argument, - => { - const offset = if (raw_offset <= math.maxInt(u12)) blk: { - break :blk Instruction.Offset.imm(@as(u12, @intCast(raw_offset))); - } else return emit.fail("TODO mirLoadStack larger offsets", .{}); - - switch (tag) { - .ldr_stack_argument => try emit.writeInstruction(Instruction.ldr(cond, rt, .sp, .{ .offset = offset })), - .ldrb_stack_argument => try emit.writeInstruction(Instruction.ldrb(cond, rt, .sp, .{ .offset = offset })), - else => unreachable, - } - }, - .ldrh_stack_argument, - .ldrsb_stack_argument, - .ldrsh_stack_argument, - => { - const offset = if (raw_offset <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@as(u8, @intCast(raw_offset))); - } else return emit.fail("TODO mirLoadStack larger offsets", .{}); - - switch (tag) { - .ldrh_stack_argument => try emit.writeInstruction(Instruction.ldrh(cond, rt, .sp, .{ .offset = offset })), - .ldrsb_stack_argument => try emit.writeInstruction(Instruction.ldrsb(cond, rt, .sp, .{ .offset = offset })), - .ldrsh_stack_argument => try emit.writeInstruction(Instruction.ldrsh(cond, rt, .sp, .{ .offset = offset })), - else => unreachable, - } - }, - else => unreachable, - } -} - -fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const rr_extra_offset = emit.mir.instructions.items(.data)[inst].rr_extra_offset; - - switch (tag) { - .ldrh => try emit.writeInstruction(Instruction.ldrh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), - .ldrsb => try emit.writeInstruction(Instruction.ldrsb(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), - .ldrsh => try emit.writeInstruction(Instruction.ldrsh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), - .strh => try emit.writeInstruction(Instruction.strh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), - else => unreachable, - } -} - -fn mirSpecialMove(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const r_imm16 = emit.mir.instructions.items(.data)[inst].r_imm16; - - switch (tag) { - .movw => try emit.writeInstruction(Instruction.movw(cond, r_imm16.rd, r_imm16.imm16)), - .movt => try emit.writeInstruction(Instruction.movt(cond, r_imm16.rd, r_imm16.imm16)), - else => unreachable, - } -} - -fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const rrr = emit.mir.instructions.items(.data)[inst].rrr; - - switch (tag) { - .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)), - .smulbb => try emit.writeInstruction(Instruction.smulbb(cond, rrr.rd, rrr.rn, rrr.rm)), - else => unreachable, - } -} - -fn mirMultiplyLong(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const rrrr = emit.mir.instructions.items(.data)[inst].rrrr; - - switch (tag) { - .smull => try emit.writeInstruction(Instruction.smull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)), - .umull => try emit.writeInstruction(Instruction.umull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)), - else => unreachable, - } -} - -fn mirNop(emit: *Emit) !void { - try emit.writeInstruction(Instruction.nop()); -} - -fn mirBlockDataTransfer(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const register_list = emit.mir.instructions.items(.data)[inst].register_list; - - switch (tag) { - .pop => try emit.writeInstruction(Instruction.ldm(cond, .sp, true, register_list)), - .push => try emit.writeInstruction(Instruction.stmdb(cond, .sp, true, register_list)), - else => unreachable, - } -} - -fn mirSupervisorCall(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const imm24 = emit.mir.instructions.items(.data)[inst].imm24; - - switch (tag) { - .svc => try emit.writeInstruction(Instruction.svc(cond, imm24)), - else => unreachable, - } -} - -fn mirBitFieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const cond = emit.mir.instructions.items(.cond)[inst]; - const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; - const rd = rr_lsb_width.rd; - const rn = rr_lsb_width.rn; - const lsb = rr_lsb_width.lsb; - const width = rr_lsb_width.width; - - switch (tag) { - .sbfx => try emit.writeInstruction(Instruction.sbfx(cond, rd, rn, lsb, width)), - .ubfx => try emit.writeInstruction(Instruction.ubfx(cond, rd, rn, lsb, width)), - else => unreachable, - } -} diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig deleted file mode 100644 index 5b7585a2ca9cd9294e1ef3f6afbfb72f1b7cfea2..0000000000000000000000000000000000000000 --- a/src/arch/arm/Mir.zig +++ /dev/null @@ -1,340 +0,0 @@ -//! Machine Intermediate Representation. -//! This data is produced by ARM Codegen or ARM assembly parsing -//! These instructions have a 1:1 correspondence with machine code instructions -//! for the target. MIR can be lowered to source-annotated textual assembly code -//! instructions, or it can be lowered to machine code. -//! The main purpose of MIR is to postpone the assignment of offsets until Isel, -//! so that, for example, the smaller encodings of jump instructions can be used. - -const Mir = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; - -const bits = @import("bits.zig"); -const Register = bits.Register; -const InternPool = @import("../../InternPool.zig"); -const Emit = @import("Emit.zig"); -const codegen = @import("../../codegen.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); - -max_end_stack: u32, -saved_regs_stack_space: u32, - -instructions: std.MultiArrayList(Inst).Slice, -/// The meaning of this data is determined by `Inst.Tag` value. -extra: []const u32, - -pub const Inst = struct { - tag: Tag, - cond: bits.Condition = .al, - /// The meaning of this depends on `tag`. - data: Data, - - pub const Tag = enum(u16) { - /// Add - add, - /// Add, update condition flags - adds, - /// Bitwise AND - @"and", - /// Arithmetic Shift Right - asr, - /// Branch - b, - /// Undefined instruction - undefined_instruction, - /// Breakpoint - bkpt, - /// Branch with Link and Exchange - blx, - /// Branch and Exchange - bx, - /// Compare - cmp, - /// Pseudo-instruction: End of prologue - dbg_prologue_end, - /// Pseudo-instruction: Beginning of epilogue - dbg_epilogue_begin, - /// Pseudo-instruction: Update debug line - dbg_line, - /// Bitwise Exclusive OR - eor, - /// Load Register - ldr, - /// Pseudo-instruction: Load pointer to stack argument offset - ldr_ptr_stack_argument, - /// Load Register - ldr_stack_argument, - /// Load Register Byte - ldrb, - /// Load Register Byte - ldrb_stack_argument, - /// Load Register Halfword - ldrh, - /// Load Register Halfword - ldrh_stack_argument, - /// Load Register Signed Byte - ldrsb, - /// Load Register Signed Byte - ldrsb_stack_argument, - /// Load Register Signed Halfword - ldrsh, - /// Load Register Signed Halfword - ldrsh_stack_argument, - /// Logical Shift Left - lsl, - /// Logical Shift Right - lsr, - /// Move - mov, - /// Move - movw, - /// Move Top - movt, - /// Multiply - mul, - /// Bitwise NOT - mvn, - /// No Operation - nop, - /// Bitwise OR - orr, - /// Pop multiple registers from Stack - pop, - /// Push multiple registers to Stack - push, - /// Reverse Subtract - rsb, - /// Signed Bit Field Extract - sbfx, - /// Signed Multiply (halfwords), bottom half, bottom half - smulbb, - /// Signed Multiply Long - smull, - /// Store Register - str, - /// Store Register Byte - strb, - /// Store Register Halfword - strh, - /// Subtract - sub, - /// Pseudo-instruction: Subtract 32-bit immediate from stack - /// - /// r4 can be used by Emit as a scratch register for loading - /// the immediate - sub_sp_scratch_r4, - /// Subtract, update condition flags - subs, - /// Supervisor Call - svc, - /// Unsigned Bit Field Extract - ubfx, - /// Unsigned Multiply Long - umull, - }; - - /// The position of an MIR instruction within the `Mir` instructions array. - pub const Index = u32; - - /// All instructions have a 8-byte payload, which is contained within - /// this union. `Tag` determines which union field is active, as well as - /// how to interpret the data within. - pub const Data = union { - /// No additional data - /// - /// Used by e.g. nop - nop: void, - /// Another instruction - /// - /// Used by e.g. b - inst: Index, - /// A 16-bit immediate value. - /// - /// Used by e.g. bkpt - imm16: u16, - /// A 24-bit immediate value. - /// - /// Used by e.g. svc - imm24: u24, - /// A 32-bit immediate value. - /// - /// Used by e.g. sub_sp_scratch_r0 - imm32: u32, - /// Index into `extra`. Meaning of what can be found there is context-dependent. - /// - /// Used by e.g. load_memory - payload: u32, - /// A register - /// - /// Used by e.g. blx - reg: Register, - /// A register and a stack offset - /// - /// Used by e.g. ldr_stack_argument - r_stack_offset: struct { - rt: Register, - stack_offset: u32, - }, - /// A register and a 16-bit unsigned immediate - /// - /// Used by e.g. movw - r_imm16: struct { - rd: Register, - imm16: u16, - }, - /// A register and an operand - /// - /// Used by mov and mvn - r_op_mov: struct { - rd: Register, - op: bits.Instruction.Operand, - }, - /// A register and an operand - /// - /// Used by cmp - r_op_cmp: struct { - rn: Register, - op: bits.Instruction.Operand, - }, - /// Two registers and a shift amount - /// - /// Used by e.g. lsl - rr_shift: struct { - rd: Register, - rm: Register, - shift_amount: bits.Instruction.ShiftAmount, - }, - /// Two registers and an operand - /// - /// Used by e.g. sub - rr_op: struct { - rd: Register, - rn: Register, - op: bits.Instruction.Operand, - }, - /// Two registers and an offset - /// - /// Used by e.g. ldr - rr_offset: struct { - rt: Register, - rn: Register, - offset: bits.Instruction.OffsetArgs, - }, - /// Two registers and an extra load/store offset - /// - /// Used by e.g. ldrh - rr_extra_offset: struct { - rt: Register, - rn: Register, - offset: bits.Instruction.ExtraLoadStoreOffsetArgs, - }, - /// Two registers and a lsb (range 0-31) and a width (range - /// 1-32) - /// - /// Used by e.g. sbfx - rr_lsb_width: struct { - rd: Register, - rn: Register, - lsb: u5, - width: u6, - }, - /// Three registers - /// - /// Used by e.g. mul - rrr: struct { - rd: Register, - rn: Register, - rm: Register, - }, - /// Four registers - /// - /// Used by e.g. smull - rrrr: struct { - rdlo: Register, - rdhi: Register, - rn: Register, - rm: Register, - }, - /// An unordered list of registers - /// - /// Used by e.g. push - register_list: bits.Instruction.RegisterList, - /// Debug info: line and column - /// - /// Used by e.g. dbg_line - dbg_line_column: struct { - line: u32, - column: u32, - }, - }; - - // Make sure we don't accidentally make instructions bigger than expected. - // Note that in safety builds, Zig is allowed to insert a secret field for safety checks. - comptime { - if (!std.debug.runtime_safety) { - assert(@sizeOf(Data) == 8); - } - } -}; - -pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { - mir.instructions.deinit(gpa); - gpa.free(mir.extra); - mir.* = undefined; -} - -pub fn emit( - mir: Mir, - lf: *link.File, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - func_index: InternPool.Index, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { - const zcu = pt.zcu; - const func = zcu.funcInfo(func_index); - const nav = func.owner_nav; - const mod = zcu.navFileScope(nav).mod.?; - var e: Emit = .{ - .mir = mir, - .bin_file = lf, - .debug_output = debug_output, - .target = &mod.resolved_target.result, - .src_loc = src_loc, - .code = code, - .prev_di_pc = 0, - .prev_di_line = func.lbrace_line, - .prev_di_column = func.lbrace_column, - .stack_size = mir.max_end_stack, - .saved_regs_stack_space = mir.saved_regs_stack_space, - }; - defer e.deinit(); - e.emitMir() catch |err| switch (err) { - error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), - else => |e1| return e1, - }; -} - -/// Returns the requested data, as well as the new index which is at the start of the -/// trailers for the object. -pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { - const fields = std.meta.fields(T); - var i: usize = index; - var result: T = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => mir.extra[i], - i32 => @as(i32, @bitCast(mir.extra[i])), - else => @compileError("bad field type"), - }; - i += 1; - } - return .{ - .data = result, - .end = i, - }; -} diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig deleted file mode 100644 index b29bb523abd20527a3e3839af9bc1cfee372f3fa..0000000000000000000000000000000000000000 --- a/src/arch/arm/abi.zig +++ /dev/null @@ -1,187 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const bits = @import("bits.zig"); -const Register = bits.Register; -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const Type = @import("../../Type.zig"); -const Zcu = @import("../../Zcu.zig"); - -pub const Class = union(enum) { - memory, - byval, - i32_array: u8, - i64_array: u8, - - fn arrSize(total_size: u64, arr_size: u64) Class { - const count = @as(u8, @intCast(std.mem.alignForward(u64, total_size, arr_size) / arr_size)); - if (arr_size == 32) { - return .{ .i32_array = count }; - } else { - return .{ .i64_array = count }; - } - } -}; - -pub const Context = enum { ret, arg }; - -pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); - - var maybe_float_bits: ?u16 = null; - const max_byval_size = 512; - const ip = &zcu.intern_pool; - switch (ty.zigTypeTag(zcu)) { - .@"struct" => { - const bit_size = ty.bitSize(zcu); - if (ty.containerLayout(zcu) == .@"packed") { - if (bit_size > 64) return .memory; - return .byval; - } - if (bit_size > max_byval_size) return .memory; - const float_count = countFloats(ty, zcu, &maybe_float_bits); - if (float_count <= byval_float_count) return .byval; - - const fields = ty.structFieldCount(zcu); - var i: u32 = 0; - while (i < fields) : (i += 1) { - const field_ty = ty.fieldType(i, zcu); - const field_alignment = ty.fieldAlignment(i, zcu); - const field_size = field_ty.bitSize(zcu); - if (field_size > 32 or field_alignment.compare(.gt, .@"32")) { - return Class.arrSize(bit_size, 64); - } - } - return Class.arrSize(bit_size, 32); - }, - .@"union" => { - const bit_size = ty.bitSize(zcu); - const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { - if (bit_size > 64) return .memory; - return .byval; - } - if (bit_size > max_byval_size) return .memory; - const float_count = countFloats(ty, zcu, &maybe_float_bits); - if (float_count <= byval_float_count) return .byval; - - for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { - if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or - ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32")) - { - return Class.arrSize(bit_size, 64); - } - } - return Class.arrSize(bit_size, 32); - }, - .bool, .float => return .byval, - .int => { - // TODO this is incorrect for _BitInt(128) but implementing - // this correctly makes implementing compiler-rt impossible. - // const bit_size = ty.bitSize(zcu); - // if (bit_size > 64) return .memory; - return .byval; - }, - .@"enum", .error_set => { - const bit_size = ty.bitSize(zcu); - if (bit_size > 64) return .memory; - return .byval; - }, - .vector => { - const bit_size = ty.bitSize(zcu); - // TODO is this controlled by a cpu feature? - if (ctx == .ret and bit_size > 128) return .memory; - if (bit_size > 512) return .memory; - return .byval; - }, - .optional => { - assert(ty.isPtrLikeOptional(zcu)); - return .byval; - }, - .pointer => { - assert(!ty.isSlice(zcu)); - return .byval; - }, - .error_union, - .frame, - .@"anyframe", - .noreturn, - .void, - .type, - .comptime_float, - .comptime_int, - .undefined, - .null, - .@"fn", - .@"opaque", - .enum_literal, - .array, - => unreachable, - } -} - -const byval_float_count = 4; -fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 { - const ip = &zcu.intern_pool; - const target = zcu.getTarget(); - const invalid = std.math.maxInt(u32); - switch (ty.zigTypeTag(zcu)) { - .@"union" => { - const union_obj = zcu.typeToUnion(ty).?; - var max_count: u32 = 0; - for (union_obj.field_types.get(ip)) |field_ty| { - const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits); - if (field_count == invalid) return invalid; - if (field_count > max_count) max_count = field_count; - if (max_count > byval_float_count) return invalid; - } - return max_count; - }, - .@"struct" => { - const fields_len = ty.structFieldCount(zcu); - var count: u32 = 0; - var i: u32 = 0; - while (i < fields_len) : (i += 1) { - const field_ty = ty.fieldType(i, zcu); - const field_count = countFloats(field_ty, zcu, maybe_float_bits); - if (field_count == invalid) return invalid; - count += field_count; - if (count > byval_float_count) return invalid; - } - return count; - }, - .float => { - const float_bits = maybe_float_bits.* orelse { - const float_bits = ty.floatBits(target); - if (float_bits != 32 and float_bits != 64) return invalid; - maybe_float_bits.* = float_bits; - return 1; - }; - if (ty.floatBits(target) == float_bits) return 1; - return invalid; - }, - .void => return 0, - else => return invalid, - } -} - -pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; -pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 }; - -pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; -pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; - -const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; -pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); - -// Register classes -const RegisterBitSet = RegisterManager.RegisterBitSet; -pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = 0, - .end = caller_preserved_regs.len + callee_preserved_regs.len, - }, true); - break :blk set; - }; -}; diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig deleted file mode 100644 index 0388ce8c631c23db425dd1913cea9a789d88c615..0000000000000000000000000000000000000000 --- a/src/arch/arm/bits.zig +++ /dev/null @@ -1,1566 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const testing = std.testing; - -/// The condition field specifies the flags necessary for an -/// Instruction to be executed -pub const Condition = enum(u4) { - /// equal - eq, - /// not equal - ne, - /// unsigned higher or same - cs, - /// unsigned lower - cc, - /// negative - mi, - /// positive or zero - pl, - /// overflow - vs, - /// no overflow - vc, - /// unsigned higer - hi, - /// unsigned lower or same - ls, - /// greater or equal - ge, - /// less than - lt, - /// greater than - gt, - /// less than or equal - le, - /// always - al, - - /// Converts a std.math.CompareOperator into a condition flag, - /// i.e. returns the condition that is true iff the result of the - /// comparison is true. Assumes signed comparison - pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { - return switch (op) { - .gte => .ge, - .gt => .gt, - .neq => .ne, - .lt => .lt, - .lte => .le, - .eq => .eq, - }; - } - - /// Converts a std.math.CompareOperator into a condition flag, - /// i.e. returns the condition that is true iff the result of the - /// comparison is true. Assumes unsigned comparison - pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { - return switch (op) { - .gte => .cs, - .gt => .hi, - .neq => .ne, - .lt => .cc, - .lte => .ls, - .eq => .eq, - }; - } - - /// Returns the condition which is true iff the given condition is - /// false (if such a condition exists) - pub fn negate(cond: Condition) Condition { - return switch (cond) { - .eq => .ne, - .ne => .eq, - .cs => .cc, - .cc => .cs, - .mi => .pl, - .pl => .mi, - .vs => .vc, - .vc => .vs, - .hi => .ls, - .ls => .hi, - .ge => .lt, - .lt => .ge, - .gt => .le, - .le => .gt, - .al => unreachable, - }; - } -}; - -test "condition from CompareOperator" { - try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq)); - try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq)); - - try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt)); - try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt)); - - try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte)); - try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte)); -} - -test "negate condition" { - try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate()); - try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate()); -} - -/// Represents a register in the ARM instruction set architecture -pub const Register = enum(u5) { - r0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15, - - /// Argument / result / scratch register 1 - a1, - /// Argument / result / scratch register 2 - a2, - /// Argument / scratch register 3 - a3, - /// Argument / scratch register 4 - a4, - /// Variable-register 1 - v1, - /// Variable-register 2 - v2, - /// Variable-register 3 - v3, - /// Variable-register 4 - v4, - /// Variable-register 5 - v5, - /// Platform register - v6, - /// Variable-register 7 - v7, - /// Frame pointer or Variable-register 8 - fp, - /// Intra-Procedure-call scratch register - ip, - /// Stack pointer - sp, - /// Link register - lr, - /// Program counter - pc, - - /// Returns the unique 4-bit ID of this register which is used in - /// the machine code - pub fn id(reg: Register) u4 { - return @truncate(@intFromEnum(reg)); - } - - pub fn dwarfNum(reg: Register) u4 { - return reg.id(); - } -}; - -test "Register.id" { - try testing.expectEqual(@as(u4, 15), Register.r15.id()); - try testing.expectEqual(@as(u4, 15), Register.pc.id()); -} - -/// Program status registers containing flags, mode bits and other -/// vital information -pub const Psr = enum { - cpsr, - spsr, -}; - -/// Represents an instruction in the ARM instruction set architecture -pub const Instruction = union(enum) { - data_processing: packed struct { - // Note to self: The order of the fields top-to-bottom is - // right-to-left in the actual 32-bit int representation - op2: u12, - rd: u4, - rn: u4, - s: u1, - opcode: u4, - i: u1, - fixed: u2 = 0b00, - cond: u4, - }, - multiply: packed struct { - rn: u4, - fixed_1: u4 = 0b1001, - rm: u4, - ra: u4, - rd: u4, - set_cond: u1, - accumulate: u1, - fixed_2: u6 = 0b000000, - cond: u4, - }, - multiply_long: packed struct { - rn: u4, - fixed_1: u4 = 0b1001, - rm: u4, - rdlo: u4, - rdhi: u4, - set_cond: u1, - accumulate: u1, - unsigned: u1, - fixed_2: u5 = 0b00001, - cond: u4, - }, - signed_multiply_halfwords: packed struct { - rn: u4, - fixed_1: u1 = 0b0, - n: u1, - m: u1, - fixed_2: u1 = 0b1, - rm: u4, - fixed_3: u4 = 0b0000, - rd: u4, - fixed_4: u8 = 0b00010110, - cond: u4, - }, - integer_saturating_arithmetic: packed struct { - rm: u4, - fixed_1: u8 = 0b0000_0101, - rd: u4, - rn: u4, - fixed_2: u1 = 0b0, - opc: u2, - fixed_3: u5 = 0b00010, - cond: u4, - }, - bit_field_extract: packed struct { - rn: u4, - fixed_1: u3 = 0b101, - lsb: u5, - rd: u4, - widthm1: u5, - fixed_2: u1 = 0b1, - unsigned: u1, - fixed_3: u5 = 0b01111, - cond: u4, - }, - single_data_transfer: packed struct { - offset: u12, - rd: u4, - rn: u4, - load_store: u1, - write_back: u1, - byte_word: u1, - up_down: u1, - pre_post: u1, - imm: u1, - fixed: u2 = 0b01, - cond: u4, - }, - extra_load_store: packed struct { - imm4l: u4, - fixed_1: u1 = 0b1, - op2: u2, - fixed_2: u1 = 0b1, - imm4h: u4, - rt: u4, - rn: u4, - o1: u1, - write_back: u1, - imm: u1, - up_down: u1, - pre_index: u1, - fixed_3: u3 = 0b000, - cond: u4, - }, - block_data_transfer: packed struct { - register_list: u16, - rn: u4, - load_store: u1, - write_back: u1, - psr_or_user: u1, - up_down: u1, - pre_post: u1, - fixed: u3 = 0b100, - cond: u4, - }, - branch: packed struct { - offset: u24, - link: u1, - fixed: u3 = 0b101, - cond: u4, - }, - branch_exchange: packed struct { - rn: u4, - fixed_1: u1 = 0b1, - link: u1, - fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, - cond: u4, - }, - supervisor_call: packed struct { - comment: u24, - fixed: u4 = 0b1111, - cond: u4, - }, - undefined_instruction: packed struct { - imm32: u32 = 0xe7ffdefe, - }, - breakpoint: packed struct { - imm4: u4, - fixed_1: u4 = 0b0111, - imm12: u12, - fixed_2_and_cond: u12 = 0b1110_0001_0010, - }, - - /// Represents the possible operations which can be performed by a - /// Data Processing instruction - const Opcode = enum(u4) { - // Rd := Op1 AND Op2 - @"and", - // Rd := Op1 EOR Op2 - eor, - // Rd := Op1 - Op2 - sub, - // Rd := Op2 - Op1 - rsb, - // Rd := Op1 + Op2 - add, - // Rd := Op1 + Op2 + C - adc, - // Rd := Op1 - Op2 + C - 1 - sbc, - // Rd := Op2 - Op1 + C - 1 - rsc, - // set condition codes on Op1 AND Op2 - tst, - // set condition codes on Op1 EOR Op2 - teq, - // set condition codes on Op1 - Op2 - cmp, - // set condition codes on Op1 + Op2 - cmn, - // Rd := Op1 OR Op2 - orr, - // Rd := Op2 - mov, - // Rd := Op1 AND NOT Op2 - bic, - // Rd := NOT Op2 - mvn, - }; - - /// Represents the second operand to a data processing instruction - /// which can either be content from a register or an immediate - /// value - pub const Operand = union(enum) { - register: packed struct { - rm: u4, - shift: u8, - }, - immediate: packed struct { - imm: u8, - rotate: u4, - }, - - /// Represents multiple ways a register can be shifted. A - /// register can be shifted by a specific immediate value or - /// by the contents of another register - pub const Shift = union(enum) { - immediate: packed struct { - fixed: u1 = 0b0, - typ: u2, - amount: u5, - }, - register: packed struct { - fixed_1: u1 = 0b1, - typ: u2, - fixed_2: u1 = 0b0, - rs: u4, - }, - - pub const Type = enum(u2) { - logical_left, - logical_right, - arithmetic_right, - rotate_right, - }; - - pub const none = Shift{ - .immediate = .{ - .amount = 0, - .typ = 0, - }, - }; - - pub fn toU8(self: Shift) u8 { - return switch (self) { - .register => |v| @as(u8, @bitCast(v)), - .immediate => |v| @as(u8, @bitCast(v)), - }; - } - - pub fn reg(rs: Register, typ: Type) Shift { - return Shift{ - .register = .{ - .rs = rs.id(), - .typ = @intFromEnum(typ), - }, - }; - } - - pub fn imm(amount: u5, typ: Type) Shift { - return Shift{ - .immediate = .{ - .amount = amount, - .typ = @intFromEnum(typ), - }, - }; - } - }; - - pub fn toU12(self: Operand) u12 { - return switch (self) { - .register => |v| @as(u12, @bitCast(v)), - .immediate => |v| @as(u12, @bitCast(v)), - }; - } - - pub fn reg(rm: Register, shift: Shift) Operand { - return Operand{ - .register = .{ - .rm = rm.id(), - .shift = shift.toU8(), - }, - }; - } - - pub fn imm(immediate: u8, rotate: u4) Operand { - return Operand{ - .immediate = .{ - .imm = immediate, - .rotate = rotate, - }, - }; - } - - /// Tries to convert an unsigned 32 bit integer into an - /// immediate operand using rotation. Returns null when there - /// is no conversion - pub fn fromU32(x: u32) ?Operand { - const masks = comptime blk: { - const base_mask: u32 = std.math.maxInt(u8); - var result = [_]u32{0} ** 16; - for (&result, 0..) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i); - break :blk result; - }; - - return for (masks, 0..) |mask, i| { - if (x & mask == x) { - break Operand{ - .immediate = .{ - .imm = @as(u8, @intCast(std.math.rotl(u32, x, 2 * i))), - .rotate = @as(u4, @intCast(i)), - }, - }; - } - } else null; - } - }; - - pub const AddressingMode = enum { - /// [, ] - /// - /// Address = Rn + offset - offset, - /// [, ]! - /// - /// Address = Rn + offset - /// Rn = Rn + offset - pre_index, - /// [], - /// - /// Address = Rn - /// Rn = Rn + offset - post_index, - }; - - /// Represents the offset operand of a load or store - /// instruction. Data can be loaded from memory with either an - /// immediate offset or an offset that is stored in some register. - pub const Offset = union(enum) { - immediate: u12, - register: packed struct { - rm: u4, - fixed: u1 = 0b0, - stype: u2, - imm5: u5, - }, - - pub const Shift = union(enum) { - /// No shift - none, - /// Logical shift left - lsl: u5, - /// Logical shift right - lsr: u5, - /// Arithmetic shift right - asr: u5, - /// Rotate right - ror: u5, - /// Rotate right one bit, with extend - rrx, - }; - - pub const none = Offset{ - .immediate = 0, - }; - - pub fn toU12(self: Offset) u12 { - return switch (self) { - .register => |v| @as(u12, @bitCast(v)), - .immediate => |v| v, - }; - } - - pub fn reg(rm: Register, shift: Shift) Offset { - return Offset{ - .register = .{ - .rm = rm.id(), - .stype = switch (shift) { - .none => 0b00, - .lsl => 0b00, - .lsr => 0b01, - .asr => 0b10, - .ror => 0b11, - .rrx => 0b11, - }, - .imm5 = switch (shift) { - .none => 0, - .lsl => |n| n, - .lsr => |n| n, - .asr => |n| n, - .ror => |n| n, - .rrx => 0, - }, - }, - }; - } - - pub fn imm(immediate: u12) Offset { - return Offset{ - .immediate = immediate, - }; - } - }; - - /// Represents the offset operand of an extra load or store - /// instruction. - pub const ExtraLoadStoreOffset = union(enum) { - immediate: u8, - register: u4, - - pub const none = ExtraLoadStoreOffset{ - .immediate = 0, - }; - - pub fn reg(register: Register) ExtraLoadStoreOffset { - return ExtraLoadStoreOffset{ - .register = register.id(), - }; - } - - pub fn imm(immediate: u8) ExtraLoadStoreOffset { - return ExtraLoadStoreOffset{ - .immediate = immediate, - }; - } - }; - - /// Represents the register list operand to a block data transfer - /// instruction - pub const RegisterList = packed struct { - r0: bool = false, - r1: bool = false, - r2: bool = false, - r3: bool = false, - r4: bool = false, - r5: bool = false, - r6: bool = false, - r7: bool = false, - r8: bool = false, - r9: bool = false, - r10: bool = false, - r11: bool = false, - r12: bool = false, - r13: bool = false, - r14: bool = false, - r15: bool = false, - }; - - pub fn toU32(self: Instruction) u32 { - return switch (self) { - .data_processing => |v| @as(u32, @bitCast(v)), - .multiply => |v| @as(u32, @bitCast(v)), - .multiply_long => |v| @as(u32, @bitCast(v)), - .signed_multiply_halfwords => |v| @as(u32, @bitCast(v)), - .integer_saturating_arithmetic => |v| @as(u32, @bitCast(v)), - .bit_field_extract => |v| @as(u32, @bitCast(v)), - .single_data_transfer => |v| @as(u32, @bitCast(v)), - .extra_load_store => |v| @as(u32, @bitCast(v)), - .block_data_transfer => |v| @as(u32, @bitCast(v)), - .branch => |v| @as(u32, @bitCast(v)), - .branch_exchange => |v| @as(u32, @bitCast(v)), - .supervisor_call => |v| @as(u32, @bitCast(v)), - .undefined_instruction => |v| v.imm32, - .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), - }; - } - - // Helper functions for the "real" functions below - - fn dataProcessing( - cond: Condition, - opcode: Opcode, - s: u1, - rd: Register, - rn: Register, - op2: Operand, - ) Instruction { - return Instruction{ - .data_processing = .{ - .cond = @intFromEnum(cond), - .i = @intFromBool(op2 == .immediate), - .opcode = @intFromEnum(opcode), - .s = s, - .rn = rn.id(), - .rd = rd.id(), - .op2 = op2.toU12(), - }, - }; - } - - fn specialMov( - cond: Condition, - rd: Register, - imm: u16, - top: bool, - ) Instruction { - return Instruction{ - .data_processing = .{ - .cond = @intFromEnum(cond), - .i = 1, - .opcode = if (top) 0b1010 else 0b1000, - .s = 0, - .rn = @as(u4, @truncate(imm >> 12)), - .rd = rd.id(), - .op2 = @as(u12, @truncate(imm)), - }, - }; - } - - fn initMultiply( - cond: Condition, - set_cond: u1, - rd: Register, - rn: Register, - rm: Register, - ra: ?Register, - ) Instruction { - return Instruction{ - .multiply = .{ - .cond = @intFromEnum(cond), - .accumulate = @intFromBool(ra != null), - .set_cond = set_cond, - .rd = rd.id(), - .rn = rn.id(), - .ra = if (ra) |reg| reg.id() else 0b0000, - .rm = rm.id(), - }, - }; - } - - fn multiplyLong( - cond: Condition, - signed: u1, - accumulate: u1, - set_cond: u1, - rdhi: Register, - rdlo: Register, - rm: Register, - rn: Register, - ) Instruction { - return Instruction{ - .multiply_long = .{ - .cond = @intFromEnum(cond), - .unsigned = signed, - .accumulate = accumulate, - .set_cond = set_cond, - .rdlo = rdlo.id(), - .rdhi = rdhi.id(), - .rn = rn.id(), - .rm = rm.id(), - }, - }; - } - - fn signedMultiplyHalfwords( - n: u1, - m: u1, - cond: Condition, - rd: Register, - rn: Register, - rm: Register, - ) Instruction { - return Instruction{ - .signed_multiply_halfwords = .{ - .rn = rn.id(), - .n = n, - .m = m, - .rm = rm.id(), - .rd = rd.id(), - .cond = @intFromEnum(cond), - }, - }; - } - - fn integerSaturationArithmetic( - cond: Condition, - rd: Register, - rm: Register, - rn: Register, - opc: u2, - ) Instruction { - return Instruction{ - .integer_saturating_arithmetic = .{ - .rm = rm.id(), - .rd = rd.id(), - .rn = rn.id(), - .opc = opc, - .cond = @intFromEnum(cond), - }, - }; - } - - fn bitFieldExtract( - unsigned: u1, - cond: Condition, - rd: Register, - rn: Register, - lsb: u5, - width: u6, - ) Instruction { - assert(width > 0 and width <= 32); - return Instruction{ - .bit_field_extract = .{ - .rn = rn.id(), - .lsb = lsb, - .rd = rd.id(), - .widthm1 = @as(u5, @intCast(width - 1)), - .unsigned = unsigned, - .cond = @intFromEnum(cond), - }, - }; - } - - fn singleDataTransfer( - cond: Condition, - rd: Register, - rn: Register, - offset: Offset, - mode: AddressingMode, - positive: bool, - byte_word: u1, - load_store: u1, - ) Instruction { - return Instruction{ - .single_data_transfer = .{ - .cond = @intFromEnum(cond), - .rn = rn.id(), - .rd = rd.id(), - .offset = offset.toU12(), - .load_store = load_store, - .write_back = switch (mode) { - .offset => 0b0, - .pre_index, .post_index => 0b1, - }, - .byte_word = byte_word, - .up_down = @intFromBool(positive), - .pre_post = switch (mode) { - .offset, .pre_index => 0b1, - .post_index => 0b0, - }, - .imm = @intFromBool(offset != .immediate), - }, - }; - } - - fn extraLoadStore( - cond: Condition, - mode: AddressingMode, - positive: bool, - o1: u1, - op2: u2, - rn: Register, - rt: Register, - offset: ExtraLoadStoreOffset, - ) Instruction { - const imm4l: u4 = switch (offset) { - .immediate => |imm| @as(u4, @truncate(imm)), - .register => |reg| reg, - }; - const imm4h: u4 = switch (offset) { - .immediate => |imm| @as(u4, @truncate(imm >> 4)), - .register => 0b0000, - }; - - return Instruction{ - .extra_load_store = .{ - .imm4l = imm4l, - .op2 = op2, - .imm4h = imm4h, - .rt = rt.id(), - .rn = rn.id(), - .o1 = o1, - .write_back = switch (mode) { - .offset => 0b0, - .pre_index, .post_index => 0b1, - }, - .imm = @intFromBool(offset == .immediate), - .up_down = @intFromBool(positive), - .pre_index = switch (mode) { - .offset, .pre_index => 0b1, - .post_index => 0b0, - }, - .cond = @intFromEnum(cond), - }, - }; - } - - fn blockDataTransfer( - cond: Condition, - rn: Register, - reg_list: RegisterList, - pre_post: u1, - up_down: u1, - psr_or_user: u1, - write_back: bool, - load_store: u1, - ) Instruction { - return Instruction{ - .block_data_transfer = .{ - .register_list = @as(u16, @bitCast(reg_list)), - .rn = rn.id(), - .load_store = load_store, - .write_back = @intFromBool(write_back), - .psr_or_user = psr_or_user, - .up_down = up_down, - .pre_post = pre_post, - .cond = @intFromEnum(cond), - }, - }; - } - - fn initBranch(cond: Condition, offset: i26, link: u1) Instruction { - return Instruction{ - .branch = .{ - .cond = @intFromEnum(cond), - .link = link, - .offset = @as(u24, @bitCast(@as(i24, @intCast(offset >> 2)))), - }, - }; - } - - fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { - return Instruction{ - .branch_exchange = .{ - .cond = @intFromEnum(cond), - .link = link, - .rn = rn.id(), - }, - }; - } - - fn supervisorCall(cond: Condition, comment: u24) Instruction { - return Instruction{ - .supervisor_call = .{ - .cond = @intFromEnum(cond), - .comment = comment, - }, - }; - } - - // This instruction has no official mnemonic equivalent so it is public as-is. - pub fn undefinedInstruction() Instruction { - return Instruction{ - .undefined_instruction = .{}, - }; - } - - fn initBreakpoint(imm: u16) Instruction { - return Instruction{ - .breakpoint = .{ - .imm12 = @as(u12, @truncate(imm >> 4)), - .imm4 = @as(u4, @truncate(imm)), - }, - }; - } - - // Public functions replicating assembler syntax as closely as - // possible - - // Data processing - - pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .@"and", 0, rd, rn, op2); - } - - pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .@"and", 1, rd, rn, op2); - } - - pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .eor, 0, rd, rn, op2); - } - - pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .eor, 1, rd, rn, op2); - } - - pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sub, 0, rd, rn, op2); - } - - pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sub, 1, rd, rn, op2); - } - - pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsb, 0, rd, rn, op2); - } - - pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsb, 1, rd, rn, op2); - } - - pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .add, 0, rd, rn, op2); - } - - pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .add, 1, rd, rn, op2); - } - - pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .adc, 0, rd, rn, op2); - } - - pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .adc, 1, rd, rn, op2); - } - - pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sbc, 0, rd, rn, op2); - } - - pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sbc, 1, rd, rn, op2); - } - - pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsc, 0, rd, rn, op2); - } - - pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsc, 1, rd, rn, op2); - } - - pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .tst, 1, .r0, rn, op2); - } - - pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .teq, 1, .r0, rn, op2); - } - - pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .cmp, 1, .r0, rn, op2); - } - - pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .cmn, 1, .r0, rn, op2); - } - - pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .orr, 0, rd, rn, op2); - } - - pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .orr, 1, rd, rn, op2); - } - - pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mov, 0, rd, .r0, op2); - } - - pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mov, 1, rd, .r0, op2); - } - - pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .bic, 0, rd, rn, op2); - } - - pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .bic, 1, rd, rn, op2); - } - - pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mvn, 0, rd, .r0, op2); - } - - pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mvn, 1, rd, .r0, op2); - } - - // Integer Saturating Arithmetic - - pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { - return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); - } - - pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { - return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); - } - - pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { - return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); - } - - pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { - return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); - } - - // movw and movt - - pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { - return specialMov(cond, rd, imm, false); - } - - pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction { - return specialMov(cond, rd, imm, true); - } - - // PSR transfer - - pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { - return Instruction{ - .data_processing = .{ - .cond = @intFromEnum(cond), - .i = 0, - .opcode = if (psr == .spsr) 0b1010 else 0b1000, - .s = 0, - .rn = 0b1111, - .rd = rd.id(), - .op2 = 0b0000_0000_0000, - }, - }; - } - - pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { - return Instruction{ - .data_processing = .{ - .cond = @intFromEnum(cond), - .i = 0, - .opcode = if (psr == .spsr) 0b1011 else 0b1001, - .s = 0, - .rn = 0b1111, - .rd = 0b1111, - .op2 = op.toU12(), - }, - }; - } - - // Multiply - - pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { - return initMultiply(cond, 0, rd, rn, rm, null); - } - - pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { - return initMultiply(cond, 1, rd, rn, rm, null); - } - - pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - return initMultiply(cond, 0, rd, rn, rm, ra); - } - - pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - return initMultiply(cond, 1, rd, rn, rm, ra); - } - - // Multiply long - - pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn); - } - - pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn); - } - - pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn); - } - - pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn); - } - - pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn); - } - - pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn); - } - - pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn); - } - - pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { - return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); - } - - // Signed Multiply (halfwords) - - pub fn smulbb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { - return signedMultiplyHalfwords(0, 0, cond, rd, rn, rm); - } - - pub fn smulbt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { - return signedMultiplyHalfwords(0, 1, cond, rd, rn, rm); - } - - pub fn smultb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { - return signedMultiplyHalfwords(1, 0, cond, rd, rn, rm); - } - - pub fn smultt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { - return signedMultiplyHalfwords(1, 1, cond, rd, rn, rm); - } - - // Bit field extract - - pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction { - return bitFieldExtract(0b1, cond, rd, rn, lsb, width); - } - - pub fn sbfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction { - return bitFieldExtract(0b0, cond, rd, rn, lsb, width); - } - - // Single data transfer - - pub const OffsetArgs = struct { - mode: AddressingMode = .offset, - positive: bool = true, - offset: Offset, - }; - - pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 1); - } - - pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 1); - } - - pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 0); - } - - pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 0); - } - - // Extra load/store - - pub const ExtraLoadStoreOffsetArgs = struct { - mode: AddressingMode = .offset, - positive: bool = true, - offset: ExtraLoadStoreOffset, - }; - - pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.mode, args.positive, 0b0, 0b01, rn, rt, args.offset); - } - - pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b01, rn, rt, args.offset); - } - - pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b11, rn, rt, args.offset); - } - - pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b10, rn, rt, args.offset); - } - - // Block data transfer - - pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); - } - - pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); - } - - pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); - } - - pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); - } - - pub const ldmfa = ldmda; - pub const ldmea = ldmdb; - pub const ldmed = ldmib; - pub const ldmfd = ldmia; - pub const ldm = ldmia; - - pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); - } - - pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); - } - - pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); - } - - pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { - return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); - } - - pub const stmed = stmda; - pub const stmfd = stmdb; - pub const stmfa = stmib; - pub const stmea = stmia; - pub const stm = stmia; - - // Branch - - pub fn b(cond: Condition, offset: i26) Instruction { - return initBranch(cond, offset, 0); - } - - pub fn bl(cond: Condition, offset: i26) Instruction { - return initBranch(cond, offset, 1); - } - - // Branch and exchange - - pub fn bx(cond: Condition, rn: Register) Instruction { - return branchExchange(cond, rn, 0); - } - - pub fn blx(cond: Condition, rn: Register) Instruction { - return branchExchange(cond, rn, 1); - } - - // Supervisor Call - - pub const swi = svc; - - pub fn svc(cond: Condition, comment: u24) Instruction { - return supervisorCall(cond, comment); - } - - // Breakpoint - - pub fn bkpt(imm: u16) Instruction { - return initBreakpoint(imm); - } - - // Aliases - - pub fn nop() Instruction { - return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)); - } - - pub fn pop(cond: Condition, args: anytype) Instruction { - if (@typeInfo(@TypeOf(args)) != .@"struct") { - @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); - } - - if (args.len < 1) { - @compileError("Expected at least one register"); - } else if (args.len == 1) { - const reg = args[0]; - return ldr(cond, reg, .sp, .{ - .mode = .post_index, - .positive = true, - .offset = Offset.imm(4), - }); - } else { - var register_list: u16 = 0; - inline for (args) |arg| { - const reg = @as(Register, arg); - register_list |= @as(u16, 1) << reg.id(); - } - return ldm(cond, .sp, true, @as(RegisterList, @bitCast(register_list))); - } - } - - pub fn push(cond: Condition, args: anytype) Instruction { - if (@typeInfo(@TypeOf(args)) != .@"struct") { - @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); - } - - if (args.len < 1) { - @compileError("Expected at least one register"); - } else if (args.len == 1) { - const reg = args[0]; - return str(cond, reg, .sp, .{ - .mode = .pre_index, - .positive = false, - .offset = Offset.imm(4), - }); - } else { - var register_list: u16 = 0; - inline for (args) |arg| { - const reg = @as(Register, arg); - register_list |= @as(u16, 1) << reg.id(); - } - return stmdb(cond, .sp, true, @as(RegisterList, @bitCast(register_list))); - } - } - - pub const ShiftAmount = union(enum) { - immediate: u5, - register: Register, - - pub fn imm(immediate: u5) ShiftAmount { - return .{ - .immediate = immediate, - }; - } - - pub fn reg(register: Register) ShiftAmount { - return .{ - .register = register, - }; - } - }; - - pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), - .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), - }; - } - - pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), - .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), - }; - } - - pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), - .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), - }; - } - - pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), - .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), - }; - } - - pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), - .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), - }; - } - - pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), - .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), - }; - } - - pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), - .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), - }; - } - - pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { - return switch (shift) { - .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), - .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), - }; - } -}; - -test "serialize instructions" { - const Testcase = struct { - inst: Instruction, - expected: u32, - }; - - const testcases = [_]Testcase{ - .{ // add r0, r0, r0 - .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), - .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, - }, - .{ // mov r4, r2 - .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), - .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, - }, - .{ // mov r0, #42 - .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), - .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, - }, - .{ // mrs r5, cpsr - .inst = Instruction.mrs(.al, .r5, .cpsr), - .expected = 0b1110_00010_0_001111_0101_000000000000, - }, - .{ // mul r0, r1, r2 - .inst = Instruction.mul(.al, .r0, .r1, .r2), - .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001, - }, - .{ // umlal r0, r1, r5, r6 - .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6), - .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101, - }, - .{ // ldr r0, [r2, #42] - .inst = Instruction.ldr(.al, .r0, .r2, .{ - .offset = Instruction.Offset.imm(42), - }), - .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, - }, - .{ // str r0, [r3] - .inst = Instruction.str(.al, .r0, .r3, .{ - .offset = Instruction.Offset.none, - }), - .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, - }, - .{ // strh r1, [r5] - .inst = Instruction.strh(.al, .r1, .r5, .{ - .offset = Instruction.ExtraLoadStoreOffset.none, - }), - .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000, - }, - .{ // b #12 - .inst = Instruction.b(.al, 12), - .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011, - }, - .{ // bl #-4 - .inst = Instruction.bl(.al, -4), - .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111, - }, - .{ // bx lr - .inst = Instruction.bx(.al, .lr), - .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, - }, - .{ // svc #0 - .inst = Instruction.svc(.al, 0), - .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, - }, - .{ // bkpt #42 - .inst = Instruction.bkpt(42), - .expected = 0b1110_0001_0010_000000000010_0111_1010, - }, - .{ // stmdb r9, {r0} - .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), - .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, - }, - .{ // ldmea r4!, {r2, r5} - .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), - .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, - }, - .{ // qadd r0, r7, r8 - .inst = Instruction.qadd(.al, .r0, .r7, .r8), - .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, - }, - .{ // smulbt r0, r0, r0 - .inst = Instruction.smulbt(.al, .r0, .r0, .r0), - .expected = 0b1110_00010110_0000_0000_0000_1_1_0_0_0000, - }, - }; - - for (testcases) |case| { - const actual = case.inst.toU32(); - try testing.expectEqual(case.expected, actual); - } -} - -test "aliases" { - const Testcase = struct { - expected: Instruction, - actual: Instruction, - }; - - const testcases = [_]Testcase{ - .{ // pop { r6 } - .actual = Instruction.pop(.al, .{.r6}), - .expected = Instruction.ldr(.al, .r6, .sp, .{ - .mode = .post_index, - .positive = true, - .offset = Instruction.Offset.imm(4), - }), - }, - .{ // pop { r1, r5 } - .actual = Instruction.pop(.al, .{ .r1, .r5 }), - .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), - }, - .{ // push { r3 } - .actual = Instruction.push(.al, .{.r3}), - .expected = Instruction.str(.al, .r3, .sp, .{ - .mode = .pre_index, - .positive = false, - .offset = Instruction.Offset.imm(4), - }), - }, - .{ // push { r0, r2 } - .actual = Instruction.push(.al, .{ .r0, .r2 }), - .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), - }, - .{ // lsl r4, r5, #5 - .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), - .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( - .r5, - Instruction.Operand.Shift.imm(5, .logical_left), - )), - }, - .{ // asrs r1, r1, r3 - .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), - .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( - .r1, - Instruction.Operand.Shift.reg(.r3, .arithmetic_right), - )), - }, - }; - - for (testcases) |case| { - try testing.expectEqual(case.expected.toU32(), case.actual.toU32()); - } -} diff --git a/src/arch/mips/abi.zig b/src/arch/mips/abi.zig deleted file mode 100644 index 02c4c637a4c362ea4b3a826fbd27b72598fcc2cb..0000000000000000000000000000000000000000 --- a/src/arch/mips/abi.zig +++ /dev/null @@ -1,84 +0,0 @@ -const std = @import("std"); -const Type = @import("../../Type.zig"); -const Zcu = @import("../../Zcu.zig"); -const assert = std.debug.assert; - -pub const Class = union(enum) { - memory, - byval, - i32_array: u8, -}; - -pub const Context = enum { ret, arg }; - -pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { - const target = zcu.getTarget(); - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); - - const max_direct_size = target.ptrBitWidth() * 2; - switch (ty.zigTypeTag(zcu)) { - .@"struct" => { - const bit_size = ty.bitSize(zcu); - if (ty.containerLayout(zcu) == .@"packed") { - if (bit_size > max_direct_size) return .memory; - return .byval; - } - if (bit_size > max_direct_size) return .memory; - // TODO: for bit_size <= 32 using byval is more correct, but that needs inreg argument attribute - const count = @as(u8, @intCast(std.mem.alignForward(u64, bit_size, 32) / 32)); - return .{ .i32_array = count }; - }, - .@"union" => { - const bit_size = ty.bitSize(zcu); - if (ty.containerLayout(zcu) == .@"packed") { - if (bit_size > max_direct_size) return .memory; - return .byval; - } - if (bit_size > max_direct_size) return .memory; - - return .byval; - }, - .bool => return .byval, - .float => return .byval, - .int, .@"enum", .error_set => { - return .byval; - }, - .vector => { - const elem_type = ty.elemType2(zcu); - switch (elem_type.zigTypeTag(zcu)) { - .bool, .int => { - const bit_size = ty.bitSize(zcu); - if (ctx == .ret and bit_size > 128) return .memory; - if (bit_size > 512) return .memory; - // TODO: byval vector arguments with non power of 2 size need inreg attribute - return .byval; - }, - .float => return .memory, - else => unreachable, - } - }, - .optional => { - std.debug.assert(ty.isPtrLikeOptional(zcu)); - return .byval; - }, - .pointer => { - std.debug.assert(!ty.isSlice(zcu)); - return .byval; - }, - .error_union, - .frame, - .@"anyframe", - .noreturn, - .void, - .type, - .comptime_float, - .comptime_int, - .undefined, - .null, - .@"fn", - .@"opaque", - .enum_literal, - .array, - => unreachable, - } -} diff --git a/src/arch/powerpc/CodeGen.zig b/src/arch/powerpc/CodeGen.zig deleted file mode 100644 index 4964fe19f484fc73bef027abcd4bffba99140846..0000000000000000000000000000000000000000 --- a/src/arch/powerpc/CodeGen.zig +++ /dev/null @@ -1,51 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const Air = @import("../../Air.zig"); -const codegen = @import("../../codegen.zig"); -const InternPool = @import("../../InternPool.zig"); -const link = @import("../../link.zig"); -const Zcu = @import("../../Zcu.zig"); - -const assert = std.debug.assert; -const log = std.log.scoped(.codegen); - -pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { - return null; -} - -pub fn generate( - bin_file: *link.File, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - func_index: InternPool.Index, - air: *const Air, - liveness: *const Air.Liveness, -) codegen.CodeGenError!noreturn { - _ = bin_file; - _ = pt; - _ = src_loc; - _ = func_index; - _ = air; - _ = liveness; - - unreachable; -} - -pub fn generateLazy( - bin_file: *link.File, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - lazy_sym: link.File.LazySymbol, - code: *std.ArrayListUnmanaged(u8), - debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { - _ = bin_file; - _ = pt; - _ = src_loc; - _ = lazy_sym; - _ = code; - _ = debug_output; - - unreachable; -} diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 6914e881a0f0108ea1837581649f41909391cfab..5aec8a91abe625cb28b7fe64e26a9ad64b259044 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -436,7 +436,7 @@ const InstTracking = struct { fn trackSpill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { try function.freeValue(inst_tracking.short); inst_tracking.reuseFrame(); - tracking_log.debug("%{f} => {f} (spilled)", .{ inst, inst_tracking.* }); + tracking_log.debug("%{d} => {f} (spilled)", .{ inst, inst_tracking.* }); } fn verifyMaterialize(inst_tracking: InstTracking, target: InstTracking) void { @@ -500,14 +500,14 @@ const InstTracking = struct { else => target.long, } else target.long; inst_tracking.short = target.short; - tracking_log.debug("%{f} => {f} (materialize)", .{ inst, inst_tracking.* }); + tracking_log.debug("%{d} => {f} (materialize)", .{ inst, inst_tracking.* }); } fn resurrect(inst_tracking: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void { switch (inst_tracking.short) { .dead => |die_generation| if (die_generation >= scope_generation) { inst_tracking.reuseFrame(); - tracking_log.debug("%{f} => {f} (resurrect)", .{ inst, inst_tracking.* }); + tracking_log.debug("%{d} => {f} (resurrect)", .{ inst, inst_tracking.* }); }, else => {}, } @@ -517,7 +517,7 @@ const InstTracking = struct { if (inst_tracking.short == .dead) return; try function.freeValue(inst_tracking.short); inst_tracking.short = .{ .dead = function.scope_generation }; - tracking_log.debug("%{f} => {f} (death)", .{ inst, inst_tracking.* }); + tracking_log.debug("%{d} => {f} (death)", .{ inst, inst_tracking.* }); } fn reuse( @@ -528,15 +528,15 @@ const InstTracking = struct { ) void { inst_tracking.short = .{ .dead = function.scope_generation }; if (new_inst) |inst| - tracking_log.debug("%{f} => {f} (reuse %{f})", .{ inst, inst_tracking.*, old_inst }) + tracking_log.debug("%{d} => {f} (reuse %{d})", .{ inst, inst_tracking.*, old_inst }) else - tracking_log.debug("tmp => {f} (reuse %{f})", .{ inst_tracking.*, old_inst }); + tracking_log.debug("tmp => {f} (reuse %{d})", .{ inst_tracking.*, old_inst }); } fn liveOut(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) void { for (inst_tracking.getRegs()) |reg| { if (function.register_manager.isRegFree(reg)) { - tracking_log.debug("%{f} => {f} (live-out)", .{ inst, inst_tracking.* }); + tracking_log.debug("%{d} => {f} (live-out)", .{ inst, inst_tracking.* }); continue; } @@ -563,14 +563,13 @@ const InstTracking = struct { // Perform side-effects of freeValue manually. function.register_manager.freeReg(reg); - tracking_log.debug("%{f} => {f} (live-out %{f})", .{ inst, inst_tracking.*, tracked_inst }); + tracking_log.debug("%{d} => {f} (live-out %{d})", .{ inst, inst_tracking.*, tracked_inst }); } } - pub fn format(inst_tracking: InstTracking, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); - if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try bw.print("|{}| ", .{inst_tracking.long}); - try bw.print("{}", .{inst_tracking.short}); + pub fn format(inst_tracking: InstTracking, writer: *std.io.Writer) std.io.Writer.Error!void { + if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try writer.print("|{}| ", .{inst_tracking.long}); + try writer.print("{}", .{inst_tracking.short}); } }; @@ -934,7 +933,7 @@ const FormatWipMirData = struct { func: *Func, inst: Mir.Inst.Index, }; -fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void { +fn formatWipMir(data: FormatWipMirData, writer: *std.io.Writer) std.io.Writer.Error!void { const pt = data.func.pt; const comp = pt.zcu.comp; var lower: Lower = .{ @@ -957,11 +956,11 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri lower.err_msg.?.deinit(data.func.gpa); lower.err_msg = null; } - try bw.writeAll(lower.err_msg.?.msg); + try writer.writeAll(lower.err_msg.?.msg); return; }, error.OutOfMemory, error.InvalidInstruction => |e| { - try bw.writeAll(switch (e) { + try writer.writeAll(switch (e) { error.OutOfMemory => "Out of memory", error.InvalidInstruction => "CodeGen failed to find a viable instruction.", }); @@ -969,12 +968,12 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri }, else => |e| return e, }).insts) |lowered_inst| { - if (!first) try bw.writeAll("\ndebug(wip_mir): "); - try bw.print(" | {}", .{lowered_inst}); + if (!first) try writer.writeAll("\ndebug(wip_mir): "); + try writer.print(" | {}", .{lowered_inst}); first = false; } } -fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { +fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(FormatWipMirData, formatWipMir) { return .{ .data = .{ .func = func, .inst = inst } }; } @@ -982,10 +981,10 @@ const FormatNavData = struct { ip: *const InternPool, nav_index: InternPool.Nav.Index, }; -fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void { - try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); +fn formatNav(data: FormatNavData, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); } -fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) { +fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(FormatNavData, formatNav) { return .{ .data = .{ .ip = ip, .nav_index = nav_index, @@ -996,27 +995,25 @@ const FormatAirData = struct { func: *Func, inst: Air.Inst.Index, }; -fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void { - comptime assert(fmt.len == 0); - // not acceptable implementation: - // data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness); +fn formatAir(data: FormatAirData, writer: *std.io.Writer) std.io.Writer.Error!void { + // Not acceptable implementation because it ignores `writer`: + //data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness); _ = data; - _ = w; - @panic("TODO: unimplemented"); + _ = writer; + @panic("unimplemented"); } -fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { +fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) { return .{ .data = .{ .func = func, .inst = inst } }; } const FormatTrackingData = struct { func: *Func, }; -fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); +fn formatTracking(data: FormatTrackingData, writer: *std.io.Writer) std.io.Writer.Error!void { var it = data.func.inst_tracking.iterator(); - while (it.next()) |entry| try bw.print("\n%{d} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* }); + while (it.next()) |entry| try writer.print("\n%{d} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* }); } -fn fmtTracking(func: *Func) std.fmt.Formatter(formatTracking) { +fn fmtTracking(func: *Func) std.fmt.Formatter(FormatTrackingData, formatTracking) { return .{ .data = .{ .func = func } }; } @@ -1826,7 +1823,7 @@ fn computeFrameLayout(func: *Func) !FrameLayout { total_alloc_size + 64 + args_frame_size + spill_frame_size + call_frame_size, @intCast(frame_align[@intFromEnum(FrameIndex.base_ptr)].toByteUnits().?), ); - log.debug("frame size: {}", .{acc_frame_size}); + log.debug("frame size: {d}", .{acc_frame_size}); // store the ra at total_size - 8, so it's the very first thing in the stack // relative to the fp diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 4653b6d73b3edf0246f97434afcbf577eb9163a7..43ccf000587aa8a781019bf53a66541b88ddaf5c 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -92,9 +92,8 @@ pub const Inst = struct { }, }; - pub fn format(inst: Inst, bw: *std.io.Writer, comptime fmt: []const u8) !void { - assert(fmt.len == 0); - try bw.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) }); + pub fn format(inst: Inst, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) }); } }; diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 9ca4808f898487171c1936d3258b0ef53e17ed41..dd9529ec3da9269326cc006f063867516669dee3 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -256,15 +256,6 @@ pub const FrameIndex = enum(u32) { pub fn isNamed(fi: FrameIndex) bool { return @intFromEnum(fi) < named_count; } - - pub fn format(fi: FrameIndex, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); - try bw.writeAll("FrameIndex"); - if (fi.isNamed()) - try bw.print(".{s}", .{@tagName(fi)}) - else - try bw.print("({d})", .{@intFromEnum(fi)}); - } }; /// A linker symbol not yet allocated in VM. diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 238bbaee9ee7b2a98745377db9570ecccc20116d..9174a5850e1563d1d86758768a470e65f3638ef4 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { - 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)] }); + 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)] }); } } } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9df6255e5299c59ea2a53dd3a13920e768d0a1bc..5678b36ef92be552f11716a689e2ba15eab115f1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -18,7 +18,7 @@ const Compilation = @import("../../Compilation.zig"); const link = @import("../../link.zig"); const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); -const abi = @import("abi.zig"); +const abi = @import("../../codegen/wasm/abi.zig"); const Alignment = InternPool.Alignment; const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; @@ -1960,7 +1960,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .wasm_memory_size => cg.airWasmMemorySize(inst), .wasm_memory_grow => cg.airWasmMemoryGrow(inst), - .memcpy => cg.airMemcpy(inst), + .memcpy, .memmove => cg.airMemcpy(inst), .ret_addr => cg.airRetAddr(inst), .tag_name => cg.airTagName(inst), @@ -1984,7 +1984,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .c_va_copy, .c_va_end, .c_va_start, - .memmove, => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), .atomic_load => cg.airAtomicLoad(inst), @@ -2047,7 +2046,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { try cg.genInst(inst); if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) { - std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{ + std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{t}')", .{ inst, cg.air.instructions.items(.tag)[@intFromEnum(inst)], }); @@ -2405,10 +2404,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) }); }, else => if (abi_size > 8) { - return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{ - ty.fmt(pt), - abi_size, - }); + return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{ ty.fmt(pt), abi_size }); }, } try cg.emitWValue(lhs); @@ -2597,10 +2593,7 @@ fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WV if (ty.zigTypeTag(zcu) == .int) { return cg.binOpBigInt(lhs, rhs, ty, op); } else { - return cg.fail( - "TODO: Implement binary operation for type: {f}", - .{ty.fmt(pt)}, - ); + return cg.fail("TODO: Implement binary operation for type: {f}", .{ty.fmt(pt)}); } } @@ -3333,7 +3326,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { }, else => unreachable, }, - else => return cg.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag(zcu)}), + else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}), } } diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig deleted file mode 100644 index a1fa8126491def2d9e2717faa0ccedcd2ee24805..0000000000000000000000000000000000000000 --- a/src/arch/wasm/abi.zig +++ /dev/null @@ -1,87 +0,0 @@ -//! Classifies Zig types to follow the C-ABI for Wasm. -//! The convention for Wasm's C-ABI can be found at the tool-conventions repo: -//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md -//! When not targeting the C-ABI, Zig is allowed to do derail from this convention. -//! Note: Above mentioned document is not an official specification, therefore called a convention. - -const std = @import("std"); -const Target = std.Target; -const assert = std.debug.assert; - -const Type = @import("../../Type.zig"); -const Zcu = @import("../../Zcu.zig"); - -/// Defines how to pass a type as part of a function signature, -/// both for parameters as well as return values. -pub const Class = union(enum) { - direct: Type, - indirect, -}; - -/// Classifies a given Zig type to determine how they must be passed -/// or returned as value within a wasm function. -pub fn classifyType(ty: Type, zcu: *const Zcu) Class { - const ip = &zcu.intern_pool; - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); - switch (ty.zigTypeTag(zcu)) { - .int, .@"enum", .error_set => return .{ .direct = ty }, - .float => return .{ .direct = ty }, - .bool => return .{ .direct = ty }, - .vector => return .{ .direct = ty }, - .array => return .indirect, - .optional => { - assert(ty.isPtrLikeOptional(zcu)); - return .{ .direct = ty }; - }, - .pointer => { - assert(!ty.isSlice(zcu)); - return .{ .direct = ty }; - }, - .@"struct" => { - const struct_type = zcu.typeToStruct(ty).?; - if (struct_type.layout == .@"packed") { - return .{ .direct = ty }; - } - if (struct_type.field_types.len > 1) { - // The struct type is non-scalar. - return .indirect; - } - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); - const explicit_align = struct_type.fieldAlign(ip, 0); - if (explicit_align != .none) { - if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) - return .indirect; - } - return classifyType(field_ty, zcu); - }, - .@"union" => { - const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { - return .{ .direct = ty }; - } - const layout = ty.unionGetLayout(zcu); - assert(layout.tag_size == 0); - if (union_obj.field_types.len > 1) return .indirect; - const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); - return classifyType(first_field_ty, zcu); - }, - .error_union, - .frame, - .@"anyframe", - .noreturn, - .void, - .type, - .comptime_float, - .comptime_int, - .undefined, - .null, - .@"fn", - .@"opaque", - .enum_literal, - => unreachable, - } -} - -pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool { - return scalar_ty.bitSize(zcu) > 64; -} diff --git a/src/arch/x86/bits.zig b/src/arch/x86/bits.zig deleted file mode 100644 index 4b69238c4fd8165513953315768b7797df410f32..0000000000000000000000000000000000000000 --- a/src/arch/x86/bits.zig +++ /dev/null @@ -1,100 +0,0 @@ -const std = @import("std"); - -// zig fmt: off -pub const Register = enum(u8) { - // 0 through 7, 32-bit registers. id is int value - eax, ecx, edx, ebx, esp, ebp, esi, edi, - - // 8-15, 16-bit registers. id is int value - 8. - ax, cx, dx, bx, sp, bp, si, di, - - // 16-23, 8-bit registers. id is int value - 16. - al, cl, dl, bl, ah, ch, dh, bh, - - /// Returns the bit-width of the register. - pub fn size(self: Register) u7 { - return switch (@intFromEnum(self)) { - 0...7 => 32, - 8...15 => 16, - 16...23 => 8, - else => unreachable, - }; - } - - /// Returns the register's id. This is used in practically every opcode the - /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move - /// instruction, and is used in the R/M byte. - pub fn id(self: Register) u3 { - return @truncate(@intFromEnum(self)); - } - - /// Convert from any register to its 32 bit alias. - pub fn to32(self: Register) Register { - return @enumFromInt(@as(u8, self.id())); - } - - /// Convert from any register to its 16 bit alias. - pub fn to16(self: Register) Register { - return @enumFromInt(@as(u8, self.id()) + 8); - } - - /// Convert from any register to its 8 bit alias. - pub fn to8(self: Register) Register { - return @enumFromInt(@as(u8, self.id()) + 16); - } - - pub fn dwarfNum(reg: Register) u8 { - return @intFromEnum(reg.to32()); - } -}; - -// zig fmt: on - -/// TODO this set is actually a set of caller-saved registers. -pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; - -// TODO add these to Register enum and corresponding dwarfNum -// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. -// RA = (8, "RA"), -// -// ST0 = (11, "st0"), -// ST1 = (12, "st1"), -// ST2 = (13, "st2"), -// ST3 = (14, "st3"), -// ST4 = (15, "st4"), -// ST5 = (16, "st5"), -// ST6 = (17, "st6"), -// ST7 = (18, "st7"), -// -// XMM0 = (21, "xmm0"), -// XMM1 = (22, "xmm1"), -// XMM2 = (23, "xmm2"), -// XMM3 = (24, "xmm3"), -// XMM4 = (25, "xmm4"), -// XMM5 = (26, "xmm5"), -// XMM6 = (27, "xmm6"), -// XMM7 = (28, "xmm7"), -// -// MM0 = (29, "mm0"), -// MM1 = (30, "mm1"), -// MM2 = (31, "mm2"), -// MM3 = (32, "mm3"), -// MM4 = (33, "mm4"), -// MM5 = (34, "mm5"), -// MM6 = (35, "mm6"), -// MM7 = (36, "mm7"), -// -// MXCSR = (39, "mxcsr"), -// -// ES = (40, "es"), -// CS = (41, "cs"), -// SS = (42, "ss"), -// DS = (43, "ds"), -// FS = (44, "fs"), -// GS = (45, "gs"), -// -// TR = (48, "tr"), -// LDTR = (49, "ldtr"), -// -// FS_BASE = (93, "fs.base"), -// GS_BASE = (94, "gs.base"), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1f67306598ffa2e36ace55f29a5c17e444e7ede7..486497a365488c98715f78e7ed2c1a754425c5c4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -525,47 +525,47 @@ pub const MCValue = union(enum) { }; } - pub fn format(mcv: MCValue, bw: *Writer, comptime _: []const u8) Writer.Error!void { + pub fn format(mcv: MCValue, w: *Writer) Writer.Error!void { switch (mcv) { - .none, .unreach, .dead, .undef => try bw.print("({s})", .{@tagName(mcv)}), - .immediate => |pl| try bw.print("0x{x}", .{pl}), - .memory => |pl| try bw.print("[ds:0x{x}]", .{pl}), - inline .eflags, .register => |pl| try bw.print("{s}", .{@tagName(pl)}), - .register_pair => |pl| try bw.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), - .register_triple => |pl| try bw.print("{s}:{s}:{s}", .{ + .none, .unreach, .dead, .undef => try w.print("({s})", .{@tagName(mcv)}), + .immediate => |pl| try w.print("0x{x}", .{pl}), + .memory => |pl| try w.print("[ds:0x{x}]", .{pl}), + inline .eflags, .register => |pl| try w.print("{s}", .{@tagName(pl)}), + .register_pair => |pl| try w.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), + .register_triple => |pl| try w.print("{s}:{s}:{s}", .{ @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), }), - .register_quadruple => |pl| try bw.print("{s}:{s}:{s}:{s}", .{ + .register_quadruple => |pl| try w.print("{s}:{s}:{s}:{s}", .{ @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), }), - .register_offset => |pl| try bw.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), - .register_overflow => |pl| try bw.print("{s}:{s}", .{ + .register_offset => |pl| try w.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), + .register_overflow => |pl| try w.print("{s}:{s}", .{ @tagName(pl.eflags), @tagName(pl.reg), }), - .register_mask => |pl| try bw.print("mask({s},{f}):{c}{s}", .{ + .register_mask => |pl| try w.print("mask({s},{f}):{c}{s}", .{ @tagName(pl.info.kind), pl.info.scalar, @as(u8, if (pl.info.inverted) '!' else ' '), @tagName(pl.reg), }), - .indirect => |pl| try bw.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), - .indirect_load_frame => |pl| try bw.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }), - .load_frame => |pl| try bw.print("[{} + 0x{x}]", .{ pl.index, pl.off }), - .lea_frame => |pl| try bw.print("{} + 0x{x}", .{ pl.index, pl.off }), - .load_nav => |pl| try bw.print("[nav:{d}]", .{@intFromEnum(pl)}), - .lea_nav => |pl| try bw.print("nav:{d}", .{@intFromEnum(pl)}), - .load_uav => |pl| try bw.print("[uav:{d}]", .{@intFromEnum(pl.val)}), - .lea_uav => |pl| try bw.print("uav:{d}", .{@intFromEnum(pl.val)}), - .load_lazy_sym => |pl| try bw.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), - .lea_lazy_sym => |pl| try bw.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), - .load_extern_func => |pl| try bw.print("[extern:{d}]", .{@intFromEnum(pl)}), - .lea_extern_func => |pl| try bw.print("extern:{d}", .{@intFromEnum(pl)}), - .elementwise_args => |pl| try bw.print("elementwise:{d}:[{} + 0x{x}]", .{ + .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), + .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }), + .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }), + .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }), + .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}), + .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}), + .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}), + .lea_uav => |pl| try w.print("uav:{d}", .{@intFromEnum(pl.val)}), + .load_lazy_sym => |pl| try w.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}), + .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}), + .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{ pl.regs, pl.frame_index, pl.frame_off, }), - .reserved_frame => |pl| try bw.print("(dead:{})", .{pl}), - .air_ref => |pl| try bw.print("(air:0x{x})", .{@intFromEnum(pl)}), + .reserved_frame => |pl| try w.print("(dead:{})", .{pl}), + .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}), } } }; @@ -812,7 +812,7 @@ const InstTracking = struct { } } - pub fn format(tracking: InstTracking, bw: *Writer, comptime _: []const u8) Writer.Error!void { + pub fn format(tracking: InstTracking, bw: *Writer) Writer.Error!void { if (!std.meta.eql(tracking.long, tracking.short)) try bw.print("|{f}| ", .{tracking.long}); try bw.print("{f}", .{tracking.short}); } @@ -1088,10 +1088,10 @@ const FormatNavData = struct { ip: *const InternPool, nav_index: InternPool.Nav.Index, }; -fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void { - try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); +fn formatNav(data: FormatNavData, w: *Writer) Writer.Error!void { + try w.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); } -fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) { +fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(FormatNavData, formatNav) { return .{ .data = .{ .ip = ip, .nav_index = nav_index, @@ -1102,15 +1102,14 @@ const FormatAirData = struct { self: *CodeGen, inst: Air.Inst.Index, }; -fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); - // not acceptable implementation: +fn formatAir(data: FormatAirData, w: *std.io.Writer) Writer.Error!void { + // not acceptable implementation because it ignores `w`: //data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness); _ = data; _ = w; @panic("TODO: unimplemented"); } -fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { +fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) { return .{ .data = .{ .self = self, .inst = inst } }; } @@ -1118,7 +1117,7 @@ const FormatWipMirData = struct { self: *CodeGen, inst: Mir.Inst.Index, }; -fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void { +fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void { var lower: Lower = .{ .target = data.self.target, .allocator = data.self.gpa, @@ -1133,27 +1132,22 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri lower.err_msg.?.deinit(data.self.gpa); lower.err_msg = null; } - try bw.writeAll(lower.err_msg.?.msg); + try w.writeAll(lower.err_msg.?.msg); return; }, - error.OutOfMemory, error.InvalidInstruction, error.CannotEncode => |e| { - try bw.writeAll(switch (e) { - error.OutOfMemory => "Out of memory", - error.InvalidInstruction => "CodeGen failed to find a viable instruction.", - error.CannotEncode => "CodeGen failed to encode the instruction.", - }); + else => |e| { + try w.writeAll(@errorName(e)); return; }, - else => |e| return e, }).insts) |lowered_inst| { - if (!first) try bw.writeAll("\ndebug(wip_mir): "); - try bw.print(" | {f}", .{lowered_inst}); + if (!first) try w.writeAll("\ndebug(wip_mir): "); + try w.print(" | {f}", .{lowered_inst}); first = false; } if (first) { const ip = &data.self.pt.zcu.intern_pool; const mir_inst = lower.mir.instructions.get(data.inst); - try bw.print(" | .{s}", .{@tagName(mir_inst.ops)}); + try w.print(" | .{s}", .{@tagName(mir_inst.ops)}); switch (mir_inst.ops) { else => unreachable, .pseudo_dbg_prologue_end_none, @@ -1165,20 +1159,20 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri .pseudo_dbg_var_none, .pseudo_dead_none, => {}, - .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try bw.print( + .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try w.print( " {[line]d}, {[column]d}", mir_inst.data.line_column, ), - .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try bw.print(" {f}", .{ + .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try w.print(" {f}", .{ ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip), }), - .pseudo_dbg_arg_i_s, .pseudo_dbg_var_i_s => try bw.print(" {d}", .{ + .pseudo_dbg_arg_i_s, .pseudo_dbg_var_i_s => try w.print(" {d}", .{ @as(i32, @bitCast(mir_inst.data.i.i)), }), - .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_u => try bw.print(" {d}", .{ + .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_u => try w.print(" {d}", .{ mir_inst.data.i.i, }), - .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try bw.print(" {d}", .{ + .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try w.print(" {d}", .{ mir_inst.data.i64, }), .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => { @@ -1186,40 +1180,39 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri .base = .{ .reg = mir_inst.data.ro.reg }, .disp = mir_inst.data.ro.off, }) }; - try bw.print(" {f}", .{mem_op.fmt(.m)}); + try w.print(" {f}", .{mem_op.fmt(.m)}); }, .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ .base = .{ .frame = mir_inst.data.fa.index }, .disp = mir_inst.data.fa.off, }) }; - try bw.print(" {f}", .{mem_op.fmt(.m)}); + try w.print(" {f}", .{mem_op.fmt(.m)}); }, .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { const mem_op: encoder.Instruction.Operand = .{ .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.x.payload).data.decode(), }; - try bw.print(" {f}", .{mem_op.fmt(.m)}); + try w.print(" {f}", .{mem_op.fmt(.m)}); }, - .pseudo_dbg_arg_val, .pseudo_dbg_var_val => try bw.print(" {}", .{ + .pseudo_dbg_arg_val, .pseudo_dbg_var_val => try w.print(" {f}", .{ Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt), }), } } } -fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { +fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(FormatWipMirData, formatWipMir) { return .{ .data = .{ .self = self, .inst = inst } }; } const FormatTrackingData = struct { self: *CodeGen, }; -fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); +fn formatTracking(data: FormatTrackingData, w: *Writer) Writer.Error!void { var it = data.self.inst_tracking.iterator(); - while (it.next()) |entry| try bw.print("\n{f} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* }); + while (it.next()) |entry| try w.print("\n{f} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* }); } -fn fmtTracking(self: *CodeGen) std.fmt.Formatter(formatTracking) { +fn fmtTracking(self: *CodeGen) std.fmt.Formatter(FormatTrackingData, formatTracking) { return .{ .data = .{ .self = self } }; } @@ -2033,7 +2026,7 @@ fn gen( .{}, ); self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; - tracking_log.debug("spill {f} to {f}", .{ self.ret_mcv.long, frame_index }); + tracking_log.debug("spill {f} to {}", .{ self.ret_mcv.long, frame_index }); }, else => unreachable, } @@ -12894,7 +12887,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{ @tagName(air_tag), cg.typeOf(bin_op.lhs).fmt(pt), ops[0].tracking(cg), @@ -21771,7 +21764,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{ @tagName(air_tag), cg.typeOf(bin_op.lhs).fmt(pt), ops[0].tracking(cg), @@ -32489,7 +32482,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp3q, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{ @tagName(air_tag), cg.typeOf(bin_op.lhs).fmt(pt), ops[0].tracking(cg), @@ -59317,7 +59310,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .@"or", .tmp4q, .tmp5q, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{ @tagName(air_tag), ty_pl.ty.toType().fmt(pt), ops[0].tracking(cg), @@ -60816,7 +60809,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .@"or", .dst0d, .tmp0d, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{ @tagName(air_tag), cg.typeOf(bin_op.rhs).fmt(pt), ops[1].tracking(cg), @@ -64073,7 +64066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp1q, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{ @tagName(air_tag), lhs_ty.fmt(pt), ops[0].tracking(cg), @@ -79435,7 +79428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .@"struct", .@"union" => { assert(ty.containerLayout(zcu) == .@"packed"); for (&ops) |*op| op.wrapInt(cg) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} wrap {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} wrap {f} {f}", .{ @tagName(air_tag), ty.fmt(pt), op.tracking(cg), @@ -86528,7 +86521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } }, }), }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {s} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {s} {f} {f} {f}", .{ @tagName(air_tag), @tagName(vector_cmp.compareOperator()), cg.typeOf(vector_cmp.lhs).fmt(pt), @@ -157193,7 +157186,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } }, } }, }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s}.{s} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s}.{s} {f} {f}", .{ @tagName(air_tag), @tagName(reduce.operation), cg.typeOf(reduce.operand).fmt(pt), @@ -157204,7 +157197,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { switch (reduce.operation) { .And, .Or, .Xor, .Min, .Max => {}, .Add, .Mul => if (cg.intInfo(res_ty)) |_| res[0].wrapInt(cg) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s}.{s} wrap {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s}.{s} wrap {f} {f}", .{ @tagName(air_tag), @tagName(reduce.operation), res_ty.fmt(pt), @@ -164487,7 +164480,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } }, } }, }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s}.{s} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s}.{s} {f} {f}", .{ @tagName(air_tag), @tagName(reduce.operation), cg.typeOf(reduce.operand).fmt(pt), @@ -166284,7 +166277,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, } }, } }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{ @tagName(air_tag), ty_op.ty.toType().fmt(pt), ops[0].tracking(cg), @@ -166300,7 +166293,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const bin_op = air_datas[@intFromEnum(inst)].bin_op; var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }) ++ .{undefined}; ops[2] = ops[0].getByteLen(cg) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f} {f}", .{ @tagName(air_tag), cg.typeOf(bin_op.lhs).fmt(pt), cg.typeOf(bin_op.rhs).fmt(pt), @@ -166340,7 +166333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } }, }}, }) catch |err| switch (err) { - error.SelectFailed => return cg.fail("failed to select {s} {} {} {} {} {}", .{ + error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f} {f} {f}", .{ @tagName(air_tag), cg.typeOf(bin_op.lhs).fmt(pt), cg.typeOf(bin_op.rhs).fmt(pt), @@ -181509,7 +181502,7 @@ fn genSetReg( assert(!ty.optionalReprIsPayload(zcu)); break :first_ty opt_child; }, - else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, ty.fmt(pt) }), + else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, ty.fmt(pt) }), }); const first_size: u31 = @intCast(first_ty.abiSize(zcu)); const frame_size = std.math.ceilPowerOfTwoAssert(u32, abi_size); @@ -186937,7 +186930,7 @@ const Temp = struct { assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable); break :part_ty .u64; } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) { - else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }), + else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }), .ptr_type => |ptr_info| { assert(ptr_info.flags.size == .slice); assert(src_regs.len == 2); @@ -186948,7 +186941,7 @@ const Temp = struct { break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_abi_size, 8)); }, .opt_type => |opt_child| switch (ip.indexToKey(opt_child)) { - else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }), + else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }), .ptr_type => |ptr_info| { assert(ptr_info.flags.size == .slice); assert(src_regs.len == 2); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index b99580f6ae1e020845dbb7e5cfb891ff8fa82092..af7304709cd9b756de00df8de509970c923deaf0 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -707,7 +707,14 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI const comp = emit.bin_file.comp; const gpa = comp.gpa; const start_offset: u32 = @intCast(emit.code.items.len); - try lowered_inst.encode(emit.code.writer(gpa), .{}); + { + var aw: std.io.Writer.Allocating = .fromArrayList(gpa, emit.code); + defer emit.code.* = aw.toArrayList(); + lowered_inst.encode(&aw.writer, .{}) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, + }; + } const end_offset: u32 = @intCast(emit.code.items.len); for (reloc_info) |reloc| switch (reloc.target.type) { .inst => { diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 9cb78972cc5cf9f92dc50ea2b456f2507f403721..9ae7ce8fef133e1565beed60a7f51a9637ac5013 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -159,14 +159,12 @@ pub fn modRmExt(encoding: Encoding) u3 { }; } -pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void { - comptime assert(fmt.len == 0); - +pub fn format(encoding: Encoding, writer: *std.io.Writer) std.io.Writer.Error!void { var opc = encoding.opcode(); if (encoding.data.mode.isVex()) { - try bw.writeAll("VEX."); + try writer.writeAll("VEX."); - try bw.writeAll(switch (encoding.data.mode) { + try writer.writeAll(switch (encoding.data.mode) { .vex_128_w0, .vex_128_w1, .vex_128_wig => "128", .vex_256_w0, .vex_256_w1, .vex_256_wig => "256", .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 { switch (opc[0]) { else => {}, 0x66, 0xf3, 0xf2 => { - try bw.print(".{X:0>2}", .{opc[0]}); + try writer.print(".{X:0>2}", .{opc[0]}); opc = opc[1..]; }, } - try bw.print(".{X}", .{opc[0 .. opc.len - 1]}); + try writer.print(".{X}", .{opc[0 .. opc.len - 1]}); opc = opc[opc.len - 1 ..]; - try bw.writeAll(".W"); - try bw.writeAll(switch (encoding.data.mode) { + try writer.writeAll(".W"); + try writer.writeAll(switch (encoding.data.mode) { .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0", .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1", .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG", else => unreachable, }); - try bw.writeByte(' '); - } else if (encoding.data.mode.isLong()) try bw.writeAll("REX.W + "); - for (opc) |byte| try bw.print("{x:0>2} ", .{byte}); + try writer.writeByte(' '); + } else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + "); + for (opc) |byte| try writer.print("{x:0>2} ", .{byte}); switch (encoding.data.op_en) { .z, .fd, .td, .i, .zi, .ii, .d => {}, @@ -212,10 +210,10 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void { .r64 => "rd", else => unreachable, }; - try bw.print("+{s} ", .{tag}); + try writer.print("+{s} ", .{tag}); }, - .ia, .m, .mi, .m1, .mc, .vm, .vmi => try bw.print("/{d} ", .{encoding.modRmExt()}), - .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try bw.writeAll("/r "), + .ia, .m, .mi, .m1, .mc, .vm, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}), + .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "), } switch (encoding.data.op_en) { @@ -244,24 +242,24 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void { .rel32 => "cd", else => unreachable, }; - try bw.print("{s} ", .{tag}); + try writer.print("{s} ", .{tag}); }, - .rvmr => try bw.writeAll("/is4 "), + .rvmr => try writer.writeAll("/is4 "), .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {}, } - try bw.print("{s} ", .{@tagName(encoding.mnemonic)}); + try writer.print("{s} ", .{@tagName(encoding.mnemonic)}); for (encoding.data.ops) |op| switch (op) { .none => break, - else => try bw.print("{s} ", .{@tagName(op)}), + else => try writer.print("{s} ", .{@tagName(op)}), }; const op_en = switch (encoding.data.op_en) { .zi => .i, else => |op_en| op_en, }; - try bw.print("{s}", .{@tagName(op_en)}); + try writer.print("{s}", .{@tagName(op_en)}); } pub const Mnemonic = enum { @@ -1016,13 +1014,21 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op }; @memcpy(inst.ops[0..ops.len], ops); - var buf: [15]u8 = undefined; - var bw: Writer = .fixed(&buf); - inst.encode(&bw, .{ + // By using a buffer with maximum length of encoded instruction, we can use + // the `end` field of the Writer for the count. + var buf: [16]u8 = undefined; + var trash: std.io.Writer.Discarding = .init(&buf); + inst.encode(&trash.writer, .{ .allow_frame_locs = true, .allow_symbols = true, - }) catch unreachable; - return @intCast(bw.end); + }) catch { + // Since the function signature for encode() does not mention under what + // conditions it can fail, I have changed `unreachable` to `@panic` here. + // This is a TODO item since it indicates this function + // (`estimateInstructionLength`) has the wrong function signature. + @panic("unexpected failure to encode"); + }; + return trash.writer.end; } const mnemonic_to_encodings_map = init: { diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index d0063c13f251dd81fd7a54d301b63a013cd9e3e1..c9f11bd28e41ed6376b4ea3a77b9ae7a0c3a4ebc 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -729,15 +729,6 @@ pub const FrameIndex = enum(u32) { pub fn isNamed(fi: FrameIndex) bool { return @intFromEnum(fi) < named_count; } - - pub fn format(fi: FrameIndex, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); - try bw.writeAll("FrameIndex"); - if (fi.isNamed()) - try bw.print(".{s}", .{@tagName(fi)}) - else - try bw.print("({d})", .{@intFromEnum(fi)}); - } }; pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; @@ -838,14 +829,13 @@ pub const Memory = struct { }; } - pub fn format(s: Size, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - comptime assert(fmt.len == 0); + pub fn format(s: Size, writer: *std.io.Writer) std.io.Writer.Error!void { if (s == .none) return; - try bw.writeAll(@tagName(s)); + try writer.writeAll(@tagName(s)); switch (s) { .none => unreachable, .ptr, .gpr => {}, - else => try bw.writeAll(" ptr"), + else => try writer.writeAll(" ptr"), } } }; @@ -901,12 +891,7 @@ pub const Immediate = union(enum) { return .{ .signed = x }; } - pub fn format( - imm: Immediate, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { + pub fn format(imm: Immediate, writer: *std.io.Writer) std.io.Writer.Error!void { switch (imm) { inline else => |int| try writer.print("{d}", .{int}), .nav => |nav_off| try writer.print("Nav({d}) + {d}", .{ @intFromEnum(nav_off.nav), nav_off.off }), diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 415b211e6a45cace76da1cfb51fa6b985017fa3a..43d23af5fc3dd67ec62dceaff5512ff5a3116235 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -353,8 +353,7 @@ pub const Instruction = struct { return inst; } - pub fn format(inst: Instruction, w: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { - comptime assert(unused_format_string.len == 0); + pub fn format(inst: Instruction, w: *Writer) Writer.Error!void { switch (inst.prefix) { .none, .directive => {}, else => try w.print("{s} ", .{@tagName(inst.prefix)}), diff --git a/src/codegen.zig b/src/codegen.zig index 3aa172332dab74d2275fa824e9385e41e7ea2441..c81dbfbe052de2e9a1a8da4a83faeebce00c8c0a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -35,7 +35,7 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature { .stage2_arm => .arm_backend, .stage2_c => .c_backend, .stage2_llvm => .llvm_backend, - .stage2_powerpc => .powerpc_backend, + .stage2_powerpc => unreachable, .stage2_riscv64 => .riscv64_backend, .stage2_sparc64 => .sparc64_backend, .stage2_spirv => .spirv_backend, @@ -49,11 +49,11 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature { fn importBackend(comptime backend: std.builtin.CompilerBackend) type { return switch (backend) { .other, .stage1 => unreachable, - .stage2_aarch64 => @import("arch/aarch64/CodeGen.zig"), - .stage2_arm => @import("arch/arm/CodeGen.zig"), + .stage2_aarch64 => unreachable, + .stage2_arm => unreachable, .stage2_c => @import("codegen/c.zig"), .stage2_llvm => @import("codegen/llvm.zig"), - .stage2_powerpc => @import("arch/powerpc/CodeGen.zig"), + .stage2_powerpc => unreachable, .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"), .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"), .stage2_spirv => @import("codegen/spirv.zig"), @@ -71,14 +71,11 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co inline .stage2_llvm, .stage2_c, .stage2_wasm, - .stage2_arm, .stage2_x86_64, - .stage2_aarch64, .stage2_x86, .stage2_riscv64, .stage2_sparc64, .stage2_spirv, - .stage2_powerpc, => |backend| { dev.check(devFeatureForBackend(backend)); return importBackend(backend).legalizeFeatures(target); @@ -90,9 +87,6 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co /// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a /// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`. pub const AnyMir = union { - aarch64: @import("arch/aarch64/Mir.zig"), - arm: @import("arch/arm/Mir.zig"), - powerpc: noreturn, //@import("arch/powerpc/Mir.zig"), riscv64: @import("arch/riscv64/Mir.zig"), sparc64: @import("arch/sparc64/Mir.zig"), x86_64: @import("arch/x86_64/Mir.zig"), @@ -103,7 +97,6 @@ pub const AnyMir = union { return switch (backend) { .stage2_aarch64 => "aarch64", .stage2_arm => "arm", - .stage2_powerpc => "powerpc", .stage2_riscv64 => "riscv64", .stage2_sparc64 => "sparc64", .stage2_x86_64 => "x86_64", @@ -118,10 +111,7 @@ pub const AnyMir = union { const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); switch (backend) { else => unreachable, - inline .stage2_aarch64, - .stage2_arm, - .stage2_powerpc, - .stage2_riscv64, + inline .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, .stage2_wasm, @@ -149,10 +139,7 @@ pub fn generateFunction( const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; switch (target_util.zigBackend(target, false)) { else => unreachable, - inline .stage2_aarch64, - .stage2_arm, - .stage2_powerpc, - .stage2_riscv64, + inline .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, .stage2_wasm, @@ -187,10 +174,7 @@ pub fn emitFunction( const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { else => unreachable, - inline .stage2_aarch64, - .stage2_arm, - .stage2_powerpc, - .stage2_riscv64, + inline .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, => |backend| { @@ -216,10 +200,7 @@ pub fn generateLazyFunction( zcu.getTarget(); switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { else => unreachable, - inline .stage2_powerpc, - .stage2_riscv64, - .stage2_x86_64, - => |backend| { + inline .stage2_riscv64, .stage2_x86_64 => |backend| { dev.check(devFeatureForBackend(backend)); return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); }, @@ -910,7 +891,7 @@ pub fn genNavRef( const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); - log.debug("genNavRef({})", .{nav.fqn.fmt(ip)}); + log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)}); const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e| .{ e.lib_name, e.linkage, e.is_threadlocal and zcu.comp.config.any_non_single_threaded } diff --git a/src/codegen/aarch64/abi.zig b/src/codegen/aarch64/abi.zig new file mode 100644 index 0000000000000000000000000000000000000000..0cd0b389b1e05def773e3f729ea738984abde3f2 --- /dev/null +++ b/src/codegen/aarch64/abi.zig @@ -0,0 +1,150 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const bits = @import("../../arch/aarch64/bits.zig"); +const Register = bits.Register; +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +pub const Class = union(enum) { + memory, + byval, + integer, + double_integer, + float_array: u8, +}; + +/// For `float_array` the second element will be the amount of floats. +pub fn classifyType(ty: Type, zcu: *Zcu) Class { + std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + + var maybe_float_bits: ?u16 = null; + switch (ty.zigTypeTag(zcu)) { + .@"struct" => { + if (ty.containerLayout(zcu) == .@"packed") return .byval; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= sret_float_count) return .{ .float_array = float_count }; + + const bit_size = ty.bitSize(zcu); + if (bit_size > 128) return .memory; + if (bit_size > 64) return .double_integer; + return .integer; + }, + .@"union" => { + if (ty.containerLayout(zcu) == .@"packed") return .byval; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= sret_float_count) return .{ .float_array = float_count }; + + const bit_size = ty.bitSize(zcu); + if (bit_size > 128) return .memory; + if (bit_size > 64) return .double_integer; + return .integer; + }, + .int, .@"enum", .error_set, .float, .bool => return .byval, + .vector => { + const bit_size = ty.bitSize(zcu); + // TODO is this controlled by a cpu feature? + if (bit_size > 128) return .memory; + return .byval; + }, + .optional => { + std.debug.assert(ty.isPtrLikeOptional(zcu)); + return .byval; + }, + .pointer => { + std.debug.assert(!ty.isSlice(zcu)); + return .byval; + }, + .error_union, + .frame, + .@"anyframe", + .noreturn, + .void, + .type, + .comptime_float, + .comptime_int, + .undefined, + .null, + .@"fn", + .@"opaque", + .enum_literal, + .array, + => unreachable, + } +} + +const sret_float_count = 4; +fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u8 { + const ip = &zcu.intern_pool; + const target = zcu.getTarget(); + const invalid = std.math.maxInt(u8); + switch (ty.zigTypeTag(zcu)) { + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + var max_count: u8 = 0; + for (union_obj.field_types.get(ip)) |field_ty| { + const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + if (field_count > max_count) max_count = field_count; + if (max_count > sret_float_count) return invalid; + } + return max_count; + }, + .@"struct" => { + const fields_len = ty.structFieldCount(zcu); + var count: u8 = 0; + var i: u32 = 0; + while (i < fields_len) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + const field_count = countFloats(field_ty, zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + count += field_count; + if (count > sret_float_count) return invalid; + } + return count; + }, + .float => { + const float_bits = maybe_float_bits.* orelse { + maybe_float_bits.* = ty.floatBits(target); + return 1; + }; + if (ty.floatBits(target) == float_bits) return 1; + return invalid; + }, + .void => return 0, + else => return invalid, + } +} + +pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type { + const ip = &zcu.intern_pool; + switch (ty.zigTypeTag(zcu)) { + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + for (union_obj.field_types.get(ip)) |field_ty| { + if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some; + } + return null; + }, + .@"struct" => { + const fields_len = ty.structFieldCount(zcu); + var i: u32 = 0; + while (i < fields_len) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + if (getFloatArrayType(field_ty, zcu)) |some| return some; + } + return null; + }, + .float => return ty, + else => return null, + } +} + +pub const callee_preserved_regs = [_]Register{ + .x19, .x20, .x21, .x22, .x23, + .x24, .x25, .x26, .x27, .x28, +}; + +pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; +pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; + +const allocatable_registers = callee_preserved_regs; diff --git a/src/codegen/arm/abi.zig b/src/codegen/arm/abi.zig new file mode 100644 index 0000000000000000000000000000000000000000..23606d6145c45d80f1dde0d1405337fb2b1958bd --- /dev/null +++ b/src/codegen/arm/abi.zig @@ -0,0 +1,163 @@ +const std = @import("std"); +const assert = std.debug.assert; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +pub const Class = union(enum) { + memory, + byval, + i32_array: u8, + i64_array: u8, + + fn arrSize(total_size: u64, arr_size: u64) Class { + const count = @as(u8, @intCast(std.mem.alignForward(u64, total_size, arr_size) / arr_size)); + if (arr_size == 32) { + return .{ .i32_array = count }; + } else { + return .{ .i64_array = count }; + } + } +}; + +pub const Context = enum { ret, arg }; + +pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { + assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + + var maybe_float_bits: ?u16 = null; + const max_byval_size = 512; + const ip = &zcu.intern_pool; + switch (ty.zigTypeTag(zcu)) { + .@"struct" => { + const bit_size = ty.bitSize(zcu); + if (ty.containerLayout(zcu) == .@"packed") { + if (bit_size > 64) return .memory; + return .byval; + } + if (bit_size > max_byval_size) return .memory; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= byval_float_count) return .byval; + + const fields = ty.structFieldCount(zcu); + var i: u32 = 0; + while (i < fields) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + const field_alignment = ty.fieldAlignment(i, zcu); + const field_size = field_ty.bitSize(zcu); + if (field_size > 32 or field_alignment.compare(.gt, .@"32")) { + return Class.arrSize(bit_size, 64); + } + } + return Class.arrSize(bit_size, 32); + }, + .@"union" => { + const bit_size = ty.bitSize(zcu); + const union_obj = zcu.typeToUnion(ty).?; + if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (bit_size > 64) return .memory; + return .byval; + } + if (bit_size > max_byval_size) return .memory; + const float_count = countFloats(ty, zcu, &maybe_float_bits); + if (float_count <= byval_float_count) return .byval; + + for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { + if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or + ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32")) + { + return Class.arrSize(bit_size, 64); + } + } + return Class.arrSize(bit_size, 32); + }, + .bool, .float => return .byval, + .int => { + // TODO this is incorrect for _BitInt(128) but implementing + // this correctly makes implementing compiler-rt impossible. + // const bit_size = ty.bitSize(zcu); + // if (bit_size > 64) return .memory; + return .byval; + }, + .@"enum", .error_set => { + const bit_size = ty.bitSize(zcu); + if (bit_size > 64) return .memory; + return .byval; + }, + .vector => { + const bit_size = ty.bitSize(zcu); + // TODO is this controlled by a cpu feature? + if (ctx == .ret and bit_size > 128) return .memory; + if (bit_size > 512) return .memory; + return .byval; + }, + .optional => { + assert(ty.isPtrLikeOptional(zcu)); + return .byval; + }, + .pointer => { + assert(!ty.isSlice(zcu)); + return .byval; + }, + .error_union, + .frame, + .@"anyframe", + .noreturn, + .void, + .type, + .comptime_float, + .comptime_int, + .undefined, + .null, + .@"fn", + .@"opaque", + .enum_literal, + .array, + => unreachable, + } +} + +const byval_float_count = 4; +fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 { + const ip = &zcu.intern_pool; + const target = zcu.getTarget(); + const invalid = std.math.maxInt(u32); + switch (ty.zigTypeTag(zcu)) { + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + var max_count: u32 = 0; + for (union_obj.field_types.get(ip)) |field_ty| { + const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + if (field_count > max_count) max_count = field_count; + if (max_count > byval_float_count) return invalid; + } + return max_count; + }, + .@"struct" => { + const fields_len = ty.structFieldCount(zcu); + var count: u32 = 0; + var i: u32 = 0; + while (i < fields_len) : (i += 1) { + const field_ty = ty.fieldType(i, zcu); + const field_count = countFloats(field_ty, zcu, maybe_float_bits); + if (field_count == invalid) return invalid; + count += field_count; + if (count > byval_float_count) return invalid; + } + return count; + }, + .float => { + const float_bits = maybe_float_bits.* orelse { + const float_bits = ty.floatBits(target); + if (float_bits != 32 and float_bits != 64) return invalid; + maybe_float_bits.* = float_bits; + return 1; + }; + if (ty.floatBits(target) == float_bits) return 1; + return invalid; + }, + .void => return 0, + else => return invalid, + } +} diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c8681ca5254aaafd7ef70724ae5a22d2585eee53..61bd5259ae829088876ee68620fb252a39fe06d1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -345,12 +345,15 @@ fn isReservedIdent(ident: []const u8) bool { } else return reserved_idents.has(ident); } -fn formatIdent( - ident: []const u8, - w: *Writer, - comptime fmt_str: []const u8, -) Writer.Error!void { - const solo = fmt_str.len != 0 and fmt_str[0] == ' '; // space means solo; not part of a bigger ident. +fn formatIdentSolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { + return formatIdentOptions(ident, w, true); +} + +fn formatIdentUnsolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { + return formatIdentOptions(ident, w, false); +} + +fn formatIdentOptions(ident: []const u8, w: *std.io.Writer, solo: bool) std.io.Writer.Error!void { if (solo and isReservedIdent(ident)) { try w.writeAll("zig_e_"); } @@ -367,29 +370,36 @@ fn formatIdent( } } } -pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { + +pub fn fmtIdentSolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentSolo) { + return .{ .data = ident }; +} + +pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentUnsolo) { return .{ .data = ident }; } const CTypePoolStringFormatData = struct { ctype_pool_string: CType.Pool.String, ctype_pool: *const CType.Pool, + solo: bool, }; -fn formatCTypePoolString( - data: CTypePoolStringFormatData, - w: *Writer, - comptime fmt_str: []const u8, -) Writer.Error!void { +fn formatCTypePoolString(data: CTypePoolStringFormatData, w: *std.io.Writer) std.io.Writer.Error!void { if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice| - try formatIdent(slice, w, fmt_str) + try formatIdentOptions(slice, w, data.solo) else try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)}); } pub fn fmtCTypePoolString( ctype_pool_string: CType.Pool.String, ctype_pool: *const CType.Pool, -) std.fmt.Formatter(formatCTypePoolString) { - return .{ .data = .{ .ctype_pool_string = ctype_pool_string, .ctype_pool = ctype_pool } }; + solo: bool, +) std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString) { + return .{ .data = .{ + .ctype_pool_string = ctype_pool_string, + .ctype_pool = ctype_pool, + .solo = solo, + } }; } // Returns true if `formatIdent` would make any edits to ident. @@ -443,7 +453,7 @@ pub const Function = struct { const ty = f.typeOf(ref); const result: CValue = if (lowersToArray(ty, pt)) result: { - const ch = &f.object.code_header.buffered_writer; + const ch = &f.object.code_header.writer; const decl_c_value = try f.allocLocalValue(.{ .ctype = try f.ctypeFromType(ty, .complete), .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)), @@ -599,8 +609,12 @@ pub const Function = struct { return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); } - fn fmtIntLiteral(f: *Function, val: Value) !std.fmt.Formatter(formatIntLiteral) { - return f.object.dg.fmtIntLiteral(val, .Other); + fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { + return f.object.dg.fmtIntLiteralDec(val, .Other); + } + + fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { + return f.object.dg.fmtIntLiteralHex(val, .Other); } fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 { @@ -619,14 +633,14 @@ pub const Function = struct { .tag_name, => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{ @tagName(key), - fmtIdent(ip.loadEnumType(enum_ty).name.toSlice(ip)), + fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)), @intFromEnum(enum_ty), }), .never_tail, .never_inline, => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{ @tagName(key), - fmtIdent(ip.getNav(owner_nav).name.toSlice(ip)), + fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)), @intFromEnum(owner_nav), }), }, @@ -662,7 +676,7 @@ pub const Function = struct { }, else => {}, } - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const a = try Assignment.start(f, w, ctype); try f.writeCValue(w, dst, .Other); try a.assign(f, w); @@ -704,7 +718,7 @@ pub const Object = struct { const indent_char = ' '; fn newline(o: *Object) !void { - const w = &o.code.buffered_writer; + const w = &o.code.writer; try w.writeByte('\n'); try w.splatByteAll(indent_char, o.indent_counter); } @@ -716,9 +730,9 @@ pub const Object = struct { const written = o.code.getWritten(); switch (written[written.len - 1]) { indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width), - '\n' => try o.code.buffered_writer.splatByteAll(indent_char, o.indent_counter), + '\n' => try o.code.writer.splatByteAll(indent_char, o.indent_counter), else => { - std.debug.print("\"{f}\"\n", .{std.zig.fmtEscapes(written[written.len -| 100..])}); + std.debug.print("\"{f}\"\n", .{std.zig.fmtString(written[written.len -| 100..])}); unreachable; }, } @@ -884,7 +898,7 @@ pub const DeclGen = struct { const addr_val = try pt.intValue(.usize, int.addr); try w.writeByte('('); try dg.renderCType(w, ptr_ctype); - try w.print("){fx}", .{try dg.fmtIntLiteral(addr_val, .Other)}); + try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .Other)}); }, .nav_ptr => |nav| try dg.renderNav(w, nav, location), @@ -924,7 +938,7 @@ pub const DeclGen = struct { const offset_val = try pt.intValue(.usize, byte_offset); try w.writeAll("((char *)"); try dg.renderPointer(w, field.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteral(offset_val, .Other)}); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)}); }, } }, @@ -946,7 +960,7 @@ pub const DeclGen = struct { // The pointer already has an appropriate type - just do the arithmetic. try w.writeByte('('); try dg.renderPointer(w, elem.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteral(index_val, .Other)}); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)}); } else { // We probably have an array pointer `T (*)[n]`. Cast to an element pointer, // and *then* apply the index. @@ -954,7 +968,7 @@ pub const DeclGen = struct { try dg.renderCType(w, result_ctype); try w.writeByte(')'); try dg.renderPointer(w, elem.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteral(index_val, .Other)}); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)}); } }, @@ -969,14 +983,14 @@ pub const DeclGen = struct { const offset_val = try pt.intValue(.usize, oac.byte_offset); try w.writeAll("((char *)"); try dg.renderPointer(w, oac.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteral(offset_val, .Other)}); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)}); } }, } } fn renderErrorName(dg: *DeclGen, w: *Writer, err_name: InternPool.NullTerminatedString) !void { - try w.print("zig_error_{f}", .{fmtIdent(err_name.toSlice(&dg.pt.zcu.intern_pool))}); + try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name.toSlice(&dg.pt.zcu.intern_pool))}); } fn renderValue( @@ -1040,11 +1054,11 @@ pub const DeclGen = struct { .empty_enum_value, => unreachable, // non-runtime values .int => |int| switch (int.storage) { - .u64, .i64, .big_int => try w.print("{f}", .{try dg.fmtIntLiteral(val, location)}), + .u64, .i64, .big_int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}), .lazy_align, .lazy_size => { try w.writeAll("(("); try dg.renderCType(w, ctype); - try w.print("){fx})", .{try dg.fmtIntLiteral( + try w.print("){f})", .{try dg.fmtIntLiteralHex( try pt.intValue(.usize, val.toUnsignedInt(zcu)), .Other, )}); @@ -1173,7 +1187,7 @@ pub const DeclGen = struct { try w.writeAll(", "); empty = false; } - try w.print("{fx}", .{try dg.fmtIntLiteral( + try w.print("{f}", .{try dg.fmtIntLiteralHex( try pt.intValue_big(repr_ty, repr_val_big.toConst()), location, )}); @@ -1281,7 +1295,7 @@ pub const DeclGen = struct { } const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { - var literal: StringLiteral = .init(w, ty.arrayLenIncludingSentinel(zcu)); + var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu))); try literal.start(); var index: usize = 0; while (index < ai.len) : (index += 1) { @@ -1562,7 +1576,7 @@ pub const DeclGen = struct { .payload => { try w.writeByte('{'); if (field_ty.hasRuntimeBits(zcu)) { - try w.print(" .{f } = ", .{fmtIdent(field_name.toSlice(ip))}); + try w.print(" .{f} = ", .{fmtIdentSolo(field_name.toSlice(ip))}); try dg.renderValue( w, Value.fromInterned(un.val), @@ -1645,15 +1659,15 @@ pub const DeclGen = struct { .enum_type, .error_set_type, .inferred_error_set_type, - => return w.print("{fx}", .{ - try dg.fmtIntLiteral(try pt.undefValue(ty), location), + => return w.print("{f}", .{ + try dg.fmtIntLiteralHex(try pt.undefValue(ty), location), }), .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .one, .many, .c => { try w.writeAll("(("); try dg.renderCType(w, ctype); - return w.print("){fx})", .{ - try dg.fmtIntLiteral(.undef_usize, .Other), + return w.print("){f})", .{ + try dg.fmtIntLiteralHex(.undef_usize, .Other), }); }, .slice => { @@ -1666,8 +1680,8 @@ pub const DeclGen = struct { try w.writeAll("{("); const ptr_ty = ty.slicePtrFieldType(zcu); try dg.renderType(w, ptr_ty); - return w.print("){fx}, {0fx}}}", .{ - try dg.fmtIntLiteral(.undef_usize, .Other), + return w.print("){f}, {0f}}}", .{ + try dg.fmtIntLiteralHex(.undef_usize, .Other), }); }, }, @@ -1730,8 +1744,8 @@ pub const DeclGen = struct { } return w.writeByte('}'); }, - .@"packed" => return w.print("{fx}", .{ - try dg.fmtIntLiteral(try pt.undefValue(ty), .Other), + .@"packed" => return w.print("{f}", .{ + try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other), }), } }, @@ -1800,8 +1814,8 @@ pub const DeclGen = struct { } if (has_tag) try w.writeByte('}'); }, - .@"packed" => return w.print("{fx}", .{ - try dg.fmtIntLiteral(try pt.undefValue(ty), .Other), + .@"packed" => return w.print("{f}", .{ + try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other), }), } }, @@ -1840,7 +1854,7 @@ pub const DeclGen = struct { const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { const c_len = ty.arrayLenIncludingSentinel(zcu); - var literal: StringLiteral = .init(w, c_len); + var literal: StringLiteral = .init(w, @intCast(c_len)); try literal.start(); var index: u64 = 0; while (index < c_len) : (index += 1) @@ -1899,7 +1913,7 @@ pub const DeclGen = struct { kind: CType.Kind, name: union(enum) { nav: InternPool.Nav.Index, - fmt_ctype_pool_string: std.fmt.Formatter(formatCTypePoolString), + fmt_ctype_pool_string: std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString), @"export": struct { main_name: InternPool.NullTerminatedString, extern_name: InternPool.NullTerminatedString, @@ -1943,8 +1957,8 @@ pub const DeclGen = struct { try w.print("{f}", .{trailing}); switch (name) { .nav => |nav| try dg.renderNavName(w, nav), - .fmt_ctype_pool_string => |fmt| try w.print("{f }", .{fmt}), - .@"export" => |@"export"| try w.print("{f }", .{fmtIdent(@"export".extern_name.toSlice(ip))}), + .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}), + .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}), } try renderTypeSuffix( @@ -1971,17 +1985,17 @@ pub const DeclGen = struct { const is_mangled = isMangledIdent(extern_name, true); const is_export = @"export".extern_name != @"export".main_name; if (is_mangled and is_export) { - try w.print(" zig_mangled_export({f }, {fs}, {fs})", .{ - fmtIdent(extern_name), + try w.print(" zig_mangled_export({f}, {f}, {f})", .{ + fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), fmtStringLiteral(@"export".main_name.toSlice(ip), null), }); } else if (is_mangled) { - try w.print(" zig_mangled({f }, {fs})", .{ - fmtIdent(extern_name), fmtStringLiteral(extern_name, null), + try w.print(" zig_mangled({f}, {f})", .{ + fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), }); } else if (is_export) { - try w.print(" zig_export({fs}, {fs})", .{ + try w.print(" zig_export({f}, {f})", .{ fmtStringLiteral(@"export".main_name.toSlice(ip), null), fmtStringLiteral(extern_name, null), }); @@ -2129,7 +2143,7 @@ pub const DeclGen = struct { } else if (dest_bits > 64 and src_bits <= 64) { try w.writeAll("zig_make_"); try dg.renderTypeForBuiltinFnName(w, dest_ty); - try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? + try w.writeAll("(0, "); if (src_is_ptr) { try w.writeByte('('); try dg.renderType(w, src_eff_ty); @@ -2209,7 +2223,7 @@ pub const DeclGen = struct { .new_local, .local => |i| try w.print("t{d}", .{i}), .constant => |uav| try renderUavName(w, uav), .nav => |nav| try dg.renderNavName(w, nav), - .identifier => |ident| try w.print("{f }", .{fmtIdent(ident)}), + .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}), else => unreachable, } } @@ -2226,13 +2240,13 @@ pub const DeclGen = struct { try dg.renderNavName(w, nav); }, .undef => |ty| try dg.renderUndefValue(w, ty, .Other), - .identifier => |ident| try w.print("{f }", .{fmtIdent(ident)}), - .payload_identifier => |ident| try w.print("{f }.{f }", .{ - fmtIdent("payload"), - fmtIdent(ident), + .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}), + .payload_identifier => |ident| try w.print("{f}.{f}", .{ + fmtIdentSolo("payload"), + fmtIdentSolo(ident), }), - .ctype_pool_string => |string| try w.print("{f }", .{ - fmtCTypePoolString(string, &dg.ctype_pool), + .ctype_pool_string => |string| try w.print("{f}", .{ + fmtCTypePoolString(string, &dg.ctype_pool, true), }), } } @@ -2256,10 +2270,10 @@ pub const DeclGen = struct { }, .nav_ref => |nav| try dg.renderNavName(w, nav), .undef => unreachable, - .identifier => |ident| try w.print("(*{f })", .{fmtIdent(ident)}), - .payload_identifier => |ident| try w.print("(*{f }.{f })", .{ - fmtIdent("payload"), - fmtIdent(ident), + .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}), + .payload_identifier => |ident| try w.print("(*{f}.{f})", .{ + fmtIdentSolo("payload"), + fmtIdentSolo(ident), }), } } @@ -2318,7 +2332,7 @@ pub const DeclGen = struct { const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); - const fwd = &dg.fwd_decl.buffered_writer; + const fwd = &dg.fwd_decl.writer; try fwd.writeAll(switch (flags.linkage) { .internal => "static ", .strong, .weak, .link_once => "zig_extern ", @@ -2349,15 +2363,15 @@ pub const DeclGen = struct { const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); if (nav.getExtern(ip)) |@"extern"| { - try w.print("{f }", .{ - fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), + try w.print("{f}", .{ + fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), }); } else { // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), // expand to 3x the length of its input, but let's cut it off at a much shorter limit. const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); try w.print("{f}__{d}", .{ - fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]), + fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]), @intFromEnum(nav_index), }); } @@ -2406,7 +2420,7 @@ pub const DeclGen = struct { }; if (is_big) try w.print(", {}", .{int_info.signedness == .signed}); - try w.print(", {f}", .{try dg.fmtIntLiteral( + try w.print(", {f}", .{try dg.fmtIntLiteralDec( try pt.intValue(if (is_big) .u16 else .u8, int_info.bits), .FunctionArgument, )}); @@ -2416,18 +2430,38 @@ pub const DeclGen = struct { dg: *DeclGen, val: Value, loc: ValueRenderLocation, - ) !std.fmt.Formatter(formatIntLiteral) { + base: u8, + case: std.fmt.Case, + ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { const zcu = dg.pt.zcu; const kind = loc.toCTypeKind(); const ty = val.typeOf(zcu); - return std.fmt.Formatter(formatIntLiteral){ .data = .{ + return .{ .data = .{ .dg = dg, .int_info = ty.intInfo(zcu), .kind = kind, .ctype = try dg.ctypeFromType(ty, kind), .val = val, + .base = base, + .case = case, } }; } + + fn fmtIntLiteralDec( + dg: *DeclGen, + val: Value, + loc: ValueRenderLocation, + ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { + return fmtIntLiteral(dg, val, loc, 10, .lower); + } + + fn fmtIntLiteralHex( + dg: *DeclGen, + val: Value, + loc: ValueRenderLocation, + ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { + return fmtIntLiteral(dg, val, loc, 16, .lower); + } }; const CTypeFix = enum { prefix, suffix }; @@ -2437,13 +2471,7 @@ const RenderCTypeTrailing = enum { no_space, maybe_space, - pub fn format( - self: @This(), - w: *Writer, - comptime fmt: []const u8, - ) Writer.Error!void { - if (fmt.len != 0) @compileError("invalid format string '" ++ - fmt ++ "' for type '" ++ @typeName(@This()) ++ "'"); + pub fn format(self: @This(), w: *Writer) Writer.Error!void { switch (self) { .no_space => {}, .maybe_space => try w.writeByte(' '), @@ -2465,7 +2493,7 @@ fn renderFwdDeclTypeName( switch (fwd_decl.name) { .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}), .index => |index| try w.print("{f}__{d}", .{ - fmtIdent(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)), + fmtIdentUnsolo(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)), @intFromEnum(index), }), } @@ -2679,7 +2707,7 @@ fn renderFields( .suffix, .{}, ); - try w.print("{f}{f }", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool) }); + try w.print("{f}{f}", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool, true) }); try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{}); try w.writeAll(";\n"); } @@ -2771,7 +2799,7 @@ pub fn genTypeDecl( pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void { for (zcu.global_assembly.values()) |asm_source| { - try w.print("__asm({fs});\n", .{fmtStringLiteral(asm_source, null)}); + try w.print("__asm({f});\n", .{fmtStringLiteral(asm_source, null)}); } } @@ -2779,7 +2807,7 @@ pub fn genErrDecls(o: *Object) Error!void { const pt = o.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const w = &o.code.buffered_writer; + const w = &o.code.writer; var max_name_len: usize = 0; // 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 { const name = name_nts.toSlice(ip); if (val > 1) try w.writeAll(", "); try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{ - fmtIdent(name), - try o.dg.fmtIntLiteral(try pt.intValue(.usize, name.len), .StaticInitializer), + fmtIdentUnsolo(name), + try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer), }); } try w.writeAll("};"); @@ -2871,7 +2899,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn const zcu = pt.zcu; const ip = &zcu.intern_pool; const ctype_pool = &o.dg.ctype_pool; - const w = &o.code.buffered_writer; + const w = &o.code.writer; const key = lazy_fn.key_ptr.*; const val = lazy_fn.value_ptr; switch (key) { @@ -2906,7 +2934,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn } }); try w.print("case {f}: {{", .{ - try o.dg.fmtIntLiteral(try tag_val.intFromEnum(enum_ty, pt), .Other), + try o.dg.fmtIntLiteralDec(try tag_val.intFromEnum(enum_ty, pt), .Other), }); o.indent(); try o.newline(); @@ -2919,8 +2947,8 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn try w.writeAll("return ("); try o.dg.renderType(w, name_slice_ty); try w.print("){{{f}, {f}}};", .{ - fmtIdent("name"), - try o.dg.fmtIntLiteral(try pt.intValue(.usize, tag_name_len), .Other), + fmtIdentUnsolo("name"), + try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other), }); try o.newline(); try o.outdent(); @@ -2939,9 +2967,9 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn const fn_val = zcu.navValue(fn_nav_index); const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete); const fn_info = fn_ctype.info(ctype_pool).function; - const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool); + const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool, true); - const fwd = &o.dg.fwd_decl.buffered_writer; + const fwd = &o.dg.fwd_decl.writer; try fwd.print("static zig_{s} ", .{@tagName(key)}); try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{ .fmt_ctype_pool_string = fn_name, @@ -3001,20 +3029,20 @@ pub fn generate( .pass = .{ .nav = func.owner_nav }, .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, .expected_block = null, - .fwd_decl = undefined, + .fwd_decl = .init(gpa), .ctype_pool = .empty, .scratch = .empty, .uavs = .empty, }, - .code_header = undefined, - .code = undefined, + .code_header = .init(gpa), + .code = .init(gpa), .indent_counter = 0, }, .lazy_fns = .empty, }; defer { - function.object.code_header.init(gpa); - function.object.code.init(gpa); + function.object.code_header.deinit(); + function.object.code.deinit(); function.object.dg.fwd_decl.deinit(); function.object.dg.ctype_pool.deinit(gpa); function.object.dg.scratch.deinit(gpa); @@ -3022,18 +3050,17 @@ pub fn generate( function.deinit(); } try function.object.dg.ctype_pool.init(gpa); - function.object.dg.fwd_decl.init(gpa); - function.object.code_header.init(gpa); - function.object.code.init(gpa); genFunc(&function) catch |err| switch (err) { error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?), - error.OutOfMemory => |e| return e, + error.OutOfMemory => return error.OutOfMemory, + error.WriteFailed => return error.OutOfMemory, }; var mir: Mir = .{ .uavs = .empty, .code = &.{}, + .code_header = &.{}, .fwd_decl = &.{}, .ctype_pool = .empty, .lazy_fns = .empty, @@ -3060,7 +3087,7 @@ pub fn genFunc(f: *Function) Error!void { const nav_val = zcu.navValue(nav_index); const nav = ip.getNav(nav_index); - const fwd = &o.dg.fwd_decl.buffered_writer; + const fwd = &o.dg.fwd_decl.writer; try fwd.writeAll("static "); try o.dg.renderFunctionSignature( fwd, @@ -3071,9 +3098,9 @@ pub fn genFunc(f: *Function) Error!void { ); try fwd.writeAll(";\n"); - const ch = &o.code_header.buffered_writer; + const ch = &o.code_header.writer; if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| - try ch.print("zig_linksection_fn({fs}) ", .{fmtStringLiteral(s, null)}); + try ch.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderFunctionSignature( ch, nav_val, @@ -3089,7 +3116,7 @@ pub fn genFunc(f: *Function) Error!void { o.indent(); try genBodyResolveState(f, undefined, &.{}, main_body, true); try o.outdent(); - try o.code.buffered_writer.writeByte('}'); + try o.code.writer.writeByte('}'); try o.newline(); if (o.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); @@ -3150,7 +3177,7 @@ pub fn genDecl(o: *Object) Error!void { .visibility = @"extern".visibility, }); - const fwd = &o.dg.fwd_decl.buffered_writer; + const fwd = &o.dg.fwd_decl.writer; try fwd.writeAll("zig_extern "); try o.dg.renderFunctionSignature( fwd, @@ -3171,10 +3198,10 @@ pub fn genDecl(o: *Object) Error!void { .linkage = .internal, .visibility = .default, }); - const w = &o.code.buffered_writer; + const w = &o.code.writer; if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s| - try w.print("zig_linksection({fs}) ", .{fmtStringLiteral(s, null)}); + try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderTypeAndName( w, nav_ty, @@ -3208,14 +3235,14 @@ pub fn genDeclValue( const zcu = o.dg.pt.zcu; const ty = val.typeOf(zcu); - const fwd = &o.dg.fwd_decl.buffered_writer; + const fwd = &o.dg.fwd_decl.writer; try fwd.writeAll("static "); try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete); try fwd.writeAll(";\n"); - const w = &o.code.buffered_writer; + const w = &o.code.writer; if (@"linksection".toSlice(&zcu.intern_pool)) |s| - try w.print("zig_linksection({fs}) ", .{fmtStringLiteral(s, null)}); + try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete); try w.writeAll(" = "); try o.dg.renderValue(w, val, .StaticInitializer); @@ -3226,7 +3253,7 @@ pub fn genDeclValue( pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void { const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; - const fwd = &dg.fwd_decl.buffered_writer; + const fwd = &dg.fwd_decl.writer; const main_name = export_indices[0].ptr(zcu).opts.name; try fwd.writeAll("#define "); @@ -3235,7 +3262,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)), } try fwd.writeByte(' '); - try fwd.print("{f }", .{fmtIdent(main_name.toSlice(ip))}); + try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))}); try fwd.writeByte('\n'); const exported_val = exported.getValue(zcu); @@ -3265,7 +3292,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const const @"export" = export_index.ptr(zcu); try fwd.writeAll("zig_extern "); if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); - if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({fs}) ", .{ + if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({f}) ", .{ fmtStringLiteral(s, null), }); const extern_name = @"export".opts.name.toSlice(ip); @@ -3280,17 +3307,17 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const .complete, ); if (is_mangled and is_export) { - try fwd.print(" zig_mangled_export({f }, {fs}, {fs})", .{ - fmtIdent(extern_name), + try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{ + fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), fmtStringLiteral(main_name.toSlice(ip), null), }); } else if (is_mangled) { - try fwd.print(" zig_mangled({f }, {fs})", .{ - fmtIdent(extern_name), fmtStringLiteral(extern_name, null), + try fwd.print(" zig_mangled({f}, {f})", .{ + fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), }); } else if (is_export) { - try fwd.print(" zig_export({fs}, {fs})", .{ + try fwd.print(" zig_export({f}, {f})", .{ fmtStringLiteral(main_name.toSlice(ip), null), fmtStringLiteral(extern_name, null), }); @@ -3304,7 +3331,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const /// have been added to `free_locals_map`. For a version of this function that restores this state, /// see `genBodyResolveState`. fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (body.len == 0) { try w.writeAll("{}"); } else { @@ -3326,7 +3353,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void { fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void { if (body.len == 0) { // Don't go to the expense of cloning everything! - if (!inner) try f.object.code.buffered_writer.writeAll("{}"); + if (!inner) try f.object.code.writer.writeAll("{}"); return; } @@ -3643,7 +3670,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { .ret => return airRet(f, inst, false), .ret_safe => return airRet(f, inst, false), // TODO .ret_load => return airRet(f, inst, true), - .trap => return airTrap(f, &f.object.code.buffered_writer), + .trap => return airTrap(f, &f.object.code.writer), .unreach => return airUnreach(&f.object), // Instructions which may be `noreturn`. @@ -3687,7 +3714,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -3713,7 +3740,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -3740,7 +3767,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -3775,7 +3802,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -3803,7 +3830,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -3832,7 +3859,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -3895,7 +3922,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { .{ .arg_array = i }; if (f.liveness.isUnused(inst)) { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try w.writeByte('('); try f.renderType(w, .void); try w.writeByte(')'); @@ -3934,7 +3961,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const is_array = lowersToArray(src_ty, pt); const need_memcpy = !is_aligned or is_array; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, src_ty); const v = try Vectorize.start(f, inst, w, ptr_ty); @@ -3979,7 +4006,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeByte('('); try f.writeCValueDeref(w, operand); try v.elem(f, w); - try w.print(", {f})", .{try f.fmtIntLiteral(bit_offset_val)}); + try w.print(", {f})", .{try f.fmtIntLiteralDec(bit_offset_val)}); if (cant_cast) try w.writeByte(')'); try f.object.dg.renderBuiltinInfo(w, field_ty, .bits); try w.writeByte(')'); @@ -4001,7 +4028,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void { const pt = f.object.dg.pt; const zcu = pt.zcu; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const op_inst = un_op.toIndex(); const op_ty = f.typeOf(un_op); 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 { try f.writeCValueDeref(w, ret_val) else try f.writeCValue(w, ret_val, .Other); - try w.write(";\n"); + try w.writeAll(";\n"); if (is_array) { try freeLocal(f, inst, ret_val.new_local, null); } @@ -4066,7 +4093,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); 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 { const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits); if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); 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 { try w.writeByte('('); try f.writeCValue(w, operand, .FunctionArgument); try v.elem(f, w); - try w.print(", {fx})", .{ - try f.fmtIntLiteral(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)), + try w.print(", {f})", .{ + try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)), }); }, .signed => { @@ -4154,9 +4181,9 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(w, operand, .FunctionArgument); try v.elem(f, w); if (c_bits == 128) try w.writeByte(')'); - try w.print(", {f})", .{try f.fmtIntLiteral(shift_val)}); + try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)}); if (c_bits == 128) try w.writeByte(')'); - try w.print(", {f})", .{try f.fmtIntLiteral(shift_val)}); + try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)}); }, } if (need_lo) try w.writeByte(')'); @@ -4180,7 +4207,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndefDeep(zcu) else false; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (val_is_undef) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 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 { try w.writeByte('('); try f.writeCValueDeref(w, ptr_val); try v.elem(f, w); - try w.print(", {fx}), zig_shl_", .{try f.fmtIntLiteral(mask_val)}); + try w.print(", {f}), zig_shl_", .{try f.fmtIntLiteralHex(mask_val)}); try f.object.dg.renderTypeForBuiltinFnName(w, host_ty); try w.writeByte('('); 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 { try f.writeCValue(w, src_val, .Other); try v.elem(f, w); if (cant_cast) try w.writeByte(')'); - try w.print(", {f}))", .{try f.fmtIntLiteral(bit_offset_val)}); + try w.print(", {f}))", .{try f.fmtIntLiteralDec(bit_offset_val)}); try a.end(f, w); try v.end(f, inst, w); } else { @@ -4335,7 +4362,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: const operand_ty = f.typeOf(bin_op.lhs); const scalar_ty = operand_ty.scalarType(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); try f.writeCValueMember(w, local, .{ .field = 1 }); @@ -4374,7 +4401,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); try f.writeCValue(w, local, .Other); @@ -4411,7 +4438,7 @@ fn airBinOp( const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); try f.writeCValue(w, local, .Other); @@ -4462,7 +4489,7 @@ fn airCmpOp( const rhs_ty = f.typeOf(data.rhs); const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, lhs_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); @@ -4515,7 +4542,7 @@ fn airEquality( const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, .bool); const a = try Assignment.start(f, w, .bool); try f.writeCValue(w, local, .Other); @@ -4573,12 +4600,12 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, .bool); try f.writeCValue(w, local, .Other); try w.writeAll(" = "); try f.writeCValue(w, operand, .Other); - try w.print(" < sizeof({f }) / sizeof(*{0f });", .{fmtIdent("zig_errorName")}); + try w.print(" < sizeof({f}) / sizeof(*{0f});", .{fmtIdentSolo("zig_errorName")}); try f.object.newline(); return local; } @@ -4600,7 +4627,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); const local = try f.allocLocal(inst, inst_ty); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const v = try Vectorize.start(f, inst, w, inst_ty); const a = try Assignment.start(f, w, inst_scalar_ctype); try f.writeCValue(w, local, .Other); @@ -4642,7 +4669,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); try f.writeCValue(w, local, .Other); @@ -4682,7 +4709,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const ptr_ty = inst_ty.slicePtrFieldType(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); { const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete)); @@ -4713,7 +4740,7 @@ fn airCall( if (f.object.dg.is_naked_fn) return .none; const gpa = f.object.dg.gpa; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = f.air.extraData(Air.Call, pl_op.payload); @@ -4864,7 +4891,7 @@ fn airCall( fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; // TODO re-evaluate whether to emit these or not. If we naively emit // these directives, the output file will report bogus line numbers because // every newline after the #line directive adds one to the line. @@ -4880,7 +4907,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { } fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue { - try f.object.code.buffered_writer.writeAll("(void)0;"); + try f.object.code.writer.writeAll("(void)0;"); try f.object.newline(); return .none; } @@ -4892,7 +4919,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload); const owner_nav = ip.getNav(zcu.funcInfo(extra.data.func).owner_nav); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)}); try f.object.newline(); 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 { if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{pl_op.operand}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) }); try f.object.newline(); return .none; @@ -4927,7 +4954,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) const block_id = f.next_block_index; f.next_block_index += 1; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const inst_ty = f.typeOfIndex(inst); const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst)) @@ -4996,7 +5023,7 @@ fn lowerTry( const err_union = try f.resolveInst(operand); const inst_ty = f.typeOfIndex(inst); const liveness_condbr = f.liveness.getCondBr(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const payload_ty = err_union_ty.errorUnionPayload(zcu); const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu); @@ -5058,7 +5085,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void { const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br; const block = f.blocks.get(branch.block_inst).?; const result = block.result; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (f.object.dg.is_naked_fn) { 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 { fn airRepeat(f: *Function, inst: Air.Inst.Index) !void { const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat; - try f.object.code.buffered_writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)}); + try f.object.code.writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)}); } fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void { const pt = f.object.dg.pt; const zcu = pt.zcu; const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (try f.air.value(br.operand, pt)) |cond_val| { // 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 const zcu = pt.zcu; const target = &f.object.dg.mod.resolved_target.result; const ctype_pool = &f.object.dg.ctype_pool; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) { const src_info = dest_ty.intInfo(zcu); @@ -5169,13 +5196,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal const operand_lval = if (operand == .constant) blk: { const operand_local = try f.allocLocal(null, operand_ty); try f.writeCValue(w, operand_local, .Other); - if (operand_ty.isAbiInt(zcu)) { - try w.writeAll(" = "); - } else { - try w.writeAll(" = ("); - try f.renderType(w, operand_ty); - try w.writeByte(')'); - } + try w.writeAll(" = "); try f.writeCValue(w, operand, .Other); try w.writeByte(';'); try f.object.newline(); @@ -5264,14 +5285,14 @@ fn airTrap(f: *Function, w: *Writer) !void { } fn airBreakpoint(f: *Function) !CValue { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try w.writeAll("zig_breakpoint();"); try f.object.newline(); return .none; } fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, .usize); try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); @@ -5282,7 +5303,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, .usize); try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); @@ -5295,14 +5316,14 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnreach(o: *Object) !void { // Not even allowed to call unreachable in a naked function. if (o.dg.is_naked_fn) return; - try o.code.buffered_writer.writeAll("zig_unreachable();\n"); + try o.code.writer.writeAll("zig_unreachable();\n"); } fn airLoop(f: *Function, inst: Air.Inst.Index) !void { const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const loop = f.air.extraData(Air.Block, ty_pl.payload); const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[loop.end..][0..loop.data.body_len]); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; // `repeat` instructions matching this loop will branch to // this label. Since we need a label for arbitrary `repeat` @@ -5321,7 +5342,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void { const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]); const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); const liveness_condbr = f.liveness.getCondBr(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try w.writeAll("if ("); try f.writeCValue(w, cond, .Other); @@ -5354,7 +5375,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void const init_condition = try f.resolveInst(switch_br.operand); try reap(f, inst, &.{switch_br.operand}); const condition_ty = f.typeOf(switch_br.operand); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; // For dispatches, we will create a local alloc to contain the condition value. // 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 write_val: { if (condition_ty.isPtrAtRuntime(zcu)) { if (item_value.?.getUnsignedInt(zcu)) |item_int| { - try w.print("{f}", .{try f.fmtIntLiteral(try pt.intValue(lowered_condition_ty, item_int))}); + try w.print("{f}", .{try f.fmtIntLiteralDec(try pt.intValue(lowered_condition_ty, item_int))}); break :write_val; } } @@ -5534,7 +5555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += inputs.len; const result = result: { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const inst_ty = f.typeOfIndex(inst); const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: { const inst_local = try f.allocLocalValue(.{ @@ -5683,7 +5704,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeAll("__asm"); if (is_volatile) try w.writeAll(" volatile"); - try w.print("({fs}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); + try w.print("({f}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); } extra_i = constraints_extra_begin; @@ -5701,7 +5722,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeByte(' '); if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name}); const is_reg = constraint[1] == '{'; - try w.print("{fs}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); + try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); if (is_reg) { try f.writeCValue(w, .{ .local = locals_index }, .Other); locals_index += 1; @@ -5727,7 +5748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[0] == '{'; const input_val = try f.resolveInst(input); - try w.print("{fs}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); + try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); try f.writeCValue(w, if (asmInputNeedsLocal(f, constraint, input_val)) local: { const input_local_idx = locals_index; locals_index += 1; @@ -5745,7 +5766,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (clobber.len == 0) continue; if (clobber_i > 0) try w.writeByte(','); - try w.print(" {fs}", .{fmtStringLiteral(clobber, null)}); + try w.print(" {f}", .{fmtStringLiteral(clobber, null)}); } try w.writeAll(");"); try f.object.newline(); @@ -5800,7 +5821,7 @@ fn airIsNull( const ctype_pool = &f.object.dg.ctype_pool; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -5868,7 +5889,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue .aligned, .array, .vector, .fwd_decl, .function => unreachable, .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { .is_null, .payload => { - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -5890,7 +5911,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.typeOf(ty_op.operand); @@ -6040,7 +6061,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { const field_ptr_val = try f.resolveInst(extra.field_ptr); try reap(f, inst, &.{extra.field_ptr}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, container_ptr_ty); try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); @@ -6070,7 +6091,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeByte(')'); try f.writeCValue(w, field_ptr_val, .Other); try w.print(" - {f})", .{ - try f.fmtIntLiteral(try pt.intValue(.usize, byte_offset)), + try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)), }); }, } @@ -6095,7 +6116,7 @@ fn fieldPtr( // Ensure complete type definition is visible before accessing fields. _ = try f.ctypeFromType(container_ty, .complete); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, field_ptr_ty); try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); @@ -6116,7 +6137,7 @@ fn fieldPtr( try w.writeByte(')'); try f.writeCValue(w, container_ptr_val, .Other); try w.print(" + {f})", .{ - try f.fmtIntLiteral(try pt.intValue(.usize, byte_offset)), + try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)), }); }, } @@ -6142,7 +6163,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const struct_byval = try f.resolveInst(extra.struct_operand); try reap(f, inst, &.{extra.struct_operand}); const struct_ty = f.typeOf(extra.struct_operand); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; // Ensure complete type definition is visible before accessing fields. _ = try f.ctypeFromType(struct_ty, .complete); @@ -6189,7 +6210,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { } try f.writeCValue(w, struct_byval, .Other); if (bit_offset > 0) try w.print(", {f})", .{ - try f.fmtIntLiteral(try pt.intValue(bit_offset_ty, bit_offset)), + try f.fmtIntLiteralDec(try pt.intValue(bit_offset_ty, bit_offset)), }); if (cant_cast) try w.writeByte(')'); try f.object.dg.renderBuiltinInfo(w, field_int_ty, .bits); @@ -6291,7 +6312,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { return local; } - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try f.writeCValue(w, local, .Other); try w.writeAll(" = "); @@ -6299,7 +6320,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(w, operand, .Other) else if (error_ty.errorSetIsEmpty(zcu)) try w.print("{f}", .{ - try f.fmtIntLiteral(try pt.intValue(try pt.errorIntType(), 0)), + try f.fmtIntLiteralDec(try pt.intValue(try pt.errorIntType(), 0)), }) else if (operand_is_ptr) try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }) @@ -6321,7 +6342,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu const operand_ty = f.typeOf(ty_op.operand); const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) { if (!is_ptr) return .none; @@ -6363,7 +6384,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { .is_null, .payload => { const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); { const a = try Assignment.start(f, w, .bool); @@ -6399,7 +6420,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const err = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); 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 { fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); @@ -6447,7 +6468,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete)); try f.writeCValueDeref(w, operand); try a.assign(f, w); - try w.print("{f}", .{try f.fmtIntLiteral(no_err)}); + try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)}); try a.end(f, w); return .none; } @@ -6455,7 +6476,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete)); try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }); try a.assign(f, w); - try w.print("{f}", .{try f.fmtIntLiteral(no_err)}); + try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)}); try a.end(f, w); } @@ -6499,7 +6520,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const err_ty = inst_ty.errorUnionSet(zcu); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); if (!repr_is_err) { 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 const zcu = pt.zcu; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); const operand_ty = f.typeOf(un_op); @@ -6567,7 +6588,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.typeOfIndex(inst); const ptr_ty = inst_ty.slicePtrFieldType(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const operand_ty = f.typeOf(ty_op.operand); const array_ty = operand_ty.childType(zcu); @@ -6593,7 +6614,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (operand_child_ctype.info(ctype_pool) == .array) { try w.writeByte('&'); try f.writeCValueDeref(w, operand); - try w.print("[{f}]", .{try f.fmtIntLiteral(.zero_usize)}); + try w.print("[{f}]", .{try f.fmtIntLiteralDec(.zero_usize)}); } else try f.writeCValue(w, operand, .Other); } try a.end(f, w); @@ -6603,7 +6624,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValueMember(w, local, .{ .identifier = "len" }); try a.assign(f, w); try w.print("{f}", .{ - try f.fmtIntLiteral(try pt.intValue(.usize, array_ty.arrayLen(zcu))), + try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))), }); try a.end(f, w); } @@ -6632,7 +6653,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { else unreachable; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); @@ -6682,7 +6703,7 @@ fn airUnBuiltinCall( const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); if (!ref_ret) { @@ -6733,7 +6754,7 @@ fn airBinBuiltinCall( const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const v = try Vectorize.start(f, inst, w, operand_ty); @@ -6784,7 +6805,7 @@ fn airCmpBuiltinCall( const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); if (!ref_ret) { @@ -6812,7 +6833,7 @@ fn airCmpBuiltinCall( try w.writeByte(')'); if (!ref_ret) try w.print("{s}{f}", .{ compareOperatorC(operator), - try f.fmtIntLiteral(try pt.intValue(.i32, 0)), + try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)), }); try w.writeByte(';'); try f.object.newline(); @@ -6834,7 +6855,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const ty = ptr_ty.childType(zcu); const ctype = try f.ctypeFromType(ty, .complete); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const new_value_mat = try Materialize.start(f, inst, ty, new_value); try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value }); @@ -6941,7 +6962,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { const ptr = try f.resolveInst(pl_op.operand); const operand = try f.resolveInst(extra.operand); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const operand_mat = try Materialize.start(f, inst, ty, operand); try reap(f, inst, &.{ pl_op.operand, extra.operand }); @@ -7002,7 +7023,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { ty; const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try w.writeAll("zig_atomic_load("); @@ -7034,7 +7055,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const ptr = try f.resolveInst(bin_op.lhs); const element = try f.resolveInst(bin_op.rhs); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const element_mat = try Materialize.start(f, inst, ty, element); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); @@ -7082,7 +7103,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const elem_ty = f.typeOf(bin_op.rhs); const elem_abi_size = elem_ty.abiSize(zcu); const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(zcu) else false; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (val_is_undef) { if (!safety) { @@ -7206,7 +7227,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV const src_ptr = try f.resolveInst(bin_op.rhs); const dest_ty = f.typeOf(bin_op.lhs); const src_ty = f.typeOf(bin_op.rhs); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; if (dest_ty.ptrSize(zcu) != .one) { try w.writeAll("if ("); @@ -7231,10 +7252,10 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void { const pt = f.object.dg.pt; const zcu = pt.zcu; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; switch (dest_ty.ptrSize(zcu)) { .one => try w.print("{f}", .{ - try f.fmtIntLiteral(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))), + try f.fmtIntLiteralDec(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))), }), .many, .c => unreachable, .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }), @@ -7254,7 +7275,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (layout.tag_size == 0) return .none; const tag_ty = union_ty.unionTagTypeSafety(zcu).?; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete)); try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" }); try a.assign(f, w); @@ -7276,7 +7297,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (layout.tag_size == 0) return .none; const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(w, local, .Other); @@ -7294,7 +7315,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(w, local, .Other); try w.print(" = {s}(", .{ @@ -7310,7 +7331,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -7335,7 +7356,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); 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 { const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); try f.writeCValue(w, local, .Other); @@ -7390,7 +7411,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(unwrapped.operand); const inst_ty = unwrapped.result_ty; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand for (mask, 0..) |mask_elem, out_idx| { @@ -7424,7 +7445,7 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = unwrapped.result_ty; const elem_ty = inst_ty.childType(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands for (mask, 0..) |mask_elem, out_idx| { @@ -7463,7 +7484,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(reduce.operand); try reap(f, inst, &.{reduce.operand}); const operand_ty = f.typeOf(reduce.operand); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const use_operator = scalar_ty.bitSize(zcu) <= 64; const op: union(enum) { @@ -7613,7 +7634,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } } - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); switch (ip.indexToKey(inst_ty.toIntern())) { inline .array_type, .vector_type => |info, tag| { @@ -7727,7 +7748,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } try w.print(", {f}", .{ - try f.fmtIntLiteral(try pt.intValue(bit_offset_ty, bit_offset)), + try f.fmtIntLiteralDec(try pt.intValue(bit_offset_ty, bit_offset)), }); try f.object.dg.renderBuiltinInfo(w, inst_ty, .bits); try w.writeByte(')'); @@ -7772,7 +7793,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const payload = try f.resolveInst(extra.init); try reap(f, inst, &.{extra.init}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, union_ty); if (loaded_union.flagsUnordered(ip).layout == .@"packed") return f.moveCValue(inst, union_ty, payload); @@ -7785,7 +7806,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete)); try f.writeCValueMember(w, local, .{ .identifier = "tag" }); try a.assign(f, w); - try w.print("{f}", .{try f.fmtIntLiteral(try tag_val.intFromEnum(tag_ty, pt))}); + try w.print("{f}", .{try f.fmtIntLiteralDec(try tag_val.intFromEnum(tag_ty, pt))}); try a.end(f, w); } break :field .{ .payload_identifier = field_name.toSlice(ip) }; @@ -7808,7 +7829,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { const ptr = try f.resolveInst(prefetch.ptr); try reap(f, inst, &.{prefetch.ptr}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; switch (prefetch.cache) { .data => { try w.writeAll("zig_prefetch("); @@ -7830,7 +7851,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const inst_ty = f.typeOfIndex(inst); const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(w, local, .Other); @@ -7845,7 +7866,7 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{pl_op.operand}); @@ -7874,7 +7895,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(zcu); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); try f.writeCValue(w, local, .Other); @@ -7899,7 +7920,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); try f.writeCValue(w, local, .Other); try w.writeAll(" = "); @@ -7917,7 +7938,7 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function; assert(function_info.varargs); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try w.writeAll("va_start(*(va_list *)&"); try f.writeCValue(w, local, .Other); @@ -7937,7 +7958,7 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { const va_list = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(w, local, .Other); try w.writeAll(" = va_arg(*(va_list *)"); @@ -7955,7 +7976,7 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { const va_list = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; try w.writeAll("va_end(*(va_list *)"); try f.writeCValue(w, va_list, .Other); try w.writeAll(");"); @@ -7970,7 +7991,7 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { const va_list = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.buffered_writer; + const w = &f.object.code.writer; const local = try f.allocLocal(inst, inst_ty); try w.writeAll("va_copy(*(va_list *)&"); try f.writeCValue(w, local, .Other); @@ -8136,8 +8157,8 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 { const StringLiteral = struct { len: usize, cur_len: usize, - start_count: usize, w: *Writer, + first: bool, // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal, // regardless of the length of the string literal initializing it. Array initializer syntax is @@ -8154,8 +8175,8 @@ const StringLiteral = struct { return .{ .cur_len = 0, .len = len, - .start_count = w.count, .w = w, + .first = true, }; } @@ -8175,50 +8196,83 @@ const StringLiteral = struct { } } - fn writeStringLiteralChar(sl: *StringLiteral, c: u8) Writer.Error!void { + fn writeStringLiteralChar(sl: *StringLiteral, c: u8) Writer.Error!usize { + const w = sl.w; switch (c) { - 7 => try sl.w.writeAll("\\a"), - 8 => try sl.w.writeAll("\\b"), - '\t' => try sl.w.writeAll("\\t"), - '\n' => try sl.w.writeAll("\\n"), - 11 => try sl.w.writeAll("\\v"), - 12 => try sl.w.writeAll("\\f"), - '\r' => try sl.w.writeAll("\\r"), - '"', '\'', '?', '\\' => try sl.w.print("\\{c}", .{c}), - else => switch (c) { - ' '...'~' => try sl.w.writeByte(c), - else => try sl.w.print("\\{o:0>3}", .{c}), + 7 => { + try w.writeAll("\\a"); + return 2; + }, + 8 => { + try w.writeAll("\\b"); + return 2; + }, + '\t' => { + try w.writeAll("\\t"); + return 2; + }, + '\n' => { + try w.writeAll("\\n"); + return 2; + }, + 11 => { + try w.writeAll("\\v"); + return 2; + }, + 12 => { + try w.writeAll("\\f"); + return 2; + }, + '\r' => { + try w.writeAll("\\r"); + return 2; + }, + '"', '\'', '?', '\\' => { + try w.print("\\{c}", .{c}); + return 2; + }, + ' '...'!', '#'...'&', '('...'>', '@'...'[', ']'...'~' => { + try w.writeByte(c); + return 1; + }, + else => { + var buf: [4]u8 = undefined; + const printed = std.fmt.bufPrint(&buf, "\\{o:0>3}", .{c}) catch unreachable; + try w.writeAll(printed); + return printed.len; }, } } pub fn writeChar(sl: *StringLiteral, c: u8) Writer.Error!void { if (sl.len <= max_string_initializer_len) { - if (sl.cur_len == 0 and sl.w.count - sl.start_count > 1) - try sl.w.writeAll("\"\""); + if (sl.cur_len == 0 and !sl.first) try sl.w.writeAll("\"\""); - const count = sl.w.count; - try sl.writeStringLiteralChar(c); - const char_len = sl.w.count - count; + const char_len = try sl.writeStringLiteralChar(c); assert(char_len <= max_char_len); sl.cur_len += char_len; - if (sl.cur_len >= max_literal_len) sl.cur_len = 0; + if (sl.cur_len >= max_literal_len) { + sl.cur_len = 0; + sl.first = false; + } } else { - if (sl.w.count - sl.start_count > 1) try sl.w.writeByte(','); - try sl.w.print("'\\x{x}'", .{c}); + if (!sl.first) try sl.w.writeByte(','); + var buf: [6]u8 = undefined; + const printed = std.fmt.bufPrint(&buf, "'\\x{x}'", .{c}) catch unreachable; + try sl.w.writeAll(printed); + sl.cur_len += printed.len; + sl.first = false; } } }; -const FormatStringContext = struct { str: []const u8, sentinel: ?u8 }; -fn formatStringLiteral( - data: FormatStringContext, - w: *Writer, - comptime fmt: []const u8, -) Writer.Error!void { - if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt); +const FormatStringContext = struct { + str: []const u8, + sentinel: ?u8, +}; +fn formatStringLiteral(data: FormatStringContext, w: *std.io.Writer) std.io.Writer.Error!void { var literal: StringLiteral = .init(w, data.str.len + @intFromBool(data.sentinel != null)); try literal.start(); for (data.str) |c| try literal.writeChar(c); @@ -8226,7 +8280,7 @@ fn formatStringLiteral( try literal.end(); } -fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) { +fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(FormatStringContext, formatStringLiteral) { return .{ .data = .{ .str = str, .sentinel = sentinel } }; } @@ -8242,12 +8296,10 @@ const FormatIntLiteralContext = struct { kind: CType.Kind, ctype: CType, val: Value, + base: u8, + case: std.fmt.Case, }; -fn formatIntLiteral( - data: FormatIntLiteralContext, - w: *Writer, - comptime fmt: []const u8, -) Writer.Error!void { +fn formatIntLiteral(data: FormatIntLiteralContext, w: *std.io.Writer) std.io.Writer.Error!void { const pt = data.dg.pt; const zcu = pt.zcu; const target = &data.dg.mod.resolved_target.result; @@ -8337,32 +8389,14 @@ fn formatIntLiteral( if (!int.positive) try w.writeByte('-'); try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool); - const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) { - 0 => .{ .base = 10 }, - 1 => switch (fmt[0]) { - 'b' => style: { - try w.writeAll("0b"); - break :style .{ .base = 2 }; - }, - 'o' => style: { - try w.writeByte('0'); - break :style .{ .base = 8 }; - }, - 'd' => .{ .base = 10 }, - 'x', 'X' => |base| style: { - try w.writeAll("0x"); - break :style .{ .base = 16, .case = switch (base) { - 'x' => .lower, - 'X' => .upper, - else => unreachable, - } }; - }, - else => @compileError("Invalid fmt: " ++ fmt), - }, - else => @compileError("Invalid fmt: " ++ fmt), - }; - - const string = int.abs().toStringAlloc(allocator, style.base, style.case) catch + switch (data.base) { + 2 => try w.writeAll("0b"), + 8 => try w.writeByte('0'), + 10 => {}, + 16 => try w.writeAll("0x"), + else => unreachable, + } + const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch return error.WriteFailed; defer allocator.free(string); try w.writeAll(string); @@ -8418,7 +8452,9 @@ fn formatIntLiteral( .ctype = c_limb_ctype, .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch return error.WriteFailed, - }, w, fmt); + .base = data.base, + .case = data.case, + }, w); } } try data.ctype.renderLiteralSuffix(w, ctype_pool); @@ -8499,11 +8535,11 @@ const Vectorize = struct { try w.writeAll("for ("); try f.writeCValue(w, local, .Other); - try w.print(" = {fd}; ", .{try f.fmtIntLiteral(.zero_usize)}); + try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)}); try f.writeCValue(w, local, .Other); - try w.print(" < {fd}; ", .{try f.fmtIntLiteral(try pt.intValue(.usize, ty.vectorLen(zcu)))}); + try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))}); try f.writeCValue(w, local, .Other); - try w.print(" += {fd}) {{\n", .{try f.fmtIntLiteral(.one_usize)}); + try w.print(" += {f}) {{\n", .{try f.fmtIntLiteralDec(.one_usize)}); f.object.indent(); try f.object.newline(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9a35440eaf31a390b852bfa9899b3f1b6693d00b..3d670dce83c7cc5e901ccb29577e84247e9b5c73 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -21,11 +21,11 @@ const Air = @import("../Air.zig"); const Value = @import("../Value.zig"); const Type = @import("../Type.zig"); const x86_64_abi = @import("../arch/x86_64/abi.zig"); -const wasm_c_abi = @import("../arch/wasm/abi.zig"); -const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); -const arm_c_abi = @import("../arch/arm/abi.zig"); +const wasm_c_abi = @import("wasm/abi.zig"); +const aarch64_c_abi = @import("aarch64/abi.zig"); +const arm_c_abi = @import("arm/abi.zig"); const riscv_c_abi = @import("../arch/riscv64/abi.zig"); -const mips_c_abi = @import("../arch/mips/abi.zig"); +const mips_c_abi = @import("mips/abi.zig"); const dev = @import("../dev.zig"); const target_util = @import("../target.zig"); @@ -945,7 +945,9 @@ pub const Object = struct { if (std.mem.eql(u8, path, "-")) { o.builder.dump(); } else { - _ = o.builder.printToFile(path); + o.builder.printToFilePath(std.fs.cwd(), path) catch |err| { + log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); + }; } } @@ -1053,6 +1055,7 @@ pub const Object = struct { comp.data_sections, float_abi, if (target_util.llvmMachineAbi(&comp.root_mod.resolved_target.result)) |s| s.ptr else null, + target_util.useEmulatedTls(&comp.root_mod.resolved_target.result), ); errdefer target_machine.dispose(); @@ -2765,7 +2768,7 @@ pub const Object = struct { llvm_arg_i += 1; } - if (fn_info.cc == .@"async") { + if (fn_info.cc == .async) { @panic("TODO: LLVM backend lower async function"); } @@ -2917,7 +2920,7 @@ pub const Object = struct { try attributes.addFnAttr(.nounwind, &o.builder); if (owner_mod.unwind_tables != .none) { try attributes.addFnAttr( - .{ .uwtable = if (owner_mod.unwind_tables == .@"async") .@"async" else .sync }, + .{ .uwtable = if (owner_mod.unwind_tables == .async) .async else .sync }, &o.builder, ); } @@ -5280,7 +5283,7 @@ pub const FuncGen = struct { switch (modifier) { .auto, .always_tail => {}, .never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder), - .async_kw, .no_async, .always_inline, .compile_time => unreachable, + .no_suspend, .always_inline, .compile_time => unreachable, } const ret_ptr = if (!sret) null else blk: { @@ -5288,7 +5291,7 @@ pub const FuncGen = struct { try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder); const alignment = return_type.abiAlignment(zcu).toLlvm(); - const ret_ptr = try self.buildAllocaWorkaround(return_type, alignment); + const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment); try llvm_args.append(ret_ptr); break :blk ret_ptr; }; @@ -5336,7 +5339,7 @@ pub const FuncGen = struct { const alignment = param_ty.abiAlignment(zcu).toLlvm(); const param_llvm_ty = try o.lowerType(param_ty); - const arg_ptr = try self.buildAllocaWorkaround(param_ty, alignment); + const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); if (isByRef(param_ty, zcu)) { const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, ""); _ = try self.wip.store(.normal, loaded, arg_ptr, alignment); @@ -5359,7 +5362,7 @@ pub const FuncGen = struct { // LLVM does not allow bitcasting structs so we must allocate // a local, store as one type, and then load as another type. const alignment = param_ty.abiAlignment(zcu).toLlvm(); - const int_ptr = try self.buildAllocaWorkaround(param_ty, alignment); + const int_ptr = try self.buildAlloca(int_llvm_ty, alignment); _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment); const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, ""); try llvm_args.append(loaded); @@ -5495,7 +5498,7 @@ pub const FuncGen = struct { .auto, .never_inline => .normal, .never_tail => .notail, .always_tail => .musttail, - .async_kw, .no_async, .always_inline, .compile_time => unreachable, + .no_suspend, .always_inline, .compile_time => unreachable, }, toLlvmCallConvTag(fn_info.cc, target).?, try attributes.finish(&o.builder), @@ -5734,7 +5737,7 @@ pub const FuncGen = struct { const llvm_va_list_ty = try o.lowerType(va_list_ty); const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm(); - const dest_list = try self.buildAllocaWorkaround(va_list_ty, result_alignment); + const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment); _ = try self.wip.callIntrinsic(.normal, .none, .va_copy, &.{dest_list.typeOfWip(&self.wip)}, &.{ dest_list, src_list }, ""); return if (isByRef(va_list_ty, zcu)) @@ -5759,7 +5762,7 @@ pub const FuncGen = struct { const llvm_va_list_ty = try o.lowerType(va_list_ty); const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm(); - const dest_list = try self.buildAllocaWorkaround(va_list_ty, result_alignment); + const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment); _ = try self.wip.callIntrinsic(.normal, .none, .va_start, &.{dest_list.typeOfWip(&self.wip)}, &.{dest_list}, ""); return if (isByRef(va_list_ty, zcu)) @@ -8037,7 +8040,7 @@ pub const FuncGen = struct { self.ret_ptr else brk: { const alignment = optional_ty.abiAlignment(zcu).toLlvm(); - const optional_ptr = try self.buildAllocaWorkaround(optional_ty, alignment); + const optional_ptr = try self.buildAlloca(llvm_optional_ty, alignment); break :brk optional_ptr; }; @@ -8074,7 +8077,7 @@ pub const FuncGen = struct { self.ret_ptr else brk: { const alignment = err_un_ty.abiAlignment(pt.zcu).toLlvm(); - const result_ptr = try self.buildAllocaWorkaround(err_un_ty, alignment); + const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment); break :brk result_ptr; }; @@ -8113,7 +8116,7 @@ pub const FuncGen = struct { self.ret_ptr else brk: { const alignment = err_un_ty.abiAlignment(zcu).toLlvm(); - const result_ptr = try self.buildAllocaWorkaround(err_un_ty, alignment); + const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment); break :brk result_ptr; }; @@ -8647,7 +8650,7 @@ pub const FuncGen = struct { if (isByRef(inst_ty, zcu)) { const result_alignment = inst_ty.abiAlignment(zcu).toLlvm(); - const alloca_inst = try self.buildAllocaWorkaround(inst_ty, result_alignment); + const alloca_inst = try self.buildAlloca(llvm_inst_ty, result_alignment); { const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, result_index, ""); _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment); @@ -9007,7 +9010,7 @@ pub const FuncGen = struct { if (isByRef(dest_ty, zcu)) { const result_alignment = dest_ty.abiAlignment(zcu).toLlvm(); - const alloca_inst = try self.buildAllocaWorkaround(dest_ty, result_alignment); + const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment); { const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, ""); _ = try self.wip.store(.normal, result, field_ptr, result_alignment); @@ -9432,7 +9435,7 @@ pub const FuncGen = struct { return self.ng.todo("implement bitcast vector to non-ref array", .{}); } const alignment = inst_ty.abiAlignment(zcu).toLlvm(); - const array_ptr = try self.buildAllocaWorkaround(inst_ty, alignment); + const array_ptr = try self.buildAlloca(llvm_dest_ty, alignment); const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8; if (bitcast_ok) { _ = try self.wip.store(.normal, operand, array_ptr, alignment); @@ -9493,7 +9496,7 @@ pub const FuncGen = struct { if (result_is_ref) { const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm(); - const result_ptr = try self.buildAllocaWorkaround(inst_ty, alignment); + const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); _ = try self.wip.store(.normal, operand, result_ptr, alignment); return result_ptr; } @@ -9506,7 +9509,7 @@ pub const FuncGen = struct { // but LLVM won't let us bitcast struct values or vectors with padding bits. // Therefore, we store operand to alloca, then load for result. const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm(); - const result_ptr = try self.buildAllocaWorkaround(inst_ty, alignment); + const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); _ = try self.wip.store(.normal, operand, result_ptr, alignment); return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, ""); } @@ -9615,9 +9618,9 @@ pub const FuncGen = struct { if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return (try o.lowerPtrToVoid(ptr_ty)).toValue(); - //const pointee_llvm_ty = try o.lowerType(pointee_type); + const pointee_llvm_ty = try o.lowerType(pointee_type); const alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); - return self.buildAllocaWorkaround(pointee_type, alignment); + return self.buildAlloca(pointee_llvm_ty, alignment); } fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { @@ -9629,9 +9632,9 @@ pub const FuncGen = struct { if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return (try o.lowerPtrToVoid(ptr_ty)).toValue(); if (self.ret_ptr != .none) return self.ret_ptr; - //const ret_llvm_ty = try o.lowerType(ret_ty); + const ret_llvm_ty = try o.lowerType(ret_ty); const alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); - return self.buildAllocaWorkaround(ret_ty, alignment); + return self.buildAlloca(ret_llvm_ty, alignment); } /// Use this instead of builder.buildAlloca, because this function makes sure to @@ -9645,16 +9648,6 @@ pub const FuncGen = struct { return buildAllocaInner(&self.wip, llvm_ty, alignment, target); } - // Workaround for https://github.com/ziglang/zig/issues/16392 - fn buildAllocaWorkaround( - self: *FuncGen, - ty: Type, - alignment: Builder.Alignment, - ) Allocator.Error!Builder.Value { - const o = self.ng.object; - return self.buildAlloca(try o.builder.arrayType(ty.abiSize(o.pt.zcu), .i8), alignment); - } - fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { const o = self.ng.object; const pt = o.pt; @@ -10693,7 +10686,7 @@ pub const FuncGen = struct { const llvm_result_ty = accum_init.typeOfWip(&self.wip); // Allocate and initialize our mutable variables - const i_ptr = try self.buildAllocaWorkaround(Type.usize, .default); + const i_ptr = try self.buildAlloca(usize_ty, .default); _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default); const accum_ptr = try self.buildAlloca(llvm_result_ty, .default); _ = try self.wip.store(.normal, accum_init, accum_ptr, .default); @@ -10906,7 +10899,7 @@ pub const FuncGen = struct { // TODO in debug builds init to undef so that the padding will be 0xaa // even if we fully populate the fields. const alignment = result_ty.abiAlignment(zcu).toLlvm(); - const alloca_inst = try self.buildAllocaWorkaround(result_ty, alignment); + const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment); for (elements, 0..) |elem, i| { if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue; @@ -10943,7 +10936,7 @@ pub const FuncGen = struct { const llvm_usize = try o.lowerType(Type.usize); const usize_zero = try o.builder.intValue(llvm_usize, 0); const alignment = result_ty.abiAlignment(zcu).toLlvm(); - const alloca_inst = try self.buildAllocaWorkaround(result_ty, alignment); + const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment); const array_info = result_ty.arrayInfo(zcu); const elem_ptr_ty = try pt.ptrType(.{ @@ -11018,7 +11011,7 @@ pub const FuncGen = struct { // We must construct the correct unnamed struct type here, in order to then set // the fields appropriately. const alignment = layout.abi_align.toLlvm(); - const result_ptr = try self.buildAllocaWorkaround(union_ty, alignment); + const result_ptr = try self.buildAlloca(union_llvm_ty, alignment); const llvm_payload = try self.resolveInst(extra.init); const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); const field_llvm_ty = try o.lowerType(field_ty); @@ -11315,7 +11308,7 @@ pub const FuncGen = struct { if (isByRef(optional_ty, zcu)) { const payload_alignment = optional_ty.abiAlignment(pt.zcu).toLlvm(); - const alloca_inst = try self.buildAllocaWorkaround(optional_ty, payload_alignment); + const alloca_inst = try self.buildAlloca(optional_llvm_ty, payload_alignment); { const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, ""); @@ -11458,10 +11451,10 @@ pub const FuncGen = struct { ) !Builder.Value { const o = fg.ng.object; const pt = o.pt; - //const pointee_llvm_ty = try o.lowerType(pointee_type); + const pointee_llvm_ty = try o.lowerType(pointee_type); const result_align = InternPool.Alignment.fromLlvm(ptr_alignment) .max(pointee_type.abiAlignment(pt.zcu)).toLlvm(); - const result_ptr = try fg.buildAllocaWorkaround(pointee_type, result_align); + const result_ptr = try fg.buildAlloca(pointee_llvm_ty, result_align); const size_bytes = pointee_type.abiSize(pt.zcu); _ = try fg.wip.callMemCpy( result_ptr, @@ -11522,7 +11515,7 @@ pub const FuncGen = struct { if (isByRef(elem_ty, zcu)) { const result_align = elem_ty.abiAlignment(zcu).toLlvm(); - const result_ptr = try self.buildAllocaWorkaround(elem_ty, result_align); + const result_ptr = try self.buildAlloca(elem_llvm_ty, result_align); const same_size_int = try o.builder.intType(@intCast(elem_bits)); 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 } return switch (cc_tag) { .@"inline" => unreachable, - .auto, .@"async" => .fastcc, + .auto, .async => .fastcc, .naked => .ccc, .x86_64_sysv => .x86_64_sysvcc, .x86_64_win => .win64cc, @@ -12386,7 +12379,7 @@ const ParamTypeIterator = struct { return .byval; } }, - .@"async" => { + .async => { @panic("TODO implement async function lowering in the LLVM backend"); }, .x86_64_sysv => return it.nextSystemV(ty), @@ -12641,7 +12634,7 @@ fn ccAbiPromoteInt( ) ?std.builtin.Signedness { const target = zcu.getTarget(); switch (cc) { - .auto, .@"inline", .@"async" => return null, + .auto, .@"inline", .async => return null, else => {}, } const int_info = switch (ty.zigTypeTag(zcu)) { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 5d2d4c2a99304911809d9677a0e2e8667b97b19c..7fa2a7da4356ec55c1e61cf11aedb12ef3cb44ee 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -79,6 +79,7 @@ pub const TargetMachine = opaque { data_sections: bool, float_abi: FloatABI, abi_name: ?[*:0]const u8, + emulated_tls: bool, ) *TargetMachine; pub const dispose = LLVMDisposeTargetMachine; diff --git a/src/codegen/mips/abi.zig b/src/codegen/mips/abi.zig new file mode 100644 index 0000000000000000000000000000000000000000..02c4c637a4c362ea4b3a826fbd27b72598fcc2cb --- /dev/null +++ b/src/codegen/mips/abi.zig @@ -0,0 +1,84 @@ +const std = @import("std"); +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); +const assert = std.debug.assert; + +pub const Class = union(enum) { + memory, + byval, + i32_array: u8, +}; + +pub const Context = enum { ret, arg }; + +pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { + const target = zcu.getTarget(); + std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + + const max_direct_size = target.ptrBitWidth() * 2; + switch (ty.zigTypeTag(zcu)) { + .@"struct" => { + const bit_size = ty.bitSize(zcu); + if (ty.containerLayout(zcu) == .@"packed") { + if (bit_size > max_direct_size) return .memory; + return .byval; + } + if (bit_size > max_direct_size) return .memory; + // TODO: for bit_size <= 32 using byval is more correct, but that needs inreg argument attribute + const count = @as(u8, @intCast(std.mem.alignForward(u64, bit_size, 32) / 32)); + return .{ .i32_array = count }; + }, + .@"union" => { + const bit_size = ty.bitSize(zcu); + if (ty.containerLayout(zcu) == .@"packed") { + if (bit_size > max_direct_size) return .memory; + return .byval; + } + if (bit_size > max_direct_size) return .memory; + + return .byval; + }, + .bool => return .byval, + .float => return .byval, + .int, .@"enum", .error_set => { + return .byval; + }, + .vector => { + const elem_type = ty.elemType2(zcu); + switch (elem_type.zigTypeTag(zcu)) { + .bool, .int => { + const bit_size = ty.bitSize(zcu); + if (ctx == .ret and bit_size > 128) return .memory; + if (bit_size > 512) return .memory; + // TODO: byval vector arguments with non power of 2 size need inreg attribute + return .byval; + }, + .float => return .memory, + else => unreachable, + } + }, + .optional => { + std.debug.assert(ty.isPtrLikeOptional(zcu)); + return .byval; + }, + .pointer => { + std.debug.assert(!ty.isSlice(zcu)); + return .byval; + }, + .error_union, + .frame, + .@"anyframe", + .noreturn, + .void, + .type, + .comptime_float, + .comptime_int, + .undefined, + .null, + .@"fn", + .@"opaque", + .enum_literal, + .array, + => unreachable, + } +} diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index e06d0c944fceb4fd83cb85ae8fc01b2806c86cfb..f263e567e8dcb04d5ed6e8b1a621fa01f387f3dd 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1259,11 +1259,13 @@ const NavGen = struct { } // Turn a Zig type's name into a cache reference. - fn resolveTypeName(self: *NavGen, ty: Type) Allocator.Error![]const u8 { + fn resolveTypeName(self: *NavGen, ty: Type) ![]const u8 { var aw: std.io.Writer.Allocating = .init(self.gpa); defer aw.deinit(); - ty.print(&aw.interface, self.pt) catch return error.OutOfMemory; - return aw.toOwnedSlice(); + ty.print(&aw.writer, self.pt) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + return try aw.toOwnedSlice(); } /// Create an integer type suitable for storing at least 'bits' bits. diff --git a/src/codegen/spirv/spec.zig b/src/codegen/spirv/spec.zig index 857d5bb03a177da15abab6a325a1c099c634d44d..82ec05ebbaece9ddc8e3f78e609994dd733e6dc3 100644 --- a/src/codegen/spirv/spec.zig +++ b/src/codegen/spirv/spec.zig @@ -1,6 +1,7 @@ //! This file is auto-generated by tools/gen_spirv_spec.zig. const std = @import("std"); +const assert = std.debug.assert; pub const Version = packed struct(Word) { padding: u8 = 0, @@ -18,11 +19,10 @@ pub const IdResult = enum(Word) { none, _, - pub fn format(self: IdResult, bw: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void { - comptime std.debug.assert(fmt.len == 0); + pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void { switch (self) { - .none => try bw.writeAll("(none)"), - else => try bw.print("%{}", .{@intFromEnum(self)}), + .none => try writer.writeAll("(none)"), + else => try writer.print("%{d}", .{@intFromEnum(self)}), } } }; diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig new file mode 100644 index 0000000000000000000000000000000000000000..a1fa8126491def2d9e2717faa0ccedcd2ee24805 --- /dev/null +++ b/src/codegen/wasm/abi.zig @@ -0,0 +1,87 @@ +//! Classifies Zig types to follow the C-ABI for Wasm. +//! The convention for Wasm's C-ABI can be found at the tool-conventions repo: +//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md +//! When not targeting the C-ABI, Zig is allowed to do derail from this convention. +//! Note: Above mentioned document is not an official specification, therefore called a convention. + +const std = @import("std"); +const Target = std.Target; +const assert = std.debug.assert; + +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +/// Defines how to pass a type as part of a function signature, +/// both for parameters as well as return values. +pub const Class = union(enum) { + direct: Type, + indirect, +}; + +/// Classifies a given Zig type to determine how they must be passed +/// or returned as value within a wasm function. +pub fn classifyType(ty: Type, zcu: *const Zcu) Class { + const ip = &zcu.intern_pool; + assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + switch (ty.zigTypeTag(zcu)) { + .int, .@"enum", .error_set => return .{ .direct = ty }, + .float => return .{ .direct = ty }, + .bool => return .{ .direct = ty }, + .vector => return .{ .direct = ty }, + .array => return .indirect, + .optional => { + assert(ty.isPtrLikeOptional(zcu)); + return .{ .direct = ty }; + }, + .pointer => { + assert(!ty.isSlice(zcu)); + return .{ .direct = ty }; + }, + .@"struct" => { + const struct_type = zcu.typeToStruct(ty).?; + if (struct_type.layout == .@"packed") { + return .{ .direct = ty }; + } + if (struct_type.field_types.len > 1) { + // The struct type is non-scalar. + return .indirect; + } + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); + const explicit_align = struct_type.fieldAlign(ip, 0); + if (explicit_align != .none) { + if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) + return .indirect; + } + return classifyType(field_ty, zcu); + }, + .@"union" => { + const union_obj = zcu.typeToUnion(ty).?; + if (union_obj.flagsUnordered(ip).layout == .@"packed") { + return .{ .direct = ty }; + } + const layout = ty.unionGetLayout(zcu); + assert(layout.tag_size == 0); + if (union_obj.field_types.len > 1) return .indirect; + const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); + return classifyType(first_field_ty, zcu); + }, + .error_union, + .frame, + .@"anyframe", + .noreturn, + .void, + .type, + .comptime_float, + .comptime_int, + .undefined, + .null, + .@"fn", + .@"opaque", + .enum_literal, + => unreachable, + } +} + +pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool { + return scalar_ty.bitSize(zcu) > 64; +} diff --git a/src/deprecated.zig b/src/deprecated.zig index 1f7d5c8c25efff0b238a609453b541d5c479c63f..91c9ede27f66ddae208ffe276c1d47a06ce10e4a 100644 --- a/src/deprecated.zig +++ b/src/deprecated.zig @@ -15,6 +15,14 @@ pub fn LinearFifo(comptime T: type) type { count: usize, const Self = @This(); +<<<<<<<< HEAD:src/deprecated.zig +|||||||| edf785db0f:lib/std/fifo.zig + pub const Reader = std.io.Reader(*Self, error{}, readFn); + pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite); +======== + pub const Reader = std.io.GenericReader(*Self, error{}, readFn); + pub const Writer = std.io.GenericWriter(*Self, error{OutOfMemory}, appendWrite); +>>>>>>>> origin/master:lib/std/fifo.zig pub fn init(allocator: Allocator) Self { return .{ @@ -160,7 +168,7 @@ pub fn LinearFifo(comptime T: type) type { } /// Same as `read` except it returns an error union - /// The purpose of this function existing is to match `std.io.Reader` API. + /// The purpose of this function existing is to match `std.io.GenericReader` API. fn readFn(self: *Self, dest: []u8) error{}!usize { return self.read(dest); } @@ -241,7 +249,7 @@ pub fn LinearFifo(comptime T: type) type { } /// Same as `write` except it returns the number of bytes written, which is always the same - /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API. + /// as `bytes.len`. The purpose of this function existing is to match `std.io.GenericWriter` API. fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { try self.write(bytes); return bytes.len; diff --git a/src/dev.zig b/src/dev.zig index babf3af69ead5649d09ab424c4e100e3d2a944c1..4c602621ecf1a947e43224d9b47b37af30bb6f2d 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -154,6 +154,7 @@ pub const Env = enum { else => Env.ast_gen.supports(feature), }, .cbe => switch (feature) { + .legalize, .c_backend, .c_linker, => true, diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 55d097b71baa8b87584d2c3db73d7a6786c90cf1..6baa899087fd45f476e961663bf47e31f34dfa36 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -497,13 +497,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lt => continue, .gt => { // TODO Expose via compile error mechanism instead of log. - log.warn("invalid target FreeBSD libc version: {}", .{target_version}); + log.warn("invalid target FreeBSD libc version: {f}", .{target_version}); return error.InvalidTargetLibCVersion; }, } } else blk: { const latest_index = metadata.all_versions.len - 1; - log.warn("zig cannot build new FreeBSD libc version {}; providing instead {}", .{ + log.warn("zig cannot build new FreeBSD libc version {f}; providing instead {f}", .{ target_version, metadata.all_versions[latest_index], }); break :blk latest_index; diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index f26f27732bf9365a1254a32a342400c4c79c66d9..eebbb9cb7395142fdeb65d84f6a345f427d895e8 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -325,7 +325,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr // See the `-fno-exceptions` logic for WASI. // The old 32-bit x86 variant of SEH doesn't use tables. const unwind_tables: std.builtin.UnwindTables = - if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) .none else .@"async"; + if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) .none else .async; const config = Compilation.Config.resolve(.{ .output_mode = output_mode, diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 36ca5faa251eec2017f5939f427392a0b92241e5..d17baf8fa1f50766afb48024a0742000a76c96b4 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -48,7 +48,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo const optimize_mode = comp.compilerRtOptMode(); const strip = comp.compilerRtStrip(); const unwind_tables: std.builtin.UnwindTables = - if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async"; + if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .async; const link_libcpp = target.os.tag.isDarwin(); const config = Compilation.Config.resolve(.{ @@ -268,7 +268,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo const skip_linker_dependencies = !target.os.tag.isDarwin(); const linker_allow_shlib_undefined = target.os.tag.isDarwin(); const install_name = if (target.os.tag.isDarwin()) - try std.fmt.allocPrintZ(arena, "@rpath/{s}", .{basename}) + try std.fmt.allocPrintSentinel(arena, "@rpath/{s}", .{basename}, 0) else null; // Workaround for https://github.com/llvm/llvm-project/issues/97627 diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index 430ff597481eacb86d795de9de178144fdbf5be0..71cc6ccbc0782358ca1b7cbdd225c54e2860b015 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -29,7 +29,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr const output_mode = .Lib; const target = &comp.root_mod.resolved_target.result; const unwind_tables: std.builtin.UnwindTables = - if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async"; + if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .async; const config = Compilation.Config.resolve(.{ .output_mode = output_mode, .resolved_target = comp.root_mod.resolved_target, diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index 3aa9b10a3911199ddefceea3e060291e29c5a865..de40b7279d993f29ca9bba7e3750d324d9f9cc23 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -29,7 +29,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre const target = comp.getTarget(); // The old 32-bit x86 variant of SEH doesn't use tables. - const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch != .x86) .@"async" else .none; + const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch != .x86) .async else .none; switch (crt_file) { .crt2_o => { @@ -325,7 +325,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { for (aro_comp.diagnostics.list.items) |diagnostic| { if (diagnostic.kind == .@"fatal error" or diagnostic.kind == .@"error") { - aro.Diagnostics.render(&aro_comp, std.io.tty.detectConfig(.stderr())); + aro.Diagnostics.render(&aro_comp, std.io.tty.detectConfig(std.fs.File.stderr())); return error.AroPreprocessorFailed; } } @@ -334,7 +334,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { // new scope to ensure definition file is written before passing the path to WriteImportLibrary const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true }); defer def_final_file.close(); - try pp.prettyPrintTokens(def_final_file.writer(), .result_only); + try pp.prettyPrintTokens(def_final_file.deprecatedWriter(), .result_only); } 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{ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c", - "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S", @@ -974,7 +973,6 @@ const mingw32_x86_32_src = [_][]const u8{ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S", - "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c", }; @@ -1013,6 +1011,7 @@ const mingw32_winpthreads_src = [_][]const u8{ "winpthreads" ++ path.sep_str ++ "thread.c", }; +// Note: kernel32 and ntdll are always linked even without targeting MinGW-w64. pub const always_link_libs = [_][]const u8{ "api-ms-win-crt-conio-l1-1-0", "api-ms-win-crt-convert-l1-1-0", @@ -1030,8 +1029,6 @@ pub const always_link_libs = [_][]const u8{ "api-ms-win-crt-time-l1-1-0", "api-ms-win-crt-utility-l1-1-0", "advapi32", - "kernel32", - "ntdll", "shell32", "user32", }; diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 91f1b92b9e9369c8f2ca0546b0337fe87b4a5395..b09a29257e5d75d84433787316860f18f87f07d9 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -821,8 +821,6 @@ const src_files = [_][]const u8{ "musl/src/malloc/replaced.c", "musl/src/math/aarch64/ceil.c", "musl/src/math/aarch64/ceilf.c", - "musl/src/math/aarch64/floor.c", - "musl/src/math/aarch64/floorf.c", "musl/src/math/aarch64/fma.c", "musl/src/math/aarch64/fmaf.c", "musl/src/math/aarch64/fmax.c", @@ -912,9 +910,6 @@ const src_files = [_][]const u8{ "musl/src/math/fdiml.c", "musl/src/math/finite.c", "musl/src/math/finitef.c", - "musl/src/math/floor.c", - "musl/src/math/floorf.c", - "musl/src/math/floorl.c", "musl/src/math/fma.c", "musl/src/math/fmaf.c", "musl/src/math/fmal.c", @@ -955,8 +950,6 @@ const src_files = [_][]const u8{ "musl/src/math/i386/exp_ld.s", "musl/src/math/i386/expl.s", "musl/src/math/i386/expm1l.s", - "musl/src/math/i386/floorf.s", - "musl/src/math/i386/floorl.s", "musl/src/math/i386/floor.s", "musl/src/math/i386/fmod.c", "musl/src/math/i386/fmodf.c", @@ -1089,8 +1082,6 @@ const src_files = [_][]const u8{ "musl/src/math/pow_data.c", "musl/src/math/powerpc64/ceil.c", "musl/src/math/powerpc64/ceilf.c", - "musl/src/math/powerpc64/floor.c", - "musl/src/math/powerpc64/floorf.c", "musl/src/math/powerpc64/fma.c", "musl/src/math/powerpc64/fmaf.c", "musl/src/math/powerpc64/fmax.c", @@ -1153,9 +1144,6 @@ const src_files = [_][]const u8{ "musl/src/math/s390x/ceil.c", "musl/src/math/s390x/ceilf.c", "musl/src/math/s390x/ceill.c", - "musl/src/math/s390x/floor.c", - "musl/src/math/s390x/floorf.c", - "musl/src/math/s390x/floorl.c", "musl/src/math/s390x/fma.c", "musl/src/math/s390x/fmaf.c", "musl/src/math/s390x/nearbyint.c", diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 38570c43a671238cc0eef878355a70312b36c71a..094165b9c58c56c622a0770a2cba99e7207b503c 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -442,13 +442,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lt => continue, .gt => { // TODO Expose via compile error mechanism instead of log. - log.warn("invalid target NetBSD libc version: {}", .{target_version}); + log.warn("invalid target NetBSD libc version: {f}", .{target_version}); return error.InvalidTargetLibCVersion; }, } } else blk: { const latest_index = metadata.all_versions.len - 1; - log.warn("zig cannot build new NetBSD libc version {}; providing instead {}", .{ + log.warn("zig cannot build new NetBSD libc version {f}; providing instead {f}", .{ target_version, metadata.all_versions[latest_index], }); break :blk latest_index; diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig index aaff0da9ba0e414b209c026ee958c09003bc216e..6dced677a7455196ad6bc998b0fb6404f64baf97 100644 --- a/src/libs/wasi_libc.zig +++ b/src/libs/wasi_libc.zig @@ -10,43 +10,8 @@ pub const CrtFile = enum { crt1_reactor_o, crt1_command_o, libc_a, - libdl_a, - libwasi_emulated_process_clocks_a, - libwasi_emulated_getpid_a, - libwasi_emulated_mman_a, - libwasi_emulated_signal_a, }; -pub fn getEmulatedLibCrtFile(lib_name: []const u8) ?CrtFile { - if (mem.eql(u8, lib_name, "dl")) { - return .libdl_a; - } - if (mem.eql(u8, lib_name, "wasi-emulated-process-clocks")) { - return .libwasi_emulated_process_clocks_a; - } - if (mem.eql(u8, lib_name, "wasi-emulated-getpid")) { - return .libwasi_emulated_getpid_a; - } - if (mem.eql(u8, lib_name, "wasi-emulated-mman")) { - return .libwasi_emulated_mman_a; - } - if (mem.eql(u8, lib_name, "wasi-emulated-signal")) { - return .libwasi_emulated_signal_a; - } - return null; -} - -pub fn emulatedLibCRFileLibName(crt_file: CrtFile) []const u8 { - return switch (crt_file) { - .libdl_a => "libdl.a", - .libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a", - .libwasi_emulated_getpid_a => "libwasi-emulated-getpid.a", - .libwasi_emulated_mman_a => "libwasi-emulated-mman.a", - .libwasi_emulated_signal_a => "libwasi-emulated-signal.a", - else => unreachable, - }; -} - pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile { return switch (wasi_exec_model) { .reactor => CrtFile.crt1_reactor_o, @@ -157,114 +122,115 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre } } - try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items, .{}); - }, - - .libdl_a => { - var args = std.ArrayList([]const u8).init(arena); - try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); - try addLibcBottomHalfIncludes(comp, arena, &args); + { + // Compile libdl. + var args = std.ArrayList([]const u8).init(arena); + try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); + try addLibcBottomHalfIncludes(comp, arena, &args); - var emu_dl_sources = std.ArrayList(Compilation.CSourceFile).init(arena); - for (emulated_dl_src_files) |file_path| { - try emu_dl_sources.append(.{ - .src_path = try comp.dirs.zig_lib.join(arena, &.{ - "libc", try sanitize(arena, file_path), - }), - .extra_flags = args.items, - .owner = undefined, - }); + for (emulated_dl_src_files) |file_path| { + try libc_sources.append(.{ + .src_path = try comp.dirs.zig_lib.join(arena, &.{ + "libc", try sanitize(arena, file_path), + }), + .extra_flags = args.items, + .owner = undefined, + }); + } } - try comp.build_crt_file("dl", .Lib, .@"wasi libdl.a", prog_node, emu_dl_sources.items, .{}); - }, - .libwasi_emulated_process_clocks_a => { - var args = std.ArrayList([]const u8).init(arena); - try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); - try addLibcBottomHalfIncludes(comp, arena, &args); - - var emu_clocks_sources = std.ArrayList(Compilation.CSourceFile).init(arena); - for (emulated_process_clocks_src_files) |file_path| { - try emu_clocks_sources.append(.{ - .src_path = try comp.dirs.zig_lib.join(arena, &.{ - "libc", try sanitize(arena, file_path), + { + // Compile libwasi-emulated-process-clocks. + var args = std.ArrayList([]const u8).init(arena); + try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); + try args.appendSlice(&.{ + "-I", + try comp.dirs.zig_lib.join(arena, &.{ + "libc", + "wasi", + "libc-bottom-half", + "cloudlibc", + "src", }), - .extra_flags = args.items, - .owner = undefined, }); - } - try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items, .{}); - }, - .libwasi_emulated_getpid_a => { - var args = std.ArrayList([]const u8).init(arena); - try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); - try addLibcBottomHalfIncludes(comp, arena, &args); - var emu_getpid_sources = std.ArrayList(Compilation.CSourceFile).init(arena); - for (emulated_getpid_src_files) |file_path| { - try emu_getpid_sources.append(.{ - .src_path = try comp.dirs.zig_lib.join(arena, &.{ - "libc", try sanitize(arena, file_path), - }), - .extra_flags = args.items, - .owner = undefined, - }); + for (emulated_process_clocks_src_files) |file_path| { + try libc_sources.append(.{ + .src_path = try comp.dirs.zig_lib.join(arena, &.{ + "libc", try sanitize(arena, file_path), + }), + .extra_flags = args.items, + .owner = undefined, + }); + } } - try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items, .{}); - }, - .libwasi_emulated_mman_a => { - var args = std.ArrayList([]const u8).init(arena); - try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); - try addLibcBottomHalfIncludes(comp, arena, &args); - var emu_mman_sources = std.ArrayList(Compilation.CSourceFile).init(arena); - for (emulated_mman_src_files) |file_path| { - try emu_mman_sources.append(.{ - .src_path = try comp.dirs.zig_lib.join(arena, &.{ - "libc", try sanitize(arena, file_path), - }), - .extra_flags = args.items, - .owner = undefined, - }); + { + // Compile libwasi-emulated-getpid. + var args = std.ArrayList([]const u8).init(arena); + try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); + try addLibcBottomHalfIncludes(comp, arena, &args); + + for (emulated_getpid_src_files) |file_path| { + try libc_sources.append(.{ + .src_path = try comp.dirs.zig_lib.join(arena, &.{ + "libc", try sanitize(arena, file_path), + }), + .extra_flags = args.items, + .owner = undefined, + }); + } } - try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items, .{}); - }, - .libwasi_emulated_signal_a => { - var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); { + // Compile libwasi-emulated-mman. var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); + try addLibcBottomHalfIncludes(comp, arena, &args); + + for (emulated_mman_src_files) |file_path| { + try libc_sources.append(.{ + .src_path = try comp.dirs.zig_lib.join(arena, &.{ + "libc", try sanitize(arena, file_path), + }), + .extra_flags = args.items, + .owner = undefined, + }); + } + } + + { + // Compile libwasi-emulated-signal. + var bottom_args = std.ArrayList([]const u8).init(arena); + try addCCArgs(comp, arena, &bottom_args, .{ .want_O3 = true }); for (emulated_signal_bottom_half_src_files) |file_path| { - try emu_signal_sources.append(.{ + try libc_sources.append(.{ .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libc", try sanitize(arena, file_path), }), - .extra_flags = args.items, + .extra_flags = bottom_args.items, .owner = undefined, }); } - } - { - var args = std.ArrayList([]const u8).init(arena); - try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); - try addLibcTopHalfIncludes(comp, arena, &args); - try args.append("-D_WASI_EMULATED_SIGNAL"); + var top_args = std.ArrayList([]const u8).init(arena); + try addCCArgs(comp, arena, &top_args, .{ .want_O3 = true }); + try addLibcTopHalfIncludes(comp, arena, &top_args); + try top_args.append("-D_WASI_EMULATED_SIGNAL"); for (emulated_signal_top_half_src_files) |file_path| { - try emu_signal_sources.append(.{ + try libc_sources.append(.{ .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libc", try sanitize(arena, file_path), }), - .extra_flags = args.items, + .extra_flags = top_args.items, .owner = undefined, }); } } - try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", prog_node, emu_signal_sources.items, .{}); + try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items, .{}); }, } } @@ -754,7 +720,6 @@ const libc_top_half_src_files = [_][]const u8{ "musl/src/math/fdiml.c", "musl/src/math/finite.c", "musl/src/math/finitef.c", - "musl/src/math/floorl.c", "musl/src/math/fma.c", "musl/src/math/fmaf.c", "musl/src/math/fmaxl.c", diff --git a/src/link.zig b/src/link.zig index 387dbe94ddc7b941b59bd070706009e14e94f09f..50b995c90bd16338c36ebf1989578a578ec55500 100644 --- a/src/link.zig +++ b/src/link.zig @@ -838,8 +838,10 @@ pub const File = struct { const cached_pp_file_path = the_key.status.success.object_path; cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| { const diags = &base.comp.link_diags; - return diags.fail("failed to copy '{f'}' to '{f'}': {s}", .{ - @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err), + return diags.fail("failed to copy '{f}' to '{f}': {s}", .{ + std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar), + std.fmt.alt(@as(Path, emit), .formatEscapeChar), + @errorName(err), }); }; return; @@ -2095,8 +2097,8 @@ fn resolvePathInputLib( }) { var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { error.FileNotFound => return .no_match, - else => |e| fatal("unable to search for {s} library '{f'}': {s}", .{ - @tagName(link_mode), test_path, @errorName(e), + else => |e| fatal("unable to search for {s} library '{f}': {s}", .{ + @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e), }), }; errdefer file.close(); @@ -2105,9 +2107,8 @@ fn resolvePathInputLib( var br = fr.interface().unbuffered(); ok: { br.readSlice(ld_script_bytes.items) catch |err| switch (err) { - error.ReadFailed => fatal("failed to read '{f'}': {s}", .{ - test_path, - @errorName(fr.err.?), + error.ReadFailed => fatal("failed to read '{f}': {s}", .{ + test_path, @errorName(fr.err.?), }), error.EndOfStream => break :ok, }; diff --git a/src/link/C.zig b/src/link/C.zig index 0c282dbc4a69865dfbbf7ed2fce7d8dc160cf5d7..a1d28f92084bb1ecb546003dc9b85c5214c8a025 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -63,6 +63,14 @@ const String = extern struct { .start = 0, .len = 0, }; + + fn concat(lhs: String, rhs: String) String { + assert(lhs.start + lhs.len == rhs.start); + return .{ + .start = lhs.start, + .len = lhs.len + rhs.len, + }; + } }; /// Per-declaration data. @@ -205,8 +213,10 @@ pub fn updateFunc( .ctype_pool = mir.c.ctype_pool.move(), .lazy_fns = mir.c.lazy_fns.move(), }; - gop.value_ptr.fwd_decl = try self.addString(&.{&function.object.dg.fwd_decl}); - gop.value_ptr.code = try self.addString(&.{ &function.object.code_header, &function.object.code }); + gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl); + const code_header = try self.addString(mir.c.code_header); + const code = try self.addString(mir.c.code); + gop.value_ptr.code = code_header.concat(code); try self.addUavsFromCodegen(&mir.c.uavs); } @@ -232,8 +242,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void { .code = undefined, .indent_counter = 0, }; - object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); - object.code.initOwnedSlice(gpa, self.code_buf); + object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); + object.code = .initOwnedSlice(gpa, self.code_buf); defer { object.dg.uavs.deinit(gpa); 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 { object.dg.ctype_pool.freeUnusedCapacity(gpa); self.uavs.values()[i] = .{ - .fwd_decl = try self.addString(&.{&object.dg.fwd_decl}), - .code = try self.addString(&.{&object.code}), + .fwd_decl = try self.addString(object.dg.fwd_decl.getWritten()), + .code = try self.addString(object.code.getWritten()), .ctype_pool = object.dg.ctype_pool.move(), }; } @@ -307,8 +317,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l .code = undefined, .indent_counter = 0, }; - object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); - object.code.initOwnedSlice(gpa, self.code_buf); + object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); + object.code = .initOwnedSlice(gpa, self.code_buf); defer { object.dg.uavs.deinit(gpa); 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 }, error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; - gop.value_ptr.fwd_decl = try self.addString(&.{&object.dg.fwd_decl}); - gop.value_ptr.code = try self.addString(&.{&object.code}); + gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.getWritten()); + gop.value_ptr.code = try self.addString(object.code.getWritten()); try self.addUavsFromCodegen(&object.dg.uavs); } @@ -339,12 +349,12 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn _ = ti_id; } -fn abiDefines(bw: *std.io.BufferedWriter, target: std.Target) !void { +fn abiDefines(w: *std.io.Writer, target: *const std.Target) !void { switch (target.abi) { - .msvc, .itanium => try bw.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), + .msvc, .itanium => try w.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), else => {}, } - try bw.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{ + try w.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{ target.cMaxIntAlignment(), }); } @@ -391,10 +401,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P }; defer f.deinit(gpa); - var abi_defines_aw: std.io.AllocatingWriter = undefined; - abi_defines_aw.init(gpa); + var abi_defines_aw: std.io.Writer.Allocating = .init(gpa); defer abi_defines_aw.deinit(); - abiDefines(&abi_defines_aw.buffered_writer, zcu.getTarget()) catch |err| switch (err) { + abiDefines(&abi_defines_aw.writer, zcu.getTarget()) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; @@ -407,10 +416,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P const ctypes_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; - var asm_aw: std.io.AllocatingWriter = undefined; - asm_aw.init(gpa); + var asm_aw: std.io.Writer.Allocating = .init(gpa); defer asm_aw.deinit(); - codegen.genGlobalAsm(zcu, &asm_aw.buffered_writer) catch |err| switch (err) { + codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; f.appendBufAssumeCapacity(asm_aw.getWritten()); @@ -501,11 +509,11 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P const file = self.base.file.?; file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)}); - var fw = file.writer(); - var bw = fw.interface().unbuffered(); - bw.writeVecAll(f.all_buffers.items) catch |err| switch (err) { - error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{ - self.base.emit, @errorName(fw.err.?), + var fw = file.writer(&.{}); + var w = &fw.interface; + w.writeVecAll(f.all_buffers.items) catch |err| switch (err) { + error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{ + std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?), }), }; } @@ -575,8 +583,8 @@ fn flushCTypes( try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len); defer global_from_decl_map.clearRetainingCapacity(); - var ctypes_aw: std.io.AllocatingWriter = undefined; - const ctypes_bw = ctypes_aw.fromArrayList(gpa, &f.ctypes); + var ctypes_aw: std.io.Writer.Allocating = .fromArrayList(gpa, &f.ctypes); + const ctypes_bw = &ctypes_aw.writer; defer f.ctypes = ctypes_aw.toArrayList(); 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 { .code = undefined, .indent_counter = 0, }; - _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl); - _ = object.code.fromArrayList(gpa, &f.lazy_code); + object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl); + object.code = .fromArrayList(gpa, &f.lazy_code); defer { object.dg.uavs.deinit(gpa); f.lazy_ctype_pool = object.dg.ctype_pool.move(); @@ -688,8 +696,8 @@ fn flushLazyFn( .code = undefined, .indent_counter = 0, }; - _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl); - _ = object.code.fromArrayList(gpa, &f.lazy_code); + object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl); + object.code = .fromArrayList(gpa, &f.lazy_code); defer { // If this assert trips just handle the anon_decl_deps the same as // `updateFunc()` does. @@ -830,7 +838,7 @@ pub fn updateExports( .scratch = .initBuffer(self.scratch_buf), .uavs = .empty, }; - dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); + dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); defer { assert(dg.uavs.count() == 0); ctype_pool.* = dg.ctype_pool.move(); @@ -842,7 +850,7 @@ pub fn updateExports( codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) { error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; - exported_block.* = .{ .fwd_decl = try self.addString(&.{&dg.fwd_decl}) }; + exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.getWritten()) }; } pub fn deleteExport( diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 88195a93916365ab692948aed08d4c63629eb865..4b2a4dfaae5e9aa1c82864913f29d3befece9a94 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -2623,7 +2623,7 @@ fn logSymtab(coff: *Coff) void { .DEBUG => unreachable, // TODO else => @intFromEnum(sym.section_number), }; - log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{ + log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{ sym_id, coff.getSymbolName(.{ .sym_index = @as(u32, @intCast(sym_id)), .file = null }), sym.value, @@ -3096,33 +3096,25 @@ const ImportTable = struct { return base_vaddr + index * @sizeOf(u64); } - const FormatContext = struct { + const Format = struct { itab: ImportTable, ctx: Context, - }; - fn format(itab: ImportTable, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { - _ = itab; - _ = bw; - _ = unused_format_string; - @compileError("do not format ImportTable directly; use itab.fmtDebug()"); - } - - fn format2(fmt_ctx: FormatContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { - comptime assert(unused_format_string.len == 0); - const lib_name = fmt_ctx.ctx.coff.temp_strtab.getAssumeExists(fmt_ctx.ctx.name_off); - const base_vaddr = getBaseAddress(fmt_ctx.ctx); - try bw.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr }); - for (fmt_ctx.itab.entries.items, 0..) |entry, i| { - try bw.print("\n {d}@{?x} => {s}", .{ - i, - fmt_ctx.itab.getImportAddress(entry, fmt_ctx.ctx), - fmt_ctx.ctx.coff.getSymbolName(entry), - }); + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const lib_name = f.ctx.coff.temp_strtab.getAssumeExists(f.ctx.name_off); + const base_vaddr = getBaseAddress(f.ctx); + try writer.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr }); + for (f.itab.entries.items, 0..) |entry, i| { + try writer.print("\n {d}@{?x} => {s}", .{ + i, + f.itab.getImportAddress(entry, f.ctx), + f.ctx.coff.getSymbolName(entry), + }); + } } - } + }; - fn fmtDebug(itab: ImportTable, ctx: Context) fmt.Formatter(format2) { + fn fmtDebug(itab: ImportTable, ctx: Context) fmt.Formatter(Format, Format.default) { return .{ .data = .{ .itab = itab, .ctx = ctx } }; } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index f33905610f98b10b398c16a36ac34447f9076e13..5b4e0b66089ad8f0633035413d7e31376edb60af 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -2557,7 +2557,7 @@ fn initWipNavInner( const addr: Loc = .{ .addr_reloc = sym_index }; const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr; switch (decl.kind) { - .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable, + .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable, .@"const" => { const const_ty_reloc_index = try wip_nav.refForward(); try wip_nav.infoExprLoc(loc); @@ -2834,7 +2834,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const is_test = switch (decl.kind) { .unnamed_test, .@"test", .decltest => true, - .@"comptime", .@"usingnamespace", .@"const", .@"var" => false, + .@"comptime", .@"const", .@"var" => false, }; if (is_test) { // 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( // For better or worse, we try to match what Clang emits. break :cc switch (func_type.cc) { .@"inline" => .nocall, - .@"async", .auto, .naked => .normal, + .async, .auto, .naked => .normal, .x86_64_sysv => .LLVM_X86_64SysV, .x86_64_win => .LLVM_Win64, .x86_64_regcall_v3_sysv => .LLVM_X86RegCall, @@ -4301,7 +4301,7 @@ fn updateContainerTypeInner(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: Intern }; defer wip_nav.deinit(); const diw = wip_nav.debug_info.writer(dwarf.gpa); - const name = try std.fmt.allocPrint(dwarf.gpa, "{}", .{ty.fmt(pt)}); + const name = try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)}); defer dwarf.gpa.free(name); switch (ip.indexToKey(type_index)) { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 388036b14601185cb89f9e68cefa06236d7376ad..6c38d4c2905fe452ba695fd8ff0efcd55066678f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3870,22 +3870,21 @@ pub fn failFile( return error.LinkFailure; } -const FormatShdrCtx = struct { +const FormatShdr = struct { elf_file: *Elf, shdr: elf.Elf64_Shdr, }; -fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) { +fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(FormatShdr, formatShdr) { return .{ .data = .{ .shdr = shdr, .elf_file = self, } }; } -fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; +fn formatShdr(ctx: FormatShdr, writer: *std.io.Writer) std.io.Writer.Error!void { const shdr = ctx.shdr; - try bw.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({f})", .{ + try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({f})", .{ ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset, shdr.sh_addr, shdr.sh_addralign, shdr.sh_size, shdr.sh_entsize, @@ -3893,74 +3892,68 @@ fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []con }); } -pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) { +pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(u64, formatShdrFlags) { return .{ .data = sh_flags }; } -fn formatShdrFlags(sh_flags: u64, bw: *Writer, comptime unused_fmt_string: []const u8) !void { - _ = unused_fmt_string; +fn formatShdrFlags(sh_flags: u64, writer: *std.io.Writer) std.io.Writer.Error!void { if (elf.SHF_WRITE & sh_flags != 0) { - try bw.writeByte('W'); + try writer.writeByte('W'); } if (elf.SHF_ALLOC & sh_flags != 0) { - try bw.writeByte('A'); + try writer.writeByte('A'); } if (elf.SHF_EXECINSTR & sh_flags != 0) { - try bw.writeByte('X'); + try writer.writeByte('X'); } if (elf.SHF_MERGE & sh_flags != 0) { - try bw.writeByte('M'); + try writer.writeByte('M'); } if (elf.SHF_STRINGS & sh_flags != 0) { - try bw.writeByte('S'); + try writer.writeByte('S'); } if (elf.SHF_INFO_LINK & sh_flags != 0) { - try bw.writeByte('I'); + try writer.writeByte('I'); } if (elf.SHF_LINK_ORDER & sh_flags != 0) { - try bw.writeByte('L'); + try writer.writeByte('L'); } if (elf.SHF_EXCLUDE & sh_flags != 0) { - try bw.writeByte('E'); + try writer.writeByte('E'); } if (elf.SHF_COMPRESSED & sh_flags != 0) { - try bw.writeByte('C'); + try writer.writeByte('C'); } if (elf.SHF_GROUP & sh_flags != 0) { - try bw.writeByte('G'); + try writer.writeByte('G'); } if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) { - try bw.writeByte('O'); + try writer.writeByte('O'); } if (elf.SHF_TLS & sh_flags != 0) { - try bw.writeByte('T'); + try writer.writeByte('T'); } if (elf.SHF_X86_64_LARGE & sh_flags != 0) { - try bw.writeByte('l'); + try writer.writeByte('l'); } if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) { - try bw.writeByte('p'); + try writer.writeByte('p'); } } -const FormatPhdrCtx = struct { +const FormatPhdr = struct { elf_file: *Elf, phdr: elf.Elf64_Phdr, }; -fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) { +fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(FormatPhdr, formatPhdr) { return .{ .data = .{ .phdr = phdr, .elf_file = self, } }; } -fn formatPhdr( - ctx: FormatPhdrCtx, - bw: *Writer, - comptime unused_fmt_string: []const u8, -) !void { - _ = unused_fmt_string; +fn formatPhdr(ctx: FormatPhdr, writer: *std.io.Writer) std.io.Writer.Error!void { const phdr = ctx.phdr; const write = phdr.p_flags & elf.PF_W != 0; const read = phdr.p_flags & elf.PF_R != 0; @@ -3981,40 +3974,34 @@ fn formatPhdr( elf.PT_NOTE => "NOTE", else => "UNKNOWN", }; - try bw.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{ + try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{ p_type, flags, phdr.p_offset, phdr.p_vaddr, phdr.p_align, phdr.p_filesz, phdr.p_memsz, }); } -pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { +pub fn dumpState(self: *Elf) std.fmt.Formatter(*Elf, fmtDumpState) { return .{ .data = self }; } -fn fmtDumpState( - self: *Elf, - bw: *Writer, - comptime unused_fmt_string: []const u8, -) !void { - _ = unused_fmt_string; - +fn fmtDumpState(self: *Elf, writer: *std.io.Writer) std.io.Writer.Error!void { const shared_objects = self.shared_objects.values(); if (self.zigObjectPtr()) |zig_object| { - try bw.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); - try bw.print("{f}{f}", .{ + try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); + try writer.print("{f}{f}", .{ zig_object.fmtAtoms(self), zig_object.fmtSymtab(self), }); - try bw.writeByte('\n'); + try writer.writeByte('\n'); } for (self.objects.items) |index| { const object = self.file(index).?.object; - try bw.print("object({d}) : {f}", .{ index, object.fmtPath() }); - if (!object.alive) try bw.writeAll(" : [*]"); - try bw.writeByte('\n'); - try bw.print("{f}{f}{f}{f}{f}\n", .{ + try writer.print("object({d}) : {f}", .{ index, object.fmtPath() }); + if (!object.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); + try writer.print("{f}{f}{f}{f}{f}\n", .{ object.fmtAtoms(self), object.fmtCies(self), object.fmtFdes(self), @@ -4025,59 +4012,59 @@ fn fmtDumpState( for (shared_objects) |index| { const shared_object = self.file(index).?.shared_object; - try bw.print("shared_object({d}) : {f} : needed({})", .{ + try writer.print("shared_object({d}) : {f} : needed({})", .{ index, shared_object.path, shared_object.needed, }); - if (!shared_object.alive) try bw.writeAll(" : [*]"); - try bw.writeByte('\n'); - try bw.print("{f}\n", .{shared_object.fmtSymtab(self)}); + if (!shared_object.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); + try writer.print("{f}\n", .{shared_object.fmtSymtab(self)}); } if (self.linker_defined_index) |index| { const linker_defined = self.file(index).?.linker_defined; - try bw.print("linker_defined({d}) : (linker defined)\n", .{index}); - try bw.print("{f}\n", .{linker_defined.fmtSymtab(self)}); + try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); + try writer.print("{f}\n", .{linker_defined.fmtSymtab(self)}); } const slice = self.sections.slice(); { - try bw.writeAll("atom lists\n"); + try writer.writeAll("atom lists\n"); for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| { - try bw.print("shdr({d}) : {s} : {f}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) }); + try writer.print("shdr({d}) : {s} : {f}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) }); } } if (self.requiresThunks()) { - try bw.writeAll("thunks\n"); + try writer.writeAll("thunks\n"); for (self.thunks.items, 0..) |th, index| { - try bw.print("thunk({d}) : {f}\n", .{ index, th.fmt(self) }); + try writer.print("thunk({d}) : {f}\n", .{ index, th.fmt(self) }); } } - try bw.print("{f}\n", .{self.got.fmt(self)}); - try bw.print("{f}\n", .{self.plt.fmt(self)}); + try writer.print("{f}\n", .{self.got.fmt(self)}); + try writer.print("{f}\n", .{self.plt.fmt(self)}); - try bw.writeAll("Output groups\n"); + try writer.writeAll("Output groups\n"); for (self.group_sections.items) |cg| { - try bw.print(" shdr({d}) : GROUP({f})\n", .{ cg.shndx, cg.cg_ref }); + try writer.print(" shdr({d}) : GROUP({f})\n", .{ cg.shndx, cg.cg_ref }); } - try bw.writeAll("\nOutput merge sections\n"); + try writer.writeAll("\nOutput merge sections\n"); for (self.merge_sections.items) |msec| { - try bw.print(" shdr({d}) : {f}\n", .{ msec.output_section_index, msec.fmt(self) }); + try writer.print(" shdr({d}) : {f}\n", .{ msec.output_section_index, msec.fmt(self) }); } - try bw.writeAll("\nOutput shdrs\n"); + try writer.writeAll("\nOutput shdrs\n"); for (slice.items(.shdr), slice.items(.phndx), 0..) |shdr, phndx, shndx| { - try bw.print(" shdr({d}) : phdr({?d}) : {f}\n", .{ + try writer.print(" shdr({d}) : phdr({d}) : {f}\n", .{ shndx, phndx, self.fmtShdr(shdr), }); } - try bw.writeAll("\nOutput phdrs\n"); + try writer.writeAll("\nOutput phdrs\n"); for (self.phdrs.items, 0..) |phdr, phndx| { - try bw.print(" phdr({d}) : {f}\n", .{ phndx, self.fmtPhdr(phdr) }); + try writer.print(" phdr({d}) : {f}\n", .{ phndx, self.fmtPhdr(phdr) }); } } @@ -4215,9 +4202,8 @@ pub const Ref = struct { return ref.index == other.index and ref.file == other.file; } - pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - try bw.print("ref({},{})", .{ ref.index, ref.file }); + pub fn format(ref: Ref, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.print("ref({d},{d})", .{ ref.index, ref.file }); } }; diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 405a3a64e6fe4581b92a6cb9004bf9b89ddacc43..df89d502643aea386cce7b239cfb70d649f6fd53 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -45,7 +45,7 @@ pub fn parse( if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { return diags.failParse(path, "invalid archive header delimiter: {f}", .{ - std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), + std.ascii.hexEscape(&hdr.ar_fmag, .lower), }); } @@ -84,7 +84,7 @@ pub fn parse( }; log.debug("extracting object '{f}' from archive '{f}'", .{ - object.path, path, + @as(Path, object.path), @as(Path, path), }); try objects.append(gpa, object); @@ -184,36 +184,28 @@ pub const ArSymtab = struct { } } - pub fn format(ar: ArSymtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = ar; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format ar symtab directly; use fmt instead"); - } - - const FormatContext = struct { + const Format = struct { ar: ArSymtab, elf_file: *Elf, + + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const ar = f.ar; + const elf_file = f.elf_file; + for (ar.symtab.items, 0..) |entry, i| { + const name = ar.strtab.getAssumeExists(entry.off); + const file = elf_file.file(entry.file_index).?; + try writer.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file_index, file.fmtPath() }); + } + } }; - pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .ar = ar, .elf_file = elf_file, } }; } - fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const ar = ctx.ar; - const elf_file = ctx.elf_file; - for (ar.symtab.items, 0..) |entry, i| { - const name = ar.strtab.getAssumeExists(entry.off); - const file = elf_file.file(entry.file_index).?; - try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file_index, file.fmtPath() }); - } - } - const Entry = struct { /// Offset into the string table. off: u32, @@ -251,9 +243,8 @@ pub const ArStrtab = struct { try writer.writeAll(ar.buffer.items); } - pub fn format(ar: ArStrtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)}); + pub fn format(ar: ArStrtab, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.print("{f}", .{std.ascii.hexEscape(ar.buffer.items, .lower)}); } }; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 3ec05eb942273b000547f8ebfda8fdb66c17f07b..4e73f702068812ec67cb20be30c5dfa4af23fd94 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -906,53 +906,45 @@ pub fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void { atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras); } -pub fn format(atom: Atom, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = atom; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format Atom directly"); -} - -pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) { +pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .atom = atom, .elf_file = elf_file, } }; } -const FormatContext = struct { +const Format = struct { atom: Atom, elf_file: *Elf, -}; -fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const atom = ctx.atom; - const elf_file = ctx.elf_file; - try bw.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{ - atom.atom_index, atom.name(elf_file), atom.address(elf_file), - atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size, - atom.prev_atom_ref, atom.next_atom_ref, - }); - if (atom.file(elf_file)) |atom_file| switch (atom_file) { - .object => |object| { - if (atom.fdes(object).len > 0) { - try bw.writeAll(" : fdes{ "); - const extras = atom.extra(elf_file); - for (atom.fdes(object), extras.fde_start..) |fde, i| { - try bw.print("{d}", .{i}); - if (!fde.alive) try bw.writeAll("([*])"); - if (i - extras.fde_start < extras.fde_count - 1) try bw.writeAll(", "); + fn default(f: Format, w: *std.io.Writer) std.io.Writer.Error!void { + const atom = f.atom; + const elf_file = f.elf_file; + try w.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{ + atom.atom_index, atom.name(elf_file), atom.address(elf_file), + atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size, + atom.prev_atom_ref, atom.next_atom_ref, + }); + if (atom.file(elf_file)) |atom_file| switch (atom_file) { + .object => |object| { + if (atom.fdes(object).len > 0) { + try w.writeAll(" : fdes{ "); + const extras = atom.extra(elf_file); + for (atom.fdes(object), extras.fde_start..) |fde, i| { + try w.print("{d}", .{i}); + if (!fde.alive) try w.writeAll("([*])"); + if (i - extras.fde_start < extras.fde_count - 1) try w.writeAll(", "); + } + try w.writeAll(" }"); } - try bw.writeAll(" }"); - } - }, - else => {}, - }; - if (!atom.alive) { - try bw.writeAll(" : [*]"); + }, + else => {}, + }; + if (!atom.alive) { + try w.writeAll(" : [*]"); + } } -} +}; pub const Index = u32; @@ -1385,9 +1377,8 @@ const x86_64 = struct { // TODO: hack to force imm32s in the assembler .{ .imm = .s(-129) }, }, t) catch return false; - var buf: [std.atomic.cache_line]u8 = undefined; - var bw = Writer.null.buffered(&buf); - inst.encode(&bw, .{}) catch return false; + var trash: std.io.Writer.Discarding = .init(&.{}); + inst.encode(&trash.writer, .{}) catch return false; return true; }, else => return false, @@ -1433,7 +1424,7 @@ const x86_64 = struct { rels: []const elf.Elf64_Rela, value: i32, elf_file: *Elf, - bw: *Writer, + writer: *Writer, ) !void { dev.check(.x86_64_backend); assert(rels.len == 2); @@ -1450,8 +1441,8 @@ const x86_64 = struct { 0x48, 0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %rax }; std.mem.writeInt(i32, insts[12..][0..4], value, .little); - bw.end -= 4; - try bw.writeAll(&insts); + try writer.seekBy(-4); + try writer.writeAll(&insts); relocs_log.debug(" relaxing {f} and {f}", .{ relocation.fmtRelocType(rels[0].r_type(), .x86_64), relocation.fmtRelocType(rels[1].r_type(), .x86_64), @@ -1481,8 +1472,8 @@ const x86_64 = struct { } fn encode(insts: []const Instruction, code: []u8) !void { - var bw: Writer = .fixed(code); - for (insts) |inst| try inst.encode(&bw, .{}); + var stream: std.io.Writer = .fixed(code); + for (insts) |inst| try inst.encode(&stream, .{}); } const bits = @import("../../arch/x86_64/bits.zig"); diff --git a/src/link/Elf/AtomList.zig b/src/link/Elf/AtomList.zig index 500105673d1bb680d6b83e2616ee2ad021298795..f0e3121417fd50de6d53c22c7209a0dc78d679db 100644 --- a/src/link/Elf/AtomList.zig +++ b/src/link/Elf/AtomList.zig @@ -167,32 +167,29 @@ pub fn lastAtom(list: AtomList, elf_file: *Elf) *Atom { return elf_file.atom(list.atoms.keys()[list.atoms.keys().len - 1]).?; } -pub fn format(list: AtomList, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = list; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format AtomList directly"); -} +const Format = struct { + atom_list: AtomList, + elf_file: *Elf, -const FormatCtx = struct { AtomList, *Elf }; - -pub fn fmt(list: AtomList, elf_file: *Elf) std.fmt.Formatter(format2) { - return .{ .data = .{ list, elf_file } }; -} - -fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const list, const elf_file = ctx; - try bw.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{ - list.address(elf_file), list.output_section_index, - list.alignment.toByteUnits() orelse 0, list.size, - }); - try bw.writeAll(" : atoms{ "); - for (list.atoms.keys(), 0..) |ref, i| { - try bw.print("{f}", .{ref}); - if (i < list.atoms.keys().len - 1) try bw.writeAll(", "); + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const list = f.atom_list; + try writer.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{ + list.address(f.elf_file), + list.output_section_index, + list.alignment.toByteUnits() orelse 0, + list.size, + }); + try writer.writeAll(" : atoms{ "); + for (list.atoms.keys(), 0..) |ref, i| { + try writer.print("{f}", .{ref}); + if (i < list.atoms.keys().len - 1) try writer.writeAll(", "); + } + try writer.writeAll(" }"); } - try bw.writeAll(" }"); +}; + +pub fn fmt(atom_list: AtomList, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { + return .{ .data = .{ .atom_list = atom_list, .elf_file = elf_file } }; } const std = @import("std"); diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 58310a9272f5c2bf2c619a24372a724cc871234e..99a40758bcc102bf5ad1f203821324ea86f81a99 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -147,9 +147,9 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void { for (slice.items(.shdr)) |shdr| { // TODO use getOrPut for incremental so that we don't create duplicates if (elf_file.getStartStopBasename(shdr)) |name| { - const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); + const start_name = try std.fmt.allocPrintSentinel(gpa, "__start_{s}", .{name}, 0); defer gpa.free(start_name); - const stop_name = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); + const stop_name = try std.fmt.allocPrintSentinel(gpa, "__stop_{s}", .{name}, 0); defer gpa.free(stop_name); for (&[_][]const u8{ start_name, stop_name }) |nn| { @@ -437,33 +437,32 @@ pub fn setSymbolExtra(self: *LinkerDefined, index: u32, extra: Symbol.Extra) voi } } -pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .self = self, .elf_file = elf_file, } }; } -const FormatContext = struct { +const Format = struct { self: *LinkerDefined, elf_file: *Elf, + + fn symtab(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const self = ctx.self; + const elf_file = ctx.elf_file; + try writer.writeAll(" globals\n"); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.resolveSymbol(@intCast(i), elf_file); + if (elf_file.symbol(ref)) |ref_sym| { + try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + } + } + } }; -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const self = ctx.self; - const elf_file = ctx.elf_file; - try bw.writeAll(" globals\n"); - for (self.symbols.items, 0..) |sym, i| { - const ref = self.resolveSymbol(@intCast(i), elf_file); - if (elf_file.symbol(ref)) |ref_sym| { - try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)}); - } else { - try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); - } - } -} - const std = @import("std"); const Allocator = mem.Allocator; const assert = std.debug.assert; diff --git a/src/link/Elf/Merge.zig b/src/link/Elf/Merge.zig index ec2a61579b3c2d1d1ce4f692acf440d35d396308..9e80907befe1f0e280b514afec7ff6c301f51a07 100644 --- a/src/link/Elf/Merge.zig +++ b/src/link/Elf/Merge.zig @@ -157,42 +157,34 @@ pub const Section = struct { } }; - pub fn format(msec: Section, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = msec; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format directly"); - } - - pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .msec = msec, .elf_file = elf_file, } }; } - const FormatContext = struct { + const Format = struct { msec: Section, elf_file: *Elf, - }; - pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const msec = ctx.msec; - const elf_file = ctx.elf_file; - try bw.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ - msec.name(elf_file), - msec.address(elf_file), - msec.size, - msec.alignment.toByteUnits() orelse 0, - msec.entsize, - msec.type, - msec.flags, - }); - for (msec.subsections.items) |msub| { - try bw.print(" {f}\n", .{msub.fmt(elf_file)}); + pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const msec = f.msec; + const elf_file = f.elf_file; + try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ + msec.name(elf_file), + msec.address(elf_file), + msec.size, + msec.alignment.toByteUnits() orelse 0, + msec.entsize, + msec.type, + msec.flags, + }); + for (msec.subsections.items) |msub| { + try writer.print(" {f}\n", .{msub.fmt(elf_file)}); + } } - } + }; pub const Index = u32; }; @@ -219,36 +211,28 @@ pub const Subsection = struct { return msec.bytes.items[msub.string_index..][0..msub.size]; } - pub fn format(msub: Subsection, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = msub; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format directly"); - } - - pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .msub = msub, .elf_file = elf_file, } }; } - const FormatContext = struct { + const Format = struct { msub: Subsection, elf_file: *Elf, - }; - pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const msub = ctx.msub; - const elf_file = ctx.elf_file; - try bw.print("@{x} : align({x}) : size({x})", .{ - msub.address(elf_file), - msub.alignment, - msub.size, - }); - if (!msub.alive) try bw.writeAll(" : [*]"); - } + pub fn default(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const msub = ctx.msub; + const elf_file = ctx.elf_file; + try writer.print("@{x} : align({x}) : size({x})", .{ + msub.address(elf_file), + msub.alignment, + msub.size, + }); + if (!msub.alive) try writer.writeAll(" : [*]"); + } + }; pub const Index = u32; }; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index be5b48c546ceb7f0f51d35d2ed6ffd98e21687e5..d202ba7f8a7ec2833b3dd1e5215d45cd54c71377 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -488,10 +488,7 @@ fn parseEhFrame( if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index)); } else { // TODO convert into an error - log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ - self.fmtPath(), - fde.offset, - }); + log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ self.fmtPath(), fde.offset }); continue; }; fde.cie_index = cie_index; @@ -582,7 +579,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { if (sym.flags.import) { if (sym.type(elf_file) != elf.STT_FUNC) // TODO convert into an error - log.debug("{fs}: {s}: CIE referencing external data reference", .{ + log.debug("{f}: {s}: CIE referencing external data reference", .{ self.fmtPath(), sym.name(elf_file), }); sym.flags.needs_plt = true; @@ -1428,129 +1425,116 @@ pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group { return &self.groups.items[index]; } -pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = self; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format objects directly"); -} - -pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .object = self, .elf_file = elf_file, } }; } -const FormatContext = struct { +const Format = struct { object: *Object, elf_file: *Elf, -}; -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - const elf_file = ctx.elf_file; - try bw.writeAll(" locals\n"); - for (object.locals()) |sym| { - try bw.print(" {f}\n", .{sym.fmt(elf_file)}); - } - try bw.writeAll(" globals\n"); - for (object.globals(), 0..) |sym, i| { - const first_global = object.first_global.?; - const ref = object.resolveSymbol(@intCast(i + first_global), elf_file); - if (elf_file.symbol(ref)) |ref_sym| { - try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)}); - } else { - try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const object = f.object; + const elf_file = f.elf_file; + try writer.writeAll(" locals\n"); + for (object.locals()) |sym| { + try writer.print(" {f}\n", .{sym.fmt(elf_file)}); + } + try writer.writeAll(" globals\n"); + for (object.globals(), 0..) |sym, i| { + const first_global = object.first_global.?; + const ref = object.resolveSymbol(@intCast(i + first_global), elf_file); + if (elf_file.symbol(ref)) |ref_sym| { + try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + } } } -} - -pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) { - return .{ .data = .{ - .object = self, - .elf_file = elf_file, - } }; -} - -fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - try bw.writeAll(" atoms\n"); - for (object.atoms_indexes.items) |atom_index| { - const atom_ptr = object.atom(atom_index) orelse continue; - try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)}); - } -} - -pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) { - return .{ .data = .{ - .object = self, - .elf_file = elf_file, - } }; -} - -fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - try bw.writeAll(" cies\n"); - for (object.cies.items, 0..) |cie, i| { - try bw.print(" cie({d}) : {f}\n", .{ i, cie.fmt(ctx.elf_file) }); - } -} - -pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) { - return .{ .data = .{ - .object = self, - .elf_file = elf_file, - } }; -} - -fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - try bw.writeAll(" fdes\n"); - for (object.fdes.items, 0..) |fde, i| { - try bw.print(" fde({d}) : {f}\n", .{ i, fde.fmt(ctx.elf_file) }); - } -} - -pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatGroups) { - return .{ .data = .{ - .object = self, - .elf_file = elf_file, - } }; -} -fn formatGroups(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const object = ctx.object; - const elf_file = ctx.elf_file; - try bw.writeAll(" groups\n"); - for (object.groups.items, 0..) |g, g_index| { - try bw.print(" {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index }); - if (!g.alive) try bw.writeAll(" : [*]"); - try bw.writeByte('\n'); - const g_members = g.members(elf_file); - for (g_members) |shndx| { - const atom_index = object.atoms_indexes.items[shndx]; + fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const object = f.object; + try writer.writeAll(" atoms\n"); + for (object.atoms_indexes.items) |atom_index| { const atom_ptr = object.atom(atom_index) orelse continue; - try bw.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) }); + try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)}); } } + + fn cies(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const object = f.object; + try writer.writeAll(" cies\n"); + for (object.cies.items, 0..) |cie, i| { + try writer.print(" cie({d}) : {f}\n", .{ i, cie.fmt(f.elf_file) }); + } + } + + fn fdes(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const object = f.object; + try writer.writeAll(" fdes\n"); + for (object.fdes.items, 0..) |fde, i| { + try writer.print(" fde({d}) : {f}\n", .{ i, fde.fmt(f.elf_file) }); + } + } + + fn groups(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const object = f.object; + const elf_file = f.elf_file; + try writer.writeAll(" groups\n"); + for (object.groups.items, 0..) |g, g_index| { + try writer.print(" {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index }); + if (!g.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); + const g_members = g.members(elf_file); + for (g_members) |shndx| { + const atom_index = object.atoms_indexes.items[shndx]; + const atom_ptr = object.atom(atom_index) orelse continue; + try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) }); + } + } + } +}; + +pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.cies) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.fdes) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.groups) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; } -pub fn fmtPath(self: Object) std.fmt.Formatter(formatPath) { +pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) { return .{ .data = self }; } -fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); +fn formatPath(object: Object, writer: *std.io.Writer) std.io.Writer.Error!void { if (object.archive) |ar| { - try bw.print("{f}({f})", .{ ar.path, object.path }); + try writer.print("{f}({f})", .{ ar.path, object.path }); } else { - try bw.print("{f}", .{object.path}); + try writer.print("{f}", .{object.path}); } } diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 941bd374ed70832686eb6af1887a3a57734218db..e6b20152e60833d028fc77633db8fb0dbab2db54 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -509,39 +509,31 @@ pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void } } -pub fn format(self: SharedObject, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = self; - _ = bw; - _ = unused_fmt_string; - @compileError("unreachable"); -} - -pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .shared = self, .elf_file = elf_file, } }; } -const FormatContext = struct { +const Format = struct { shared: SharedObject, elf_file: *Elf, -}; -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const shared = ctx.shared; - const elf_file = ctx.elf_file; - try bw.writeAll(" globals\n"); - for (shared.symbols.items, 0..) |sym, i| { - const ref = shared.resolveSymbol(@intCast(i), elf_file); - if (elf_file.symbol(ref)) |ref_sym| { - try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)}); - } else { - try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const shared = f.shared; + const elf_file = f.elf_file; + try writer.writeAll(" globals\n"); + for (shared.symbols.items, 0..) |sym, i| { + const ref = shared.resolveSymbol(@intCast(i), elf_file); + if (elf_file.symbol(ref)) |ref_sym| { + try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + } } } -} +}; const SharedObject = @This(); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 2790afdd12479091082ba60235e67ec207db5031..f27aebe5ba87a10c18d9498e0e4288c99af7aa03 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -316,81 +316,72 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { out.st_size = esym.st_size; } -pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = symbol; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format Symbol directly"); -} - -const FormatContext = struct { +const Format = struct { symbol: Symbol, elf_file: *Elf, + + fn name(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const elf_file = f.elf_file; + const symbol = f.symbol; + try writer.writeAll(symbol.name(elf_file)); + switch (symbol.version_index.VERSION) { + @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {}, + else => { + const file_ptr = symbol.file(elf_file).?; + assert(file_ptr == .shared_object); + const shared_object = file_ptr.shared_object; + try writer.print("@{s}", .{shared_object.versionString(symbol.version_index)}); + }, + } + } + + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const symbol = f.symbol; + const elf_file = f.elf_file; + try writer.print("%{d} : {f} : @{x}", .{ + symbol.esym_index, + symbol.fmtName(elf_file), + symbol.address(.{ .plt = false, .trampoline = false }, elf_file), + }); + if (symbol.file(elf_file)) |file_ptr| { + if (symbol.isAbs(elf_file)) { + if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) { + try writer.writeAll(" : undef"); + } else { + try writer.writeAll(" : absolute"); + } + } else if (symbol.outputShndx(elf_file)) |shndx| { + try writer.print(" : shdr({d})", .{shndx}); + } + if (symbol.atom(elf_file)) |atom_ptr| { + try writer.print(" : atom({d})", .{atom_ptr.atom_index}); + } + var buf: [2]u8 = .{'_'} ** 2; + if (symbol.flags.@"export") buf[0] = 'E'; + if (symbol.flags.import) buf[1] = 'I'; + try writer.print(" : {s}", .{&buf}); + if (symbol.flags.weak) try writer.writeAll(" : weak"); + switch (file_ptr) { + inline else => |x| try writer.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }), + } + } else try writer.writeAll(" : unresolved"); + } }; -pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(formatName) { +pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.name) { return .{ .data = .{ .symbol = symbol, .elf_file = elf_file, } }; } -fn formatName(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const elf_file = ctx.elf_file; - const symbol = ctx.symbol; - try bw.writeAll(symbol.name(elf_file)); - switch (symbol.version_index.VERSION) { - @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {}, - else => { - const file_ptr = symbol.file(elf_file).?; - assert(file_ptr == .shared_object); - const shared_object = file_ptr.shared_object; - try bw.print("@{s}", .{shared_object.versionString(symbol.version_index)}); - }, - } -} - -pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(format2) { +pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .symbol = symbol, .elf_file = elf_file, } }; } -fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const symbol = ctx.symbol; - const elf_file = ctx.elf_file; - try bw.print("%{d} : {f} : @{x}", .{ - symbol.esym_index, - symbol.fmtName(elf_file), - symbol.address(.{ .plt = false, .trampoline = false }, elf_file), - }); - if (symbol.file(elf_file)) |file_ptr| { - if (symbol.isAbs(elf_file)) { - if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) { - try bw.writeAll(" : undef"); - } else { - try bw.writeAll(" : absolute"); - } - } else if (symbol.outputShndx(elf_file)) |shndx| { - try bw.print(" : shdr({d})", .{shndx}); - } - if (symbol.atom(elf_file)) |atom_ptr| { - try bw.print(" : atom({d})", .{atom_ptr.atom_index}); - } - var buf: [2]u8 = .{'_'} ** 2; - if (symbol.flags.@"export") buf[0] = 'E'; - if (symbol.flags.import) buf[1] = 'I'; - try bw.print(" : {s}", .{&buf}); - if (symbol.flags.weak) try bw.writeAll(" : weak"); - switch (file_ptr) { - inline else => |x| try bw.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }), - } - } else try bw.writeAll(" : unresolved"); -} - pub const Flags = packed struct { /// Whether the symbol is imported at runtime. import: bool = false, diff --git a/src/link/Elf/Thunk.zig b/src/link/Elf/Thunk.zig index 8c3a1448bd1c61bea8fd2662bcafcbe9126a2909..38692e795e6c56c3cbdd610bc9a88b68510f650a 100644 --- a/src/link/Elf/Thunk.zig +++ b/src/link/Elf/Thunk.zig @@ -65,35 +65,27 @@ fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize { }; } -pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = thunk; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format Thunk directly"); -} - -pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) { +pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .thunk = thunk, .elf_file = elf_file, } }; } -const FormatContext = struct { +const Format = struct { thunk: Thunk, elf_file: *Elf, -}; -fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const thunk = ctx.thunk; - const elf_file = ctx.elf_file; - try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); - for (thunk.symbols.keys()) |ref| { - const sym = elf_file.symbol(ref).?; - try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const thunk = f.thunk; + const elf_file = f.elf_file; + try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); + for (thunk.symbols.keys()) |ref| { + const sym = elf_file.symbol(ref).?; + try writer.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); + } } -} +}; pub const Index = u32; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index c969b6a3b06b721c07b7bd345e32b1c7aa58af2d..78b93a3ec6e3dc62b1d78fe2770633d58ad32392 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -799,9 +799,9 @@ pub fn initRelaSections(self: *ZigObject, elf_file: *Elf) !void { const out_shndx = atom_ptr.output_section_index; const out_shdr = elf_file.sections.items(.shdr)[out_shndx]; if (out_shdr.sh_type == elf.SHT_NOBITS) continue; - const rela_sect_name = try std.fmt.allocPrintZ(gpa, ".rela{s}", .{ + const rela_sect_name = try std.fmt.allocPrintSentinel(gpa, ".rela{s}", .{ elf_file.getShString(out_shdr.sh_name), - }); + }, 0); defer gpa.free(rela_sect_name); _ = elf_file.sectionByName(rela_sect_name) orelse 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 { const out_shndx = atom_ptr.output_section_index; const out_shdr = elf_file.sections.items(.shdr)[out_shndx]; if (out_shdr.sh_type == elf.SHT_NOBITS) continue; - const rela_sect_name = try std.fmt.allocPrintZ(gpa, ".rela{s}", .{ + const rela_sect_name = try std.fmt.allocPrintSentinel(gpa, ".rela{s}", .{ elf_file.getShString(out_shdr.sh_name), - }); + }, 0); defer gpa.free(rela_sect_name); const out_rela_shndx = elf_file.sectionByName(rela_sect_name).?; 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 .requires_padding = requires_padding, }); atom_ptr.value = @intCast(alloc_res.value); - log.debug("allocated {s} at {x}\n placement {?}", .{ + log.debug("allocated {s} at {x}\n placement {f}", .{ atom_ptr.name(elf_file), atom_ptr.offset(elf_file), alloc_res.placement, @@ -1977,7 +1977,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 }; } - log.debug(" prev {?}, next {?}", .{ atom_ptr.prev_atom_ref, atom_ptr.next_atom_ref }); + log.debug(" prev {f}, next {f}", .{ atom_ptr.prev_atom_ref, atom_ptr.next_atom_ref }); } pub fn resetShdrIndexes(self: *ZigObject, backlinks: []const u32) void { @@ -2186,48 +2186,46 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void { } } -pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { - return .{ .data = .{ - .self = self, - .elf_file = elf_file, - } }; -} - -const FormatContext = struct { +const Format = struct { self: *ZigObject, elf_file: *Elf, + + fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const self = f.self; + const elf_file = f.elf_file; + try writer.writeAll(" locals\n"); + for (self.local_symbols.items) |index| { + const local = self.symbols.items[index]; + try writer.print(" {f}\n", .{local.fmt(elf_file)}); + } + try writer.writeAll(" globals\n"); + for (f.self.global_symbols.items) |index| { + const global = self.symbols.items[index]; + try writer.print(" {f}\n", .{global.fmt(elf_file)}); + } + } + + fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.writeAll(" atoms\n"); + for (f.self.atoms_indexes.items) |atom_index| { + const atom_ptr = f.self.atom(atom_index) orelse continue; + try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)}); + } + } }; -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const self = ctx.self; - const elf_file = ctx.elf_file; - try bw.writeAll(" locals\n"); - for (self.local_symbols.items) |index| { - const local = self.symbols.items[index]; - try bw.print(" {f}\n", .{local.fmt(elf_file)}); - } - try bw.writeAll(" globals\n"); - for (ctx.self.global_symbols.items) |index| { - const global = self.symbols.items[index]; - try bw.print(" {f}\n", .{global.fmt(elf_file)}); - } -} - -pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatAtoms) { +pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .self = self, .elf_file = elf_file, } }; } -fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - try bw.writeAll(" atoms\n"); - for (ctx.self.atoms_indexes.items) |atom_index| { - const atom_ptr = ctx.self.atom(atom_index) orelse continue; - try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)}); - } +pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) { + return .{ .data = .{ + .self = self, + .elf_file = elf_file, + } }; } const ElfSym = struct { diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 1586a0675d472f669cb4626aa6deb5913d5083b7..671c0a80d849d8324e4b191f5599a153a85715f2 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -47,48 +47,32 @@ pub const Fde = struct { return object.relocs.items[fde.rel_index..][0..fde.rel_num]; } - pub fn format( - fde: Fde, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = fde; - _ = unused_fmt_string; - _ = bw; - @compileError("do not format FDEs directly"); - } - - pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .fde = fde, .elf_file = elf_file, } }; } - const FdeFormatContext = struct { + const Format = struct { fde: Fde, elf_file: *Elf, - }; - fn format2( - ctx: FdeFormatContext, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; - const fde = ctx.fde; - const elf_file = ctx.elf_file; - const base_addr = fde.address(elf_file); - const object = elf_file.file(fde.file_index).?.object; - const atom_name = fde.atom(object).name(elf_file); - try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{ - base_addr + fde.out_offset, - fde.calcSize(), - fde.cie_index, - atom_name, - }); - if (!fde.alive) try bw.writeAll(" : [*]"); - } + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const fde = f.fde; + const elf_file = f.elf_file; + const base_addr = fde.address(elf_file); + const object = elf_file.file(fde.file_index).?.object; + const atom_name = fde.atom(object).name(elf_file); + try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ + base_addr + fde.out_offset, + fde.calcSize(), + fde.cie_index, + atom_name, + }); + if (!fde.alive) try writer.writeAll(" : [*]"); + } + }; }; pub const Cie = struct { @@ -146,44 +130,28 @@ pub const Cie = struct { return true; } - pub fn format( - cie: Cie, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = cie; - _ = unused_fmt_string; - _ = bw; - @compileError("do not format CIEs directly"); - } - - pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .cie = cie, .elf_file = elf_file, } }; } - const CieFormatContext = struct { + const Format = struct { cie: Cie, elf_file: *Elf, - }; - fn format2( - ctx: CieFormatContext, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; - const cie = ctx.cie; - const elf_file = ctx.elf_file; - const base_addr = cie.address(elf_file); - try bw.print("@{x} : size({x})", .{ - base_addr + cie.out_offset, - cie.calcSize(), - }); - if (!cie.alive) try bw.writeAll(" : [*]"); - } + fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const cie = f.cie; + const elf_file = f.elf_file; + const base_addr = cie.address(elf_file); + try writer.print("@{x} : size({x})", .{ + base_addr + cie.out_offset, + cie.calcSize(), + }); + if (!cie.alive) try writer.writeAll(" : [*]"); + } + }; }; pub const Iterator = struct { diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 68bd3034072459d7a5585170df8e914743548fed..99425457e04eacf1bba7c6db19dfb65894b583a0 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -10,17 +10,16 @@ pub const File = union(enum) { }; } - pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { + pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) { return .{ .data = file }; } - fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); + fn formatPath(file: File, writer: *std.io.Writer) std.io.Writer.Error!void { switch (file) { - .zig_object => |zo| try bw.writeAll(zo.basename), - .linker_defined => try bw.writeAll("(linker defined)"), - .object => |x| try bw.print("{f}", .{x.fmtPath()}), - .shared_object => |x| try bw.print("{f}", .{x.path}), + .zig_object => |zo| try writer.writeAll(zo.basename), + .linker_defined => try writer.writeAll("(linker defined)"), + .object => |x| try writer.print("{f}", .{x.fmtPath()}), + .shared_object => |x| try writer.print("{f}", .{@as(Path, x.path)}), } } diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig index 44b2ced73ddfb6ef401128f29c2f3a9f395bc7f2..e4b68e4305a2d99c3a25783df6ae157d2de53dec 100644 --- a/src/link/Elf/gc.zig +++ b/src/link/Elf/gc.zig @@ -185,9 +185,8 @@ const Level = struct { self.value += 1; } - pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - try bw.splatByteAll(' ', self.value); + pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void { + try w.splatByteAll(' ', self.value); } }; diff --git a/src/link/Elf/relocation.zig b/src/link/Elf/relocation.zig index 6b73b041fab980768f6beeb828b123f0b9ff5b4c..0a66f337e65903a702def32a8166bf48d98856f1 100644 --- a/src/link/Elf/relocation.zig +++ b/src/link/Elf/relocation.zig @@ -141,20 +141,19 @@ const FormatRelocTypeCtx = struct { cpu_arch: std.Target.Cpu.Arch, }; -pub fn fmtRelocType(r_type: u32, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatRelocType) { +pub fn fmtRelocType(r_type: u32, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(FormatRelocTypeCtx, formatRelocType) { return .{ .data = .{ .r_type = r_type, .cpu_arch = cpu_arch, } }; } -fn formatRelocType(ctx: FormatRelocTypeCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); +fn formatRelocType(ctx: FormatRelocTypeCtx, writer: *std.io.Writer) std.io.Writer.Error!void { const r_type = ctx.r_type; switch (ctx.cpu_arch) { - .x86_64 => try bw.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}), - .aarch64 => try bw.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}), - .riscv64 => try bw.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}), + .x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}), + .aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}), + .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}), else => unreachable, } } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index c7436534ea6660e4d447a7e99dc6c05066849631..56c4d9faa23c7f8cff5af11c894cbe545f69e31f 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -606,31 +606,30 @@ pub const GotSection = struct { } } - const FormatCtx = struct { + const Format = struct { got: GotSection, elf_file: *Elf, + + pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const got = f.got; + const elf_file = f.elf_file; + try writer.writeAll("GOT\n"); + for (got.entries.items) |entry| { + const symbol = elf_file.symbol(entry.ref).?; + try writer.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + entry.cell_index, + entry.address(elf_file), + entry.ref, + symbol.address(.{}, elf_file), + symbol.name(elf_file), + }); + } + } }; - pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .got = got, .elf_file = elf_file } }; } - - pub fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const got = ctx.got; - const elf_file = ctx.elf_file; - try bw.writeAll("GOT\n"); - for (got.entries.items) |entry| { - const symbol = elf_file.symbol(entry.ref).?; - try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ - entry.cell_index, - entry.address(elf_file), - entry.ref, - symbol.address(.{}, elf_file), - symbol.name(elf_file), - }); - } - } }; pub const PltSection = struct { @@ -743,32 +742,31 @@ pub const PltSection = struct { } } - const FormatCtx = struct { + const Format = struct { plt: PltSection, elf_file: *Elf, + + pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { + const plt = f.plt; + const elf_file = f.elf_file; + try writer.writeAll("PLT\n"); + for (plt.symbols.items, 0..) |ref, i| { + const symbol = elf_file.symbol(ref).?; + try writer.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.pltAddress(elf_file), + ref, + symbol.address(.{}, elf_file), + symbol.name(elf_file), + }); + } + } }; - pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Formatter(format2) { + pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .plt = plt, .elf_file = elf_file } }; } - pub fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const plt = ctx.plt; - const elf_file = ctx.elf_file; - try bw.writeAll("PLT\n"); - for (plt.symbols.items, 0..) |ref, i| { - const symbol = elf_file.symbol(ref).?; - try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ - i, - symbol.pltAddress(elf_file), - ref, - symbol.address(.{}, elf_file), - symbol.name(elf_file), - }); - } - } - const x86_64 = struct { fn write(plt: PltSection, elf_file: *Elf, bw: *Writer) Writer.Error!void { const shdrs = elf_file.sections.items(.shdr); diff --git a/src/link/LdScript.zig b/src/link/LdScript.zig index eadde7ce6c12a54879b8607215589d93095056b6..534f9766bbc32d88cc4e6fc8922a8670399d0a82 100644 --- a/src/link/LdScript.zig +++ b/src/link/LdScript.zig @@ -42,7 +42,7 @@ pub fn parse( switch (tok.id) { .invalid => { return diags.failParse(path, "invalid token in LD script: '{f}' ({d}:{d})", .{ - std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column, + std.ascii.hexEscape(tok.get(data), .lower), line, column, }); }, .new_line => { diff --git a/src/link/Lld.zig b/src/link/Lld.zig index 4b3c611b4a9f20addb56e41ea525c959424faf9f..1aeeb5d2145e3c3a170cfb076ef86be7fcfd2a5f 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -294,7 +294,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); } else null; - log.debug("zcu_obj_path={?}", .{zcu_obj_path}); + log.debug("zcu_obj_path={?f}", .{zcu_obj_path}); const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj) comp.compiler_rt_obj.?.full_object_path @@ -437,7 +437,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename})); } if (comp.version) |version| { - try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor })); + try argv.append(try allocPrint(arena, "-VERSION:{d}.{d}", .{ version.major, version.minor })); } if (target_util.llvmMachineAbi(target)) |mabi| { @@ -507,7 +507,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { if (comp.emit_implib) |raw_emit_path| { const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path); - try argv.append(try allocPrint(arena, "-IMPLIB:{}", .{path})); + try argv.append(try allocPrint(arena, "-IMPLIB:{f}", .{path})); } if (comp.config.link_libc) { @@ -533,7 +533,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { }, .object, .archive => |obj| { if (obj.must_link) { - argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Cache.Path, obj.path)})); + argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{f}", .{@as(Cache.Path, obj.path)})); } else { argv.appendAssumeCapacity(try obj.path.toString(arena)); } @@ -933,9 +933,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ @tagName(base.build_id), })), - .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ - std.fmt.fmtSliceHexLower(hs.toSlice()), - })), + .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})), } 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 { if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue; } - const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ + const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, }); try argv.append(lib_path); @@ -1231,14 +1229,14 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { })); } else if (target.isFreeBSDLibC()) { for (freebsd.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ + const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{ comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, }); try argv.append(lib_path); } } else if (target.isNetBSDLibC()) { for (netbsd.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ + const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{ comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, }); try argv.append(lib_path); @@ -1511,9 +1509,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ @tagName(base.build_id), })), - .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ - std.fmt.fmtSliceHexLower(hs.toSlice()), - })), + .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})), .md5 => {}, } @@ -1539,13 +1535,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { if (comp.config.link_libc and is_exe_or_dyn_lib) { if (target.os.tag == .wasi) { - for (comp.wasi_emulated_libs) |crt_file| { - try argv.append(try comp.crtFileAsString( - arena, - wasi_libc.emulatedLibCRFileLibName(crt_file), - )); - } - try argv.append(try comp.crtFileAsString( arena, wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), @@ -1660,7 +1649,7 @@ fn spawnLld( child.stderr_behavior = .Pipe; child.spawn() catch |err| break :term err; - stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); + stderr = try child.stderr.?.deprecatedReader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); break :term child.wait(); }) catch |first_err| term: { const err = switch (first_err) { @@ -1674,7 +1663,7 @@ fn spawnLld( log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); { defer rsp_file.close(); - var rsp_buf = std.io.bufferedWriter(rsp_file.writer()); + var rsp_buf = std.io.bufferedWriter(rsp_file.deprecatedWriter()); const rsp_writer = rsp_buf.writer(); for (argv[2..]) |arg| { try rsp_writer.writeByte('"'); @@ -1708,7 +1697,7 @@ fn spawnLld( rsp_child.stderr_behavior = .Pipe; rsp_child.spawn() catch |err| break :err err; - stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); + stderr = try rsp_child.stderr.?.deprecatedReader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); break :term rsp_child.wait() catch |err| break :err err; } }, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b319df165266a9342a7bccc91d7b5196e239d43b..b5f2344b7ea3dfbf76e86aef1424ed8594f2122a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3898,29 +3898,28 @@ pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void { self.hot_state.mach_task = null; } -pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) { +pub fn dumpState(self: *MachO) std.fmt.Formatter(*MachO, fmtDumpState) { return .{ .data = self }; } -fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; +fn fmtDumpState(self: *MachO, w: *Writer) Writer.Error!void { if (self.getZigObject()) |zo| { - try bw.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); - try bw.print("{f}{f}\n", .{ + try w.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); + try w.print("{f}{f}\n", .{ zo.fmtAtoms(self), zo.fmtSymtab(self), }); } for (self.objects.items) |index| { const object = self.getFile(index).?.object; - try bw.print("object({d}) : {f} : has_debug({})", .{ + try w.print("object({d}) : {f} : has_debug({})", .{ index, object.fmtPath(), object.hasDebugInfo(), }); - if (!object.alive) try bw.writeAll(" : ([*])"); - try bw.writeByte('\n'); - try bw.print("{f}{f}{f}{f}{f}\n", .{ + if (!object.alive) try w.writeAll(" : ([*])"); + try w.writeByte('\n'); + try w.print("{f}{f}{f}{f}{f}\n", .{ object.fmtAtoms(self), object.fmtCies(self), object.fmtFdes(self), @@ -3930,42 +3929,41 @@ fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u } for (self.dylibs.items) |index| { const dylib = self.getFile(index).?.dylib; - try bw.print("dylib({d}) : {f} : needed({}) : weak({})", .{ + try w.print("dylib({d}) : {f} : needed({}) : weak({})", .{ index, @as(Path, dylib.path), dylib.needed, dylib.weak, }); - if (!dylib.isAlive(self)) try bw.writeAll(" : ([*])"); - try bw.writeByte('\n'); - try bw.print("{f}\n", .{dylib.fmtSymtab(self)}); + if (!dylib.isAlive(self)) try w.writeAll(" : ([*])"); + try w.writeByte('\n'); + try w.print("{f}\n", .{dylib.fmtSymtab(self)}); } if (self.getInternalObject()) |internal| { - try bw.print("internal({d}) : internal\n", .{internal.index}); - try bw.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); + try w.print("internal({d}) : internal\n", .{internal.index}); + try w.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); } - try bw.writeAll("thunks\n"); + try w.writeAll("thunks\n"); for (self.thunks.items, 0..) |thunk, index| { - try bw.print("thunk({d}) : {f}\n", .{ index, thunk.fmt(self) }); + try w.print("thunk({d}) : {f}\n", .{ index, thunk.fmt(self) }); } - try bw.print("stubs\n{f}\n", .{self.stubs.fmt(self)}); - try bw.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)}); - try bw.print("got\n{f}\n", .{self.got.fmt(self)}); - try bw.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)}); - try bw.writeByte('\n'); - try bw.print("sections\n{f}\n", .{self.fmtSections()}); - try bw.print("segments\n{f}\n", .{self.fmtSegments()}); + try w.print("stubs\n{f}\n", .{self.stubs.fmt(self)}); + try w.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)}); + try w.print("got\n{f}\n", .{self.got.fmt(self)}); + try w.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)}); + try w.writeByte('\n'); + try w.print("sections\n{f}\n", .{self.fmtSections()}); + try w.print("segments\n{f}\n", .{self.fmtSegments()}); } -fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) { +fn fmtSections(self: *MachO) std.fmt.Formatter(*MachO, formatSections) { return .{ .data = self }; } -fn formatSections(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; +fn formatSections(self: *MachO, w: *Writer) Writer.Error!void { const slice = self.sections.slice(); for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| { - try bw.print( + try w.print( "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n", .{ 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 } } -fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) { +fn fmtSegments(self: *MachO) std.fmt.Formatter(*MachO, formatSegments) { return .{ .data = self }; } -fn formatSegments(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; +fn formatSegments(self: *MachO, w: *Writer) Writer.Error!void { for (self.segments.items, 0..) |seg, i| { - try bw.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{ + try w.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{ i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize, seg.fileoff, seg.fileoff + seg.filesize, }); } } -pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) { +pub fn fmtSectType(tt: u8) std.fmt.Formatter(u8, formatSectType) { return .{ .data = tt }; } -fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; +fn formatSectType(tt: u8, w: *Writer) Writer.Error!void { const name = switch (tt) { macho.S_REGULAR => "REGULAR", macho.S_ZEROFILL => "ZEROFILL", @@ -4018,9 +4014,9 @@ fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) W macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS", macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS", macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS", - else => |x| return bw.print("UNKNOWN({x})", .{x}), + else => |x| return w.print("UNKNOWN({x})", .{x}), }; - try bw.print("{s}", .{name}); + try w.print("{s}", .{name}); } const is_hot_update_compatible = switch (builtin.target.os.tag) { @@ -4253,28 +4249,27 @@ pub const Platform = struct { return false; } - pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) { + pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.target) { return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } }; } - const FmtCtx = struct { + const Format = struct { platform: Platform, cpu_arch: std.Target.Cpu.Arch, - }; - pub fn formatTarget(ctx: FmtCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - try bw.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) }); - if (ctx.platform.abi != .none) { - try bw.print("-{s}", .{@tagName(ctx.platform.abi)}); + pub fn target(f: Format, w: *Writer) Writer.Error!void { + try w.print("{s}-{s}", .{ @tagName(f.cpu_arch), @tagName(f.platform.os_tag) }); + if (f.platform.abi != .none) { + try w.print("-{s}", .{@tagName(f.platform.abi)}); + } } - } + }; /// Caller owns the memory. pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 { var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); - try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)}); + try buffer.writer().print("{f}", .{plat.fmtTarget(cpu_arch)}); return buffer.toOwnedSlice(); } @@ -4475,8 +4470,7 @@ pub const Ref = struct { }; } - pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); + pub fn format(ref: Ref, bw: *Writer) Writer.Error!void { try bw.print("%{d} in file({d})", .{ ref.index, ref.file }); } }; diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index d625b8ead82f0a4b466060341c0ac4e96dd679a3..f074d740a774abf0631927ad3e92581ba44fd832 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -30,7 +30,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{ - std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), + std.ascii.hexEscape(ARFMAG, .lower), std.ascii.hexEscape(&hdr.ar_fmag, .lower), }); } @@ -203,26 +203,25 @@ pub const ArSymtab = struct { try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len); } - const FormatContext = struct { + const PrintFormat = struct { ar: ArSymtab, macho_file: *MachO, + + fn default(f: PrintFormat, bw: *Writer) Writer.Error!void { + const ar = f.ar; + const macho_file = f.macho_file; + for (ar.entries.items, 0..) |entry, i| { + const name = ar.strtab.getAssumeExists(entry.off); + const file = macho_file.getFile(entry.file).?; + try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file, file.fmtPath() }); + } + } }; - pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(PrintFormat, PrintFormat.default) { return .{ .data = .{ .ar = ar, .macho_file = macho_file } }; } - fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const ar = ctx.ar; - const macho_file = ctx.macho_file; - for (ar.entries.items, 0..) |entry, i| { - const name = ar.strtab.getAssumeExists(entry.off); - const file = macho_file.getFile(entry.file).?; - try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file, file.fmtPath() }); - } - } - const Entry = struct { /// Symbol name offset off: u32, diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 7e100c059dfa8eae56bc2b4da10460c37da6b7c4..b41a0031bc09a7cd0fabd4521a87df26e3a05d64 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -937,8 +937,8 @@ const x86_64 = struct { } fn encode(insts: []const Instruction, code: []u8) !void { - var bw: Writer = .fixed(code); - for (insts) |inst| try inst.encode(&bw, .{}); + var stream: Writer = .fixed(code); + for (insts) |inst| try inst.encode(&stream, .{}); } 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 assert(i == buffer.len); } -pub fn format( - atom: Atom, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = atom; - _ = unused_fmt_string; - _ = options; - _ = writer; - @compileError("do not format Atom directly"); -} - -pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(format2) { +pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .atom = atom, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { atom: Atom, macho_file: *MachO, -}; -fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const atom = ctx.atom; - const macho_file = ctx.macho_file; - const file = atom.getFile(macho_file); - try bw.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ - atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), - atom.out_n_sect, atom.alignment, atom.size, - atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, - }); - if (!atom.isAlive()) try bw.writeAll(" : [*]"); - if (atom.getUnwindRecords(macho_file).len > 0) { - try bw.writeAll(" : unwind{ "); - const extra = atom.getExtra(macho_file); - for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { - const rec = file.object.getUnwindRecord(index); - try bw.print("{d}", .{index}); - if (!rec.alive) try bw.writeAll("([*])"); - if (i < extra.unwind_index + extra.unwind_count - 1) try bw.writeAll(", "); + fn print(f: Format, w: *Writer) Writer.Error!void { + const atom = f.atom; + const macho_file = f.macho_file; + const file = atom.getFile(macho_file); + try w.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ + atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), + atom.out_n_sect, atom.alignment, atom.size, + atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, + }); + if (!atom.isAlive()) try w.writeAll(" : [*]"); + if (atom.getUnwindRecords(macho_file).len > 0) { + try w.writeAll(" : unwind{ "); + const extra = atom.getExtra(macho_file); + for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { + const rec = file.object.getUnwindRecord(index); + try w.print("{d}", .{index}); + if (!rec.alive) try w.writeAll("([*])"); + if (i < extra.unwind_index + extra.unwind_count - 1) try w.writeAll(", "); + } + try w.writeAll(" }"); } - try bw.writeAll(" }"); } -} +}; pub const Index = u32; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index f7af39aa938c333299bf3fcb69e47453fff7c22e..d950f791ffe8315cd7113dcd5a8c7bbf33709b43 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -650,46 +650,32 @@ pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void { } } -pub fn format( - self: *Dylib, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = self; - _ = unused_fmt_string; - _ = options; - _ = writer; - @compileError("do not format dylib directly"); -} - -pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .dylib = self, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { dylib: *Dylib, macho_file: *MachO, -}; -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const dylib = ctx.dylib; - const macho_file = ctx.macho_file; - try bw.writeAll(" globals\n"); - for (dylib.symbols.items, 0..) |sym, i| { - const ref = dylib.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + fn symtab(f: Format, w: *Writer) Writer.Error!void { + const dylib = f.dylib; + const macho_file = f.macho_file; + try w.writeAll(" globals\n"); + for (dylib.symbols.items, 0..) |sym, i| { + const ref = dylib.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } } } -} +}; pub const TargetMatcher = struct { allocator: Allocator, diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 19eb04b7127a50853560c6c90ad29815130fbb90..1329572dc8fb7c75db909a1de07fceab0724470c 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -836,50 +836,48 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool { return false; } -const FormatContext = struct { +const Format = struct { self: *InternalObject, macho_file: *MachO, + + fn atoms(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll(" atoms\n"); + for (f.self.getAtoms()) |atom_index| { + const atom = f.self.getAtom(atom_index) orelse continue; + try w.print(" {f}\n", .{atom.fmt(f.macho_file)}); + } + } + + fn symtab(f: Format, w: *Writer) Writer.Error!void { + const macho_file = f.macho_file; + const self = f.self; + try w.writeAll(" symbols\n"); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } + } + } }; -pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { +pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { return .{ .data = .{ .self = self, .macho_file = macho_file, } }; } -fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - try bw.writeAll(" atoms\n"); - for (ctx.self.getAtoms()) |atom_index| { - const atom = ctx.self.getAtom(atom_index) orelse continue; - try bw.print(" {f}\n", .{atom.fmt(ctx.macho_file)}); - } -} - -pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .self = self, .macho_file = macho_file, } }; } -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const macho_file = ctx.macho_file; - const self = ctx.self; - try bw.writeAll(" symbols\n"); - for (self.symbols.items, 0..) |sym, i| { - const ref = self.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); - } - } -} - const Section = struct { header: macho.section_64, relocs: std.ArrayListUnmanaged(Relocation) = .empty, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 4f344f536d6289008e090a2e2ef1dca920d3fd7b..d4ac509a625110ffc241f11f4a8368e6b795b264 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -308,7 +308,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void { } else nlists.len; if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) { - const name = try std.fmt.allocPrintZ(allocator, "{s}${s}$begin", .{ sect.segName(), sect.sectName() }); + const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$begin", .{ + sect.segName(), sect.sectName(), + }, 0); defer allocator.free(name); const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr; const atom_index = try self.addAtom(allocator, .{ @@ -364,7 +366,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void { // which cannot be contained in any non-zero atom (since then this atom // would exceed section boundaries). In order to facilitate this behaviour, // we create a dummy zero-sized atom at section end (addr + size). - const name = try std.fmt.allocPrintZ(allocator, "{s}${s}$end", .{ sect.segName(), sect.sectName() }); + const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$end", .{ + sect.segName(), sect.sectName(), + }, 0); defer allocator.free(name); const atom_index = try self.addAtom(allocator, .{ .name = try self.addString(allocator, name), @@ -394,7 +398,7 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void { if (isFixedSizeLiteral(sect)) continue; if (isPtrLiteral(sect)) continue; - const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }); + const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }, 0); defer allocator.free(name); const atom_index = try self.addAtom(allocator, .{ @@ -462,7 +466,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m } end += 1; - const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count}); + const name = try std.fmt.allocPrintSentinel(allocator, "l._str{d}", .{count}, 0); defer allocator.free(name); const name_str = try self.addString(allocator, name); @@ -529,7 +533,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO pos += rec_size; count += 1; }) { - const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count}); + const name = try std.fmt.allocPrintSentinel(allocator, "l._literal{d}", .{count}, 0); defer allocator.free(name); const name_str = try self.addString(allocator, name); @@ -587,7 +591,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) for (0..num_ptrs) |i| { const pos: u32 = @as(u32, @intCast(i)) * rec_size; - const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i}); + const name = try std.fmt.allocPrintSentinel(allocator, "l._ptr{d}", .{i}, 0); defer allocator.free(name); const name_str = try self.addString(allocator, name); @@ -1558,7 +1562,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { const nlist = &self.symtab.items(.nlist)[nlist_idx]; const nlist_atom = &self.symtab.items(.atom)[nlist_idx]; - const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)}); + const name = try std.fmt.allocPrintSentinel(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)}, 0); defer gpa.free(name); const alignment = (nlist.n_desc >> 8) & 0x0f; @@ -2512,130 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_ return data; } -pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = self; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format objects directly"); -} - -const FormatContext = struct { +const Format = struct { object: *Object, macho_file: *MachO, + + fn atoms(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + const macho_file = f.macho_file; + try w.writeAll(" atoms\n"); + for (object.getAtoms()) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; + try w.print(" {f}\n", .{atom.fmt(macho_file)}); + } + } + fn cies(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + try w.writeAll(" cies\n"); + for (object.cies.items, 0..) |cie, i| { + try w.print(" cie({d}) : {f}\n", .{ i, cie.fmt(f.macho_file) }); + } + } + fn fdes(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + try w.writeAll(" fdes\n"); + for (object.fdes.items, 0..) |fde, i| { + try w.print(" fde({d}) : {f}\n", .{ i, fde.fmt(f.macho_file) }); + } + } + fn unwindRecords(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + const macho_file = f.macho_file; + try w.writeAll(" unwind records\n"); + for (object.unwind_records_indexes.items) |rec| { + try w.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); + } + } + + fn symtab(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + const macho_file = f.macho_file; + try w.writeAll(" symbols\n"); + for (object.symbols.items, 0..) |sym, i| { + const ref = object.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } + } + for (object.stab_files.items) |sf| { + try w.print(" stabs({s},{s},{s})\n", .{ + sf.getCompDir(object.*), + sf.getTuName(object.*), + sf.getOsoPath(object.*), + }); + for (sf.stabs.items) |stab| { + try w.print(" {f}", .{stab.fmt(object.*)}); + } + } + } }; -pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { +pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { return .{ .data = .{ .object = self, .macho_file = macho_file, } }; } -fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - const macho_file = ctx.macho_file; - try bw.writeAll(" atoms\n"); - for (object.getAtoms()) |atom_index| { - const atom = object.getAtom(atom_index) orelse continue; - try bw.print(" {f}\n", .{atom.fmt(macho_file)}); - } -} - -pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatCies) { +pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.cies) { return .{ .data = .{ .object = self, .macho_file = macho_file, } }; } -fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - try bw.writeAll(" cies\n"); - for (object.cies.items, 0..) |cie, i| { - try bw.print(" cie({d}) : {f}\n", .{ i, cie.fmt(ctx.macho_file) }); - } -} - -pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatFdes) { +pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.fdes) { return .{ .data = .{ .object = self, .macho_file = macho_file, } }; } -fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - try bw.writeAll(" fdes\n"); - for (object.fdes.items, 0..) |fde, i| { - try bw.print(" fde({d}) : {f}\n", .{ i, fde.fmt(ctx.macho_file) }); - } -} - -pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatUnwindRecords) { +pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.unwindRecords) { return .{ .data = .{ .object = self, .macho_file = macho_file, } }; } -fn formatUnwindRecords(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - const macho_file = ctx.macho_file; - try bw.writeAll(" unwind records\n"); - for (object.unwind_records_indexes.items) |rec| { - try bw.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); - } -} - -pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .object = self, .macho_file = macho_file, } }; } -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const object = ctx.object; - const macho_file = ctx.macho_file; - try bw.writeAll(" symbols\n"); - for (object.symbols.items, 0..) |sym, i| { - const ref = object.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); - } - } - for (object.stab_files.items) |sf| { - try bw.print(" stabs({s},{s},{s})\n", .{ - sf.getCompDir(object.*), - sf.getTuName(object.*), - sf.getOsoPath(object.*), - }); - for (sf.stabs.items) |stab| { - try bw.print(" {f}", .{stab.fmt(object.*)}); - } - } -} - -pub fn fmtPath(self: Object) std.fmt.Formatter(formatPath) { +pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) { return .{ .data = self }; } -fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; +fn formatPath(object: Object, w: *Writer) Writer.Error!void { if (object.in_archive) |ar| { - try bw.print("{f}({s})", .{ + try w.print("{f}({s})", .{ ar.path, object.path.basename(), }); } else { - try bw.print("{f}", .{object.path}); + try w.print("{f}", .{object.path}); } } @@ -2689,30 +2677,25 @@ const StabFile = struct { return object.symbols.items[index]; } - pub fn format(stab: Stab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = stab; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format stabs directly"); - } + const Format = struct { + stab: Stab, + object: Object, - const StabFormatContext = struct { Stab, Object }; - - pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) { - return .{ .data = .{ stab, object } }; - } - - fn format2(ctx: StabFormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const stab, const object = ctx; - const sym = stab.getSymbol(object).?; - if (stab.is_func) { - try bw.print("func({d})", .{stab.index.?}); - } else if (sym.visibility == .global) { - try bw.print("gsym({d})", .{stab.index.?}); - } else { - try bw.print("stsym({d})", .{stab.index.?}); + fn default(f: Stab.Format, w: *Writer) Writer.Error!void { + const stab = f.stab; + const sym = stab.getSymbol(f.object).?; + if (stab.is_func) { + try w.print("func({d})", .{stab.index.?}); + } else if (sym.visibility == .global) { + try w.print("gsym({d})", .{stab.index.?}); + } else { + try w.print("stsym({d})", .{stab.index.?}); + } } + }; + + pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(Stab.Format, Stab.Format.default) { + return .{ .data = .{ .stab = stab, .object = object } }; } }; }; diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 30eef438ae087bc9843c730740f51ccf80f33372..7982dff6a4adf405b27ce29191256251b0d6256c 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -70,50 +70,51 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool { return lhs.offset < rhs.offset; } -const FormatCtx = struct { Relocation, std.Target.Cpu.Arch }; - -pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatPretty) { - return .{ .data = .{ rel, cpu_arch } }; +pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) { + return .{ .data = .{ .relocation = rel, .arch = cpu_arch } }; } -fn formatPretty(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const rel, const cpu_arch = ctx; - try bw.writeAll(switch (rel.type) { - .signed => "X86_64_RELOC_SIGNED", - .signed1 => "X86_64_RELOC_SIGNED_1", - .signed2 => "X86_64_RELOC_SIGNED_2", - .signed4 => "X86_64_RELOC_SIGNED_4", - .got_load => "X86_64_RELOC_GOT_LOAD", - .tlv => "X86_64_RELOC_TLV", - .page => "ARM64_RELOC_PAGE21", - .pageoff => "ARM64_RELOC_PAGEOFF12", - .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21", - .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", - .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", - .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", - .branch => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_BRANCH", - .aarch64 => "ARM64_RELOC_BRANCH26", - else => unreachable, - }, - .got => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_GOT", - .aarch64 => "ARM64_RELOC_POINTER_TO_GOT", - else => unreachable, - }, - .subtractor => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_SUBTRACTOR", - .aarch64 => "ARM64_RELOC_SUBTRACTOR", - else => unreachable, - }, - .unsigned => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_UNSIGNED", - .aarch64 => "ARM64_RELOC_UNSIGNED", - else => unreachable, - }, - }); -} +const Format = struct { + relocation: Relocation, + arch: std.Target.Cpu.Arch, + + fn pretty(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll(switch (f.relocation.type) { + .signed => "X86_64_RELOC_SIGNED", + .signed1 => "X86_64_RELOC_SIGNED_1", + .signed2 => "X86_64_RELOC_SIGNED_2", + .signed4 => "X86_64_RELOC_SIGNED_4", + .got_load => "X86_64_RELOC_GOT_LOAD", + .tlv => "X86_64_RELOC_TLV", + .page => "ARM64_RELOC_PAGE21", + .pageoff => "ARM64_RELOC_PAGEOFF12", + .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21", + .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", + .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", + .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", + .branch => switch (f.arch) { + .x86_64 => "X86_64_RELOC_BRANCH", + .aarch64 => "ARM64_RELOC_BRANCH26", + else => unreachable, + }, + .got => switch (f.arch) { + .x86_64 => "X86_64_RELOC_GOT", + .aarch64 => "ARM64_RELOC_POINTER_TO_GOT", + else => unreachable, + }, + .subtractor => switch (f.arch) { + .x86_64 => "X86_64_RELOC_SUBTRACTOR", + .aarch64 => "ARM64_RELOC_SUBTRACTOR", + else => unreachable, + }, + .unsigned => switch (f.arch) { + .x86_64 => "X86_64_RELOC_UNSIGNED", + .aarch64 => "ARM64_RELOC_UNSIGNED", + else => unreachable, + }, + }); + } +}; pub const Type = enum { // x86_64 diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 9e8660d8cf5c3179db3cad4ce416afdd4a6a5efe..654e7c402c82d3ed81adc0c9e08b98245f6f88c8 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -286,59 +286,51 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo } } -pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = symbol; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format symbols directly"); -} - -const FormatContext = struct { - symbol: Symbol, - macho_file: *MachO, -}; - -pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(format2) { +pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .symbol = symbol, .macho_file = macho_file, } }; } -fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - comptime assert(unused_fmt_string.len == 0); - const symbol = ctx.symbol; - try bw.print("%{d} : {s} : @{x}", .{ - symbol.nlist_idx, - symbol.getName(ctx.macho_file), - symbol.getAddress(.{}, ctx.macho_file), - }); - if (symbol.getFile(ctx.macho_file)) |file| { - if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) { - try bw.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)}); - } - if (symbol.getAtom(ctx.macho_file)) |atom| { - try bw.print(" : atom({d})", .{atom.atom_index}); - } - var buf: [3]u8 = .{'_'} ** 3; - if (symbol.flags.@"export") buf[0] = 'E'; - if (symbol.flags.import) buf[1] = 'I'; - switch (symbol.visibility) { - .local => buf[2] = 'L', - .hidden => buf[2] = 'H', - .global => buf[2] = 'G', - } - try bw.print(" : {s}", .{&buf}); - if (symbol.flags.weak) try bw.writeAll(" : weak"); - if (symbol.isSymbolStab(ctx.macho_file)) try bw.writeAll(" : stab"); - switch (file) { - .zig_object => |x| try bw.print(" : zig_object({d})", .{x.index}), - .internal => |x| try bw.print(" : internal({d})", .{x.index}), - .object => |x| try bw.print(" : object({d})", .{x.index}), - .dylib => |x| try bw.print(" : dylib({d})", .{x.index}), - } - } else try bw.writeAll(" : unresolved"); -} +const Format = struct { + symbol: Symbol, + macho_file: *MachO, + + fn default(f: Format, w: *Writer) Writer.Error!void { + const symbol = f.symbol; + try w.print("%{d} : {s} : @{x}", .{ + symbol.nlist_idx, + symbol.getName(f.macho_file), + symbol.getAddress(.{}, f.macho_file), + }); + if (symbol.getFile(f.macho_file)) |file| { + if (symbol.getOutputSectionIndex(f.macho_file) != 0) { + try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)}); + } + if (symbol.getAtom(f.macho_file)) |atom| { + try w.print(" : atom({d})", .{atom.atom_index}); + } + var buf: [3]u8 = .{'_'} ** 3; + if (symbol.flags.@"export") buf[0] = 'E'; + if (symbol.flags.import) buf[1] = 'I'; + switch (symbol.visibility) { + .local => buf[2] = 'L', + .hidden => buf[2] = 'H', + .global => buf[2] = 'G', + } + try w.print(" : {s}", .{&buf}); + if (symbol.flags.weak) try w.writeAll(" : weak"); + if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab"); + switch (file) { + .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}), + .internal => |x| try w.print(" : internal({d})", .{x.index}), + .object => |x| try w.print(" : object({d})", .{x.index}), + .dylib => |x| try w.print(" : dylib({d})", .{x.index}), + } + } else try w.writeAll(" : unresolved"); + } +}; pub const Flags = packed struct { /// Whether the symbol is imported at runtime. diff --git a/src/link/MachO/Thunk.zig b/src/link/MachO/Thunk.zig index b84cc4c8f2f81d5fc558989ba26859f8cb9562a7..5821c88ab7d0f6a322397e5270989d83dec7a8b5 100644 --- a/src/link/MachO/Thunk.zig +++ b/src/link/MachO/Thunk.zig @@ -61,36 +61,28 @@ pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void { } } -pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = thunk; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format Thunk directly"); -} - -pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) { +pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .thunk = thunk, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { thunk: Thunk, macho_file: *MachO, + + fn default(f: Format, w: *Writer) Writer.Error!void { + const thunk = f.thunk; + const macho_file = f.macho_file; + try w.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); + for (thunk.symbols.keys()) |ref| { + const sym = ref.getSymbol(macho_file).?; + try w.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); + } + } }; -fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const thunk = ctx.thunk; - const macho_file = ctx.macho_file; - try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); - for (thunk.symbols.keys()) |ref| { - const sym = ref.getSymbol(macho_file).?; - try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); - } -} - const trampoline_size = 3 * @sizeOf(u32); pub const Index = u32; diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index a8afef13b035febefc758ac96c8e6b84e77a8fe0..5d1d4b3e3f28e8eee4a0ac42b9a610b398b5beee 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -449,9 +449,8 @@ pub const Encoding = extern struct { return enc.enc == other.enc; } - pub fn format(enc: Encoding, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - try bw.print("0x{x:0>8}", .{enc.enc}); + pub fn format(enc: Encoding, w: *Writer) Writer.Error!void { + try w.print("0x{x:0>8}", .{enc.enc}); } }; @@ -505,36 +504,28 @@ pub const Record = struct { return lsda.getAddress(macho_file) + rec.lsda_offset; } - pub fn format(rec: Record, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = rec; - _ = bw; - _ = unused_fmt_string; - @compileError("do not format UnwindInfo.Records directly"); - } - - pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .rec = rec, .macho_file = macho_file, } }; } - const FormatContext = struct { + const Format = struct { rec: Record, macho_file: *MachO, - }; - fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const rec = ctx.rec; - const macho_file = ctx.macho_file; - try bw.print("{x} : len({x})", .{ - rec.enc.enc, rec.length, - }); - if (rec.enc.isDwarf(macho_file)) try bw.print(" : fde({d})", .{rec.fde}); - try bw.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); - if (!rec.alive) try bw.writeAll(" : [*]"); - } + fn default(f: Format, w: *Writer) Writer.Error!void { + const rec = f.rec; + const macho_file = f.macho_file; + try w.print("{x} : len({x})", .{ + rec.enc.enc, rec.length, + }); + if (rec.enc.isDwarf(macho_file)) try w.print(" : fde({d})", .{rec.fde}); + try w.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); + if (!rec.alive) try w.writeAll(" : [*]"); + } + }; pub const Index = u32; @@ -589,33 +580,25 @@ const Page = struct { return null; } - fn format(page: *const Page, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { - _ = page; - _ = bw; - _ = unused_format_string; - @compileError("do not format Page directly; use page.fmt()"); - } - - const FormatPageContext = struct { + const Format = struct { page: Page, info: UnwindInfo, - }; - fn format2(ctx: FormatPageContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { - _ = unused_format_string; - try bw.writeAll("Page:\n"); - try bw.print(" kind: {s}\n", .{@tagName(ctx.page.kind)}); - try bw.print(" entries: {d} - {d}\n", .{ - ctx.page.start, - ctx.page.start + ctx.page.count, - }); - try bw.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count}); - for (ctx.page.page_encodings[0..ctx.page.page_encodings_count], 0..) |enc, i| { - try bw.print(" {d}: {f}\n", .{ ctx.info.common_encodings_count + i, enc }); + fn default(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll("Page:\n"); + try w.print(" kind: {s}\n", .{@tagName(f.page.kind)}); + try w.print(" entries: {d} - {d}\n", .{ + f.page.start, + f.page.start + f.page.count, + }); + try w.print(" encodings (count = {d})\n", .{f.page.page_encodings_count}); + for (f.page.page_encodings[0..f.page.page_encodings_count], 0..) |enc, i| { + try w.print(" {d}: {f}\n", .{ f.info.common_encodings_count + i, enc }); + } } - } + }; - fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(format2) { + fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .page = page, .info = info, diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 54d25fe6e3c27e4c63f2ded2318db39c78b24051..023fad64c7a8d012d17c2c4f3f7790b538e1d9c5 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -957,7 +957,7 @@ fn updateNavCode( sym.out_n_sect = sect_index; atom.out_n_sect = sect_index; - const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{nav.fqn.toSlice(ip)}); + const sym_name = try std.fmt.allocPrintSentinel(gpa, "_{s}", .{nav.fqn.toSlice(ip)}, 0); defer gpa.free(sym_name); sym.name = try self.addString(gpa, sym_name); atom.setAlive(true); @@ -1676,52 +1676,50 @@ pub fn asFile(self: *ZigObject) File { return .{ .zig_object = self }; } -pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .self = self, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { self: *ZigObject, macho_file: *MachO, + + fn symtab(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll(" symbols\n"); + const self = f.self; + const macho_file = f.macho_file; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } + } + } + + fn atoms(f: Format, w: *Writer) Writer.Error!void { + const self = f.self; + const macho_file = f.macho_file; + try w.writeAll(" atoms\n"); + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + try w.print(" {f}\n", .{atom.fmt(macho_file)}); + } + } }; -fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - try bw.writeAll(" symbols\n"); - const self = ctx.self; - const macho_file = ctx.macho_file; - for (self.symbols.items, 0..) |sym, i| { - const ref = self.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); - } - } -} - -pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { +pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { return .{ .data = .{ .self = self, .macho_file = macho_file, } }; } -fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const self = ctx.self; - const macho_file = ctx.macho_file; - try bw.writeAll(" atoms\n"); - for (self.getAtoms()) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; - try bw.print(" {f}\n", .{atom.fmt(macho_file)}); - } -} - const AvMetadata = struct { symbol_index: Symbol.Index, /// A list of all exports aliases of this Av. diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 17bd302d82c4d0314b1f05b4945b0d229c74a5bd..79ff4e17073cb200c8407acb70c8bd47771ec8c5 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -196,9 +196,8 @@ const Level = struct { self.value += 1; } - pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - try bw.splatByteAll(' ', self.value); + pub fn format(self: *const @This(), w: *Writer) Writer.Error!void { + try w.splatByteAll(' ', self.value); } }; diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig index e057d8b1667a559d3d8bce63c4fd66e0576f75b8..f2e36fd876f879216e9fefbb8a41ed3e34ad54b8 100644 --- a/src/link/MachO/dyld_info/bind.zig +++ b/src/link/MachO/dyld_info/bind.zig @@ -193,7 +193,7 @@ pub const Bind = struct { } } - log.debug("{x}, {d}, {x}, {?x}, {s}", .{ offset, count, skip, addend, @tagName(state) }); + log.debug("{x}, {d}, {x}, {x}, {s}", .{ offset, count, skip, addend, @tagName(state) }); log.debug(" => {x}", .{current.offset}); switch (state) { .start => { @@ -423,7 +423,7 @@ pub const WeakBind = struct { } } - log.debug("{x}, {d}, {x}, {?x}, {s}", .{ offset, count, skip, addend, @tagName(state) }); + log.debug("{x}, {d}, {x}, {x}, {s}", .{ offset, count, skip, addend, @tagName(state) }); log.debug(" => {x}", .{current.offset}); switch (state) { .start => { diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 3d77115604562fc91541f1d4833ed33dd5679456..5e766369ff7594a1e4cb8e4594b289a77cf15abf 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -78,40 +78,26 @@ pub const Cie = struct { return true; } - pub fn format( - cie: Cie, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = cie; - _ = unused_fmt_string; - _ = options; - _ = writer; - @compileError("do not format CIEs directly"); - } - - pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .cie = cie, .macho_file = macho_file, } }; } - const FormatContext = struct { + const Format = struct { cie: Cie, macho_file: *MachO, - }; - fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const cie = ctx.cie; - try bw.print("@{x} : size({x})", .{ - cie.offset, - cie.getSize(), - }); - if (!cie.alive) try bw.writeAll(" : [*]"); - } + fn default(f: Format, w: *Writer) Writer.Error!void { + const cie = f.cie; + try w.print("@{x} : size({x})", .{ + cie.offset, + cie.getSize(), + }); + if (!cie.alive) try w.writeAll(" : [*]"); + } + }; pub const Index = u32; @@ -223,43 +209,29 @@ pub const Fde = struct { return fde.getObject(macho_file).getAtom(fde.lsda); } - pub fn format( - fde: Fde, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = fde; - _ = unused_fmt_string; - _ = options; - _ = writer; - @compileError("do not format FDEs directly"); - } - - pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .fde = fde, .macho_file = macho_file, } }; } - const FormatContext = struct { + const Format = struct { fde: Fde, macho_file: *MachO, - }; - fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; - const fde = ctx.fde; - const macho_file = ctx.macho_file; - try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{ - fde.offset, - fde.getSize(), - fde.cie, - fde.getAtom(macho_file).getName(macho_file), - }); - if (!fde.alive) try bw.writeAll(" : [*]"); - } + fn default(f: Format, writer: *Writer) Writer.Error!void { + const fde = f.fde; + const macho_file = f.macho_file; + try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ + fde.offset, + fde.getSize(), + fde.cie, + fde.getAtom(macho_file).getName(macho_file), + }); + if (!fde.alive) try writer.writeAll(" : [*]"); + } + }; pub const Index = u32; }; diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index b40b7709597497978d64a934c077ca985c6d587d..fde8553260deb39ec5ea11ec160f521cc4542352 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -10,17 +10,16 @@ pub const File = union(enum) { }; } - pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { + pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) { return .{ .data = file }; } - fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { - _ = unused_fmt_string; + fn formatPath(file: File, w: *Writer) Writer.Error!void { switch (file) { - .zig_object => |zo| try bw.writeAll(zo.basename), - .internal => try bw.writeAll("internal"), - .object => |x| try bw.print("{f}", .{x.fmtPath()}), - .dylib => |dl| try bw.print("{f}", .{@as(Path, dl.path)}), + .zig_object => |zo| try w.writeAll(zo.basename), + .internal => try w.writeAll("internal"), + .object => |x| try w.print("{f}", .{x.fmtPath()}), + .dylib => |dl| try w.print("{f}", .{@as(Path, dl.path)}), } } diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index 09e3ea13684bb8163449ff9324dd7efe74da6ff0..6ac4860103cd8ed65d26ff71c0de12c49157bca6 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -37,32 +37,27 @@ pub const GotSection = struct { } } - const FormatCtx = struct { + const Format = struct { got: GotSection, macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.got.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getGotAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } + } }; - pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .got = got, .macho_file = macho_file } }; } - - pub fn format2( - ctx: FormatCtx, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; - for (ctx.got.symbols.items, 0..) |ref, i| { - const symbol = ref.getSymbol(ctx.macho_file).?; - try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ - i, - symbol.getGotAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); - } - } }; pub const StubsSection = struct { @@ -126,32 +121,27 @@ pub const StubsSection = struct { } } - const FormatCtx = struct { - stubs: StubsSection, - macho_file: *MachO, - }; - - pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } }; } - pub fn format2( - ctx: FormatCtx, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; - for (ctx.stubs.symbols.items, 0..) |ref, i| { - const symbol = ref.getSymbol(ctx.macho_file).?; - try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ - i, - symbol.getStubsAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); + const Format = struct { + stubs: StubsSection, + macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.stubs.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getStubsAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } } - } + }; }; pub const StubsHelperSection = struct { @@ -353,32 +343,27 @@ pub const TlvPtrSection = struct { } } - const FormatCtx = struct { - tlv: TlvPtrSection, - macho_file: *MachO, - }; - - pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } }; } - pub fn format2( - ctx: FormatCtx, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; - for (ctx.tlv.symbols.items, 0..) |ref, i| { - const symbol = ref.getSymbol(ctx.macho_file).?; - try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ - i, - symbol.getTlvPtrAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); + const Format = struct { + tlv: TlvPtrSection, + macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.tlv.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getTlvPtrAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } } - } + }; }; pub const ObjcStubsSection = struct { @@ -476,32 +461,27 @@ pub const ObjcStubsSection = struct { } } - const FormatCtx = struct { - objc: ObjcStubsSection, - macho_file: *MachO, - }; - - pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .objc = objc, .macho_file = macho_file } }; } - pub fn format2( - ctx: FormatCtx, - bw: *Writer, - comptime unused_fmt_string: []const u8, - ) !void { - _ = unused_fmt_string; - for (ctx.objc.symbols.items, 0..) |ref, i| { - const symbol = ref.getSymbol(ctx.macho_file).?; - try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ - i, - symbol.getObjcStubsAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); + const Format = struct { + objc: ObjcStubsSection, + macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.objc.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getObjcStubsAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } } - } + }; pub const Index = u32; }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index c9a8a188437a3cbeaf418e0ceb15e6a447f8ff1e..93f70004013b155168e07c0a7030d0a91a6f5383 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -206,7 +206,7 @@ pub fn flush( var error_info: std.io.Writer.Allocating = .init(self.object.gpa); defer error_info.deinit(); - try error_info.writer.writeAll("zig_errors:"); + error_info.writer.writeAll("zig_errors:") catch return error.OutOfMemory; const ip = &self.base.comp.zcu.?.intern_pool; for (ip.global_error_set.getNamesFromMainThread()) |name| { // Errors can contain pretty much any character - to encode them in a string we must escape @@ -214,8 +214,8 @@ pub fn flush( // name if it contains no strange characters is nice for debugging. URI encoding fits the bill. // We're using : as separator, which is a reserved character. - try error_info.writer.writeByte(':'); - try std.Uri.Component.percentEncode( + error_info.writer.writeByte(':') catch return error.OutOfMemory; + std.Uri.Component.percentEncode( &error_info.writer, name.toSlice(ip), struct { @@ -226,7 +226,7 @@ pub fn flush( }; } }.isValidChar, - ); + ) catch return error.OutOfMemory; } try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{ .extension = error_info.getWritten(), diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 084146acea373155be6a3a9fe1e6ce5fbcaecb9c..17e398f3aff166c654b6d376ed4bea9c11ced8e1 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -32,7 +32,7 @@ const Writer = std.io.Writer; const Mir = @import("../arch/wasm/Mir.zig"); const CodeGen = @import("../arch/wasm/CodeGen.zig"); -const abi = @import("../arch/wasm/abi.zig"); +const abi = @import("../codegen/wasm/abi.zig"); const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); const InternPool = @import("../InternPool.zig"); @@ -2125,26 +2125,25 @@ pub const FunctionType = extern struct { wasm: *const Wasm, ft: FunctionType, - pub fn format(self: Formatter, bw: *Writer, comptime format_string: []const u8) Writer.Error!void { - comptime assert(format_string.len == 0); + pub fn format(self: Formatter, writer: *std.io.Writer) std.io.Writer.Error!void { const params = self.ft.params.slice(self.wasm); const returns = self.ft.returns.slice(self.wasm); - try bw.writeByte('('); + try writer.writeByte('('); for (params, 0..) |param, i| { - try bw.print("{s}", .{@tagName(param)}); + try writer.print("{s}", .{@tagName(param)}); if (i + 1 != params.len) { - try bw.writeAll(", "); + try writer.writeAll(", "); } } - try bw.writeAll(") -> "); + try writer.writeAll(") -> "); if (returns.len == 0) { - try bw.writeAll("nil"); + try writer.writeAll("nil"); } else { for (returns, 0..) |return_ty, i| { - try bw.print("{s}", .{@tagName(return_ty)}); + try writer.print("{s}", .{@tagName(return_ty)}); if (i + 1 != returns.len) { - try bw.writeAll(", "); + try writer.writeAll(", "); } } } @@ -2905,9 +2904,8 @@ pub const Feature = packed struct(u8) { @"=", }; - pub fn format(feature: Feature, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { - _ = fmt; - try bw.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) }); + pub fn format(feature: Feature, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) }); } 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 .variable => |variable| .{ variable.init, variable.owner_nav }, else => .{ nav.status.fully_resolved.val, nav_index }, }; - //log.debug("updateNav {} {d}", .{ nav.fqn.fmt(ip), chased_nav_index }); + //log.debug("updateNav {f} {d}", .{ nav.fqn.fmt(ip), chased_nav_index }); assert(!wasm.imports.contains(chased_nav_index)); if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) { diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index fdd1e97eb3967fa13d35ed722c41ad8bc6cd2ac5..d1cfa83cc186ec71e3f0a649d1061370b0ed98e0 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -534,7 +534,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { wasm.memories.limits.max = @intCast(max_memory / page_size); wasm.memories.limits.flags.has_max = true; if (shared_memory) wasm.memories.limits.flags.is_shared = true; - log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max}); + log.debug("maximum memory pages: {d}", .{wasm.memories.limits.max}); } f.memory_layout_finished = true; diff --git a/src/link/table_section.zig b/src/link/table_section.zig index 100aa9c2f7a552d87785e8f3cf7ab92adc9b9e43..6dbbad62a22c49d712fdde6986460b624bf73918 100644 --- a/src/link/table_section.zig +++ b/src/link/table_section.zig @@ -39,11 +39,10 @@ pub fn TableSection(comptime Entry: type) type { return self.entries.items.len; } - pub fn format(self: Self, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { - comptime assert(unused_format_string.len == 0); - try bw.writeAll("TableSection:\n"); + pub fn format(self: Self, writer: *std.io.Writer) std.io.Writer.Error!void { + try writer.writeAll("TableSection:\n"); for (self.entries.items, 0..) |entry, i| { - try bw.print(" {d} => {}\n", .{ i, entry }); + try writer.print(" {d} => {}\n", .{ i, entry }); } } diff --git a/src/link/tapi/parse.zig b/src/link/tapi/parse.zig index f6556dd5ddb671bfe183a418f6a7b1780e0ad972..0f418dea30e74d2b33022cb1d636119e0dba2fef 100644 --- a/src/link/tapi/parse.zig +++ b/src/link/tapi/parse.zig @@ -57,14 +57,9 @@ pub const Node = struct { } } - pub fn format( - self: *const Node, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { + pub fn format(self: *const Node, writer: *std.io.Writer) std.io.Writer.Error!void { switch (self.tag) { - inline else => |tag| return @as(*tag.Type(), @fieldParentPtr("base", self)).format(fmt, options, writer), + inline else => |tag| return @as(*tag.Type(), @fieldParentPtr("base", self)).format(writer), } } @@ -86,24 +81,17 @@ pub const Node = struct { } } - pub fn format( - self: *const Doc, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = fmt; + pub fn format(self: *const Doc, writer: *std.io.Writer) std.io.Writer.Error!void { if (self.directive) |id| { - try std.fmt.format(writer, "{{ ", .{}); + try writer.print("{{ ", .{}); const directive = self.base.tree.getRaw(id, id); - try std.fmt.format(writer, ".directive = {s}, ", .{directive}); + try writer.print(".directive = {s}, ", .{directive}); } if (self.value) |node| { - try std.fmt.format(writer, "{}", .{node}); + try writer.print("{}", .{node}); } if (self.directive != null) { - try std.fmt.format(writer, " }}", .{}); + try writer.print(" }}", .{}); } } }; @@ -133,14 +121,7 @@ pub const Node = struct { self.values.deinit(allocator); } - pub fn format( - self: *const Map, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = fmt; + pub fn format(self: *const Map, writer: *std.io.Writer) std.io.Writer.Error!void { try std.fmt.format(writer, "{{ ", .{}); for (self.values.items) |entry| { const key = self.base.tree.getRaw(entry.key, entry.key); @@ -172,14 +153,7 @@ pub const Node = struct { self.values.deinit(allocator); } - pub fn format( - self: *const List, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = fmt; + pub fn format(self: *const List, writer: *std.io.Writer) std.io.Writer.Error!void { try std.fmt.format(writer, "[ ", .{}); for (self.values.items) |node| { try std.fmt.format(writer, "{}, ", .{node}); @@ -203,14 +177,7 @@ pub const Node = struct { self.string_value.deinit(allocator); } - pub fn format( - self: *const Value, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = fmt; + pub fn format(self: *const Value, writer: *std.io.Writer) std.io.Writer.Error!void { const raw = self.base.tree.getRaw(self.base.start, self.base.end); return std.fmt.format(writer, "{s}", .{raw}); } diff --git a/src/main.zig b/src/main.zig index 038b0233a321071233118e7248b4be9cad17ddaa..d8a6788a0f356e06215d8e09db1bd950d8c66241 100644 --- a/src/main.zig +++ b/src/main.zig @@ -309,6 +309,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { return jitCmd(gpa, arena, cmd_args, .{ .cmd_name = "resinator", .root_src_path = "resinator/main.zig", + .windows_libs = &.{"advapi32"}, .depend_on_aro = true, .prepend_zig_lib_dir_path = true, .server = use_server, @@ -972,8 +973,6 @@ fn buildOutputType( .windows_libs = .empty, .link_inputs = .empty, - .wasi_emulated_libs = .{}, - .c_source_files = .{}, .rc_source_files = .{}, @@ -1418,7 +1417,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-funwind-tables")) { mod_opts.unwind_tables = .sync; } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) { - mod_opts.unwind_tables = .@"async"; + mod_opts.unwind_tables = .async; } else if (mem.eql(u8, arg, "-fno-unwind-tables")) { mod_opts.unwind_tables = .none; } else if (mem.eql(u8, arg, "-fstack-check")) { @@ -2039,15 +2038,15 @@ fn buildOutputType( .none => { mod_opts.unwind_tables = .sync; }, - .sync, .@"async" => {}, + .sync, .async => {}, } else { mod_opts.unwind_tables = .sync; }, .no_unwind_tables => mod_opts.unwind_tables = .none, - .asynchronous_unwind_tables => mod_opts.unwind_tables = .@"async", + .asynchronous_unwind_tables => mod_opts.unwind_tables = .async, .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) { .none, .sync => {}, - .@"async" => { + .async => { mod_opts.unwind_tables = .sync; }, } else { @@ -2955,7 +2954,7 @@ fn buildOutputType( create_module.opts.any_fuzz = true; if (mod_opts.unwind_tables) |uwt| switch (uwt) { .none => {}, - .sync, .@"async" => create_module.opts.any_unwind_tables = true, + .sync, .async => create_module.opts.any_unwind_tables = true, }; if (mod_opts.strip == false) create_module.opts.any_non_stripped = true; @@ -3331,16 +3330,16 @@ fn buildOutputType( // We are providing our own cache key, because this file has nothing // to do with the cache manifest. var file_writer = f.writer(&.{}); - var hasher_writer = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef")); var buffer: [1000]u8 = undefined; - var bw = hasher_writer.writer(&buffer); - bw.writeFileAll(.stdin(), .{}) catch |err| switch (err) { - error.WriteFailed => fatal("failed to write {s}: {s}", .{ dump_path, file_writer.err.? }), - else => fatal("failed to pipe stdin to {s}: {s}", .{ dump_path, err }), + var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer); + var stdin_reader = fs.File.stdin().readerStreaming(&.{}); + _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) { + error.WriteFailed => fatal("failed to write {s}: {t}", .{ dump_path, file_writer.err.? }), + else => fatal("failed to pipe stdin to {s}: {t}", .{ dump_path, err }), }; - try bw.flush(); + try hasher.writer.flush(); - const bin_digest: Cache.BinDigest = hasher_writer.final(); + const bin_digest: Cache.BinDigest = hasher.hasher.finalResult(); const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{ &bin_digest, ext.canonicalName(target), @@ -3412,7 +3411,6 @@ fn buildOutputType( .framework_dirs = create_module.framework_dirs.items, .frameworks = resolved_frameworks.items, .windows_lib_names = create_module.windows_libs.keys(), - .wasi_emulated_libs = create_module.wasi_emulated_libs.items, .want_compiler_rt = want_compiler_rt, .want_ubsan_rt = want_ubsan_rt, .hash_style = hash_style, @@ -3562,8 +3560,8 @@ fn buildOutputType( .stdio => { try serve( comp, - fs.File.stdin(), - fs.File.stdout(), + .stdin(), + .stdout(), test_exec_args.items, self_exe_path, arg_mode, @@ -3638,7 +3636,6 @@ fn buildOutputType( } else if (target.os.tag == .windows) { try test_exec_args.appendSlice(arena, &.{ "--subsystem", "console", - "-lkernel32", "-lntdll", }); } @@ -3694,8 +3691,6 @@ const CreateModule = struct { /// output. Allocated with gpa. link_inputs: std.ArrayListUnmanaged(link.Input), - wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile), - c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile), rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile), @@ -3826,14 +3821,6 @@ fn createModule( .name_query => |nq| { const lib_name = nq.name; - if (target.os.tag == .wasi) { - if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| { - try create_module.wasi_emulated_libs.append(arena, crt_file); - create_module.opts.link_libc = true; - continue; - } - } - if (std.zig.target.isLibCLibName(target, lib_name)) { create_module.opts.link_libc = true; continue; @@ -3852,7 +3839,8 @@ fn createModule( .only_compiler_rt => continue, } - if (target.isMinGW()) { + // We currently prefer import libraries provided by MinGW-w64 even for MSVC. + if (target.os.tag == .windows) { const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| { fatal("failed to check zig installation for DLL import libs: {s}", .{ @errorName(err), @@ -5228,6 +5216,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { try root_mod.deps.put(arena, "@build", build_mod); + var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; + + if (resolved_target.result.os.tag == .windows) { + try windows_libs.put(arena, "advapi32", {}); + } + const comp = Compilation.create(gpa, arena, .{ .dirs = dirs, .root_name = "build", @@ -5249,6 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { .cache_mode = .whole, .reference_trace = reference_trace, .debug_compile_errors = debug_compile_errors, + .windows_lib_names = windows_libs.keys(), }) catch |err| { fatal("unable to create compilation: {s}", .{@errorName(err)}); }; @@ -5299,7 +5294,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { const s = fs.path.sep_str; const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce; const stdout = dirs.local_cache.handle.readFileAlloc(arena, tmp_sub_path, 50 * 1024 * 1024) catch |err| { - fatal("unable to read results of configure phase from '{}{s}': {s}", .{ + fatal("unable to read results of configure phase from '{f}{s}': {s}", .{ dirs.local_cache, tmp_sub_path, @errorName(err), }); }; @@ -5352,6 +5347,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { const JitCmdOptions = struct { cmd_name: []const u8, root_src_path: []const u8, + windows_libs: []const []const u8 = &.{}, prepend_zig_lib_dir_path: bool = false, prepend_global_cache_path: bool = false, prepend_zig_exe_path: bool = false, @@ -5468,6 +5464,13 @@ fn jitCmd( try root_mod.deps.put(arena, "aro", aro_mod); } + var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; + + if (resolved_target.result.os.tag == .windows) { + try windows_libs.ensureUnusedCapacity(arena, options.windows_libs.len); + for (options.windows_libs) |lib| windows_libs.putAssumeCapacity(lib, {}); + } + const comp = Compilation.create(gpa, arena, .{ .dirs = dirs, .root_name = options.cmd_name, @@ -5478,6 +5481,7 @@ fn jitCmd( .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .cache_mode = .whole, + .windows_lib_names = windows_libs.keys(), }) catch |err| { fatal("unable to create compilation: {s}", .{@errorName(err)}); }; @@ -6049,7 +6053,7 @@ fn cmdAstCheck( break :file fs.cwd().openFile(p, .{}) catch |err| { fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) }); }; - } else io.getStdIn(); + } else fs.File.stdin(); defer if (zig_source_path != null) f.close(); break :s std.zig.readSourceFileToEndAlloc(arena, f, null) catch |err| { fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) }); @@ -6068,7 +6072,8 @@ fn cmdAstCheck( const tree = try Ast.parse(arena, source, mode); - var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer); + var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer); + const stdout_bw = &stdout_writer.interface; switch (mode) { .zig => { const zir = try AstGen.generate(arena, tree); @@ -6133,7 +6138,7 @@ fn cmdAstCheck( // zig fmt: on } - try @import("print_zir.zig").renderAsText(arena, tree, zir, &stdout_bw); + try @import("print_zir.zig").renderAsText(arena, tree, zir, stdout_bw); try stdout_bw.flush(); if (zir.hasCompileErrors()) { @@ -6161,7 +6166,7 @@ fn cmdAstCheck( fatal("-t option only available in builds of zig with debug extensions", .{}); } - try @import("print_zoir.zig").renderToWriter(zoir, arena, &stdout_bw); + try @import("print_zoir.zig").renderToWriter(zoir, arena, stdout_bw); try stdout_bw.flush(); return cleanExit(); }, @@ -6282,7 +6287,8 @@ fn detectNativeCpuWithLLVM( } fn printCpu(cpu: std.Target.Cpu) !void { - var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer); + var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer); + const stdout_bw = &stdout_writer.interface; if (cpu.model.llvm_name) |llvm_name| { try stdout_bw.print("{s}\n", .{llvm_name}); @@ -6326,11 +6332,12 @@ fn cmdDumpLlvmInts( if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad"); break :t target; }; - const tm = llvm.TargetMachine.create(target, triple, null, null, .None, .Default, .Default, false, false, .Default, null); + const tm = llvm.TargetMachine.create(target, triple, null, null, .None, .Default, .Default, false, false, .Default, null, false); const dl = tm.createTargetDataLayout(); const context = llvm.Context.create(); - var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer); + var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer); + const stdout_bw = &stdout_writer.interface; for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| { const int_type = context.intType(bits); const alignment = dl.abiAlignmentOfType(int_type); @@ -6358,9 +6365,8 @@ fn cmdDumpZir( defer f.close(); const zir = try Zcu.loadZirCache(arena, f); - - var stdout_fw = fs.File.stdout().writer(); - var stdout_bw = stdout_fw.interface().buffered(&stdio_buffer); + var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer); + const stdout_bw = &stdout_writer.interface; { const instruction_bytes = zir.instructions.len * // Here we don't use @sizeOf(Zir.Inst.Data) because it would include @@ -6385,7 +6391,7 @@ fn cmdDumpZir( // zig fmt: on } - try @import("print_zir.zig").renderAsText(arena, null, zir, &stdout_bw); + try @import("print_zir.zig").renderAsText(arena, null, zir, stdout_bw); try stdout_bw.flush(); } @@ -6444,7 +6450,8 @@ fn cmdChangelist( var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty; try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map); - var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer); + var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer); + const stdout_bw = &stdout_writer.interface; { try stdout_bw.print("Instruction mappings:\n", .{}); var it = inst_map.iterator(); @@ -6903,9 +6910,9 @@ fn cmdFetch( const name = switch (save) { .no => { - var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer); - try stdout_bw.print("{s}\n", .{package_hash_slice}); - try stdout_bw.flush(); + var stdout = fs.File.stdout().writerStreaming(&stdio_buffer); + try stdout.interface.print("{s}\n", .{package_hash_slice}); + try stdout.interface.flush(); return cleanExit(); }, .yes, .exact => |name| name: { @@ -6954,7 +6961,9 @@ fn cmdFetch( std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex }); // include the original refspec in a query parameter, could be used to check for updates - uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f%}", .{fragment}) }; + uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{ + std.fmt.alt(fragment, .formatEscaped), + }) }; } else { std.log.info("resolved to commit {s}", .{latest_commit_hex}); } @@ -6974,12 +6983,12 @@ fn cmdFetch( \\ .hash = "{f}", \\ }} , .{ - std.zig.fmtEscapes(saved_path_or_url), - std.zig.fmtEscapes(package_hash_slice), + std.zig.fmtString(saved_path_or_url), + std.zig.fmtString(package_hash_slice), }); - const new_node_text = try std.fmt.allocPrint(arena, ".{fp_} = {s},\n", .{ - std.zig.fmtId(name), new_node_init, + const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{ + std.zig.fmtIdPU(name), new_node_init, }); const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{ @@ -7006,12 +7015,12 @@ fn cmdFetch( const location_replace = try std.fmt.allocPrint( arena, "\"{f}\"", - .{std.zig.fmtEscapes(saved_path_or_url)}, + .{std.zig.fmtString(saved_path_or_url)}, ); const hash_replace = try std.fmt.allocPrint( arena, "\"{f}\"", - .{std.zig.fmtEscapes(package_hash_slice)}, + .{std.zig.fmtString(package_hash_slice)}, ); warn("overwriting existing dependency named '{s}'", .{name}); @@ -7411,7 +7420,7 @@ fn handleModArg( create_module.opts.any_fuzz = true; if (mod_opts.unwind_tables) |uwt| switch (uwt) { .none => {}, - .sync, .@"async" => create_module.opts.any_unwind_tables = true, + .sync, .async => create_module.opts.any_unwind_tables = true, }; if (mod_opts.strip == false) create_module.opts.any_non_stripped = true; diff --git a/src/print_value.zig b/src/print_value.zig index 4d3e29422b21a9ee1d9b6a9716d11af7fc34ede9..e0a489ee4076cc5007156796773f4b0848a0e090 100644 --- a/src/print_value.zig +++ b/src/print_value.zig @@ -9,7 +9,6 @@ const Sema = @import("Sema.zig"); const InternPool = @import("InternPool.zig"); const Allocator = std.mem.Allocator; const Target = std.Target; -const Writer = std.io.Writer; const max_aggregate_items = 100; const max_string_len = 256; @@ -21,10 +20,9 @@ pub const FormatContext = struct { depth: u8, }; -pub fn formatSema(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.Writer.Error!void { +pub fn formatSema(ctx: FormatContext, writer: *std.io.Writer) std.io.Writer.Error!void { const sema = ctx.opt_sema.?; - comptime std.debug.assert(fmt.len == 0); - return print(ctx.val, bw, ctx.depth, ctx.pt, sema) catch |err| switch (err) { + return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function error.ComptimeBreak, error.ComptimeReturn => unreachable, 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 }; } -pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.Writer.Error!void { +pub fn format(ctx: FormatContext, writer: *std.io.Writer) std.io.Writer.Error!void { std.debug.assert(ctx.opt_sema == null); - comptime std.debug.assert(fmt.len == 0); - return print(ctx.val, bw, ctx.depth, ctx.pt, null) catch |err| switch (err) { + return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable, else => |e| return e, @@ -44,7 +41,7 @@ pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io. pub fn print( val: Value, - bw: *Writer, + writer: *std.io.Writer, level: u8, pt: Zcu.PerThread, opt_sema: ?*Sema, @@ -68,62 +65,62 @@ pub fn print( .func_type, .error_set_type, .inferred_error_set_type, - => try Type.print(val.toType(), bw, pt), - .undef => try bw.writeAll("undefined"), + => try Type.print(val.toType(), writer, pt), + .undef => try writer.writeAll("undefined"), .simple_value => |simple_value| switch (simple_value) { - .void => try bw.writeAll("{}"), - .empty_tuple => try bw.writeAll(".{}"), - else => try bw.writeAll(@tagName(simple_value)), + .void => try writer.writeAll("{}"), + .empty_tuple => try writer.writeAll(".{}"), + else => try writer.writeAll(@tagName(simple_value)), }, - .variable => try bw.writeAll("(variable)"), - .@"extern" => |e| try bw.print("(extern '{f}')", .{e.name.fmt(ip)}), - .func => |func| try bw.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}), + .variable => try writer.writeAll("(variable)"), + .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}), + .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}), .int => |int| switch (int.storage) { - inline .u64, .i64 => |x| try bw.print("{d}", .{x}), - .big_int => |x| try bw.print("{f}", .{x}), + inline .u64, .i64 => |x| try writer.print("{d}", .{x}), + .big_int => |x| try writer.print("{d}", .{x}), .lazy_align => |ty| if (opt_sema != null) { const a = try Type.fromInterned(ty).abiAlignmentSema(pt); - try bw.print("{}", .{a.toByteUnits() orelse 0}); - } else try bw.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}), + try writer.print("{d}", .{a.toByteUnits() orelse 0}); + } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}), .lazy_size => |ty| if (opt_sema != null) { const s = try Type.fromInterned(ty).abiSizeSema(pt); - try bw.print("{}", .{s}); - } else try bw.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}), + try writer.print("{d}", .{s}); + } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}), }, - .err => |err| try bw.print("error.{f}", .{ + .err => |err| try writer.print("error.{f}", .{ err.name.fmt(ip), }), .error_union => |error_union| switch (error_union.val) { - .err_name => |err_name| try bw.print("error.{f}", .{ + .err_name => |err_name| try writer.print("error.{f}", .{ err_name.fmt(ip), }), - .payload => |payload| try print(Value.fromInterned(payload), bw, level, pt, opt_sema), + .payload => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema), }, - .enum_literal => |enum_literal| try bw.print(".{f}", .{ + .enum_literal => |enum_literal| try writer.print(".{f}", .{ enum_literal.fmt(ip), }), .enum_tag => |enum_tag| { const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern()); if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| { - return bw.print(".{fi}", .{enum_type.names.get(ip)[tag_index].fmt(ip)}); + return writer.print(".{f}", .{enum_type.names.get(ip)[tag_index].fmt(ip)}); } if (level == 0) { - return bw.writeAll("@enumFromInt(...)"); + return writer.writeAll("@enumFromInt(...)"); } - try bw.writeAll("@enumFromInt("); - try print(Value.fromInterned(enum_tag.int), bw, level - 1, pt, opt_sema); - try bw.writeAll(")"); + try writer.writeAll("@enumFromInt("); + try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema); + try writer.writeAll(")"); }, - .empty_enum_value => try bw.writeAll("(empty enum value)"), + .empty_enum_value => try writer.writeAll("(empty enum value)"), .float => |float| switch (float.storage) { - inline else => |x| try bw.print("{d}", .{@as(f64, @floatCast(x))}), + inline else => |x| try writer.print("{d}", .{@as(f64, @floatCast(x))}), }, .slice => |slice| { if (ip.isUndef(slice.ptr)) { if (slice.len == .zero_usize) { - return bw.writeAll("&.{}"); + return writer.writeAll("&.{}"); } - try print(.fromInterned(slice.ptr), bw, level - 1, pt, opt_sema); + try print(.fromInterned(slice.ptr), writer, level - 1, pt, opt_sema); } else { const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) { .field, .arr_elem, .eu_payload, .opt_payload => unreachable, @@ -134,15 +131,15 @@ pub fn print( // TODO: eventually we want to load the slice as an array with `sema`, but that's // currently not possible without e.g. triggering compile errors. } - try printPtr(Value.fromInterned(slice.ptr), null, bw, level, pt, opt_sema); + try printPtr(Value.fromInterned(slice.ptr), null, writer, level, pt, opt_sema); } - try bw.writeAll("[0.."); + try writer.writeAll("[0.."); if (level == 0) { - try bw.writeAll("(...)"); + try writer.writeAll("(...)"); } else { - try print(Value.fromInterned(slice.len), bw, level - 1, pt, opt_sema); + try print(Value.fromInterned(slice.len), writer, level - 1, pt, opt_sema); } - try bw.writeAll("]"); + try writer.writeAll("]"); }, .ptr => { const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) { @@ -154,29 +151,29 @@ pub fn print( // TODO: eventually we want to load the pointer with `sema`, but that's // currently not possible without e.g. triggering compile errors. } - try printPtr(val, .rvalue, bw, level, pt, opt_sema); + try printPtr(val, .rvalue, writer, level, pt, opt_sema); }, .opt => |opt| switch (opt.val) { - .none => try bw.writeAll("null"), - else => |payload| try print(Value.fromInterned(payload), bw, level, pt, opt_sema), + .none => try writer.writeAll("null"), + else => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema), }, - .aggregate => |aggregate| try printAggregate(val, aggregate, false, bw, level, pt, opt_sema), + .aggregate => |aggregate| try printAggregate(val, aggregate, false, writer, level, pt, opt_sema), .un => |un| { if (level == 0) { - try bw.writeAll(".{ ... }"); + try writer.writeAll(".{ ... }"); return; } if (un.tag == .none) { const backing_ty = try val.typeOf(zcu).unionBackingType(pt); - try bw.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)}); - try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema); - try bw.writeAll("))"); + try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)}); + try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); + try writer.writeAll("))"); } else { - try bw.writeAll(".{ "); - try print(Value.fromInterned(un.tag), bw, level - 1, pt, opt_sema); - try bw.writeAll(" = "); - try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema); - try bw.writeAll(" }"); + try writer.writeAll(".{ "); + try print(Value.fromInterned(un.tag), writer, level - 1, pt, opt_sema); + try writer.writeAll(" = "); + try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); + try writer.writeAll(" }"); } }, .memoized_call => unreachable, @@ -187,33 +184,33 @@ fn printAggregate( val: Value, aggregate: InternPool.Key.Aggregate, is_ref: bool, - bw: *Writer, + writer: *std.io.Writer, level: u8, pt: Zcu.PerThread, opt_sema: ?*Sema, ) (std.io.Writer.Error || Zcu.CompileError)!void { if (level == 0) { - if (is_ref) try bw.writeByte('&'); - return bw.writeAll(".{ ... }"); + if (is_ref) try writer.writeByte('&'); + return writer.writeAll(".{ ... }"); } const zcu = pt.zcu; const ip = &zcu.intern_pool; const ty = Type.fromInterned(aggregate.ty); switch (ty.zigTypeTag(zcu)) { .@"struct" => if (!ty.isTuple(zcu)) { - if (is_ref) try bw.writeByte('&'); + if (is_ref) try writer.writeByte('&'); if (ty.structFieldCount(zcu) == 0) { - return bw.writeAll(".{}"); + return writer.writeAll(".{}"); } - try bw.writeAll(".{ "); + try writer.writeAll(".{ "); const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items); for (0..max_len) |i| { - if (i != 0) try bw.writeAll(", "); + if (i != 0) try writer.writeAll(", "); const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?; - try bw.print(".{fi} = ", .{field_name.fmt(ip)}); - try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema); + try writer.print(".{f} = ", .{field_name.fmt(ip)}); + try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); } - try bw.writeAll(" }"); + try writer.writeAll(" }"); return; }, .array => { @@ -222,16 +219,16 @@ fn printAggregate( const len = ty.arrayLenIncludingSentinel(zcu); if (len == 0) break :string; const slice = bytes.toSlice(if (bytes.at(len - 1, ip) == 0) len - 1 else len, ip); - try bw.print("\"{f}\"", .{std.zig.fmtEscapes(slice)}); - if (!is_ref) try bw.writeAll(".*"); + try writer.print("\"{f}\"", .{std.zig.fmtString(slice)}); + if (!is_ref) try writer.writeAll(".*"); return; }, .elems, .repeated_elem => {}, } switch (ty.arrayLen(zcu)) { 0 => { - if (is_ref) try bw.writeByte('&'); - return bw.writeAll(".{}"); + if (is_ref) try writer.writeByte('&'); + return writer.writeAll(".{}"); }, 1 => one_byte_str: { // The repr isn't `bytes`, but we might still be able to print this as a string @@ -239,47 +236,47 @@ fn printAggregate( const elem_val = Value.fromInterned(aggregate.storage.values()[0]); if (elem_val.isUndef(zcu)) break :one_byte_str; const byte = elem_val.toUnsignedInt(zcu); - try bw.print("\"{f}\"", .{std.zig.fmtEscapes(&.{@intCast(byte)})}); - if (!is_ref) try bw.writeAll(".*"); + try writer.print("\"{f}\"", .{std.zig.fmtString(&.{@intCast(byte)})}); + if (!is_ref) try writer.writeAll(".*"); return; }, else => {}, } }, .vector => if (ty.arrayLen(zcu) == 0) { - if (is_ref) try bw.writeByte('&'); - return bw.writeAll(".{}"); + if (is_ref) try writer.writeByte('&'); + return writer.writeAll(".{}"); }, else => unreachable, } const len = ty.arrayLen(zcu); - if (is_ref) try bw.writeByte('&'); - try bw.writeAll(".{ "); + if (is_ref) try writer.writeByte('&'); + try writer.writeAll(".{ "); const max_len = @min(len, max_aggregate_items); for (0..max_len) |i| { - if (i != 0) try bw.writeAll(", "); - try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema); + if (i != 0) try writer.writeAll(", "); + try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); } if (len > max_aggregate_items) { - try bw.writeAll(", ..."); + try writer.writeAll(", ..."); } - return bw.writeAll(" }"); + return writer.writeAll(" }"); } fn printPtr( ptr_val: Value, /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen. want_kind: ?PrintPtrKind, - bw: *Writer, + writer: *std.io.Writer, level: u8, pt: Zcu.PerThread, opt_sema: ?*Sema, ) (std.io.Writer.Error || Zcu.CompileError)!void { const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) { - .undef => return bw.writeAll("undefined"), + .undef => return writer.writeAll("undefined"), .ptr => |ptr| ptr, else => unreachable, }; @@ -291,7 +288,7 @@ fn printPtr( Value.fromInterned(ptr.base_addr.uav.val), agg, true, - bw, + writer, level, pt, opt_sema, @@ -307,7 +304,7 @@ fn printPtr( else try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null); - _ = try printPtrDerivation(derivation, bw, pt, want_kind, .{ .print_val = .{ + _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{ .level = level, .opt_sema = opt_sema, } }, 20); @@ -319,7 +316,7 @@ const PrintPtrKind = enum { lvalue, rvalue }; /// Returns the root derivation, which may be ignored. pub fn printPtrDerivation( derivation: Value.PointerDeriveStep, - bw: *Writer, + writer: *std.io.Writer, pt: Zcu.PerThread, /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen. /// 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( /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary, /// so at this depth we just write "..." to prevent stack overflow. ptr_depth: u8, -) (std.io.Writer.Error || Zcu.CompileError)!Value.PointerDeriveStep { +) !Value.PointerDeriveStep { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -351,7 +348,7 @@ pub fn printPtrDerivation( => |step| continue :root step.parent.*, else => |step| break :root step, }; - try bw.writeAll("..."); + try writer.writeAll("..."); return root_step; } @@ -374,39 +371,39 @@ pub fn printPtrDerivation( const need_kind = want_kind orelse result_kind; if (need_kind == .rvalue and result_kind == .lvalue) { - try bw.writeByte('&'); + try writer.writeByte('&'); } // null if `derivation` is the root. const root_or_null: ?Value.PointerDeriveStep = switch (derivation) { .eu_payload_ptr => |info| root: { - try bw.writeByte('('); - const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1); - try bw.writeAll(" catch unreachable)"); + try writer.writeByte('('); + const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1); + try writer.writeAll(" catch unreachable)"); break :root root; }, .opt_payload_ptr => |info| root: { - const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1); - try bw.writeAll(".?"); + const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1); + try writer.writeAll(".?"); break :root root; }, .field_ptr => |field| root: { - const root = try printPtrDerivation(field.parent.*, bw, pt, null, root_strat, ptr_depth - 1); + const root = try printPtrDerivation(field.parent.*, writer, pt, null, root_strat, ptr_depth - 1); const agg_ty = (try field.parent.ptrType(pt)).childType(zcu); switch (agg_ty.zigTypeTag(zcu)) { .@"struct" => if (agg_ty.structFieldName(field.field_idx, zcu).unwrap()) |field_name| { - try bw.print(".{fi}", .{field_name.fmt(ip)}); + try writer.print(".{f}", .{field_name.fmt(ip)}); } else { - try bw.print("[{d}]", .{field.field_idx}); + try writer.print("[{d}]", .{field.field_idx}); }, .@"union" => { const tag_ty = agg_ty.unionTagTypeHypothetical(zcu); const field_name = tag_ty.enumFieldName(field.field_idx, zcu); - try bw.print(".{fi}", .{field_name.fmt(ip)}); + try writer.print(".{f}", .{field_name.fmt(ip)}); }, .pointer => switch (field.field_idx) { - Value.slice_ptr_index => try bw.writeAll(".ptr"), - Value.slice_len_index => try bw.writeAll(".len"), + Value.slice_ptr_index => try writer.writeAll(".ptr"), + Value.slice_len_index => try writer.writeAll(".len"), else => unreachable, }, else => unreachable, @@ -414,20 +411,20 @@ pub fn printPtrDerivation( break :root root; }, .elem_ptr => |elem| root: { - const root = try printPtrDerivation(elem.parent.*, bw, pt, null, root_strat, ptr_depth - 1); - try bw.print("[{d}]", .{elem.elem_idx}); + const root = try printPtrDerivation(elem.parent.*, writer, pt, null, root_strat, ptr_depth - 1); + try writer.print("[{d}]", .{elem.elem_idx}); break :root root; }, .offset_and_cast => |oac| if (oac.byte_offset == 0) root: { - try bw.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)}); - const root = try printPtrDerivation(oac.parent.*, bw, pt, .rvalue, root_strat, ptr_depth - 1); - try bw.writeAll("))"); + try writer.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)}); + const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1); + try writer.writeAll("))"); break :root root; } else root: { - try bw.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)}); - const root = try printPtrDerivation(oac.parent.*, bw, pt, .rvalue, root_strat, ptr_depth - 1); - try bw.print(") + {d}))", .{oac.byte_offset}); + try writer.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)}); + const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1); + try writer.print(") + {d}))", .{oac.byte_offset}); break :root root; }, @@ -435,33 +432,33 @@ pub fn printPtrDerivation( }; if (root_or_null == null) switch (root_strat) { - .str => |x| try bw.writeAll(x), + .str => |x| try writer.writeAll(x), .print_val => |x| switch (derivation) { - .int => |int| try bw.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }), - .nav_ptr => |nav| try bw.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}), + .int => |int| try writer.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }), + .nav_ptr => |nav| try writer.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}), .uav_ptr => |uav| { const ty = Value.fromInterned(uav.val).typeOf(zcu); - try bw.print("@as({f}, ", .{ty.fmt(pt)}); - try print(Value.fromInterned(uav.val), bw, x.level - 1, pt, x.opt_sema); - try bw.writeByte(')'); + try writer.print("@as({f}, ", .{ty.fmt(pt)}); + try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema); + try writer.writeByte(')'); }, .comptime_alloc_ptr => |info| { - try bw.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)}); - try print(info.val, bw, x.level - 1, pt, x.opt_sema); - try bw.writeByte(')'); + try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)}); + try print(info.val, writer, x.level - 1, pt, x.opt_sema); + try writer.writeByte(')'); }, .comptime_field_ptr => |val| { const ty = val.typeOf(zcu); - try bw.print("@as({f}, ", .{ty.fmt(pt)}); - try print(val, bw, x.level - 1, pt, x.opt_sema); - try bw.writeByte(')'); + try writer.print("@as({f}, ", .{ty.fmt(pt)}); + try print(val, writer, x.level - 1, pt, x.opt_sema); + try writer.writeByte(')'); }, else => unreachable, }, }; if (need_kind == .lvalue and result_kind == .rvalue) { - try bw.writeAll(".*"); + try writer.writeAll(".*"); } return root_or_null orelse derivation; diff --git a/src/print_zir.zig b/src/print_zir.zig index 5a0c829a50b3d43c92d43e60c3bba2f83c2600a8..ae674e43e5a6320a6bc934e3c2bc5f21749bcce3 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -253,14 +253,12 @@ const Writer = struct { .tag_name, .type_name, .frame_type, - .frame_size, .clz, .ctz, .pop_count, .byte_swap, .bit_reverse, .@"resume", - .@"await", .make_ptr_const, .validate_deref, .validate_const, @@ -557,7 +555,6 @@ const Writer = struct { .tuple_decl => try self.writeTupleDecl(stream, extended), - .await_nosuspend, .c_undef, .c_include, .set_float_mode, @@ -603,7 +600,6 @@ const Writer = struct { try self.writeSrcNode(stream, inst_data.node); }, - .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended), .cmpxchg => try self.writeCmpxchg(stream, extended), .ptr_cast_full => try self.writePtrCastFull(stream, extended), .ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended), @@ -924,19 +920,6 @@ const Writer = struct { try self.writeSrcNode(stream, extra.src_node); } - fn writeBuiltinAsyncCall(self: *Writer, stream: *std.io.Writer, extended: Zir.Inst.Extended.InstData) !void { - const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data; - try self.writeInstRef(stream, extra.frame_buffer); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.result_ptr); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.fn_ptr); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.args); - try stream.writeAll(") "); - try self.writeSrcNode(stream, extra.node); - } - fn writeParam(self: *Writer, stream: *std.io.Writer, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index); @@ -1229,8 +1212,8 @@ const Writer = struct { const name = self.code.nullTerminatedString(output.data.name); const constraint = self.code.nullTerminatedString(output.data.constraint); - try stream.print("output({fp}, \"{f}\", ", .{ - std.zig.fmtId(name), std.zig.fmtString(constraint), + try stream.print("output({f}, \"{f}\", ", .{ + std.zig.fmtIdP(name), std.zig.fmtString(constraint), }); try self.writeFlag(stream, "->", is_type); try self.writeInstRef(stream, output.data.operand); @@ -1248,8 +1231,8 @@ const Writer = struct { const name = self.code.nullTerminatedString(input.data.name); const constraint = self.code.nullTerminatedString(input.data.constraint); - try stream.print("input({fp}, \"{f}\", ", .{ - std.zig.fmtId(name), std.zig.fmtString(constraint), + try stream.print("input({f}, \"{f}\", ", .{ + std.zig.fmtIdP(name), std.zig.fmtString(constraint), }); try self.writeInstRef(stream, input.data.operand); try stream.writeAll(")"); @@ -1264,7 +1247,7 @@ const Writer = struct { const str_index = self.code.extra[extra_i]; extra_i += 1; const clobber = self.code.nullTerminatedString(@enumFromInt(str_index)); - try stream.print("{fp}", .{std.zig.fmtId(clobber)}); + try stream.print("{f}", .{std.zig.fmtIdP(clobber)}); if (i + 1 < clobbers_len) { try stream.writeAll(", "); } @@ -1528,7 +1511,7 @@ const Writer = struct { try self.writeFlag(stream, "comptime ", field.is_comptime); if (field.name != .empty) { const field_name = self.code.nullTerminatedString(field.name); - try stream.print("{fp}: ", .{std.zig.fmtId(field_name)}); + try stream.print("{f}: ", .{std.zig.fmtIdP(field_name)}); } else { try stream.print("@\"{d}\": ", .{i}); } @@ -1691,7 +1674,7 @@ const Writer = struct { extra_index += 1; try stream.splatByteAll(' ', self.indent); - try stream.print("{fp}", .{std.zig.fmtId(field_name)}); + try stream.print("{f}", .{std.zig.fmtIdP(field_name)}); if (has_type) { const field_type = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); @@ -1825,7 +1808,7 @@ const Writer = struct { extra_index += 1; try stream.splatByteAll(' ', self.indent); - try stream.print("{fp}", .{std.zig.fmtId(field_name)}); + try stream.print("{f}", .{std.zig.fmtIdP(field_name)}); if (has_tag_value) { const tag_value_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); @@ -1930,7 +1913,7 @@ const Writer = struct { const name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]); const name = self.code.nullTerminatedString(name_index); try stream.splatByteAll(' ', self.indent); - try stream.print("{fp},\n", .{std.zig.fmtId(name)}); + try stream.print("{f},\n", .{std.zig.fmtIdP(name)}); } self.indent -= 2; @@ -2597,7 +2580,6 @@ const Writer = struct { } switch (decl.kind) { .@"comptime" => try stream.writeAll("comptime"), - .@"usingnamespace" => try stream.writeAll("usingnamespace"), .unnamed_test => try stream.writeAll("test"), .@"test", .decltest, .@"const", .@"var" => { try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) }); diff --git a/src/print_zoir.zig b/src/print_zoir.zig index df587fd3658f3e5f7418cb6b6e2aaa83a62464c7..85d4c22b483bea20838d3f267c0537e280001d30 100644 --- a/src/print_zoir.zig +++ b/src/print_zoir.zig @@ -1,4 +1,6 @@ -pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: *Writer) error{ WriteFailed, OutOfMemory }!void { +pub const Error = error{ WriteFailed, OutOfMemory }; + +pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: *Writer) Error!void { assert(!zoir.hasCompileErrors()); const bytes_per_node = comptime n: { @@ -46,8 +48,6 @@ const PrintZon = struct { zoir: Zoir, indent: u32, - const Error = Writer.Error; - fn renderRoot(pz: *PrintZon) Error!void { try pz.renderNode(.root); try pz.w.writeByte('\n'); diff --git a/src/target.zig b/src/target.zig index d57e1e4adb668485e60856b1f4bc073ab7522808..8c33a269399092103680e73ad1f308e823dad0ca 100644 --- a/src/target.zig +++ b/src/target.zig @@ -85,6 +85,19 @@ pub fn defaultSingleThreaded(target: *const std.Target) bool { return false; } +pub fn useEmulatedTls(target: *const std.Target) bool { + if (target.abi.isAndroid()) { + if (target.os.version_range.linux.android < 29) return true; + return false; + } + if (target.abi.isOpenHarmony()) return true; + return switch (target.os.tag) { + .openbsd => true, + .windows => target.abi == .cygnus, + else => false, + }; +} + pub fn hasValgrindSupport(target: *const std.Target, backend: std.builtin.CompilerBackend) bool { // We can't currently output the necessary Valgrind client request assembly when using the C // backend and compiling with an MSVC-like compiler. @@ -222,10 +235,16 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { /// than or equal to the number of behavior tests as the respective LLVM backend. pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool { if (target.cpu.arch.isSpirV()) return true; - if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) return switch (target.ofmt) { - .elf, .macho => true, - else => false, - }; + if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) { + if (target.os.tag == .netbsd) { + // Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341 + return false; + } + return switch (target.ofmt) { + .elf, .macho => true, + else => false, + }; + } return false; } @@ -464,12 +483,12 @@ pub fn clangSupportsNoImplicitFloatArg(target: *const std.Target) bool { pub fn defaultUnwindTables(target: *const std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables { if (target.os.tag == .windows) { // The old 32-bit x86 variant of SEH doesn't use tables. - return if (target.cpu.arch != .x86) .@"async" else .none; + return if (target.cpu.arch != .x86) .async else .none; } - if (target.os.tag.isDarwin()) return .@"async"; - if (libunwind) return .@"async"; - if (libtsan) return .@"async"; - if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .@"async"; + if (target.os.tag.isDarwin()) return .async; + if (libunwind) return .async; + if (libtsan) return .async; + if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .async; return .none; } @@ -796,7 +815,7 @@ pub fn compilerRtIntAbbrev(bits: u16) []const u8 { pub fn fnCallConvAllowsZigTypes(cc: std.builtin.CallingConvention) bool { return switch (cc) { - .auto, .@"async", .@"inline" => true, + .auto, .async, .@"inline" => true, // For now we want to authorize PTX kernel to use zig objects, even if // we end up exposing the ABI. The goal is to experiment with more // integrated CPU/GPU code. diff --git a/src/tracy.zig b/src/tracy.zig index e4730889222c93eecdbb92d8e6e94becd6b1b59d..50d1b2e1a70cc7cc82845665affb197a28bc33d3 100644 --- a/src/tracy.zig +++ b/src/tracy.zig @@ -120,20 +120,21 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { .vtable = &.{ .alloc = allocFn, .resize = resizeFn, + .remap = remapFn, .free = freeFn, }, }; } - fn allocFn(ptr: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 { + fn allocFn(ptr: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 { const self: *Self = @ptrCast(@alignCast(ptr)); - const result = self.parent_allocator.rawAlloc(len, ptr_align, ret_addr); - if (result) |data| { + const result = self.parent_allocator.rawAlloc(len, alignment, ret_addr); + if (result) |memory| { if (len != 0) { if (name) |n| { - allocNamed(data, len, n); + allocNamed(memory, len, n); } else { - alloc(data, len); + alloc(memory, len); } } } else { @@ -142,15 +143,15 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { return result; } - fn resizeFn(ptr: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool { + fn resizeFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { const self: *Self = @ptrCast(@alignCast(ptr)); - if (self.parent_allocator.rawResize(buf, buf_align, new_len, ret_addr)) { + if (self.parent_allocator.rawResize(memory, alignment, new_len, ret_addr)) { if (name) |n| { - freeNamed(buf.ptr, n); - allocNamed(buf.ptr, new_len, n); + freeNamed(memory.ptr, n); + allocNamed(memory.ptr, new_len, n); } else { - free(buf.ptr); - alloc(buf.ptr, new_len); + free(memory.ptr); + alloc(memory.ptr, new_len); } return true; @@ -161,16 +162,33 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { return false; } - fn freeFn(ptr: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void { + fn remapFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { const self: *Self = @ptrCast(@alignCast(ptr)); - self.parent_allocator.rawFree(buf, buf_align, ret_addr); + if (self.parent_allocator.rawRemap(memory, alignment, new_len, ret_addr)) |new_memory| { + if (name) |n| { + freeNamed(memory.ptr, n); + allocNamed(new_memory, new_len, n); + } else { + free(memory.ptr); + alloc(new_memory, new_len); + } + return new_memory; + } else { + messageColor("reallocation failed", 0xFF0000); + return null; + } + } + + fn freeFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void { + const self: *Self = @ptrCast(@alignCast(ptr)); + self.parent_allocator.rawFree(memory, alignment, ret_addr); // this condition is to handle free being called on an empty slice that was never even allocated // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}` - if (buf.len != 0) { + if (memory.len != 0) { if (name) |n| { - freeNamed(buf.ptr, n); + freeNamed(memory.ptr, n); } else { - free(buf.ptr); + free(memory.ptr); } } } diff --git a/src/translate_c.zig b/src/translate_c.zig index 865b6939c2927841c6c6098d967da320e96bb9e3..301e0a219d18a01c1440fc1f9ef8721c5b500224 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -357,7 +357,7 @@ fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.Fi var len: usize = undefined; const bytes_ptr = asm_string.getString_bytes_begin_size(&len); - const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); + const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtString(bytes_ptr[0..len])}); const str_node = try Tag.string_literal.create(c.arena, str); const asm_node = try Tag.asm_simple.create(c.arena, str_node); @@ -2276,7 +2276,7 @@ fn transNarrowStringLiteral( var len: usize = undefined; const bytes_ptr = stmt.getString_bytes_begin_size(&len); - const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); + const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtString(bytes_ptr[0..len])}); const node = try Tag.string_literal.create(c.arena, str); return maybeSuppressResult(c, result_used, node); } @@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { return Tag.char_literal.create(c.arena, if (narrow) - try std.fmt.allocPrint(c.arena, "'{f'}'", .{std.zig.fmtEscapes(&.{@as(u8, @intCast(val))})}) + try std.fmt.allocPrint(c.arena, "'{f}'", .{std.zig.fmtChar(&.{@as(u8, @intCast(val))})}) else try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val})); } @@ -5832,7 +5832,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { num += c - 'A' + 10; }, else => { - i += std.fmt.printInt(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 }); num = 0; if (c == '\\') state = .escape @@ -5858,7 +5858,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { }; num += c - '0'; } else { - i += std.fmt.printInt(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 }); num = 0; count = 0; if (c == '\\') @@ -5872,19 +5872,19 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { } } if (state == .hex or state == .octal) - i += std.fmt.printInt(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 }); return bytes[0..i]; } -/// non-ASCII characters (c > 127) are also treated as non-printable by fmtSliceEscapeLower. +/// non-ASCII characters (c > 127) are also treated as non-printable by ascii.hexEscape. /// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape /// non-ASCII characters so that the Zig source we output will itself be UTF-8. fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 { const zigified = try zigifyEscapeSequences(ctx, m); if (std.unicode.utf8ValidateSlice(zigified)) return zigified; - const formatter = std.fmt.fmtSliceEscapeLower(zigified); - const encoded_size = std.fmt.count("{f}", .{formatter}); + const formatter = std.ascii.hexEscape(zigified, .lower); + const encoded_size: usize = @intCast(std.fmt.count("{f}", .{formatter})); const output = try ctx.arena.alloc(u8, encoded_size); return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index fe4e421dc3238380cc5c159506074cfcefd5c39c..c9a30bb167e5319f944f998fd8a8991c1b98ad46 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -83,7 +83,7 @@ static const bool assertions_on = false; LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi, - const char *abi_name) + const char *abi_name, bool emulated_tls) { std::optional RM; switch (Reloc){ @@ -149,6 +149,10 @@ LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Tri opt.MCOptions.ABIName = abi_name; } + if (emulated_tls) { + opt.EmulatedTLS = true; + } + TargetMachine *TM = reinterpret_cast(T)->createTargetMachine(Triple, CPU, Features, opt, RM, CM, OL, JIT); return reinterpret_cast(TM); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index e2dba338c3af31aa89d4255c2dd8f266a1d64ee5..db331512bd908d7e7e73b50d097314a617726153 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -105,7 +105,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi, - const char *abi_name); + const char *abi_name, bool emulated_tls); ZIG_EXTERN_C void ZigLLVMSetOptBisectLimit(LLVMContextRef context_ref, int limit); diff --git a/stage1/wasi.c b/stage1/wasi.c index ef2183dae849d56d86dd6c7a2eff7272f91aaf0b..83240d39b41dc08a3617d0dd8ab42078ce3d5554 100644 --- a/stage1/wasi.c +++ b/stage1/wasi.c @@ -520,12 +520,15 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov default: panic("unimplemented: fd_read special file"); } + if (fds[fd].stream == NULL) { + store32_align2(res_size_ptr, 0); + return wasi_errno_success; + } + size_t size = 0; for (uint32_t i = 0; i < iovs_len; i += 1) { uint32_t len = load32_align2(&iovs_ptr[i].len); - size_t read_size = 0; - if (fds[fd].stream != NULL) - read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream); + size_t read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream); size += read_size; if (read_size < len) break; } @@ -633,8 +636,10 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i } fpos_t pos; - if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; - if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; + if (fds[fd].stream != NULL) { + if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; + } size_t size = 0; 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 if (written_size < len) break; } - if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + if (fds[fd].stream != NULL) { + if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + } if (size > 0) { 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 default: panic("unimplemented: fd_pread special file"); } + if (fds[fd].stream == NULL) { + store32_align2(res_size_ptr, 0); + return wasi_errno_success; + } + fpos_t pos; if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 8457058d61217ed84a5a7f2866b2e9189e5f57e3..f1c50d438d1e9f61635c47e9cfd91686b4ae591a 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ diff --git a/test/behavior.zig b/test/behavior.zig index 049278a69ff08c44d4d3d40ed95a3483de03dbfe..414ce2e00aff3b2d3c5630af0836cf8178e9868f 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -5,9 +5,7 @@ test { _ = @import("behavior/align.zig"); _ = @import("behavior/alignof.zig"); _ = @import("behavior/array.zig"); - _ = @import("behavior/async_fn.zig"); _ = @import("behavior/atomics.zig"); - _ = @import("behavior/await_struct.zig"); _ = @import("behavior/basic.zig"); _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bitcast.zig"); @@ -103,7 +101,6 @@ test { _ = @import("behavior/underscore.zig"); _ = @import("behavior/union.zig"); _ = @import("behavior/union_with_members.zig"); - _ = @import("behavior/usingnamespace.zig"); _ = @import("behavior/var_args.zig"); // https://github.com/llvm/llvm-project/issues/118879 // https://github.com/llvm/llvm-project/issues/134659 diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 2921986cba08ebd4d13d78e9d0d5315ca5636c36..b3750c9d3d67cbfa2d6308642cf53fe2fd0ed794 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -425,30 +425,6 @@ test "struct field explicit alignment" { try expect(@intFromPtr(&node.massive_byte) % 64 == 0); } -test "align(@alignOf(T)) T does not force resolution of T" { - if (true) return error.SkipZigTest; // TODO - - const S = struct { - const A = struct { - a: *align(@alignOf(A)) A, - }; - fn doTheTest() void { - suspend { - resume @frame(); - } - _ = bar(@Frame(doTheTest)); - } - fn bar(comptime T: type) *align(@alignOf(T)) T { - ok = true; - return undefined; - } - - var ok = false; - }; - _ = async S.doTheTest(); - try expect(S.ok); -} - test "align(N) on functions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/async_fn.zig b/test/behavior/async_fn.zig deleted file mode 100644 index e66ecd52db0fc0e0698d386bed6c93b547416eb7..0000000000000000000000000000000000000000 --- a/test/behavior/async_fn.zig +++ /dev/null @@ -1,1911 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; -const expectEqualStrings = std.testing.expectEqualStrings; -const expectError = std.testing.expectError; - -var global_x: i32 = 1; - -test "simple coroutine suspend and resume" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - var frame = async simpleAsyncFn(); - try expect(global_x == 2); - resume frame; - try expect(global_x == 3); - const af: anyframe->void = &frame; - _ = af; - resume frame; - try expect(global_x == 4); -} -fn simpleAsyncFn() void { - global_x += 1; - suspend {} - global_x += 1; - suspend {} - global_x += 1; -} - -var global_y: i32 = 1; - -test "pass parameter to coroutine" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - var p = async simpleAsyncFnWithArg(2); - try expect(global_y == 3); - resume p; - try expect(global_y == 5); -} -fn simpleAsyncFnWithArg(delta: i32) void { - global_y += delta; - suspend {} - global_y += delta; -} - -test "suspend at end of function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var x: i32 = 1; - - fn doTheTest() !void { - try expect(x == 1); - const p = async suspendAtEnd(); - _ = p; - try expect(x == 2); - } - - fn suspendAtEnd() void { - x += 1; - suspend {} - } - }; - try S.doTheTest(); -} - -test "local variable in async function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var x: i32 = 0; - - fn doTheTest() !void { - try expect(x == 0); - var p = async add(1, 2); - try expect(x == 0); - resume p; - try expect(x == 0); - resume p; - try expect(x == 0); - resume p; - try expect(x == 3); - } - - fn add(a: i32, b: i32) void { - var accum: i32 = 0; - suspend {} - accum += a; - suspend {} - accum += b; - suspend {} - x = accum; - } - }; - try S.doTheTest(); -} - -test "calling an inferred async function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var x: i32 = 1; - var other_frame: *@Frame(other) = undefined; - - fn doTheTest() !void { - _ = async first(); - try expect(x == 1); - resume other_frame.*; - try expect(x == 2); - } - - fn first() void { - other(); - } - fn other() void { - other_frame = @frame(); - suspend {} - x += 1; - } - }; - try S.doTheTest(); -} - -test "@frameSize" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - if (builtin.target.cpu.arch == .thumb or builtin.target.cpu.arch == .thumbeb) - return error.SkipZigTest; - - const S = struct { - fn doTheTest() !void { - { - var ptr = @as(fn (i32) callconv(.@"async") void, @ptrCast(other)); - _ = &ptr; - const size = @frameSize(ptr); - try expect(size == @sizeOf(@Frame(other))); - } - { - var ptr = @as(fn () callconv(.@"async") void, @ptrCast(first)); - _ = &ptr; - const size = @frameSize(ptr); - try expect(size == @sizeOf(@Frame(first))); - } - } - - fn first() void { - other(1); - } - fn other(param: i32) void { - _ = param; - var local: i32 = undefined; - _ = &local; - suspend {} - } - }; - try S.doTheTest(); -} - -test "coroutine suspend, resume" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - - fn doTheTest() !void { - _ = async amain(); - seq('d'); - resume frame; - seq('h'); - - try expect(std.mem.eql(u8, &points, "abcdefgh")); - } - - fn amain() void { - seq('a'); - var f = async testAsyncSeq(); - seq('c'); - await f; - seq('g'); - } - - fn testAsyncSeq() void { - defer seq('f'); - - seq('b'); - suspend { - frame = @frame(); - } - seq('e'); - } - var points = [_]u8{'x'} ** "abcdefgh".len; - var index: usize = 0; - - fn seq(c: u8) void { - points[index] = c; - index += 1; - } - }; - try S.doTheTest(); -} - -test "coroutine suspend with block" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const p = async testSuspendBlock(); - _ = p; - try expect(!global_result); - resume a_promise; - try expect(global_result); -} - -var a_promise: anyframe = undefined; -var global_result = false; -fn testSuspendBlock() callconv(.@"async") void { - suspend { - comptime assert(@TypeOf(@frame()) == *@Frame(testSuspendBlock)) catch unreachable; - a_promise = @frame(); - } - - // Test to make sure that @frame() works as advertised (issue #1296) - // var our_handle: anyframe = @frame(); - expect(a_promise == @as(anyframe, @frame())) catch @panic("test failed"); - - global_result = true; -} - -var await_a_promise: anyframe = undefined; -var await_final_result: i32 = 0; - -test "coroutine await" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - await_seq('a'); - var p = async await_amain(); - _ = &p; - await_seq('f'); - resume await_a_promise; - await_seq('i'); - try expect(await_final_result == 1234); - try expect(std.mem.eql(u8, &await_points, "abcdefghi")); -} -fn await_amain() callconv(.@"async") void { - await_seq('b'); - var p = async await_another(); - await_seq('e'); - await_final_result = await p; - await_seq('h'); -} -fn await_another() callconv(.@"async") i32 { - await_seq('c'); - suspend { - await_seq('d'); - await_a_promise = @frame(); - } - await_seq('g'); - return 1234; -} - -var await_points = [_]u8{0} ** "abcdefghi".len; -var await_seq_index: usize = 0; - -fn await_seq(c: u8) void { - await_points[await_seq_index] = c; - await_seq_index += 1; -} - -var early_final_result: i32 = 0; - -test "coroutine await early return" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - early_seq('a'); - var p = async early_amain(); - _ = &p; - early_seq('f'); - try expect(early_final_result == 1234); - try expect(std.mem.eql(u8, &early_points, "abcdef")); -} -fn early_amain() callconv(.@"async") void { - early_seq('b'); - var p = async early_another(); - early_seq('d'); - early_final_result = await p; - early_seq('e'); -} -fn early_another() callconv(.@"async") i32 { - early_seq('c'); - return 1234; -} - -var early_points = [_]u8{0} ** "abcdef".len; -var early_seq_index: usize = 0; - -fn early_seq(c: u8) void { - early_points[early_seq_index] = c; - early_seq_index += 1; -} - -test "async function with dot syntax" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var y: i32 = 1; - fn foo() callconv(.@"async") void { - y += 1; - suspend {} - } - }; - const p = async S.foo(); - _ = p; - try expect(S.y == 2); -} - -test "async fn pointer in a struct field" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - var data: i32 = 1; - const Foo = struct { - bar: fn (*i32) callconv(.@"async") void, - }; - var foo = Foo{ .bar = simpleAsyncFn2 }; - _ = &foo; - var bytes: [64]u8 align(16) = undefined; - const f = @asyncCall(&bytes, {}, foo.bar, .{&data}); - comptime assert(@TypeOf(f) == anyframe->void); - try expect(data == 2); - resume f; - try expect(data == 4); - _ = async doTheAwait(f); - try expect(data == 4); -} - -fn doTheAwait(f: anyframe->void) void { - await f; -} -fn simpleAsyncFn2(y: *i32) callconv(.@"async") void { - defer y.* += 2; - y.* += 1; - suspend {} -} - -test "@asyncCall with return type" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const Foo = struct { - bar: fn () callconv(.@"async") i32, - - var global_frame: anyframe = undefined; - fn middle() callconv(.@"async") i32 { - return afunc(); - } - - fn afunc() i32 { - global_frame = @frame(); - suspend {} - return 1234; - } - }; - var foo = Foo{ .bar = Foo.middle }; - _ = &foo; - var bytes: [150]u8 align(16) = undefined; - var aresult: i32 = 0; - _ = @asyncCall(&bytes, &aresult, foo.bar, .{}); - try expect(aresult == 0); - resume Foo.global_frame; - try expect(aresult == 1234); -} - -test "async fn with inferred error set" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - - fn doTheTest() !void { - var frame: [1]@Frame(middle) = undefined; - var fn_ptr = middle; - _ = &fn_ptr; - var result: @typeInfo(@typeInfo(@TypeOf(fn_ptr)).@"fn".return_type.?).error_union.error_set!void = undefined; - _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{}); - resume global_frame; - try std.testing.expectError(error.Fail, result); - } - fn middle() callconv(.@"async") !void { - var f = async middle2(); - return await f; - } - - fn middle2() !void { - return failing(); - } - - fn failing() !void { - global_frame = @frame(); - suspend {} - return error.Fail; - } - }; - try S.doTheTest(); -} - -test "error return trace across suspend points - early return" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const p = nonFailing(); - resume p; - const p2 = async printTrace(p); - _ = p2; -} - -test "error return trace across suspend points - async return" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const p = nonFailing(); - const p2 = async printTrace(p); - _ = p2; - resume p; -} - -fn nonFailing() (anyframe->anyerror!void) { - const Static = struct { - var frame: @Frame(suspendThenFail) = undefined; - }; - Static.frame = async suspendThenFail(); - return &Static.frame; -} -fn suspendThenFail() callconv(.@"async") anyerror!void { - suspend {} - return error.Fail; -} -fn printTrace(p: anyframe->(anyerror!void)) callconv(.@"async") void { - (await p) catch |e| { - std.testing.expect(e == error.Fail) catch @panic("test failure"); - if (@errorReturnTrace()) |trace| { - expect(trace.index == 1) catch @panic("test failure"); - } else switch (builtin.mode) { - .Debug, .ReleaseSafe => @panic("expected return trace"), - .ReleaseFast, .ReleaseSmall => {}, - } - }; -} - -test "break from suspend" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - var my_result: i32 = 1; - const p = async testBreakFromSuspend(&my_result); - _ = p; - try std.testing.expect(my_result == 2); -} -fn testBreakFromSuspend(my_result: *i32) callconv(.@"async") void { - suspend { - resume @frame(); - } - my_result.* += 1; - suspend {} - my_result.* += 1; -} - -test "heap allocated async function frame" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var x: i32 = 42; - - fn doTheTest() !void { - const frame = try std.testing.allocator.create(@Frame(someFunc)); - defer std.testing.allocator.destroy(frame); - - try expect(x == 42); - frame.* = async someFunc(); - try expect(x == 43); - resume frame; - try expect(x == 44); - } - - fn someFunc() void { - x += 1; - suspend {} - x += 1; - } - }; - try S.doTheTest(); -} - -test "async function call return value" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - var pt = Point{ .x = 10, .y = 11 }; - - fn doTheTest() !void { - try expectEqual(pt.x, 10); - try expectEqual(pt.y, 11); - _ = async first(); - try expectEqual(pt.x, 10); - try expectEqual(pt.y, 11); - resume frame; - try expectEqual(pt.x, 1); - try expectEqual(pt.y, 2); - } - - fn first() void { - pt = second(1, 2); - } - - fn second(x: i32, y: i32) Point { - return other(x, y); - } - - fn other(x: i32, y: i32) Point { - frame = @frame(); - suspend {} - return Point{ - .x = x, - .y = y, - }; - } - - const Point = struct { - x: i32, - y: i32, - }; - }; - try S.doTheTest(); -} - -test "suspension points inside branching control flow" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var result: i32 = 10; - - fn doTheTest() !void { - try expect(10 == result); - var frame = async func(true); - try expect(10 == result); - resume frame; - try expect(11 == result); - resume frame; - try expect(12 == result); - resume frame; - try expect(13 == result); - } - - fn func(b: bool) void { - while (b) { - suspend {} - result += 1; - } - } - }; - try S.doTheTest(); -} - -test "call async function which has struct return type" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - - fn doTheTest() void { - _ = async atest(); - resume frame; - } - - fn atest() void { - const result = func(); - expect(result.x == 5) catch @panic("test failed"); - expect(result.y == 6) catch @panic("test failed"); - } - - const Point = struct { - x: usize, - y: usize, - }; - - fn func() Point { - suspend { - frame = @frame(); - } - return Point{ - .x = 5, - .y = 6, - }; - } - }; - S.doTheTest(); -} - -test "pass string literal to async function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - var ok: bool = false; - - fn doTheTest() !void { - _ = async hello("hello"); - resume frame; - try expect(ok); - } - - fn hello(msg: []const u8) void { - frame = @frame(); - suspend {} - expectEqualStrings("hello", msg) catch @panic("test failed"); - ok = true; - } - }; - try S.doTheTest(); -} - -test "await inside an errdefer" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - - fn doTheTest() !void { - _ = async amainWrap(); - resume frame; - } - - fn amainWrap() !void { - var foo = async func(); - errdefer await foo; - return error.Bad; - } - - fn func() void { - frame = @frame(); - suspend {} - } - }; - try S.doTheTest(); -} - -test "try in an async function with error union and non-zero-bit payload" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - var ok = false; - - fn doTheTest() !void { - _ = async amain(); - resume frame; - try expect(ok); - } - - fn amain() void { - std.testing.expectError(error.Bad, theProblem()) catch @panic("test failed"); - ok = true; - } - - fn theProblem() ![]u8 { - frame = @frame(); - suspend {} - const result = try other(); - return result; - } - - fn other() ![]u8 { - return error.Bad; - } - }; - try S.doTheTest(); -} - -test "returning a const error from async function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - var ok = false; - - fn doTheTest() !void { - _ = async amain(); - resume frame; - try expect(ok); - } - - fn amain() !void { - var download_frame = async fetchUrl(10, "a string"); - const download_text = try await download_frame; - _ = download_text; - - @panic("should not get here"); - } - - fn fetchUrl(unused: i32, url: []const u8) ![]u8 { - _ = unused; - _ = url; - frame = @frame(); - suspend {} - ok = true; - return error.OutOfMemory; - } - }; - try S.doTheTest(); -} - -test "async/await typical usage" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - inline for ([_]bool{ false, true }) |b1| { - inline for ([_]bool{ false, true }) |b2| { - inline for ([_]bool{ false, true }) |b3| { - inline for ([_]bool{ false, true }) |b4| { - testAsyncAwaitTypicalUsage(b1, b2, b3, b4).doTheTest(); - } - } - } - } -} - -fn testAsyncAwaitTypicalUsage( - comptime simulate_fail_download: bool, - comptime simulate_fail_file: bool, - comptime suspend_download: bool, - comptime suspend_file: bool, -) type { - return struct { - fn doTheTest() void { - _ = async amainWrap(); - if (suspend_file) { - resume global_file_frame; - } - if (suspend_download) { - resume global_download_frame; - } - } - fn amainWrap() void { - if (amain()) |_| { - expect(!simulate_fail_download) catch @panic("test failure"); - expect(!simulate_fail_file) catch @panic("test failure"); - } else |e| switch (e) { - error.NoResponse => expect(simulate_fail_download) catch @panic("test failure"), - error.FileNotFound => expect(simulate_fail_file) catch @panic("test failure"), - else => @panic("test failure"), - } - } - - fn amain() !void { - const allocator = std.testing.allocator; - var download_frame = async fetchUrl(allocator, "https://example.com/"); - var download_awaited = false; - errdefer if (!download_awaited) { - if (await download_frame) |x| allocator.free(x) else |_| {} - }; - - var file_frame = async readFile(allocator, "something.txt"); - var file_awaited = false; - errdefer if (!file_awaited) { - if (await file_frame) |x| allocator.free(x) else |_| {} - }; - - download_awaited = true; - const download_text = try await download_frame; - defer allocator.free(download_text); - - file_awaited = true; - const file_text = try await file_frame; - defer allocator.free(file_text); - - try expect(std.mem.eql(u8, "expected download text", download_text)); - try expect(std.mem.eql(u8, "expected file text", file_text)); - } - - var global_download_frame: anyframe = undefined; - fn fetchUrl(allocator: std.mem.Allocator, url: []const u8) anyerror![]u8 { - _ = url; - const result = try allocator.dupe(u8, "expected download text"); - errdefer allocator.free(result); - if (suspend_download) { - suspend { - global_download_frame = @frame(); - } - } - if (simulate_fail_download) return error.NoResponse; - return result; - } - - var global_file_frame: anyframe = undefined; - fn readFile(allocator: std.mem.Allocator, filename: []const u8) anyerror![]u8 { - _ = filename; - const result = try allocator.dupe(u8, "expected file text"); - errdefer allocator.free(result); - if (suspend_file) { - suspend { - global_file_frame = @frame(); - } - } - if (simulate_fail_file) return error.FileNotFound; - return result; - } - }; -} - -test "alignment of local variables in async functions" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - var y: u8 = 123; - _ = &y; - var x: u8 align(128) = 1; - try expect(@intFromPtr(&x) % 128 == 0); - } - }; - try S.doTheTest(); -} - -test "no reason to resolve frame still works" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - _ = async simpleNothing(); -} -fn simpleNothing() void { - var x: i32 = 1234; - _ = &x; -} - -test "async call a generic function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - var f = async func(i32, 2); - const result = await f; - try expect(result == 3); - } - - fn func(comptime T: type, inc: T) T { - var x: T = 1; - suspend { - resume @frame(); - } - x += inc; - return x; - } - }; - _ = async S.doTheTest(); -} - -test "return from suspend block" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - expect(func() == 1234) catch @panic("test failure"); - } - fn func() i32 { - suspend { - return 1234; - } - } - }; - _ = async S.doTheTest(); -} - -test "struct parameter to async function is copied to the frame" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - const Point = struct { - x: i32, - y: i32, - }; - - var frame: anyframe = undefined; - - fn doTheTest() void { - _ = async atest(); - resume frame; - } - - fn atest() void { - var f: @Frame(foo) = undefined; - bar(&f); - clobberStack(10); - } - - fn clobberStack(x: i32) void { - if (x == 0) return; - clobberStack(x - 1); - var y: i32 = x; - _ = &y; - } - - fn bar(f: *@Frame(foo)) void { - var pt = Point{ .x = 1, .y = 2 }; - _ = &pt; - f.* = async foo(pt); - const result = await f; - expect(result == 1) catch @panic("test failure"); - } - - fn foo(point: Point) i32 { - suspend { - frame = @frame(); - } - return point.x; - } - }; - S.doTheTest(); -} - -test "cast fn to async fn when it is inferred to be async" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - var ok = false; - - fn doTheTest() void { - var ptr: fn () callconv(.@"async") i32 = undefined; - ptr = func; - var buf: [100]u8 align(16) = undefined; - var result: i32 = undefined; - const f = @asyncCall(&buf, &result, ptr, .{}); - _ = await f; - expect(result == 1234) catch @panic("test failure"); - ok = true; - } - - fn func() i32 { - suspend { - frame = @frame(); - } - return 1234; - } - }; - _ = async S.doTheTest(); - resume S.frame; - try expect(S.ok); -} - -test "cast fn to async fn when it is inferred to be async, awaited directly" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var frame: anyframe = undefined; - var ok = false; - - fn doTheTest() void { - var ptr: fn () callconv(.@"async") i32 = undefined; - ptr = func; - var buf: [100]u8 align(16) = undefined; - var result: i32 = undefined; - _ = await @asyncCall(&buf, &result, ptr, .{}); - expect(result == 1234) catch @panic("test failure"); - ok = true; - } - - fn func() i32 { - suspend { - frame = @frame(); - } - return 1234; - } - }; - _ = async S.doTheTest(); - resume S.frame; - try expect(S.ok); -} - -test "await does not force async if callee is blocking" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn simple() i32 { - return 1234; - } - }; - var x = async S.simple(); - try expect(await x == 1234); -} - -test "recursive async function" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - try expect(recursiveAsyncFunctionTest(false).doTheTest() == 55); - try expect(recursiveAsyncFunctionTest(true).doTheTest() == 55); -} - -fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type { - return struct { - fn fib(allocator: std.mem.Allocator, x: u32) error{OutOfMemory}!u32 { - if (x <= 1) return x; - - if (suspending_implementation) { - suspend { - resume @frame(); - } - } - - const f1 = try allocator.create(@Frame(fib)); - defer allocator.destroy(f1); - - const f2 = try allocator.create(@Frame(fib)); - defer allocator.destroy(f2); - - f1.* = async fib(allocator, x - 1); - var f1_awaited = false; - errdefer if (!f1_awaited) { - _ = await f1; - }; - - f2.* = async fib(allocator, x - 2); - var f2_awaited = false; - errdefer if (!f2_awaited) { - _ = await f2; - }; - - var sum: u32 = 0; - - f1_awaited = true; - sum += try await f1; - - f2_awaited = true; - sum += try await f2; - - return sum; - } - - fn doTheTest() u32 { - if (suspending_implementation) { - var result: u32 = undefined; - _ = async amain(&result); - return result; - } else { - return fib(std.testing.allocator, 10) catch unreachable; - } - } - - fn amain(result: *u32) void { - var x = async fib(std.testing.allocator, 10); - result.* = (await x) catch unreachable; - } - }; -} - -test "@asyncCall with comptime-known function, but not awaited directly" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - - fn doTheTest() !void { - var frame: [1]@Frame(middle) = undefined; - var result: @typeInfo(@typeInfo(@TypeOf(middle)).@"fn".return_type.?).error_union.error_set!void = undefined; - _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{}); - resume global_frame; - try std.testing.expectError(error.Fail, result); - } - fn middle() callconv(.@"async") !void { - var f = async middle2(); - return await f; - } - - fn middle2() !void { - return failing(); - } - - fn failing() !void { - global_frame = @frame(); - suspend {} - return error.Fail; - } - }; - try S.doTheTest(); -} - -test "@asyncCall with actual frame instead of byte buffer" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn func() i32 { - suspend {} - return 1234; - } - }; - var frame: @Frame(S.func) = undefined; - var result: i32 = undefined; - const ptr = @asyncCall(&frame, &result, S.func, .{}); - resume ptr; - try expect(result == 1234); -} - -test "@asyncCall using the result location inside the frame" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn simple2(y: *i32) callconv(.@"async") i32 { - defer y.* += 2; - y.* += 1; - suspend {} - return 1234; - } - fn getAnswer(f: anyframe->i32, out: *i32) void { - out.* = await f; - } - }; - var data: i32 = 1; - const Foo = struct { - bar: fn (*i32) callconv(.@"async") i32, - }; - var foo = Foo{ .bar = S.simple2 }; - _ = &foo; - var bytes: [64]u8 align(16) = undefined; - const f = @asyncCall(&bytes, {}, foo.bar, .{&data}); - comptime assert(@TypeOf(f) == anyframe->i32); - try expect(data == 2); - resume f; - try expect(data == 4); - _ = async S.getAnswer(f, &data); - try expect(data == 1234); -} - -test "@TypeOf an async function call of generic fn with error union type" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn func(comptime x: anytype) anyerror!i32 { - const T = @TypeOf(async func(x)); - comptime assert(T == @typeInfo(@TypeOf(@frame())).pointer.child); - return undefined; - } - }; - _ = async S.func(i32); -} - -test "using @TypeOf on a generic function call" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_ok = false; - - var buf: [100]u8 align(16) = undefined; - - fn amain(x: anytype) void { - if (x == 0) { - global_ok = true; - return; - } - suspend { - global_frame = @frame(); - } - const F = @TypeOf(async amain(x - 1)); - const frame = @as(*F, @ptrFromInt(@intFromPtr(&buf))); - return await @asyncCall(frame, {}, amain, .{x - 1}); - } - }; - _ = async S.amain(@as(u32, 1)); - resume S.global_frame; - try expect(S.global_ok); -} - -test "recursive call of await @asyncCall with struct return type" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_ok = false; - - var buf: [100]u8 align(16) = undefined; - - fn amain(x: anytype) Foo { - if (x == 0) { - global_ok = true; - return Foo{ .x = 1, .y = 2, .z = 3 }; - } - suspend { - global_frame = @frame(); - } - const F = @TypeOf(async amain(x - 1)); - const frame = @as(*F, @ptrFromInt(@intFromPtr(&buf))); - return await @asyncCall(frame, {}, amain, .{x - 1}); - } - - const Foo = struct { - x: u64, - y: u64, - z: u64, - }; - }; - var res: S.Foo = undefined; - var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined; - _ = @asyncCall(&frame, &res, S.amain, .{@as(u32, 1)}); - resume S.global_frame; - try expect(S.global_ok); - try expect(res.x == 1); - try expect(res.y == 2); - try expect(res.z == 3); -} - -test "nosuspend function call" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - const result = nosuspend add(50, 100); - try expect(result == 150); - } - fn add(a: i32, b: i32) i32 { - if (a > 100) { - suspend {} - } - return a + b; - } - }; - try S.doTheTest(); -} - -test "await used in expression and awaiting fn with no suspend but async calling convention" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn atest() void { - var f1 = async add(1, 2); - var f2 = async add(3, 4); - - const sum = (await f1) + (await f2); - expect(sum == 10) catch @panic("test failure"); - } - fn add(a: i32, b: i32) callconv(.@"async") i32 { - return a + b; - } - }; - _ = async S.atest(); -} - -test "await used in expression after a fn call" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn atest() void { - var f1 = async add(3, 4); - var sum: i32 = 0; - sum = foo() + await f1; - expect(sum == 8) catch @panic("test failure"); - } - fn add(a: i32, b: i32) callconv(.@"async") i32 { - return a + b; - } - fn foo() i32 { - return 1; - } - }; - _ = async S.atest(); -} - -test "async fn call used in expression after a fn call" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - fn atest() void { - var sum: i32 = 0; - sum = foo() + add(3, 4); - expect(sum == 8) catch @panic("test failure"); - } - fn add(a: i32, b: i32) callconv(.@"async") i32 { - return a + b; - } - fn foo() i32 { - return 1; - } - }; - _ = async S.atest(); -} - -test "suspend in for loop" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: ?anyframe = null; - - fn doTheTest() void { - _ = async atest(); - while (global_frame) |f| resume f; - } - - fn atest() void { - expect(func(&[_]u8{ 1, 2, 3 }) == 6) catch @panic("test failure"); - } - fn func(stuff: []const u8) u32 { - global_frame = @frame(); - var sum: u32 = 0; - for (stuff) |x| { - suspend {} - sum += x; - } - global_frame = null; - return sum; - } - }; - S.doTheTest(); -} - -test "suspend in while loop" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: ?anyframe = null; - - fn doTheTest() void { - _ = async atest(); - while (global_frame) |f| resume f; - } - - fn atest() void { - expect(optional(6) == 6) catch @panic("test failure"); - expect(errunion(6) == 6) catch @panic("test failure"); - } - fn optional(stuff: ?u32) u32 { - global_frame = @frame(); - defer global_frame = null; - while (stuff) |val| { - suspend {} - return val; - } - return 0; - } - fn errunion(stuff: anyerror!u32) u32 { - global_frame = @frame(); - defer global_frame = null; - while (stuff) |val| { - suspend {} - return val; - } else |err| { - err catch {}; - return 0; - } - } - }; - S.doTheTest(); -} - -test "correctly spill when returning the error union result of another async fn" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - - fn doTheTest() !void { - expect((atest() catch unreachable) == 1234) catch @panic("test failure"); - } - - fn atest() !i32 { - return fallible1(); - } - - fn fallible1() anyerror!i32 { - suspend { - global_frame = @frame(); - } - return 1234; - } - }; - _ = async S.doTheTest(); - resume S.global_frame; -} - -test "spill target expr in a for loop" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - - fn doTheTest() !void { - var foo = Foo{ - .slice = &[_]i32{ 1, 2 }, - }; - expect(atest(&foo) == 3) catch @panic("test failure"); - } - - const Foo = struct { - slice: []const i32, - }; - - fn atest(foo: *Foo) i32 { - var sum: i32 = 0; - for (foo.slice) |x| { - suspend { - global_frame = @frame(); - } - sum += x; - } - return sum; - } - }; - _ = async S.doTheTest(); - resume S.global_frame; - resume S.global_frame; -} - -test "spill target expr in a for loop, with a var decl in the loop body" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - - fn doTheTest() !void { - var foo = Foo{ - .slice = &[_]i32{ 1, 2 }, - }; - expect(atest(&foo) == 3) catch @panic("test failure"); - } - - const Foo = struct { - slice: []const i32, - }; - - fn atest(foo: *Foo) i32 { - var sum: i32 = 0; - for (foo.slice) |x| { - // Previously this var decl would prevent spills. This test makes sure - // the for loop spills still happen even though there is a VarDecl in scope - // before the suspend. - var anything = true; - _ = &anything; - suspend { - global_frame = @frame(); - } - sum += x; - } - return sum; - } - }; - _ = async S.doTheTest(); - resume S.global_frame; - resume S.global_frame; -} - -test "async call with @call" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - fn doTheTest() void { - _ = @call(.{ .modifier = .async_kw }, atest, .{}); - resume global_frame; - } - fn atest() void { - var frame = @call(.{ .modifier = .async_kw }, afoo, .{}); - const res = await frame; - expect(res == 42) catch @panic("test failure"); - } - fn afoo() i32 { - suspend { - global_frame = @frame(); - } - return 42; - } - }; - S.doTheTest(); -} - -test "async function passed 0-bit arg after non-0-bit arg" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_int: i32 = 0; - - fn foo() void { - bar(1, .{}) catch unreachable; - } - - fn bar(x: i32, args: anytype) anyerror!void { - _ = args; - global_frame = @frame(); - suspend {} - global_int = x; - } - }; - _ = async S.foo(); - resume S.global_frame; - try expect(S.global_int == 1); -} - -test "async function passed align(16) arg after align(8) arg" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_int: u128 = 0; - - fn foo() void { - var a: u128 = 99; - _ = &a; - bar(10, .{a}) catch unreachable; - } - - fn bar(x: u64, args: anytype) anyerror!void { - try expect(x == 10); - global_frame = @frame(); - suspend {} - global_int = args[0]; - } - }; - _ = async S.foo(); - resume S.global_frame; - try expect(S.global_int == 99); -} - -test "async function call resolves target fn frame, comptime func" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_int: i32 = 9; - - fn foo() anyerror!void { - const stack_size = 1000; - var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; - return await @asyncCall(&stack_frame, {}, bar, .{}); - } - - fn bar() anyerror!void { - global_frame = @frame(); - suspend {} - global_int += 1; - } - }; - _ = async S.foo(); - resume S.global_frame; - try expect(S.global_int == 10); -} - -test "async function call resolves target fn frame, runtime func" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_int: i32 = 9; - - fn foo() anyerror!void { - const stack_size = 1000; - var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; - var func: fn () callconv(.@"async") anyerror!void = bar; - _ = &func; - return await @asyncCall(&stack_frame, {}, func, .{}); - } - - fn bar() anyerror!void { - global_frame = @frame(); - suspend {} - global_int += 1; - } - }; - _ = async S.foo(); - resume S.global_frame; - try expect(S.global_int == 10); -} - -test "properly spill optional payload capture value" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var global_int: usize = 2; - - fn foo() void { - var opt: ?usize = 1234; - _ = &opt; - if (opt) |x| { - bar(); - global_int += x; - } - } - - fn bar() void { - global_frame = @frame(); - suspend {} - global_int += 1; - } - }; - _ = async S.foo(); - resume S.global_frame; - try expect(S.global_int == 1237); -} - -test "handle defer interfering with return value spill" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame1: anyframe = undefined; - var global_frame2: anyframe = undefined; - var finished = false; - var baz_happened = false; - - fn doTheTest() !void { - _ = async testFoo(); - resume global_frame1; - resume global_frame2; - try expect(baz_happened); - try expect(finished); - } - - fn testFoo() void { - expectError(error.Bad, foo()) catch @panic("test failure"); - finished = true; - } - - fn foo() anyerror!void { - defer baz(); - return bar() catch |err| return err; - } - - fn bar() anyerror!void { - global_frame1 = @frame(); - suspend {} - return error.Bad; - } - - fn baz() void { - global_frame2 = @frame(); - suspend {} - baz_happened = true; - } - }; - try S.doTheTest(); -} - -test "take address of temporary async frame" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var global_frame: anyframe = undefined; - var finished = false; - - fn doTheTest() !void { - _ = async asyncDoTheTest(); - resume global_frame; - try expect(finished); - } - - fn asyncDoTheTest() void { - expect(finishIt(&async foo(10)) == 1245) catch @panic("test failure"); - finished = true; - } - - fn foo(arg: i32) i32 { - global_frame = @frame(); - suspend {} - return arg + 1234; - } - - fn finishIt(frame: anyframe->i32) i32 { - return (await frame) + 1; - } - }; - try S.doTheTest(); -} - -test "nosuspend await" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var finished = false; - - fn doTheTest() !void { - var frame = async foo(false); - try expect(nosuspend await frame == 42); - finished = true; - } - - fn foo(want_suspend: bool) i32 { - if (want_suspend) { - suspend {} - } - return 42; - } - }; - try S.doTheTest(); - try expect(S.finished); -} - -test "nosuspend on function calls" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S0 = struct { - b: i32 = 42, - }; - const S1 = struct { - fn c() S0 { - return S0{}; - } - fn d() !S0 { - return S0{}; - } - }; - try expectEqual(@as(i32, 42), nosuspend S1.c().b); - try expectEqual(@as(i32, 42), (try nosuspend S1.d()).b); -} - -test "nosuspend on async function calls" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S0 = struct { - b: i32 = 42, - }; - const S1 = struct { - fn c() S0 { - return S0{}; - } - fn d() !S0 { - return S0{}; - } - }; - var frame_c = nosuspend async S1.c(); - try expectEqual(@as(i32, 42), (await frame_c).b); - var frame_d = nosuspend async S1.d(); - try expectEqual(@as(i32, 42), (try await frame_d).b); -} - -// test "resume nosuspend async function calls" { -// if (true) return error.SkipZigTest; // if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO -// const S0 = struct { -// b: i32 = 42, -// }; -// const S1 = struct { -// fn c() S0 { -// suspend {} -// return S0{}; -// } -// fn d() !S0 { -// suspend {} -// return S0{}; -// } -// }; -// var frame_c = nosuspend async S1.c(); -// resume frame_c; -// try expectEqual(@as(i32, 42), (await frame_c).b); -// var frame_d = nosuspend async S1.d(); -// resume frame_d; -// try expectEqual(@as(i32, 42), (try await frame_d).b); -// } - -test "nosuspend resume async function calls" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S0 = struct { - b: i32 = 42, - }; - const S1 = struct { - fn c() S0 { - suspend {} - return S0{}; - } - fn d() !S0 { - suspend {} - return S0{}; - } - }; - var frame_c = async S1.c(); - nosuspend resume frame_c; - try expectEqual(@as(i32, 42), (await frame_c).b); - var frame_d = async S1.d(); - nosuspend resume frame_d; - try expectEqual(@as(i32, 42), (try await frame_d).b); -} - -test "avoid forcing frame alignment resolution implicit cast to *anyopaque" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const S = struct { - var x: ?*anyopaque = null; - - fn foo() bool { - suspend { - x = @frame(); - } - return true; - } - }; - var frame = async S.foo(); - resume @as(anyframe->bool, @ptrCast(@alignCast(S.x))); - try expect(nosuspend await frame); -} - -test "@asyncCall with pass-by-value arguments" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const F0: u64 = 0xbeefbeefbeefbeef; - const F1: u64 = 0xf00df00df00df00d; - const F2: u64 = 0xcafecafecafecafe; - - const S = struct { - pub const ST = struct { f0: usize, f1: usize }; - pub const AT = [5]u8; - - pub fn f(_fill0: u64, s: ST, _fill1: u64, a: AT, _fill2: u64) callconv(.@"async") void { - _ = s; - _ = a; - // Check that the array and struct arguments passed by value don't - // end up overflowing the adjacent fields in the frame structure. - expectEqual(F0, _fill0) catch @panic("test failure"); - expectEqual(F1, _fill1) catch @panic("test failure"); - expectEqual(F2, _fill2) catch @panic("test failure"); - } - }; - - var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined; - // The function pointer must not be comptime-known. - var t = S.f; - _ = &t; - var frame_ptr = @asyncCall(&buffer, {}, t, .{ - F0, - .{ .f0 = 1, .f1 = 2 }, - F1, - [_]u8{ 1, 2, 3, 4, 5 }, - F2, - }); - _ = &frame_ptr; -} - -test "@asyncCall with arguments having non-standard alignment" { - if (true) return error.SkipZigTest; // TODO - if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO - - const F0: u64 = 0xbeefbeef; - const F1: u64 = 0xf00df00df00df00d; - - const S = struct { - pub fn f(_fill0: u32, s: struct { x: u64 align(16) }, _fill1: u64) callconv(.@"async") void { - _ = s; - // The compiler inserts extra alignment for s, check that the - // generated code picks the right slot for fill1. - expectEqual(F0, _fill0) catch @panic("test failure"); - expectEqual(F1, _fill1) catch @panic("test failure"); - } - }; - - var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined; - // The function pointer must not be comptime-known. - var t = S.f; - _ = &t; - var frame_ptr = @asyncCall(&buffer, {}, t, .{ F0, undefined, F1 }); - _ = &frame_ptr; -} diff --git a/test/behavior/await_struct.zig b/test/behavior/await_struct.zig deleted file mode 100644 index c919922f030362cb3bf227af15faff759b9cf37b..0000000000000000000000000000000000000000 --- a/test/behavior/await_struct.zig +++ /dev/null @@ -1,47 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const expect = std.testing.expect; - -const Foo = struct { - x: i32, -}; - -var await_a_promise: anyframe = undefined; -var await_final_result = Foo{ .x = 0 }; - -test "coroutine await struct" { - if (true) return error.SkipZigTest; // TODO - - await_seq('a'); - var p = async await_amain(); - _ = &p; - await_seq('f'); - resume await_a_promise; - await_seq('i'); - try expect(await_final_result.x == 1234); - try expect(std.mem.eql(u8, &await_points, "abcdefghi")); -} -fn await_amain() callconv(.@"async") void { - await_seq('b'); - var p = async await_another(); - await_seq('e'); - await_final_result = await p; - await_seq('h'); -} -fn await_another() callconv(.@"async") Foo { - await_seq('c'); - suspend { - await_seq('d'); - await_a_promise = @frame(); - } - await_seq('g'); - return Foo{ .x = 1234 }; -} - -var await_points = [_]u8{0} ** "abcdefghi".len; -var await_seq_index: usize = 0; - -fn await_seq(c: u8) void { - await_points[await_seq_index] = c; - await_seq_index += 1; -} diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index c8166a06ab676bc4c8a236a3d89c0557a3f71235..bcd72d5176e9b9664557537bffaf5d9c415a50e8 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1107,27 +1107,6 @@ test "inline call of function with a switch inside the return statement" { try expect(S.foo(1) == 1); } -test "ambiguous reference error ignores current declaration" { - const S = struct { - const foo = 666; - - const a = @This(); - const b = struct { - const foo = a.foo; - const bar = struct { - bar: u32 = b.foo, - }; - - comptime { - _ = b.foo; - } - }; - - usingnamespace b; - }; - try expect(S.b.foo == 666); -} - test "pointer to zero sized global is mutable" { const S = struct { const Thing = struct {}; diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 5cfae1b35d901db76c9cbc13ade41483523b019f..e509fcda35972feb1ed18866d8bb37be79bc79f2 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -37,7 +37,7 @@ test "basic invocations" { comptime { // comptime calls with supported modifiers try expect(@call(.auto, foo, .{2}) == 1234); - try expect(@call(.no_async, foo, .{3}) == 1234); + try expect(@call(.no_suspend, foo, .{3}) == 1234); try expect(@call(.always_tail, foo, .{4}) == 1234); try expect(@call(.always_inline, foo, .{5}) == 1234); } @@ -45,7 +45,7 @@ test "basic invocations" { const result = @call(.compile_time, foo, .{6}) == 1234; comptime assert(result); // runtime calls of comptime-known function - try expect(@call(.no_async, foo, .{7}) == 1234); + try expect(@call(.no_suspend, foo, .{7}) == 1234); try expect(@call(.never_tail, foo, .{8}) == 1234); try expect(@call(.never_inline, foo, .{9}) == 1234); // CBE does not support attributes on runtime functions @@ -53,7 +53,7 @@ test "basic invocations" { // runtime calls of non comptime-known function var alias_foo = &foo; _ = &alias_foo; - try expect(@call(.no_async, alias_foo, .{10}) == 1234); + try expect(@call(.no_suspend, alias_foo, .{10}) == 1234); try expect(@call(.never_tail, alias_foo, .{11}) == 1234); try expect(@call(.never_inline, alias_foo, .{12}) == 1234); } @@ -507,29 +507,6 @@ test "call inline fn through pointer" { try f(123); } -test "call coerced function" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - - const T = struct { - x: f64, - const T = @This(); - usingnamespace Implement(1); - const F = fn (comptime f64) type; - const Implement: F = opaque { - fn implementer(comptime val: anytype) type { - return opaque { - fn incr(self: T) T { - return .{ .x = self.x + val }; - } - }; - } - }.implementer; - }; - - const a = T{ .x = 3 }; - try std.testing.expect(a.incr().x == 4); -} - test "call function in comptime field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 6d00f4bba2cf0c76194a5289f2a97d9bd09576f4..70edb900ff73134eff20cca8a66a06c62f5f96fe 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -1032,44 +1032,6 @@ test "function called at runtime is properly analyzed for inferred error set" { }; } -test "generic type constructed from inferred error set of unresolved function" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - const S = struct { - fn write(_: void, bytes: []const u8) !usize { - _ = bytes; - return 0; - } - fn Writer( - comptime Context: type, - comptime WriteError: type, - comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize, - ) type { - return struct { - context: Context, - comptime { - _ = writeFn; - } - }; - } - const T = Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).@"fn".return_type.?).error_union.error_set, write); - fn writer() T { - return .{ .context = {} }; - } - fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) { - return .{ .streams = streams }; - } - fn MultiWriter(comptime Writers: type) type { - return struct { - streams: Writers, - }; - } - }; - _ = S.multiWriter(.{S.writer()}); -} - test "errorCast to adhoc inferred error set" { const S = struct { inline fn baz() !i32 { diff --git a/test/behavior/import.zig b/test/behavior/import.zig index ef3080e7145e4b868a0c8305e4e08c4ce61a9660..faa3a08141558864248a10bff2e5a60f7993fe42 100644 --- a/test/behavior/import.zig +++ b/test/behavior/import.zig @@ -18,16 +18,6 @@ test "importing the same thing gives the same import" { try expect(@import("std") == @import("std")); } -test "import in non-toplevel scope" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - - const S = struct { - usingnamespace @import("import/a_namespace.zig"); - }; - try expect(@as(i32, 1234) == S.foo()); -} - test "import empty file" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 03e757235a0ae828ee9ec6a044d39f3f6c78c501..4a66c56cea2b03ee24daa5d84a68c0def16e3cfd 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -236,17 +236,6 @@ test "call method with mutable reference to struct with no fields" { try expect(s.do()); } -test "usingnamespace within struct scope" { - const S = struct { - usingnamespace struct { - pub fn inner() i32 { - return 42; - } - }; - }; - try expect(@as(i32, 42) == S.inner()); -} - test "struct field init with catch" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 3a0b254aa27cba318d93abceacb98fdcd7155e86..f19d90696be0cfcaed6c71dad13f5776ab9433c7 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -592,24 +592,6 @@ test "StructField.is_comptime" { try expect(info.fields[1].is_comptime); } -test "typeInfo resolves usingnamespace declarations" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const A = struct { - pub const f1 = 42; - }; - - const B = struct { - pub const f0 = 42; - pub usingnamespace A; - }; - - const decls = @typeInfo(B).@"struct".decls; - try expect(decls.len == 2); - try expectEqualStrings(decls[0].name, "f0"); - try expectEqualStrings(decls[1].name, "f1"); -} - test "value from struct @typeInfo default_value_ptr can be loaded at comptime" { comptime { 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" { } } -test "@typeInfo decls and usingnamespace" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const A = struct { - pub const x = 5; - pub const y = 34; - - comptime {} - }; - const B = struct { - pub usingnamespace A; - pub const z = 56; - - test {} - }; - const decls = @typeInfo(B).@"struct".decls; - try expect(decls.len == 3); - try expectEqualStrings(decls[0].name, "z"); - try expectEqualStrings(decls[1].name, "x"); - try expectEqualStrings(decls[2].name, "y"); -} - -test "@typeInfo decls ignore dependency loops" { - const S = struct { - pub fn Def(comptime T: type) type { - std.debug.assert(@typeInfo(T).@"struct".decls.len == 1); - return struct { - const foo = u32; - }; - } - usingnamespace Def(@This()); - }; - _ = S.foo; -} - test "type info of tuple of string literal default value" { const struct_field = @typeInfo(@TypeOf(.{"hi"})).@"struct".fields[0]; const value = struct_field.defaultValue().?; comptime std.debug.assert(value[0] == 'h'); } -test "@typeInfo only contains pub decls" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const other = struct { - const std = @import("std"); - - usingnamespace struct { - pub const inside_non_pub_usingnamespace = 0; - }; - - pub const Enum = enum { - a, - b, - c, - }; - - pub const Struct = struct { - foo: i32, - }; - }; - const ti = @typeInfo(other); - const decls = ti.@"struct".decls; - - try std.testing.expectEqual(2, decls.len); - try std.testing.expectEqualStrings("Enum", decls[0].name); - try std.testing.expectEqualStrings("Struct", decls[1].name); -} - test "@typeInfo function with generic return type and inferred error set" { const S = struct { fn testFn(comptime T: type) !T {} diff --git a/test/behavior/usingnamespace.zig b/test/behavior/usingnamespace.zig deleted file mode 100644 index aa6c5a78f461d708ac012af3b9609cc508b14e4e..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace.zig +++ /dev/null @@ -1,125 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const expect = std.testing.expect; - -const A = struct { - pub const B = bool; -}; - -const C = struct { - usingnamespace A; -}; - -test "basic usingnamespace" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - try std.testing.expect(C.B == bool); -} - -fn Foo(comptime T: type) type { - return struct { - usingnamespace T; - }; -} - -test "usingnamespace inside a generic struct" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const std2 = Foo(std); - const testing2 = Foo(std.testing); - try std2.testing.expect(true); - try testing2.expect(true); -} - -usingnamespace struct { - pub const foo = 42; -}; - -test "usingnamespace does not redeclare an imported variable" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - try comptime std.testing.expect(@This().foo == 42); -} - -usingnamespace @import("usingnamespace/foo.zig"); -test "usingnamespace omits mixing in private functions" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - try expect(@This().privateFunction()); - try expect(!@This().printText()); -} -fn privateFunction() bool { - return true; -} - -test { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - _ = @import("usingnamespace/import_segregation.zig"); -} - -usingnamespace @import("usingnamespace/a.zig"); -test "two files usingnamespace import each other" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - try expect(@This().ok()); -} - -test { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const AA = struct { - x: i32, - fn b(x: i32) @This() { - return .{ .x = x }; - } - fn c() type { - return if (true) struct { - const expected: i32 = 42; - } else struct {}; - } - usingnamespace c(); - }; - const a = AA.b(42); - try expect(a.x == AA.c().expected); -} - -const Bar = struct { - usingnamespace Mixin; -}; - -const Mixin = struct { - pub fn two(self: Bar) void { - _ = self; - } -}; - -test "container member access usingnamespace decls" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - var foo = Bar{}; - foo.two(); -} - -usingnamespace opaque {}; - -usingnamespace @Type(.{ .@"struct" = .{ - .layout = .auto, - .fields = &.{}, - .decls = &.{}, - .is_tuple = false, -} }); diff --git a/test/behavior/usingnamespace/a.zig b/test/behavior/usingnamespace/a.zig deleted file mode 100644 index 21d4278e15eeca5021022c8b2a74de65c028f3d1..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace/a.zig +++ /dev/null @@ -1,7 +0,0 @@ -usingnamespace @import("b.zig"); - -pub const a_text = "OK\n"; - -pub fn ok() bool { - return @import("std").mem.eql(u8, @This().b_text, "OK\n"); -} diff --git a/test/behavior/usingnamespace/b.zig b/test/behavior/usingnamespace/b.zig deleted file mode 100644 index 08dafa3c50159fee9df2d533b139eb8a466a6300..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace/b.zig +++ /dev/null @@ -1,3 +0,0 @@ -usingnamespace @import("a.zig"); - -pub const b_text = @This().a_text; diff --git a/test/behavior/usingnamespace/bar.zig b/test/behavior/usingnamespace/bar.zig deleted file mode 100644 index faa3f27dbe92deae30ebe2d194c278aeda965e62..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace/bar.zig +++ /dev/null @@ -1,8 +0,0 @@ -usingnamespace @import("other.zig"); - -pub var saw_bar_function = false; -pub fn bar_function() void { - if (@This().foo_function()) { - saw_bar_function = true; - } -} diff --git a/test/behavior/usingnamespace/foo.zig b/test/behavior/usingnamespace/foo.zig deleted file mode 100644 index 6b2b14e51ce35fa39486837e853d7a249c8f9a53..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace/foo.zig +++ /dev/null @@ -1,14 +0,0 @@ -// purposefully conflicting function with main source file -// but it's private so it should be OK -fn privateFunction() bool { - return false; -} - -pub fn printText() bool { - return privateFunction(); -} - -pub var saw_foo_function = false; -pub fn foo_function() void { - saw_foo_function = true; -} diff --git a/test/behavior/usingnamespace/import_segregation.zig b/test/behavior/usingnamespace/import_segregation.zig deleted file mode 100644 index f06a5bb4f6d809348248b623191416ea34cd4b4a..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace/import_segregation.zig +++ /dev/null @@ -1,20 +0,0 @@ -const expect = @import("std").testing.expect; -const builtin = @import("builtin"); - -usingnamespace @import("foo.zig"); -usingnamespace @import("bar.zig"); - -test "no clobbering happened" { - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { - // https://github.com/ziglang/zig/issues/16846 - return error.SkipZigTest; - } - - @This().foo_function(); - @This().bar_function(); - try expect(@This().saw_foo_function); - try expect(@This().saw_bar_function); -} diff --git a/test/behavior/usingnamespace/other.zig b/test/behavior/usingnamespace/other.zig deleted file mode 100644 index 02e0f39e90108e9c856b7edb2a6f25124d05ecbf..0000000000000000000000000000000000000000 --- a/test/behavior/usingnamespace/other.zig +++ /dev/null @@ -1,4 +0,0 @@ -pub fn foo_function() bool { - // this one conflicts with the one from foo - return true; -} diff --git a/test/cases/compile_errors/async/async_function_depends_on_its_own_frame.zig b/test/cases/compile_errors/async/async_function_depends_on_its_own_frame.zig deleted file mode 100644 index a1bd3c01e73c07581ba5f483fac8c986514083d3..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/async_function_depends_on_its_own_frame.zig +++ /dev/null @@ -1,13 +0,0 @@ -export fn entry() void { - _ = async amain(); -} -fn amain() callconv(.@"async") void { - var x: [@sizeOf(@Frame(amain))]u8 = undefined; - _ = &x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet diff --git a/test/cases/compile_errors/async/async_function_indirectly_depends_on_its_own_frame.zig b/test/cases/compile_errors/async/async_function_indirectly_depends_on_its_own_frame.zig deleted file mode 100644 index b4944eefc308630a5fb951947eb0f995a9bcf3e6..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/async_function_indirectly_depends_on_its_own_frame.zig +++ /dev/null @@ -1,17 +0,0 @@ -export fn entry() void { - _ = async amain(); -} -fn amain() callconv(.@"async") void { - other(); -} -fn other() void { - var x: [@sizeOf(@Frame(amain))]u8 = undefined; - _ = &x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:4:1: error: unable to determine async function frame of 'amain' -// tmp.zig:5:10: note: analysis of function 'other' depends on the frame diff --git a/test/cases/compile_errors/async/const_frame_cast_to_anyframe.zig b/test/cases/compile_errors/async/const_frame_cast_to_anyframe.zig deleted file mode 100644 index 52b866afcffe2a820fd23bacf90c4c916e6c6864..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/const_frame_cast_to_anyframe.zig +++ /dev/null @@ -1,19 +0,0 @@ -export fn a() void { - const f = async func(); - resume f; -} -export fn b() void { - const f = async func(); - var x: anyframe = &f; - _ = &x; -} -fn func() void { - suspend {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)' -// tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)' diff --git a/test/cases/compile_errors/async/function_with_ccc_indirectly_calling_async_function.zig b/test/cases/compile_errors/async/function_with_ccc_indirectly_calling_async_function.zig deleted file mode 100644 index 9fadb992b4c68407ab3c65c208d968b29123bf7a..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/function_with_ccc_indirectly_calling_async_function.zig +++ /dev/null @@ -1,18 +0,0 @@ -export fn entry() void { - foo(); -} -fn foo() void { - bar(); -} -fn bar() void { - suspend {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:1: error: function with calling convention 'C' cannot be async -// tmp.zig:2:8: note: async function call here -// tmp.zig:5:8: note: async function call here -// tmp.zig:8:5: note: suspends here diff --git a/test/cases/compile_errors/async/indirect_recursion_of_async_functions_detected.zig b/test/cases/compile_errors/async/indirect_recursion_of_async_functions_detected.zig deleted file mode 100644 index 804baed45fbdc72cbdae4df649a7d29401868296..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/indirect_recursion_of_async_functions_detected.zig +++ /dev/null @@ -1,36 +0,0 @@ -var frame: ?anyframe = null; - -export fn a() void { - _ = async rangeSum(10); - while (frame) |f| resume f; -} - -fn rangeSum(x: i32) i32 { - suspend { - frame = @frame(); - } - frame = null; - - if (x == 0) return 0; - const child = rangeSumIndirect(x - 1); - return child + 1; -} - -fn rangeSumIndirect(x: i32) i32 { - suspend { - frame = @frame(); - } - frame = null; - - if (x == 0) return 0; - const child = rangeSum(x - 1); - return child + 1; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself -// tmp.zig:15:35: note: when analyzing type '@Frame(rangeSum)' here -// tmp.zig:28:25: note: when analyzing type '@Frame(rangeSumIndirect)' here diff --git a/test/cases/compile_errors/async/invalid_suspend_in_exported_function.zig b/test/cases/compile_errors/async/invalid_suspend_in_exported_function.zig deleted file mode 100644 index fb3488ce9500d7df4fde80712f53a7587d7e98e0..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/invalid_suspend_in_exported_function.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - var frame = async func(); - var result = await frame; - _ = &result; -} -fn func() void { - suspend {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:1: error: function with calling convention 'C' cannot be async -// tmp.zig:3:18: note: await here is a suspend point diff --git a/test/cases/compile_errors/async/returning_error_from_void_async_function.zig b/test/cases/compile_errors/async/returning_error_from_void_async_function.zig deleted file mode 100644 index 97e29accd268257f7fa39feac53800755464d511..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/returning_error_from_void_async_function.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn entry() void { - _ = async amain(); -} -fn amain() callconv(.@"async") void { - return error.ShouldBeCompileError; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}' diff --git a/test/cases/compile_errors/async/runtime-known_async_function_called.zig b/test/cases/compile_errors/async/runtime-known_async_function_called.zig deleted file mode 100644 index b3e7fabceacc5a74d66503a8df962e264dea53a2..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/runtime-known_async_function_called.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - _ = async amain(); -} -fn amain() void { - var ptr = afunc; - _ = ptr(); - _ = &ptr; -} -fn afunc() callconv(.@"async") void {} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:12: error: function is not comptime-known; @asyncCall required diff --git a/test/cases/compile_errors/async/runtime-known_function_called_with_async_keyword.zig b/test/cases/compile_errors/async/runtime-known_function_called_with_async_keyword.zig deleted file mode 100644 index 082251c137efc2aa343ab7f109ee0cdc95ebafaa..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/runtime-known_function_called_with_async_keyword.zig +++ /dev/null @@ -1,13 +0,0 @@ -export fn entry() void { - var ptr = afunc; - _ = async ptr(); - _ = &ptr; -} - -fn afunc() callconv(.@"async") void {} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:15: error: function is not comptime-known; @asyncCall required diff --git a/test/cases/compile_errors/async/wrong_frame_type_used_for_async_call.zig b/test/cases/compile_errors/async/wrong_frame_type_used_for_async_call.zig deleted file mode 100644 index c02c20f495d8760272493a144f2054264cf474f6..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/wrong_frame_type_used_for_async_call.zig +++ /dev/null @@ -1,16 +0,0 @@ -export fn entry() void { - var frame: @Frame(foo) = undefined; - frame = async bar(); -} -fn foo() void { - suspend {} -} -fn bar() void { - suspend {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)' diff --git a/test/cases/compile_errors/async/wrong_type_for_result_ptr_to_asyncCall.zig b/test/cases/compile_errors/async/wrong_type_for_result_ptr_to_asyncCall.zig deleted file mode 100644 index b4193d4de11d50385900de8afa2b18c4ee2d3f74..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/async/wrong_type_for_result_ptr_to_asyncCall.zig +++ /dev/null @@ -1,16 +0,0 @@ -export fn entry() void { - _ = async amain(); -} -fn amain() i32 { - var frame: @Frame(foo) = undefined; - return await @asyncCall(&frame, false, foo, .{}); -} -fn foo() i32 { - return 1234; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:37: error: expected type '*i32', found 'bool' diff --git a/test/cases/compile_errors/bad_usingnamespace_transitive_failure.zig b/test/cases/compile_errors/bad_usingnamespace_transitive_failure.zig deleted file mode 100644 index f48863091b34f67eec1fcfdc62c319e5dcda4274..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/bad_usingnamespace_transitive_failure.zig +++ /dev/null @@ -1,31 +0,0 @@ -//! The full test name would be: -//! struct field type resolution marks transitive error from bad usingnamespace in @typeInfo call from non-initial field type -//! -//! This test is rather esoteric. It's ensuring that errors triggered by `@typeInfo` analyzing -//! a bad `usingnamespace` correctly trigger transitive errors when analyzed by struct field type -//! resolution, meaning we don't incorrectly analyze code past the uses of `S`. - -const S = struct { - ok: u32, - bad: @typeInfo(T), -}; - -const T = struct { - pub usingnamespace @compileError("usingnamespace analyzed"); -}; - -comptime { - const a: S = .{ .ok = 123, .bad = undefined }; - _ = a; - @compileError("should not be reached"); -} - -comptime { - const b: S = .{ .ok = 123, .bad = undefined }; - _ = b; - @compileError("should not be reached"); -} - -// error -// -// :14:24: error: usingnamespace analyzed diff --git a/test/cases/compile_errors/combination_of_nosuspend_and_async.zig b/test/cases/compile_errors/combination_of_nosuspend_and_async.zig deleted file mode 100644 index dd853432b6e8f0c862ee1ee3e6de37464244edd7..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/combination_of_nosuspend_and_async.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - nosuspend { - const bar = async foo(); - suspend {} - resume bar; - } -} -fn foo() void {} - -// error -// backend=stage2 -// target=native -// -// :4:9: error: suspend inside nosuspend block -// :2:5: note: nosuspend block here diff --git a/test/cases/compile_errors/suspend_inside_suspend_block.zig b/test/cases/compile_errors/suspend_inside_suspend_block.zig deleted file mode 100644 index 5963f1c0ec9526e9a0dae559f8dd6152624eda7b..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/suspend_inside_suspend_block.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - _ = async foo(); -} -fn foo() void { - suspend { - suspend {} - } -} - -// error -// backend=stage2 -// target=native -// -// :6:9: error: cannot suspend inside suspend block -// :5:5: note: other suspend block here diff --git a/test/cases/compile_errors/usingnamespace_with_wrong_type.zig b/test/cases/compile_errors/usingnamespace_with_wrong_type.zig deleted file mode 100644 index 294fd8c012843109463a9c218cc38aec71bc751e..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/usingnamespace_with_wrong_type.zig +++ /dev/null @@ -1,7 +0,0 @@ -usingnamespace void; - -// error -// backend=stage2 -// target=native -// -// :1:16: error: type void has no namespace diff --git a/test/cases/safety/@asyncCall with too small a frame.zig b/test/cases/safety/@asyncCall with too small a frame.zig deleted file mode 100644 index 00fbcef537bf2178a1cd1488d47daa03085fee03..0000000000000000000000000000000000000000 --- a/test/cases/safety/@asyncCall with too small a frame.zig +++ /dev/null @@ -1,26 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -pub fn main() !void { - if (builtin.zig_backend == .stage1 and builtin.os.tag == .wasi) { - // TODO file a bug for this failure - std.process.exit(0); // skip the test - } - var bytes: [1]u8 align(16) = undefined; - var ptr = other; - _ = &ptr; - var frame = @asyncCall(&bytes, {}, ptr, .{}); - _ = &frame; - return error.TestFailed; -} -fn other() callconv(.@"async") void { - suspend {} -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/awaiting twice.zig b/test/cases/safety/awaiting twice.zig deleted file mode 100644 index 6767ad4e767dba58c7b3c4292f2d936c5826c833..0000000000000000000000000000000000000000 --- a/test/cases/safety/awaiting twice.zig +++ /dev/null @@ -1,29 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -var frame: anyframe = undefined; - -pub fn main() !void { - _ = async amain(); - resume frame; - return error.TestFailed; -} - -fn amain() void { - var f = async func(); - await f; - await f; -} - -fn func() void { - suspend { - frame = @frame(); - } -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/error return trace across suspend points.zig b/test/cases/safety/error return trace across suspend points.zig deleted file mode 100644 index efd611662a4f8c6372590676df2e030ace255fa3..0000000000000000000000000000000000000000 --- a/test/cases/safety/error return trace across suspend points.zig +++ /dev/null @@ -1,38 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} - -var failing_frame: @Frame(failing) = undefined; - -pub fn main() !void { - const p = nonFailing(); - resume p; - const p2 = async printTrace(p); - _ = p2; - return error.TestFailed; -} - -fn nonFailing() anyframe->anyerror!void { - failing_frame = async failing(); - return &failing_frame; -} - -fn failing() anyerror!void { - suspend {} - return second(); -} - -fn second() callconv(.@"async") anyerror!void { - return error.Fail; -} - -fn printTrace(p: anyframe->anyerror!void) void { - (await p) catch unreachable; -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/invalid resume of async function.zig b/test/cases/safety/invalid resume of async function.zig deleted file mode 100644 index c58f13b99d05389a8bf3dd34d98090190593539f..0000000000000000000000000000000000000000 --- a/test/cases/safety/invalid resume of async function.zig +++ /dev/null @@ -1,19 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -pub fn main() !void { - var p = async suspendOnce(); - resume p; //ok - resume p; //bad - return error.TestFailed; -} -fn suspendOnce() void { - suspend {} -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/resuming a function which is awaiting a call.zig b/test/cases/safety/resuming a function which is awaiting a call.zig deleted file mode 100644 index 47545584eaf19b95d65c209462fc9fde2896bd3b..0000000000000000000000000000000000000000 --- a/test/cases/safety/resuming a function which is awaiting a call.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -pub fn main() !void { - var frame = async first(); - resume frame; - return error.TestFailed; -} -fn first() void { - other(); -} -fn other() void { - suspend {} -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/resuming a function which is awaiting a frame.zig b/test/cases/safety/resuming a function which is awaiting a frame.zig deleted file mode 100644 index 26df1ae900b470e279f6aa35a53d96dab9cee0f6..0000000000000000000000000000000000000000 --- a/test/cases/safety/resuming a function which is awaiting a frame.zig +++ /dev/null @@ -1,22 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -pub fn main() !void { - var frame = async first(); - resume frame; - return error.TestFailed; -} -fn first() void { - var frame = async other(); - await frame; -} -fn other() void { - suspend {} -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig b/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig deleted file mode 100644 index f8bf6d44c08f8551d6ce6311eea2af8ec6f679fa..0000000000000000000000000000000000000000 --- a/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -fn foo() void { - suspend { - global_frame = @frame(); - } - var f = async bar(@frame()); - _ = &f; - std.process.exit(1); -} - -fn bar(frame: anyframe) void { - suspend { - resume frame; - } - std.process.exit(1); -} - -var global_frame: anyframe = undefined; -pub fn main() !void { - _ = async foo(); - resume global_frame; - std.process.exit(1); -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/resuming a non-suspended function which never been suspended.zig b/test/cases/safety/resuming a non-suspended function which never been suspended.zig deleted file mode 100644 index af288ab8ba398d2d603bb4336a81721b8ae7d6c5..0000000000000000000000000000000000000000 --- a/test/cases/safety/resuming a non-suspended function which never been suspended.zig +++ /dev/null @@ -1,27 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = message; - _ = stack_trace; - std.process.exit(0); -} -fn foo() void { - var f = async bar(@frame()); - _ = &f; - std.process.exit(1); -} - -fn bar(frame: anyframe) void { - suspend { - resume frame; - } - std.process.exit(1); -} - -pub fn main() !void { - _ = async foo(); - return error.TestFailed; -} -// run -// backend=stage1 -// target=native diff --git a/test/cases/safety/slice sentinel mismatch - floats.zig b/test/cases/safety/slice sentinel mismatch - floats.zig index 45577acb00d45c57782acc5156b4a7cbde7cfeb5..be63272f0ce04b4aaa3893b037ff33f48618c2c1 100644 --- a/test/cases/safety/slice sentinel mismatch - floats.zig +++ b/test/cases/safety/slice sentinel mismatch - floats.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "sentinel mismatch: expected 1.2e0, found 4e0")) { + if (std.mem.eql(u8, message, "sentinel mismatch: expected 1.2, found 4")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/compare_output.zig b/test/compare_output.zig index 3603163a2d3e56982e4de698c3a1a1a86f1a47b1..283e6d90c91ef72bb3b4b0afcfcb9ce5b5dc061b 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -17,15 +17,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} , "Hello, world!" ++ if (@import("builtin").os.tag == .windows) "\r\n" else "\n"); - cases.add("hello world without libc", - \\const io = @import("std").io; - \\ - \\pub fn main() void { - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable; - \\} - , "Hello, world!\n 12 12 a\n"); - cases.addC("number literals", \\const std = @import("std"); \\const builtin = @import("builtin"); @@ -158,24 +149,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ ); - cases.add("order-independent declarations", - \\const io = @import("std").io; - \\const z = io.stdin_fileno; - \\const x : @TypeOf(y) = 1234; - \\const y : u16 = 5678; - \\pub fn main() void { - \\ var x_local : i32 = print_ok(x); - \\ _ = &x_local; - \\} - \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) { - \\ _ = val; - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print("OK\n", .{}) catch unreachable; - \\ return 0; - \\} - \\const foo : i32 = 0; - , "OK\n"); - cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ @@ -236,267 +209,11 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} , "3.25\n3\n3.00\n-0.40\n"); - cases.add("same named methods in incomplete struct", - \\const io = @import("std").io; - \\ - \\const Foo = struct { - \\ field1: Bar, - \\ - \\ fn method(a: *const Foo) bool { - \\ _ = a; - \\ return true; - \\ } - \\}; - \\ - \\const Bar = struct { - \\ field2: i32, - \\ - \\ fn method(b: *const Bar) bool { - \\ _ = b; - \\ return true; - \\ } - \\}; - \\ - \\pub fn main() void { - \\ const bar = Bar {.field2 = 13,}; - \\ const foo = Foo {.field1 = bar,}; - \\ const stdout = io.getStdOut().writer(); - \\ if (!foo.method()) { - \\ stdout.print("BAD\n", .{}) catch unreachable; - \\ } - \\ if (!bar.method()) { - \\ stdout.print("BAD\n", .{}) catch unreachable; - \\ } - \\ stdout.print("OK\n", .{}) catch unreachable; - \\} - , "OK\n"); - - cases.add("defer with only fallthrough", - \\const io = @import("std").io; - \\pub fn main() void { - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print("before\n", .{}) catch unreachable; - \\ defer stdout.print("defer1\n", .{}) catch unreachable; - \\ defer stdout.print("defer2\n", .{}) catch unreachable; - \\ defer stdout.print("defer3\n", .{}) catch unreachable; - \\ stdout.print("after\n", .{}) catch unreachable; - \\} - , "before\nafter\ndefer3\ndefer2\ndefer1\n"); - - cases.add("defer with return", - \\const io = @import("std").io; - \\const os = @import("std").os; - \\pub fn main() void { - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print("before\n", .{}) catch unreachable; - \\ defer stdout.print("defer1\n", .{}) catch unreachable; - \\ defer stdout.print("defer2\n", .{}) catch unreachable; - \\ var gpa: @import("std").heap.GeneralPurposeAllocator(.{}) = .init; - \\ defer _ = gpa.deinit(); - \\ var arena = @import("std").heap.ArenaAllocator.init(gpa.allocator()); - \\ defer arena.deinit(); - \\ var args_it = @import("std").process.argsWithAllocator(arena.allocator()) catch unreachable; - \\ if (args_it.skip() and !args_it.skip()) return; - \\ defer stdout.print("defer3\n", .{}) catch unreachable; - \\ stdout.print("after\n", .{}) catch unreachable; - \\} - , "before\ndefer2\ndefer1\n"); - - cases.add("errdefer and it fails", - \\const io = @import("std").io; - \\pub fn main() void { - \\ do_test() catch return; - \\} - \\fn do_test() !void { - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print("before\n", .{}) catch unreachable; - \\ defer stdout.print("defer1\n", .{}) catch unreachable; - \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; - \\ try its_gonna_fail(); - \\ defer stdout.print("defer3\n", .{}) catch unreachable; - \\ stdout.print("after\n", .{}) catch unreachable; - \\} - \\fn its_gonna_fail() !void { - \\ return error.IToldYouItWouldFail; - \\} - , "before\ndeferErr\ndefer1\n"); - - cases.add("errdefer and it passes", - \\const io = @import("std").io; - \\pub fn main() void { - \\ do_test() catch return; - \\} - \\fn do_test() !void { - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print("before\n", .{}) catch unreachable; - \\ defer stdout.print("defer1\n", .{}) catch unreachable; - \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; - \\ try its_gonna_pass(); - \\ defer stdout.print("defer3\n", .{}) catch unreachable; - \\ stdout.print("after\n", .{}) catch unreachable; - \\} - \\fn its_gonna_pass() anyerror!void { } - , "before\nafter\ndefer3\ndefer1\n"); - - cases.addCase(x: { - var tc = cases.create("@embedFile", - \\const foo_txt = @embedFile("foo.txt"); - \\const io = @import("std").io; - \\ - \\pub fn main() void { - \\ const stdout = io.getStdOut().writer(); - \\ stdout.print(foo_txt, .{}) catch unreachable; - \\} - , "1234\nabcd\n"); - - tc.addSourceFile("foo.txt", "1234\nabcd\n"); - - break :x tc; - }); - - cases.addCase(x: { - var tc = cases.create("parsing args", - \\const std = @import("std"); - \\const io = std.io; - \\const os = std.os; - \\ - \\pub fn main() !void { - \\ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - \\ defer _ = gpa.deinit(); - \\ var arena = std.heap.ArenaAllocator.init(gpa.allocator()); - \\ defer arena.deinit(); - \\ var args_it = try std.process.argsWithAllocator(arena.allocator()); - \\ const stdout = io.getStdOut().writer(); - \\ var index: usize = 0; - \\ _ = args_it.skip(); - \\ while (args_it.next()) |arg| : (index += 1) { - \\ try stdout.print("{}: {s}\n", .{index, arg}); - \\ } - \\} - , - \\0: first arg - \\1: 'a' 'b' \ - \\2: bare - \\3: ba""re - \\4: " - \\5: last arg - \\ - ); - - tc.setCommandLineArgs(&[_][]const u8{ - "first arg", - "'a' 'b' \\", - "bare", - "ba\"\"re", - "\"", - "last arg", - }); - - break :x tc; - }); - - cases.addCase(x: { - var tc = cases.create("parsing args new API", - \\const std = @import("std"); - \\const io = std.io; - \\const os = std.os; - \\ - \\pub fn main() !void { - \\ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - \\ defer _ = gpa.deinit(); - \\ var arena = std.heap.ArenaAllocator.init(gpa.allocator()); - \\ defer arena.deinit(); - \\ var args_it = try std.process.argsWithAllocator(arena.allocator()); - \\ const stdout = io.getStdOut().writer(); - \\ var index: usize = 0; - \\ _ = args_it.skip(); - \\ while (args_it.next()) |arg| : (index += 1) { - \\ try stdout.print("{}: {s}\n", .{index, arg}); - \\ } - \\} - , - \\0: first arg - \\1: 'a' 'b' \ - \\2: bare - \\3: ba""re - \\4: " - \\5: last arg - \\ - ); - - tc.setCommandLineArgs(&[_][]const u8{ - "first arg", - "'a' 'b' \\", - "bare", - "ba\"\"re", - "\"", - "last arg", - }); - - break :x tc; - }); - - // It is required to override the log function in order to print to stdout instead of stderr - cases.add("std.log per scope log level override", - \\const std = @import("std"); - \\ - \\pub const std_options: std.Options = .{ - \\ .log_level = .debug, - \\ - \\ .log_scope_levels = &.{ - \\ .{ .scope = .a, .level = .warn }, - \\ .{ .scope = .c, .level = .err }, - \\ }, - \\ .logFn = log, - \\}; - \\ - \\const loga = std.log.scoped(.a); - \\const logb = std.log.scoped(.b); - \\const logc = std.log.scoped(.c); - \\ - \\pub fn main() !void { - \\ loga.debug("", .{}); - \\ logb.debug("", .{}); - \\ logc.debug("", .{}); - \\ - \\ loga.info("", .{}); - \\ logb.info("", .{}); - \\ logc.info("", .{}); - \\ - \\ loga.warn("", .{}); - \\ logb.warn("", .{}); - \\ logc.warn("", .{}); - \\ - \\ loga.err("", .{}); - \\ logb.err("", .{}); - \\ logc.err("", .{}); - \\} - \\pub fn log( - \\ comptime level: std.log.Level, - \\ comptime scope: @TypeOf(.EnumLiteral), - \\ comptime format: []const u8, - \\ args: anytype, - \\) void { - \\ const level_txt = comptime level.asText(); - \\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "):"; - \\ const stdout = std.io.getStdOut().writer(); - \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; - \\} - , - \\debug(b): - \\info(b): - \\warning(a): - \\warning(b): - \\error(a): - \\error(b): - \\error(c): - \\ - ); - - cases.add("valid carriage return example", "const io = @import(\"std\").io;\r\n" ++ // Testing CRLF line endings are valid + cases.add("valid carriage return example", "const std = @import(\"std\");\r\n" ++ // Testing CRLF line endings are valid "\r\n" ++ "pub \r fn main() void {\r\n" ++ // Testing isolated carriage return as whitespace is valid - " const stdout = io.getStdOut().writer();\r\n" ++ + " var file_writer = std.fs.File.stdout().writerStreaming(&.{});\r\n" ++ + " const stdout = &file_writer.interface;\r\n" ++ " stdout.print(\\\\A Multiline\r\n" ++ // testing CRLF at end of multiline string line is valid and normalises to \n in the output " \\\\String\r\n" ++ " , .{}) catch unreachable;\r\n" ++ diff --git a/test/incremental/add_decl b/test/incremental/add_decl index 87f33b1c51839963e2a48c78b4a63f5285383753..39a25e72de9c0b0af5a2c896445b5bc71264bc8a 100644 --- a/test/incremental/add_decl +++ b/test/incremental/add_decl @@ -6,7 +6,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(foo); + try std.fs.File.stdout().writeAll(foo); } const foo = "good morning\n"; #expect_stdout="good morning\n" @@ -15,7 +15,7 @@ const foo = "good morning\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(foo); + try std.fs.File.stdout().writeAll(foo); } const foo = "good morning\n"; const bar = "good evening\n"; @@ -25,7 +25,7 @@ const bar = "good evening\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(bar); + try std.fs.File.stdout().writeAll(bar); } const foo = "good morning\n"; const bar = "good evening\n"; @@ -35,17 +35,17 @@ const bar = "good evening\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(qux); + try std.fs.File.stdout().writeAll(qux); } const foo = "good morning\n"; const bar = "good evening\n"; -#expect_error=main.zig:3:37: error: use of undeclared identifier 'qux' +#expect_error=main.zig:3:39: error: use of undeclared identifier 'qux' #update=add missing declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(qux); + try std.fs.File.stdout().writeAll(qux); } const foo = "good morning\n"; const bar = "good evening\n"; @@ -56,7 +56,7 @@ const qux = "good night\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(qux); + try std.fs.File.stdout().writeAll(qux); } const qux = "good night\n"; #expect_stdout="good night\n" diff --git a/test/incremental/add_decl_namespaced b/test/incremental/add_decl_namespaced index 84472effb5463af138b076331d1a8cf55facbf2e..7e2fe5742ce9272c26ee0bc73bd22d71d87acb19 100644 --- a/test/incremental/add_decl_namespaced +++ b/test/incremental/add_decl_namespaced @@ -6,7 +6,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@This().foo); + try std.fs.File.stdout().writeAll(@This().foo); } const foo = "good morning\n"; #expect_stdout="good morning\n" @@ -15,7 +15,7 @@ const foo = "good morning\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@This().foo); + try std.fs.File.stdout().writeAll(@This().foo); } const foo = "good morning\n"; const bar = "good evening\n"; @@ -25,7 +25,7 @@ const bar = "good evening\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@This().bar); + try std.fs.File.stdout().writeAll(@This().bar); } const foo = "good morning\n"; const bar = "good evening\n"; @@ -35,18 +35,18 @@ const bar = "good evening\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@This().qux); + try std.fs.File.stdout().writeAll(@This().qux); } const foo = "good morning\n"; const bar = "good evening\n"; -#expect_error=main.zig:3:44: error: root source file struct 'main' has no member named 'qux' +#expect_error=main.zig:3:46: error: root source file struct 'main' has no member named 'qux' #expect_error=main.zig:1:1: note: struct declared here #update=add missing declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@This().qux); + try std.fs.File.stdout().writeAll(@This().qux); } const foo = "good morning\n"; const bar = "good evening\n"; @@ -57,7 +57,7 @@ const qux = "good night\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@This().qux); + try std.fs.File.stdout().writeAll(@This().qux); } const qux = "good night\n"; #expect_stdout="good night\n" diff --git a/test/incremental/bad_import b/test/incremental/bad_import index a26ab75423d7bcab6830aabc1cc8d49012dcce14..4e78b7074ad991b421f33ace946ff22463825fe4 100644 --- a/test/incremental/bad_import +++ b/test/incremental/bad_import @@ -7,7 +7,7 @@ #file=main.zig pub fn main() !void { _ = @import("foo.zig"); - try std.io.getStdOut().writeAll("success\n"); + try std.fs.File.stdout().writeAll("success\n"); } const std = @import("std"); #file=foo.zig @@ -29,7 +29,7 @@ comptime { #file=main.zig pub fn main() !void { //_ = @import("foo.zig"); - try std.io.getStdOut().writeAll("success\n"); + try std.fs.File.stdout().writeAll("success\n"); } const std = @import("std"); #expect_stdout="success\n" diff --git a/test/incremental/change_embed_file b/test/incremental/change_embed_file index 171e4d31780cf7c969df8696658c12226c8cf964..7c23b120f11824d1a5297232245bba70c72062ac 100644 --- a/test/incremental/change_embed_file +++ b/test/incremental/change_embed_file @@ -7,7 +7,7 @@ const std = @import("std"); const string = @embedFile("string.txt"); pub fn main() !void { - try std.io.getStdOut().writeAll(string); + try std.fs.File.stdout().writeAll(string); } #file=string.txt Hello, World! @@ -27,7 +27,7 @@ Hello again, World! const std = @import("std"); const string = @embedFile("string.txt"); pub fn main() !void { - try std.io.getStdOut().writeAll("a hardcoded string\n"); + try std.fs.File.stdout().writeAll("a hardcoded string\n"); } #expect_stdout="a hardcoded string\n" @@ -36,7 +36,7 @@ pub fn main() !void { const std = @import("std"); const string = @embedFile("string.txt"); pub fn main() !void { - try std.io.getStdOut().writeAll(string); + try std.fs.File.stdout().writeAll(string); } #expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound diff --git a/test/incremental/change_enum_tag_type b/test/incremental/change_enum_tag_type index d3f6c85c3743c4f14cd077c781b91e2df340bc61..1691764fbcc98e4f082a424f106c4b8e2530cb0d 100644 --- a/test/incremental/change_enum_tag_type +++ b/test/incremental/change_enum_tag_type @@ -14,7 +14,8 @@ const Foo = enum(Tag) { pub fn main() !void { var val: Foo = undefined; val = .a; - try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } const std = @import("std"); #expect_stdout="a\n" @@ -31,7 +32,8 @@ const Foo = enum(Tag) { pub fn main() !void { var val: Foo = undefined; val = .a; - try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } comptime { // 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) { pub fn main() !void { var val: Foo = undefined; val = .a; - try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } const std = @import("std"); #expect_stdout="a\n" diff --git a/test/incremental/change_exports b/test/incremental/change_exports index f0e2ea8d349e106351bfb2816c6fbd9d3492b0b4..e492930031e7fb5d22d68402bc3e59299a084402 100644 --- a/test/incremental/change_exports +++ b/test/incremental/change_exports @@ -16,7 +16,8 @@ pub fn main() !void { extern const bar: u32; }; S.foo(); - try std.io.getStdOut().writer().print("{}\n", .{S.bar}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{}\n", .{S.bar}); } const std = @import("std"); #expect_stdout="123\n" @@ -37,7 +38,8 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other }); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); #expect_error=main.zig:6:5: error: exported symbol collision: foo @@ -59,7 +61,8 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other }); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); #expect_stdout="123 456\n" @@ -83,7 +86,8 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other }); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); #expect_stdout="123 456\n" @@ -128,7 +132,8 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other }); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); #expect_stdout="123 456\n" @@ -152,7 +157,8 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other }); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); #expect_error=main.zig:5:5: error: exported symbol collision: bar diff --git a/test/incremental/change_fn_type b/test/incremental/change_fn_type index 892d0dd9b6b46f160b13a79f21e3e67f762eb681..24392b25f7503f2a1d1b850a71afa95a524fb7f5 100644 --- a/test/incremental/change_fn_type +++ b/test/incremental/change_fn_type @@ -7,7 +7,8 @@ pub fn main() !void { try foo(123); } fn foo(x: u8) !void { - return std.io.getStdOut().writer().print("{d}\n", .{x}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); #expect_stdout="123\n" @@ -18,7 +19,8 @@ pub fn main() !void { try foo(123); } fn foo(x: i64) !void { - return std.io.getStdOut().writer().print("{d}\n", .{x}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); #expect_stdout="123\n" @@ -29,7 +31,8 @@ pub fn main() !void { try foo(-42); } fn foo(x: i64) !void { - return std.io.getStdOut().writer().print("{d}\n", .{x}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); #expect_stdout="-42\n" diff --git a/test/incremental/change_generic_line_number b/test/incremental/change_generic_line_number index bed4372b37126b3cb0c16a984b8134ec0e8c7c4a..c9eb2be929c0ce3231405b1d5a701ca560f8e882 100644 --- a/test/incremental/change_generic_line_number +++ b/test/incremental/change_generic_line_number @@ -6,7 +6,7 @@ const std = @import("std"); fn Printer(message: []const u8) type { return struct { fn print() !void { - try std.io.getStdOut().writeAll(message); + try std.fs.File.stdout().writeAll(message); } }; } @@ -22,7 +22,7 @@ const std = @import("std"); fn Printer(message: []const u8) type { return struct { fn print() !void { - try std.io.getStdOut().writeAll(message); + try std.fs.File.stdout().writeAll(message); } }; } diff --git a/test/incremental/change_line_number b/test/incremental/change_line_number index 887e5ffd21d7027e6030063490c988a7484c46c0..0754d39182dc665519d9c9b3575f5f77de2ef8d3 100644 --- a/test/incremental/change_line_number +++ b/test/incremental/change_line_number @@ -4,7 +4,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("foo\n"); + try std.fs.File.stdout().writeAll("foo\n"); } #expect_stdout="foo\n" #update=change line number @@ -12,6 +12,6 @@ pub fn main() !void { const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("foo\n"); + try std.fs.File.stdout().writeAll("foo\n"); } #expect_stdout="foo\n" diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler index 34a1f32dabe7d4c21baf74aa330321585fac50cc..699134100eec6942bc2414d29ab7188332f3a45a 100644 --- a/test/incremental/change_panic_handler +++ b/test/incremental/change_panic_handler @@ -11,7 +11,8 @@ pub fn main() !u8 { } pub const panic = std.debug.FullPanic(myPanic); fn myPanic(msg: []const u8, _: ?usize) noreturn { - std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); @@ -27,7 +28,8 @@ pub fn main() !u8 { } pub const panic = std.debug.FullPanic(myPanic); fn myPanic(msg: []const u8, _: ?usize) noreturn { - std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); @@ -43,7 +45,8 @@ pub fn main() !u8 { } pub const panic = std.debug.FullPanic(myPanicNew); fn myPanicNew(msg: []const u8, _: ?usize) noreturn { - std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); diff --git a/test/incremental/change_panic_handler_explicit b/test/incremental/change_panic_handler_explicit index ad5d3d124a398e2d0c9f4dbdc72996d6bb2b3529..2d068d593eeb77a07b9c249fc0d7542f1cee4c6c 100644 --- a/test/incremental/change_panic_handler_explicit +++ b/test/incremental/change_panic_handler_explicit @@ -41,7 +41,8 @@ pub const panic = struct { pub const noreturnReturned = no_panic.noreturnReturned; }; fn myPanic(msg: []const u8, _: ?usize) noreturn { - std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); @@ -87,7 +88,8 @@ pub const panic = struct { pub const noreturnReturned = no_panic.noreturnReturned; }; fn myPanic(msg: []const u8, _: ?usize) noreturn { - std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); @@ -133,7 +135,8 @@ pub const panic = struct { pub const noreturnReturned = no_panic.noreturnReturned; }; fn myPanicNew(msg: []const u8, _: ?usize) noreturn { - std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); diff --git a/test/incremental/change_shift_op b/test/incremental/change_shift_op index bface3a3838db8cf6333f6e9278ac48d4e136dd4..ccb904581df9ad2d3807e410e396e566d6a3cc0c 100644 --- a/test/incremental/change_shift_op +++ b/test/incremental/change_shift_op @@ -8,7 +8,8 @@ pub fn main() !void { try foo(0x1300); } fn foo(x: u16) !void { - try std.io.getStdOut().writer().print("0x{x}\n", .{x << 4}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("0x{x}\n", .{x << 4}); } const std = @import("std"); #expect_stdout="0x3000\n" @@ -18,7 +19,8 @@ pub fn main() !void { try foo(0x1300); } fn foo(x: u16) !void { - try std.io.getStdOut().writer().print("0x{x}\n", .{x >> 4}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("0x{x}\n", .{x >> 4}); } const std = @import("std"); #expect_stdout="0x130\n" diff --git a/test/incremental/change_struct_same_fields b/test/incremental/change_struct_same_fields index 60fdcc8944adc82154e9790c0f2971cacb8b2621..97049a1fc03ae2d9dfbdd07e6c06a9e756f133aa 100644 --- a/test/incremental/change_struct_same_fields +++ b/test/incremental/change_struct_same_fields @@ -10,7 +10,8 @@ pub fn main() !void { try foo(&val); } fn foo(val: *const S) !void { - try std.io.getStdOut().writer().print( + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print( "{d} {d}\n", .{ val.x, val.y }, ); @@ -26,7 +27,8 @@ pub fn main() !void { try foo(&val); } fn foo(val: *const S) !void { - try std.io.getStdOut().writer().print( + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print( "{d} {d}\n", .{ val.x, val.y }, ); @@ -42,7 +44,8 @@ pub fn main() !void { try foo(&val); } fn foo(val: *const S) !void { - try std.io.getStdOut().writer().print( + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print( "{d} {d}\n", .{ val.x, val.y }, ); diff --git a/test/incremental/change_zon_file b/test/incremental/change_zon_file index e12f3db0804565069465be60ac745bbdb5293325..247f78828e641222007faeb3a8bd924ddeac1c25 100644 --- a/test/incremental/change_zon_file +++ b/test/incremental/change_zon_file @@ -7,7 +7,7 @@ const std = @import("std"); const message: []const u8 = @import("message.zon"); pub fn main() !void { - try std.io.getStdOut().writeAll(message); + try std.fs.File.stdout().writeAll(message); } #file=message.zon "Hello, World!\n" @@ -28,7 +28,7 @@ pub fn main() !void { const std = @import("std"); const message: []const u8 = @import("message.zon"); pub fn main() !void { - try std.io.getStdOut().writeAll("a hardcoded string\n"); + try std.fs.File.stdout().writeAll("a hardcoded string\n"); } #expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound #expect_error=main.zig:2:37: note: file imported here @@ -43,6 +43,6 @@ pub fn main() !void { const std = @import("std"); const message: []const u8 = @import("message.zon"); pub fn main() !void { - try std.io.getStdOut().writeAll(message); + try std.fs.File.stdout().writeAll(message); } #expect_stdout="We're back, World!\n" diff --git a/test/incremental/change_zon_file_no_result_type b/test/incremental/change_zon_file_no_result_type index 84f4a69bcf3aba1fd2715cb4d8218b124928f0e9..231558e3e9b228e13629947dbac46af51648a31b 100644 --- a/test/incremental/change_zon_file_no_result_type +++ b/test/incremental/change_zon_file_no_result_type @@ -6,7 +6,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(@import("foo.zon").message); + try std.fs.File.stdout().writeAll(@import("foo.zon").message); } #file=foo.zon .{ diff --git a/test/incremental/compile_log b/test/incremental/compile_log index f7fa8ff7e1781a92655b3e22f8c07dbca9590551..de4152456307c60cf28d7a7febdf21431a52b690 100644 --- a/test/incremental/compile_log +++ b/test/incremental/compile_log @@ -7,7 +7,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } #expect_stdout="Hello, World!\n" @@ -15,7 +15,7 @@ pub fn main() !void { #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); @compileLog("this is a log"); } #expect_error=main.zig:4:5: error: found compile log statement @@ -25,6 +25,6 @@ pub fn main() !void { #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } #expect_stdout="Hello, World!\n" diff --git a/test/incremental/fix_astgen_failure b/test/incremental/fix_astgen_failure index 51972e9232baf90877500932b3b3d6e8d088edf2..9c427c7a96d577dcf88528691df85e7936ec54a2 100644 --- a/test/incremental/fix_astgen_failure +++ b/test/incremental/fix_astgen_failure @@ -9,28 +9,28 @@ pub fn main() !void { } #file=foo.zig pub fn hello() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } #expect_error=foo.zig:2:9: error: use of undeclared identifier 'std' #update=fix the error #file=foo.zig const std = @import("std"); pub fn hello() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } #expect_stdout="Hello, World!\n" #update=add new error #file=foo.zig const std = @import("std"); pub fn hello() !void { - try std.io.getStdOut().writeAll(hello_str); + try std.fs.File.stdout().writeAll(hello_str); } -#expect_error=foo.zig:3:37: error: use of undeclared identifier 'hello_str' +#expect_error=foo.zig:3:39: error: use of undeclared identifier 'hello_str' #update=fix the new error #file=foo.zig const std = @import("std"); const hello_str = "Hello, World! Again!\n"; pub fn hello() !void { - try std.io.getStdOut().writeAll(hello_str); + try std.fs.File.stdout().writeAll(hello_str); } #expect_stdout="Hello, World! Again!\n" diff --git a/test/incremental/function_becomes_inline b/test/incremental/function_becomes_inline index 607cd6805eb60121fd5d6ec9f9e4f316636db10b..8f36a31b695838d41be8d11b8481cafc05390528 100644 --- a/test/incremental/function_becomes_inline +++ b/test/incremental/function_becomes_inline @@ -7,7 +7,7 @@ pub fn main() !void { try foo(); } fn foo() !void { - try std.io.getStdOut().writer().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } const std = @import("std"); #expect_stdout="Hello, World!\n" @@ -18,7 +18,7 @@ pub fn main() !void { try foo(); } inline fn foo() !void { - try std.io.getStdOut().writer().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } const std = @import("std"); #expect_stdout="Hello, World!\n" @@ -29,7 +29,7 @@ pub fn main() !void { try foo(); } inline fn foo() !void { - try std.io.getStdOut().writer().writeAll("Hello, `inline` World!\n"); + try std.fs.File.stdout().writeAll("Hello, `inline` World!\n"); } const std = @import("std"); #expect_stdout="Hello, `inline` World!\n" diff --git a/test/incremental/hello b/test/incremental/hello index d1bc8760711c39ab515b97bf4050ffadc75fbbf0..c30cd50c6fdc5e32ad54ada7dc1fa7bee6d86316 100644 --- a/test/incremental/hello +++ b/test/incremental/hello @@ -6,13 +6,13 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("good morning\n"); + try std.fs.File.stdout().writeAll("good morning\n"); } #expect_stdout="good morning\n" #update=change the string #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll("おはようございます\n"); + try std.fs.File.stdout().writeAll("おはようございます\n"); } #expect_stdout="おはようございます\n" diff --git a/test/incremental/make_decl_pub b/test/incremental/make_decl_pub index 89388dca74977d278d36ebc5e209be18dbd3d115..139593b2b0442f0c2a1bbb24ace4a97c15443bda 100644 --- a/test/incremental/make_decl_pub +++ b/test/incremental/make_decl_pub @@ -11,7 +11,7 @@ pub fn main() !void { #file=foo.zig const std = @import("std"); fn hello() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } #expect_error=main.zig:3:12: error: 'hello' is not marked 'pub' #expect_error=foo.zig:2:1: note: declared here @@ -20,6 +20,6 @@ fn hello() !void { #file=foo.zig const std = @import("std"); pub fn hello() !void { - try std.io.getStdOut().writeAll("Hello, World!\n"); + try std.fs.File.stdout().writeAll("Hello, World!\n"); } #expect_stdout="Hello, World!\n" diff --git a/test/incremental/modify_inline_fn b/test/incremental/modify_inline_fn index 726b2ca22ad60c81237aa9d0a96e3d3203429dad..ef31df2d5eb2d8d1ffb768f30d4388d2a63a3f68 100644 --- a/test/incremental/modify_inline_fn +++ b/test/incremental/modify_inline_fn @@ -7,7 +7,7 @@ const std = @import("std"); pub fn main() !void { const str = getStr(); - try std.io.getStdOut().writeAll(str); + try std.fs.File.stdout().writeAll(str); } inline fn getStr() []const u8 { return "foo\n"; @@ -18,7 +18,7 @@ inline fn getStr() []const u8 { const std = @import("std"); pub fn main() !void { const str = getStr(); - try std.io.getStdOut().writeAll(str); + try std.fs.File.stdout().writeAll(str); } inline fn getStr() []const u8 { return "bar\n"; diff --git a/test/incremental/move_src b/test/incremental/move_src index 3e935134305ae3c9e8ef8a46e2a787a70aa3d447..c2ff12761fd30b22513032c7f0903df7558ba8cf 100644 --- a/test/incremental/move_src +++ b/test/incremental/move_src @@ -6,23 +6,9 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() }); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{d} {d}\n", .{ foo(), bar() }); } -fn foo() u32 { - return @src().line; -} -fn bar() u32 { - return 123; -} -#expect_stdout="6 123\n" - -#update=add newline -#file=main.zig -const std = @import("std"); -pub fn main() !void { - try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() }); -} - fn foo() u32 { return @src().line; } @@ -30,3 +16,19 @@ fn bar() u32 { return 123; } #expect_stdout="7 123\n" + +#update=add newline +#file=main.zig +const std = @import("std"); +pub fn main() !void { + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{d} {d}\n", .{ foo(), bar() }); +} + +fn foo() u32 { + return @src().line; +} +fn bar() u32 { + return 123; +} +#expect_stdout="8 123\n" diff --git a/test/incremental/no_change_preserves_tag_names b/test/incremental/no_change_preserves_tag_names index 55219b835611aaf085e95bf56390662a5c5ea273..f7386db2a482a4776dd906fe7d4e9db574087ba5 100644 --- a/test/incremental/no_change_preserves_tag_names +++ b/test/incremental/no_change_preserves_tag_names @@ -7,7 +7,7 @@ const std = @import("std"); var some_enum: enum { first, second } = .first; pub fn main() !void { - try std.io.getStdOut().writeAll(@tagName(some_enum)); + try std.fs.File.stdout().writeAll(@tagName(some_enum)); } #expect_stdout="first" #update=no change @@ -15,6 +15,6 @@ pub fn main() !void { const std = @import("std"); var some_enum: enum { first, second } = .first; pub fn main() !void { - try std.io.getStdOut().writeAll(@tagName(some_enum)); + try std.fs.File.stdout().writeAll(@tagName(some_enum)); } #expect_stdout="first" diff --git a/test/incremental/recursive_function_becomes_non_recursive b/test/incremental/recursive_function_becomes_non_recursive index 2ec483e3e5b4aec6cd5869ddbd5335cff80eccad..9bba6bc038ac72c2f6b16aebd565685b91d6810b 100644 --- a/test/incremental/recursive_function_becomes_non_recursive +++ b/test/incremental/recursive_function_becomes_non_recursive @@ -8,7 +8,7 @@ pub fn main() !void { try foo(false); } fn foo(recurse: bool) !void { - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout(); if (recurse) return foo(true); try stdout.writeAll("non-recursive path\n"); } @@ -21,7 +21,7 @@ pub fn main() !void { try foo(true); } fn foo(recurse: bool) !void { - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout(); if (recurse) return stdout.writeAll("x==1\n"); try stdout.writeAll("non-recursive path\n"); } diff --git a/test/incremental/remove_enum_field b/test/incremental/remove_enum_field index 8d3796b7c34af0dfead84f08b4234dd0182d3403..7623922d3dff892b5d7e9f45e011a7b5291941a2 100644 --- a/test/incremental/remove_enum_field +++ b/test/incremental/remove_enum_field @@ -9,7 +9,8 @@ const MyEnum = enum(u8) { bar = 2, }; pub fn main() !void { - try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); } const std = @import("std"); #expect_stdout="1\n" @@ -20,8 +21,9 @@ const MyEnum = enum(u8) { bar = 2, }; pub fn main() !void { - try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)}); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); } const std = @import("std"); -#expect_error=main.zig:6:73: error: enum 'main.MyEnum' has no member named 'foo' +#expect_error=main.zig:7:69: error: enum 'main.MyEnum' has no member named 'foo' #expect_error=main.zig:1:16: note: enum declared here diff --git a/test/incremental/unreferenced_error b/test/incremental/unreferenced_error index 6025f3fdae9ee4a7d43b829c61bf7e407631a154..51e078a82a72e601f68dbb73b1340203f97e0f82 100644 --- a/test/incremental/unreferenced_error +++ b/test/incremental/unreferenced_error @@ -6,7 +6,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(a); + try std.fs.File.stdout().writeAll(a); } const a = "Hello, World!\n"; #expect_stdout="Hello, World!\n" @@ -15,7 +15,7 @@ const a = "Hello, World!\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(a); + try std.fs.File.stdout().writeAll(a); } const a = @compileError("bad a"); #expect_error=main.zig:5:11: error: bad a @@ -24,7 +24,7 @@ const a = @compileError("bad a"); #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(b); + try std.fs.File.stdout().writeAll(b); } const a = @compileError("bad a"); const b = "Hi there!\n"; @@ -34,7 +34,7 @@ const b = "Hi there!\n"; #file=main.zig const std = @import("std"); pub fn main() !void { - try std.io.getStdOut().writeAll(a); + try std.fs.File.stdout().writeAll(a); } const a = "Back to a\n"; const b = @compileError("bad b"); diff --git a/test/link/bss/main.zig b/test/link/bss/main.zig index aaf865a0c82c062b4ffd3f0d59f8a4c5d562fb54..2785a8360f0b5c9c7cab18d9a18c27d24f4c7ef6 100644 --- a/test/link/bss/main.zig +++ b/test/link/bss/main.zig @@ -4,8 +4,11 @@ const std = @import("std"); var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; pub fn main() anyerror!void { + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + buffer[0x10] = 1; - try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{ + + try stdout_writer.interface.print("{d}, {d}, {d}\n", .{ // workaround the dreaded decl_val (&buffer)[0], (&buffer)[0x10], diff --git a/test/link/elf.zig b/test/link/elf.zig index 14b70441bab56e52c40e1307b1ed120dbc9fd7ef..f6dfbbea86fc3410c405415b340c079002406ec1 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -1315,8 +1315,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\extern var live_var2: i32; \\extern fn live_fn2() void; \\pub fn main() void { - \\ const stdout = std.io.getStdOut(); - \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; + \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); \\ live_fn2(); \\} , @@ -1357,8 +1357,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\extern var live_var2: i32; \\extern fn live_fn2() void; \\pub fn main() void { - \\ const stdout = std.io.getStdOut(); - \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; + \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); \\ live_fn2(); \\} , diff --git a/test/link/macho.zig b/test/link/macho.zig index e5e38f88e085cee8390c4f4abebeaf90468710c2..80d861eea093423ab216ce5962847d7644bdabce 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -710,7 +710,7 @@ fn testHelloZig(b: *Build, opts: Options) *Step { const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = \\const std = @import("std"); \\pub fn main() void { - \\ std.io.getStdOut().writer().print("Hello world!\n", .{}) catch unreachable; + \\ std.fs.File.stdout().writeAll("Hello world!\n") catch @panic("fail"); \\} }); @@ -2365,10 +2365,11 @@ fn testTlsZig(b: *Build, opts: Options) *Step { \\threadlocal var x: i32 = 0; \\threadlocal var y: i32 = -1; \\pub fn main() void { - \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable; + \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable; \\ x -= 1; \\ y += 1; - \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable; + \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable; \\} }); diff --git a/test/link/wasm/extern/main.zig b/test/link/wasm/extern/main.zig index b9fa1226eb7ca6f0c57101461a9d252d0ce370a6..9635f64a4000fd178ad6e0d0edf3901e88510791 100644 --- a/test/link/wasm/extern/main.zig +++ b/test/link/wasm/extern/main.zig @@ -3,6 +3,6 @@ const std = @import("std"); extern const foo: u32; pub fn main() void { - const std_out = std.io.getStdOut(); - std_out.writer().print("Result: {d}", .{foo}) catch {}; + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + stdout_writer.interface.print("Result: {d}", .{foo}) catch {}; } diff --git a/test/src/Cases.zig b/test/src/Cases.zig index d2f784a5b02e71d4d16f1cfc474159a1ee5f85f7..9f6a7fe189e114c25d6078a25156540332fd095b 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -594,30 +594,57 @@ pub fn lowerToTranslateCSteps( }; } +pub const CaseTestOptions = struct { + test_filters: []const []const u8, + test_target_filters: []const []const u8, + skip_non_native: bool, + skip_freebsd: bool, + skip_netbsd: bool, + skip_windows: bool, + skip_macos: bool, + skip_linux: bool, + skip_llvm: bool, + skip_libc: bool, +}; + pub fn lowerToBuildSteps( self: *Cases, b: *std.Build, parent_step: *std.Build.Step, - test_filters: []const []const u8, - test_target_filters: []const []const u8, + options: CaseTestOptions, ) void { const host = std.zig.system.resolveTargetQuery(.{}) catch |err| std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM"); for (self.cases.items) |case| { - for (test_filters) |test_filter| { + for (options.test_filters) |test_filter| { if (std.mem.indexOf(u8, case.name, test_filter)) |_| break; - } else if (test_filters.len > 0) continue; + } else if (options.test_filters.len > 0) continue; + + if (options.skip_non_native and !case.target.query.isNative()) + continue; + + if (options.skip_freebsd and case.target.query.os_tag == .freebsd) continue; + if (options.skip_netbsd and case.target.query.os_tag == .netbsd) continue; + if (options.skip_windows and case.target.query.os_tag == .windows) continue; + if (options.skip_macos and case.target.query.os_tag == .macos) continue; + if (options.skip_linux and case.target.query.os_tag == .linux) continue; + + const would_use_llvm = @import("../tests.zig").wouldUseLlvm(case.backend == .llvm, case.target.query, case.optimize_mode); + if (options.skip_llvm and would_use_llvm) continue; const triple_txt = case.target.query.zigTriple(b.allocator) catch @panic("OOM"); - if (test_target_filters.len > 0) { - for (test_target_filters) |filter| { + if (options.test_target_filters.len > 0) { + for (options.test_target_filters) |filter| { if (std.mem.indexOf(u8, triple_txt, filter) != null) break; } else continue; } + if (options.skip_libc and case.link_libc) + continue; + const writefiles = b.addWriteFiles(); var file_sources = std.StringHashMap(std.Build.LazyPath).init(b.allocator); defer file_sources.deinit(); diff --git a/test/src/check-stack-trace.zig b/test/src/check-stack-trace.zig index 43800086afd324eb99c6c6dc03e1ff8d7ac94282..30f3dd9789ba4b7a037b24839c06c25422461301 100644 --- a/test/src/check-stack-trace.zig +++ b/test/src/check-stack-trace.zig @@ -84,5 +84,5 @@ pub fn main() !void { break :got_result try buf.toOwnedSlice(); }; - try std.io.getStdOut().writeAll(got); + try std.fs.File.stdout().writeAll(got); } diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 635d36d51044ab4dedcb13de437ece69774ac970..a62ba23c5ab60285905405fdb14cecaf5cfcc227 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -48,9 +48,6 @@ .pkg_import = .{ .path = "pkg_import", }, - .use_alias = .{ - .path = "use_alias", - }, .install_raw_hex = .{ .path = "install_raw_hex", }, diff --git a/test/standalone/child_process/child.zig b/test/standalone/child_process/child.zig index e9edcf9f4b8ee7d355dd9a5ace8fdf5c9ff91b8b..b02bec350007cc87f0c484e7704fe0308125aba6 100644 --- a/test/standalone/child_process/child.zig +++ b/test/standalone/child_process/child.zig @@ -27,12 +27,12 @@ fn run(allocator: std.mem.Allocator) !void { } // test stdout pipe; parent verifies - try std.io.getStdOut().writer().writeAll("hello from stdout"); + try std.fs.File.stdout().writeAll("hello from stdout"); // test stdin pipe from parent const hello_stdin = "hello from stdin"; var buf: [hello_stdin.len]u8 = undefined; - const stdin = std.io.getStdIn().reader(); + const stdin: std.fs.File = .stdin(); const n = try stdin.readAll(&buf); if (!std.mem.eql(u8, buf[0..n], hello_stdin)) { testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin }); @@ -40,7 +40,8 @@ fn run(allocator: std.mem.Allocator) !void { } fn testError(comptime fmt: []const u8, args: anytype) void { - const stderr = std.io.getStdErr().writer(); + var stderr_writer = std.fs.File.stderr().writer(&.{}); + const stderr = &stderr_writer.interface; stderr.print("CHILD TEST ERROR: ", .{}) catch {}; stderr.print(fmt, args) catch {}; if (fmt[fmt.len - 1] != '\n') { diff --git a/test/standalone/child_process/main.zig b/test/standalone/child_process/main.zig index 068ff8d1a9a59e98484971b60fdc0e3a4a48f823..6537f90acf1825df70fd16727a77819ed9d0a10e 100644 --- a/test/standalone/child_process/main.zig +++ b/test/standalone/child_process/main.zig @@ -19,13 +19,13 @@ pub fn main() !void { child.stderr_behavior = .Inherit; try child.spawn(); const child_stdin = child.stdin.?; - try child_stdin.writer().writeAll("hello from stdin"); // verified in child + try child_stdin.writeAll("hello from stdin"); // verified in child child_stdin.close(); child.stdin = null; const hello_stdout = "hello from stdout"; var buf: [hello_stdout.len]u8 = undefined; - const n = try child.stdout.?.reader().readAll(&buf); + const n = try child.stdout.?.deprecatedReader().readAll(&buf); if (!std.mem.eql(u8, buf[0..n], hello_stdout)) { testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout }); } @@ -45,7 +45,8 @@ pub fn main() !void { var parent_test_error = false; fn testError(comptime fmt: []const u8, args: anytype) void { - const stderr = std.io.getStdErr().writer(); + var stderr_writer = std.fs.File.stderr().writer(&.{}); + const stderr = &stderr_writer.interface; stderr.print("PARENT TEST ERROR: ", .{}) catch {}; stderr.print(fmt, args) catch {}; if (fmt[fmt.len - 1] != '\n') { diff --git a/test/standalone/run_output_paths/create_file.zig b/test/standalone/run_output_paths/create_file.zig index 041ebc3e5034d3ff573a47d24a840c920b2995a5..da949d4fc127e58d5c735e26eb9bd9d522dc3105 100644 --- a/test/standalone/run_output_paths/create_file.zig +++ b/test/standalone/run_output_paths/create_file.zig @@ -10,7 +10,7 @@ pub fn main() !void { dir_name, .{}); const file_name = args.next().?; const file = try dir.createFile(file_name, .{}); - try file.writer().print( + try file.deprecatedWriter().print( \\{s} \\{s} \\Hello, world! diff --git a/test/standalone/sigpipe/breakpipe.zig b/test/standalone/sigpipe/breakpipe.zig index 293a6839a1e83b54c11a19d27dddf8c365ec3c77..51f667fc15dd634fba5c3d13ddb3d1125f87c43e 100644 --- a/test/standalone/sigpipe/breakpipe.zig +++ b/test/standalone/sigpipe/breakpipe.zig @@ -10,7 +10,7 @@ pub fn main() !void { std.posix.close(pipe[0]); _ = std.posix.write(pipe[1], "a") catch |err| switch (err) { error.BrokenPipe => { - try std.io.getStdOut().writer().writeAll("BrokenPipe\n"); + try std.fs.File.stdout().writeAll("BrokenPipe\n"); std.posix.exit(123); }, else => |e| return e, diff --git a/test/standalone/simple/brace_expansion.zig b/test/standalone/simple/brace_expansion.zig deleted file mode 100644 index facaf4a7548383afb7dae765b513da3f07f93c4d..0000000000000000000000000000000000000000 --- a/test/standalone/simple/brace_expansion.zig +++ /dev/null @@ -1,292 +0,0 @@ -const std = @import("std"); -const io = std.io; -const mem = std.mem; -const debug = std.debug; -const assert = debug.assert; -const testing = std.testing; -const ArrayList = std.ArrayList; -const maxInt = std.math.maxInt; - -const Token = union(enum) { - Word: []const u8, - OpenBrace, - CloseBrace, - Comma, - Eof, -}; - -var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; -var global_allocator = gpa.allocator(); - -fn tokenize(input: []const u8) !ArrayList(Token) { - const State = enum { - Start, - Word, - }; - - var token_list = ArrayList(Token).init(global_allocator); - errdefer token_list.deinit(); - var tok_begin: usize = undefined; - var state = State.Start; - - for (input, 0..) |b, i| { - switch (state) { - .Start => switch (b) { - 'a'...'z', 'A'...'Z' => { - state = State.Word; - tok_begin = i; - }, - '{' => try token_list.append(Token.OpenBrace), - '}' => try token_list.append(Token.CloseBrace), - ',' => try token_list.append(Token.Comma), - else => return error.InvalidInput, - }, - .Word => switch (b) { - 'a'...'z', 'A'...'Z' => {}, - '{', '}', ',' => { - try token_list.append(Token{ .Word = input[tok_begin..i] }); - switch (b) { - '{' => try token_list.append(Token.OpenBrace), - '}' => try token_list.append(Token.CloseBrace), - ',' => try token_list.append(Token.Comma), - else => unreachable, - } - state = State.Start; - }, - else => return error.InvalidInput, - }, - } - } - switch (state) { - State.Start => {}, - State.Word => try token_list.append(Token{ .Word = input[tok_begin..] }), - } - try token_list.append(Token.Eof); - return token_list; -} - -const Node = union(enum) { - Scalar: []const u8, - List: ArrayList(Node), - Combine: []Node, - - fn deinit(self: Node) void { - switch (self) { - .Scalar => {}, - .Combine => |pair| { - pair[0].deinit(); - pair[1].deinit(); - global_allocator.free(pair); - }, - .List => |list| { - for (list.items) |item| { - item.deinit(); - } - list.deinit(); - }, - } - } -}; - -const ParseError = error{ - InvalidInput, - OutOfMemory, -}; - -fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { - const first_token = tokens.items[token_index.*]; - token_index.* += 1; - - const result_node = switch (first_token) { - .Word => |word| Node{ .Scalar = word }, - .OpenBrace => blk: { - var list = ArrayList(Node).init(global_allocator); - errdefer { - for (list.items) |node| node.deinit(); - list.deinit(); - } - while (true) { - try list.append(try parse(tokens, token_index)); - - const token = tokens.items[token_index.*]; - token_index.* += 1; - - switch (token) { - .CloseBrace => break, - .Comma => continue, - else => return error.InvalidInput, - } - } - break :blk Node{ .List = list }; - }, - else => return error.InvalidInput, - }; - - switch (tokens.items[token_index.*]) { - .Word, .OpenBrace => { - const pair = try global_allocator.alloc(Node, 2); - errdefer global_allocator.free(pair); - pair[0] = result_node; - pair[1] = try parse(tokens, token_index); - return Node{ .Combine = pair }; - }, - else => return result_node, - } -} - -fn expandString(input: []const u8, output: *ArrayList(u8)) !void { - const tokens = try tokenize(input); - defer tokens.deinit(); - if (tokens.items.len == 1) { - return output.resize(0); - } - - var token_index: usize = 0; - const root = try parse(&tokens, &token_index); - defer root.deinit(); - const last_token = tokens.items[token_index]; - switch (last_token) { - Token.Eof => {}, - else => return error.InvalidInput, - } - - var result_list = ArrayList(ArrayList(u8)).init(global_allocator); - defer { - for (result_list.items) |*buf| buf.deinit(); - result_list.deinit(); - } - - try expandNode(root, &result_list); - - try output.resize(0); - for (result_list.items, 0..) |buf, i| { - if (i != 0) { - try output.append(' '); - } - try output.appendSlice(buf.items); - } -} - -const ExpandNodeError = error{OutOfMemory}; - -fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void { - assert(output.items.len == 0); - switch (node) { - .Scalar => |scalar| { - var list = ArrayList(u8).init(global_allocator); - errdefer list.deinit(); - try list.appendSlice(scalar); - try output.append(list); - }, - .Combine => |pair| { - const a_node = pair[0]; - const b_node = pair[1]; - - var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator); - defer { - for (child_list_a.items) |*buf| buf.deinit(); - child_list_a.deinit(); - } - try expandNode(a_node, &child_list_a); - - var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator); - defer { - for (child_list_b.items) |*buf| buf.deinit(); - child_list_b.deinit(); - } - try expandNode(b_node, &child_list_b); - - for (child_list_a.items) |buf_a| { - for (child_list_b.items) |buf_b| { - var combined_buf = ArrayList(u8).init(global_allocator); - errdefer combined_buf.deinit(); - - try combined_buf.appendSlice(buf_a.items); - try combined_buf.appendSlice(buf_b.items); - try output.append(combined_buf); - } - } - }, - .List => |list| { - for (list.items) |child_node| { - var child_list = ArrayList(ArrayList(u8)).init(global_allocator); - errdefer for (child_list.items) |*buf| buf.deinit(); - defer child_list.deinit(); - - try expandNode(child_node, &child_list); - - for (child_list.items) |buf| { - try output.append(buf); - } - } - }, - } -} - -pub fn main() !void { - defer _ = gpa.deinit(); - const stdin_file = io.getStdIn(); - const stdout_file = io.getStdOut(); - - const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize)); - defer global_allocator.free(stdin); - - var result_buf = ArrayList(u8).init(global_allocator); - defer result_buf.deinit(); - - try expandString(stdin, &result_buf); - try stdout_file.writeAll(result_buf.items); -} - -test "invalid inputs" { - global_allocator = std.testing.allocator; - - try expectError("}ABC", error.InvalidInput); - try expectError("{ABC", error.InvalidInput); - try expectError("}{", error.InvalidInput); - try expectError("{}", error.InvalidInput); - try expectError("A,B,C", error.InvalidInput); - try expectError("{A{B,C}", error.InvalidInput); - try expectError("{A,}", error.InvalidInput); - - try expectError("\n", error.InvalidInput); -} - -fn expectError(test_input: []const u8, expected_err: anyerror) !void { - var output_buf = ArrayList(u8).init(global_allocator); - defer output_buf.deinit(); - - try testing.expectError(expected_err, expandString(test_input, &output_buf)); -} - -test "valid inputs" { - global_allocator = std.testing.allocator; - - try expectExpansion("{x,y,z}", "x y z"); - try expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); - try expectExpansion("{A,B{x,y}}", "A Bx By"); - - try expectExpansion("{ABC}", "ABC"); - try expectExpansion("{A,B,C}", "A B C"); - try expectExpansion("ABC", "ABC"); - - try expectExpansion("", ""); - try expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh"); - try expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh"); - try expectExpansion("{A,B}a", "Aa Ba"); - try expectExpansion("{C,{x,y}}", "C x y"); - try expectExpansion("z{C,{x,y}}", "zC zx zy"); - try expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg"); - try expectExpansion("a{x,y}b", "axb ayb"); - try expectExpansion("z{{a,b}}", "za zb"); - try expectExpansion("a{b}", "ab"); -} - -fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void { - var result = ArrayList(u8).init(global_allocator); - defer result.deinit(); - - expandString(test_input, &result) catch unreachable; - - try testing.expectEqualSlices(u8, expected_result, result.items); -} diff --git a/test/standalone/simple/build.zig b/test/standalone/simple/build.zig index ba4464cefcea85c3a7582b1a1f026d4033e16f88..51d9b3a9b1b9fc94aec844efc8fb60ff2bded2d0 100644 --- a/test/standalone/simple/build.zig +++ b/test/standalone/simple/build.zig @@ -50,6 +50,10 @@ pub fn build(b: *std.Build) void { }); if (case.link_libc) exe.root_module.link_libc = true; + if (resolved_target.result.os.tag == .windows) { + exe.root_module.linkSystemLibrary("advapi32", .{}); + } + _ = exe.getEmittedBin(); step.dependOn(&exe.step); @@ -66,6 +70,10 @@ pub fn build(b: *std.Build) void { }); if (case.link_libc) exe.root_module.link_libc = true; + if (resolved_target.result.os.tag == .windows) { + exe.root_module.linkSystemLibrary("advapi32", .{}); + } + const run = b.addRunArtifact(exe); step.dependOn(&run.step); } @@ -101,10 +109,6 @@ const cases = [_]Case{ //.{ // .src_path = "issue_9693/main.zig", //}, - .{ - .src_path = "brace_expansion.zig", - .is_test = true, - }, .{ .src_path = "issue_7030.zig", .target = .{ diff --git a/test/standalone/simple/cat/main.zig b/test/standalone/simple/cat/main.zig index 740e73a33e21e027c5f7a2c87db83547e6cc152b..7bb976e9e7f76184296320bf04a374e9805c6881 100644 --- a/test/standalone/simple/cat/main.zig +++ b/test/standalone/simple/cat/main.zig @@ -1,42 +1,42 @@ const std = @import("std"); const io = std.io; -const process = std.process; const fs = std.fs; const mem = std.mem; const warn = std.log.warn; +const fatal = std.process.fatal; pub fn main() !void { var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena_instance.deinit(); const arena = arena_instance.allocator(); - const args = try process.argsAlloc(arena); + const args = try std.process.argsAlloc(arena); const exe = args[0]; var catted_anything = false; - const stdout_file = io.getStdOut(); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + const stdout = &stdout_writer.interface; + var stdin_reader = std.fs.File.stdin().reader(&.{}); const cwd = fs.cwd(); for (args[1..]) |arg| { if (mem.eql(u8, arg, "-")) { catted_anything = true; - try stdout_file.writeFileAll(io.getStdIn(), .{}); + _ = try stdout.sendFileAll(&stdin_reader, .unlimited); } else if (mem.startsWith(u8, arg, "-")) { return usage(exe); } else { - const file = cwd.openFile(arg, .{}) catch |err| { - warn("Unable to open file: {s}\n", .{@errorName(err)}); - return err; - }; + const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err}); defer file.close(); catted_anything = true; - try stdout_file.writeFileAll(file, .{}); + var file_reader = file.reader(&.{}); + _ = try stdout.sendFileAll(&file_reader, .unlimited); } } if (!catted_anything) { - try stdout_file.writeFileAll(io.getStdIn(), .{}); + _ = try stdout.sendFileAll(&stdin_reader, .unlimited); } } diff --git a/test/standalone/simple/guess_number/main.zig b/test/standalone/simple/guess_number/main.zig index 2c95c8993f5a521fccc4c2a5f4accba0df642ab2..d477de2b7888dd528265f7286709d797ed8861ad 100644 --- a/test/standalone/simple/guess_number/main.zig +++ b/test/standalone/simple/guess_number/main.zig @@ -1,37 +1,35 @@ const builtin = @import("builtin"); const std = @import("std"); -const io = std.io; -const fmt = std.fmt; pub fn main() !void { - const stdout = io.getStdOut().writer(); - const stdin = io.getStdIn(); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + const out = &stdout_writer.interface; + const stdin: std.fs.File = .stdin(); - try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{}); + try out.writeAll("Welcome to the Guess Number Game in Zig.\n"); const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1; while (true) { - try stdout.print("\nGuess a number between 1 and 100: ", .{}); + try out.writeAll("\nGuess a number between 1 and 100: "); var line_buf: [20]u8 = undefined; - const amt = try stdin.read(&line_buf); if (amt == line_buf.len) { - try stdout.print("Input too long.\n", .{}); + try out.writeAll("Input too long.\n"); continue; } const line = std.mem.trimEnd(u8, line_buf[0..amt], "\r\n"); - const guess = fmt.parseUnsigned(u8, line, 10) catch { - try stdout.print("Invalid number.\n", .{}); + const guess = std.fmt.parseUnsigned(u8, line, 10) catch { + try out.writeAll("Invalid number.\n"); continue; }; if (guess > answer) { - try stdout.print("Guess lower.\n", .{}); + try out.writeAll("Guess lower.\n"); } else if (guess < answer) { - try stdout.print("Guess higher.\n", .{}); + try out.writeAll("Guess higher.\n"); } else { - try stdout.print("You win!\n", .{}); + try out.writeAll("You win!\n"); return; } } diff --git a/test/standalone/simple/std_enums_big_enums.zig b/test/standalone/simple/std_enums_big_enums.zig index 1ad24a414713d951089bbbdae9a4def836951139..de6cfe3ec75e1fe2d895bfc6a7780c471633f2f0 100644 --- a/test/standalone/simple/std_enums_big_enums.zig +++ b/test/standalone/simple/std_enums_big_enums.zig @@ -6,6 +6,7 @@ pub fn main() void { const Big = @Type(.{ .@"enum" = .{ .tag_type = u16, .fields = make_fields: { + @setEvalBranchQuota(500000); var fields: [1001]std.builtin.Type.EnumField = undefined; for (&fields, 0..) |*field, i| { field.* = .{ .name = std.fmt.comptimePrint("field_{d}", .{i}), .value = i }; diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index ee69f33d2e14060442269befc0128318f4e8198a..a036a64ab70a631f37f4fff19c2322ef985ba908 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("unwind.zig"), .target = target, .optimize = optimize, - .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null, + .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null, .omit_frame_pointer = false, }), }); @@ -54,7 +54,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("unwind.zig"), .target = target, .optimize = optimize, - .unwind_tables = .@"async", + .unwind_tables = .async, .omit_frame_pointer = true, }), // self-hosted lacks omit_frame_pointer support @@ -101,7 +101,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("shared_lib_unwind.zig"), .target = target, .optimize = optimize, - .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null, + .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null, .omit_frame_pointer = true, }), // zig objcopy doesn't support incremental binaries diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig deleted file mode 100644 index 399a23d924ca43eb56e2e4533d29bde77848d138..0000000000000000000000000000000000000000 --- a/test/standalone/use_alias/build.zig +++ /dev/null @@ -1,17 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - const optimize: std.builtin.OptimizeMode = .Debug; - - const main = b.addTest(.{ .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = b.graph.host, - .optimize = optimize, - }) }); - main.root_module.addIncludePath(b.path(".")); - - test_step.dependOn(&b.addRunArtifact(main).step); -} diff --git a/test/standalone/use_alias/c.zig b/test/standalone/use_alias/c.zig deleted file mode 100644 index 2835a51615cab602363baf3d6fc85291f32128fd..0000000000000000000000000000000000000000 --- a/test/standalone/use_alias/c.zig +++ /dev/null @@ -1 +0,0 @@ -pub usingnamespace @cImport(@cInclude("foo.h")); diff --git a/test/standalone/use_alias/foo.h b/test/standalone/use_alias/foo.h deleted file mode 100644 index 900ab6b51a29e81ec99dbeb8a03925e9723459af..0000000000000000000000000000000000000000 --- a/test/standalone/use_alias/foo.h +++ /dev/null @@ -1,4 +0,0 @@ -struct Foo { - int a; - int b; -}; diff --git a/test/standalone/use_alias/main.zig b/test/standalone/use_alias/main.zig deleted file mode 100644 index 68ef6f6588497413b0f7d539fa03f7dd9ea09779..0000000000000000000000000000000000000000 --- a/test/standalone/use_alias/main.zig +++ /dev/null @@ -1,11 +0,0 @@ -const c = @import("c.zig"); -const expect = @import("std").testing.expect; - -test "symbol exists" { - var foo = c.Foo{ - .a = 1, - .b = 1, - }; - _ = &foo; - try expect(foo.a + foo.b == 2); -} diff --git a/test/standalone/windows_argv/build.zig b/test/standalone/windows_argv/build.zig index df988d2371e0a3e4dd0bca7f5b93090c64f3840e..9ace58088af057f338314178a14d4055ed04cfc1 100644 --- a/test/standalone/windows_argv/build.zig +++ b/test/standalone/windows_argv/build.zig @@ -47,6 +47,8 @@ pub fn build(b: *std.Build) !void { }), }); + fuzz.root_module.linkSystemLibrary("advapi32", .{}); + const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); diff --git a/test/standalone/windows_argv/fuzz.zig b/test/standalone/windows_argv/fuzz.zig index 6d08c1bf84234fb85beb940e148f2aa6a3fd8820..bbe956c36562ab03d5d5c0fcdcb6d34bd461e4b4 100644 --- a/test/standalone/windows_argv/fuzz.zig +++ b/test/standalone/windows_argv/fuzz.zig @@ -58,7 +58,7 @@ pub fn main() !void { std.debug.print(">>> found discrepancy <<<\n", .{}); const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w); defer allocator.free(cmd_line_wtf8); - std.debug.print("\"{}\"\n\n", .{std.zig.fmtEscapes(cmd_line_wtf8)}); + std.debug.print("\"{f}\"\n\n", .{std.zig.fmtString(cmd_line_wtf8)}); errors += 1; } diff --git a/test/standalone/windows_argv/lib.zig b/test/standalone/windows_argv/lib.zig index 074273ae21aa4e3049cad2f31d8f1637820e7be2..d41ad953130985100e3dcdf2e84893e73c459ada 100644 --- a/test/standalone/windows_argv/lib.zig +++ b/test/standalone/windows_argv/lib.zig @@ -27,8 +27,8 @@ fn testArgv(expected_args: []const [*:0]const u16) !void { wtf8_buf.clearRetainingCapacity(); try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(expected_arg)); if (!std.mem.eql(u8, wtf8_buf.items, arg_wtf8)) { - std.debug.print("{}: expected: \"{}\"\n", .{ i, std.zig.fmtEscapes(wtf8_buf.items) }); - std.debug.print("{}: actual: \"{}\"\n", .{ i, std.zig.fmtEscapes(arg_wtf8) }); + std.debug.print("{}: expected: \"{f}\"\n", .{ i, std.zig.fmtString(wtf8_buf.items) }); + std.debug.print("{}: actual: \"{f}\"\n", .{ i, std.zig.fmtString(arg_wtf8) }); eql = false; } } @@ -36,22 +36,22 @@ fn testArgv(expected_args: []const [*:0]const u16) !void { for (expected_args[min_len..], min_len..) |arg, i| { wtf8_buf.clearRetainingCapacity(); try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(arg)); - std.debug.print("{}: expected: \"{}\"\n", .{ i, std.zig.fmtEscapes(wtf8_buf.items) }); + std.debug.print("{}: expected: \"{f}\"\n", .{ i, std.zig.fmtString(wtf8_buf.items) }); } for (args[min_len..], min_len..) |arg, i| { - std.debug.print("{}: actual: \"{}\"\n", .{ i, std.zig.fmtEscapes(arg) }); + std.debug.print("{}: actual: \"{f}\"\n", .{ i, std.zig.fmtString(arg) }); } const peb = std.os.windows.peb(); const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer); wtf8_buf.clearRetainingCapacity(); try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(lpCmdLine)); - std.debug.print("command line: \"{}\"\n", .{std.zig.fmtEscapes(wtf8_buf.items)}); + std.debug.print("command line: \"{f}\"\n", .{std.zig.fmtString(wtf8_buf.items)}); std.debug.print("expected argv:\n", .{}); std.debug.print("&.{{\n", .{}); for (expected_args) |arg| { wtf8_buf.clearRetainingCapacity(); try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(arg)); - std.debug.print(" \"{}\",\n", .{std.zig.fmtEscapes(wtf8_buf.items)}); + std.debug.print(" \"{f}\",\n", .{std.zig.fmtString(wtf8_buf.items)}); } std.debug.print("}}\n", .{}); return error.ArgvMismatch; diff --git a/test/standalone/windows_bat_args/build.zig b/test/standalone/windows_bat_args/build.zig index b91b4fb3bbfd510bf3f89eb9eb0dce16067410f7..e30f5b36c1663f1a2bb16e6586d55e5fecd5934e 100644 --- a/test/standalone/windows_bat_args/build.zig +++ b/test/standalone/windows_bat_args/build.zig @@ -28,6 +28,8 @@ pub fn build(b: *std.Build) !void { }), }); + test_exe.root_module.linkSystemLibrary("advapi32", .{}); + const run = b.addRunArtifact(test_exe); run.addArtifactArg(echo_args); run.expectExitCode(0); @@ -44,6 +46,8 @@ pub fn build(b: *std.Build) !void { }), }); + fuzz.root_module.linkSystemLibrary("advapi32", .{}); + const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); diff --git a/test/standalone/windows_bat_args/echo-args.zig b/test/standalone/windows_bat_args/echo-args.zig index 2552045aed2e2ac9cf672bde6360acc82b24560c..054c4a697544ba2a38ef3f93ac20620a70d33ab2 100644 --- a/test/standalone/windows_bat_args/echo-args.zig +++ b/test/standalone/windows_bat_args/echo-args.zig @@ -5,7 +5,8 @@ pub fn main() !void { defer arena_state.deinit(); const arena = arena_state.allocator(); - const stdout = std.io.getStdOut().writer(); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + const stdout = &stdout_writer.interface; var args = try std.process.argsAlloc(arena); for (args[1..], 1..) |arg, i| { try stdout.writeAll(arg); diff --git a/test/standalone/windows_spawn/build.zig b/test/standalone/windows_spawn/build.zig index 2b967b16d5b3572db345d34ddc538053f2ce0d62..628b1a6900a4d3c6f940dbe9ae4c11256f1f6842 100644 --- a/test/standalone/windows_spawn/build.zig +++ b/test/standalone/windows_spawn/build.zig @@ -28,6 +28,8 @@ pub fn build(b: *std.Build) void { }), }); + main.root_module.linkSystemLibrary("advapi32", .{}); + const run = b.addRunArtifact(main); run.addArtifactArg(hello); run.expectExitCode(0); diff --git a/test/standalone/windows_spawn/hello.zig b/test/standalone/windows_spawn/hello.zig index dcf917c430a4bceebc2cdc0a28a0db26570112c1..fb4a827e2310a1ef2d48bb08e7e92674b8645a7b 100644 --- a/test/standalone/windows_spawn/hello.zig +++ b/test/standalone/windows_spawn/hello.zig @@ -1,6 +1,7 @@ const std = @import("std"); pub fn main() !void { - const stdout = std.io.getStdOut().writer(); + var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + const stdout = &stdout_writer.interface; try stdout.writeAll("hello from exe\n"); } diff --git a/test/tests.zig b/test/tests.zig index c9414081e3ff0bbfa2184b37ea704c99d2b0df62..3693e18d91718b2540b966a072820cf80d4a4814 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -918,14 +918,16 @@ const test_targets = blk: { .link_libc = true, }, - .{ - .target = std.Target.Query.parse(.{ - .arch_os_abi = "riscv64-linux-none", - .cpu_features = "baseline+v+zbb", - }) catch unreachable, - .use_llvm = false, - .use_lld = false, - }, + // TODO implement codegen airFieldParentPtr + // TODO implement airMemmove for riscv64 + //.{ + // .target = std.Target.Query.parse(.{ + // .arch_os_abi = "riscv64-linux-none", + // .cpu_features = "baseline+v+zbb", + // }) catch unreachable, + // .use_llvm = false, + // .use_lld = false, + //}, .{ .target = .{ .cpu_arch = .riscv64, @@ -1480,16 +1482,8 @@ const test_targets = blk: { .target = .{ .cpu_arch = .aarch64, .os_tag = .windows, - .abi = .none, - }, - }, - .{ - .target = .{ - .cpu_arch = .aarch64, - .os_tag = .windows, - .abi = .gnu, + .abi = .msvc, }, - .link_libc = true, }, .{ .target = .{ @@ -1499,37 +1493,36 @@ const test_targets = blk: { }, .link_libc = true, }, - .{ .target = .{ - .cpu_arch = .x86, + .cpu_arch = .aarch64, .os_tag = .windows, - .abi = .none, + .abi = .gnu, }, }, .{ .target = .{ - .cpu_arch = .x86, + .cpu_arch = .aarch64, .os_tag = .windows, .abi = .gnu, }, .link_libc = true, }, + .{ .target = .{ - .cpu_arch = .x86, + .cpu_arch = .thumb, .os_tag = .windows, .abi = .msvc, }, - .link_libc = true, }, - .{ .target = .{ .cpu_arch = .thumb, .os_tag = .windows, - .abi = .none, + .abi = .msvc, }, + .link_libc = true, }, // https://github.com/ziglang/zig/issues/24016 // .{ @@ -1538,22 +1531,52 @@ const test_targets = blk: { // .os_tag = .windows, // .abi = .gnu, // }, + // }, + // .{ + // .target = .{ + // .cpu_arch = .thumb, + // .os_tag = .windows, + // .abi = .gnu, + // }, // .link_libc = true, // }, + + .{ + .target = .{ + .cpu_arch = .x86, + .os_tag = .windows, + .abi = .msvc, + }, + }, .{ .target = .{ - .cpu_arch = .thumb, + .cpu_arch = .x86, .os_tag = .windows, .abi = .msvc, }, .link_libc = true, }, + .{ + .target = .{ + .cpu_arch = .x86, + .os_tag = .windows, + .abi = .gnu, + }, + }, + .{ + .target = .{ + .cpu_arch = .x86, + .os_tag = .windows, + .abi = .gnu, + }, + .link_libc = true, + }, .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, - .abi = .none, + .abi = .msvc, }, .use_llvm = false, .use_lld = false, @@ -1562,17 +1585,16 @@ const test_targets = blk: { .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, - .abi = .gnu, + .abi = .msvc, }, - .use_llvm = false, - .use_lld = false, }, .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, - .abi = .none, + .abi = .msvc, }, + .link_libc = true, }, .{ .target = .{ @@ -1580,13 +1602,21 @@ const test_targets = blk: { .os_tag = .windows, .abi = .gnu, }, - .link_libc = true, + .use_llvm = false, + .use_lld = false, }, .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, - .abi = .msvc, + .abi = .gnu, + }, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, }, .link_libc = true, }, @@ -2280,6 +2310,7 @@ const ModuleTestOptions = struct { desc: []const u8, optimize_modes: []const OptimizeMode, include_paths: []const []const u8, + windows_libs: []const []const u8, skip_single_threaded: bool, skip_non_native: bool, skip_freebsd: bool, @@ -2409,6 +2440,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path)); + if (target.os.tag == .windows) { + for (options.windows_libs) |lib| these_tests.linkSystemLibrary(lib); + } + const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{ options.name, triple_txt, @@ -2517,7 +2552,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { return step; } -fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool { +pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool { if (use_llvm) |x| return x; if (query.ofmt == .c) return false; switch (optimize_mode) { @@ -2629,9 +2664,8 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step { pub fn addCases( b: *std.Build, parent_step: *Step, - test_filters: []const []const u8, - test_target_filters: []const []const u8, target: std.Build.ResolvedTarget, + case_test_options: @import("src/Cases.zig").CaseTestOptions, translate_c_options: @import("src/Cases.zig").TranslateCOptions, build_options: @import("cases.zig").BuildOptions, ) !void { @@ -2646,13 +2680,19 @@ pub fn addCases( cases.addFromDir(dir, b); try @import("cases.zig").addCases(&cases, build_options, b); - cases.lowerToTranslateCSteps(b, parent_step, test_filters, test_target_filters, target, translate_c_options); + cases.lowerToTranslateCSteps( + b, + parent_step, + case_test_options.test_filters, + case_test_options.test_target_filters, + target, + translate_c_options, + ); cases.lowerToBuildSteps( b, parent_step, - test_filters, - test_target_filters, + case_test_options, ); } @@ -2699,6 +2739,10 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { }), }); + if (b.graph.host.result.os.tag == .windows) { + incr_check.root_module.linkSystemLibrary("advapi32", .{}); + } + var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); defer dir.close(); diff --git a/tools/docgen.zig b/tools/docgen.zig index 6b3e898b9f86f85fc283f24d069f5b87c3eab7eb..3f48ba39a80f686b87a5081380b0bb8aa184aca5 100644 --- a/tools/docgen.zig +++ b/tools/docgen.zig @@ -43,8 +43,7 @@ pub fn main() !void { while (args_it.next()) |arg| { if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - const stdout = io.getStdOut().writer(); - try stdout.writeAll(usage); + try fs.File.stdout().writeAll(usage); process.exit(0); } else if (mem.eql(u8, arg, "--code-dir")) { if (args_it.next()) |param| { @@ -76,9 +75,9 @@ pub fn main() !void { var code_dir = try fs.cwd().openDir(code_dir_path, .{}); defer code_dir.close(); - const input_file_bytes = try in_file.reader().readAllAlloc(arena, max_doc_file_size); + const input_file_bytes = try in_file.deprecatedReader().readAllAlloc(arena, max_doc_file_size); - var buffered_writer = io.bufferedWriter(out_file.writer()); + var buffered_writer = io.bufferedWriter(out_file.deprecatedWriter()); var tokenizer = Tokenizer.init(input_path, input_file_bytes); var toc = try genToc(arena, &tokenizer); @@ -426,7 +425,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc { try toc.writeByte('\n'); try toc.writeByteNTimes(' ', header_stack_size * 4); if (last_columns) |n| { - try toc.print("
    \n", .{n}); + try toc.print("
      \n", .{n}); } else { try toc.writeAll("
        \n"); } @@ -710,8 +709,6 @@ fn tokenizeAndPrintRaw( .keyword_align, .keyword_and, .keyword_asm, - .keyword_async, - .keyword_await, .keyword_break, .keyword_catch, .keyword_comptime, @@ -748,7 +745,6 @@ fn tokenizeAndPrintRaw( .keyword_try, .keyword_union, .keyword_unreachable, - .keyword_usingnamespace, .keyword_var, .keyword_volatile, .keyword_allowzero, diff --git a/tools/doctest.zig b/tools/doctest.zig index af5ce9f983aa7b5f6b15ee77e4c25ce2328912b1..8f9d1fe8cf13a503472bbd835d80ea91462174c8 100644 --- a/tools/doctest.zig +++ b/tools/doctest.zig @@ -44,7 +44,7 @@ pub fn main() !void { while (args_it.next()) |arg| { if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try std.io.getStdOut().writeAll(usage); + try std.fs.File.stdout().writeAll(usage); process.exit(0); } else if (mem.eql(u8, arg, "-i")) { opt_input = args_it.next() orelse fatal("expected parameter after -i", .{}); @@ -85,7 +85,7 @@ pub fn main() !void { var out_file = try fs.cwd().createFile(output_path, .{}); defer out_file.close(); - var bw = std.io.bufferedWriter(out_file.writer()); + var bw = std.io.bufferedWriter(out_file.deprecatedWriter()); const out = bw.writer(); 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 { .keyword_align, .keyword_and, .keyword_asm, - .keyword_async, - .keyword_await, .keyword_break, .keyword_catch, .keyword_comptime, @@ -691,7 +689,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void { .keyword_try, .keyword_union, .keyword_unreachable, - .keyword_usingnamespace, .keyword_var, .keyword_volatile, .keyword_allowzero, diff --git a/tools/dump-cov.zig b/tools/dump-cov.zig index 65bd19000dbfd3bdfe52e2f35ced0d208b61609e..7699ac702b2108fedf1ace34b5365f4c8bad08d2 100644 --- a/tools/dump-cov.zig +++ b/tools/dump-cov.zig @@ -48,8 +48,9 @@ pub fn main() !void { fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) }); }; - var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); - const stdout = bw.writer(); + var stdout_buffer: [4000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); + const stdout = &stdout_writer.interface; const header: *SeenPcsHeader = @ptrCast(cov_bytes); try stdout.print("{any}\n", .{header.*}); @@ -83,5 +84,5 @@ pub fn main() !void { }); } - try bw.flush(); + try stdout.flush(); } diff --git a/tools/fetch_them_macos_headers.zig b/tools/fetch_them_macos_headers.zig index 4b6bb87ae088704620b0e9a45e8c601a2a5437ec..b81ee001b5b56117812f63f419c07bb8e1a13b54 100644 --- a/tools/fetch_them_macos_headers.zig +++ b/tools/fetch_them_macos_headers.zig @@ -5,6 +5,8 @@ const mem = std.mem; const process = std.process; const assert = std.debug.assert; const tmpDir = std.testing.tmpDir; +const fatal = std.process.fatal; +const info = std.log.info; const Allocator = mem.Allocator; const OsTag = std.Target.Os.Tag; @@ -245,19 +247,6 @@ const ArgsIterator = struct { } }; -fn info(comptime format: []const u8, args: anytype) void { - const msg = std.fmt.allocPrint(gpa, "info: " ++ format ++ "\n", args) catch return; - std.io.getStdOut().writeAll(msg) catch {}; -} - -fn fatal(comptime format: []const u8, args: anytype) noreturn { - ret: { - const msg = std.fmt.allocPrint(gpa, "fatal: " ++ format ++ "\n", args) catch break :ret; - std.io.getStdErr().writeAll(msg) catch {}; - } - std.process.exit(1); -} - const Version = struct { major: u16, minor: u8, diff --git a/tools/gen_macos_headers_c.zig b/tools/gen_macos_headers_c.zig index 69f7cc33ffc88c849edf95e0723b1cddabba373f..a56194a9a80623f32ac124b00e4a22d2839eb6b3 100644 --- a/tools/gen_macos_headers_c.zig +++ b/tools/gen_macos_headers_c.zig @@ -1,5 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; +const info = std.log.info; +const fatal = std.process.fatal; const Allocator = std.mem.Allocator; @@ -13,19 +15,6 @@ const usage = \\-h, --help Print this help and exit ; -fn info(comptime format: []const u8, args: anytype) void { - const msg = std.fmt.allocPrint(gpa, "info: " ++ format ++ "\n", args) catch return; - std.io.getStdOut().writeAll(msg) catch {}; -} - -fn fatal(comptime format: []const u8, args: anytype) noreturn { - ret: { - const msg = std.fmt.allocPrint(gpa, "fatal: " ++ format ++ "\n", args) catch break :ret; - std.io.getStdErr().writeAll(msg) catch {}; - } - std.process.exit(1); -} - pub fn main() anyerror!void { var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); @@ -58,16 +47,19 @@ pub fn main() anyerror!void { std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan); - const stdout = std.io.getStdOut().writer(); - try stdout.writeAll("#define _XOPEN_SOURCE\n"); + var buffer: [2000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer); + const w = &stdout_writer.interface; + try w.writeAll("#define _XOPEN_SOURCE\n"); for (paths.items) |path| { - try stdout.print("#include <{s}>\n", .{path}); + try w.print("#include <{s}>\n", .{path}); } - try stdout.writeAll( + try w.writeAll( \\int main(int argc, char **argv) { \\ return 0; \\} ); + try w.flush(); } fn findHeaders( diff --git a/tools/gen_outline_atomics.zig b/tools/gen_outline_atomics.zig index 2f989ed1a1043c1ed6c5580454ebde505f9d481f..bcd757978a969e8ed4e95f554c8e5d821c2302c5 100644 --- a/tools/gen_outline_atomics.zig +++ b/tools/gen_outline_atomics.zig @@ -17,8 +17,9 @@ pub fn main() !void { //const args = try std.process.argsAlloc(arena); - var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); - const w = bw.writer(); + var stdout_buffer: [2000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); + const w = &stdout_writer.interface; try w.writeAll( \\//! This file is generated by tools/gen_outline_atomics.zig. @@ -57,7 +58,7 @@ pub fn main() !void { try w.writeAll(footer.items); try w.writeAll("}\n"); - try bw.flush(); + try w.flush(); } fn writeFunction( diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 0ba27c49a30fecc45caa2f9067fd957e346dcf9b..a772d99660af7d0e5aac4c518f63cd4a1ffcd4fe 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -91,9 +91,10 @@ pub fn main() !void { try readExtRegistry(&exts, a, std.fs.cwd(), args[2]); - var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); - try render(bw.writer(), a, core_spec, exts.items); - try bw.flush(); + var buffer: [4000]u8 = undefined; + var w = std.fs.File.stdout().writerStreaming(&buffer); + try render(&w, a, core_spec, exts.items); + try w.flush(); } 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 { } } -fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void { +fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void { try writer.writeAll( \\//! This file is auto-generated by tools/gen_spirv_spec.zig. \\ @@ -188,15 +189,10 @@ fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []c \\ none, \\ _, \\ - \\ pub fn format( - \\ self: IdResult, - \\ comptime _: []const u8, - \\ _: std.fmt.FormatOptions, - \\ writer: anytype, - \\ ) @TypeOf(writer).Error!void { + \\ pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void { \\ switch (self) { \\ .none => try writer.writeAll("(none)"), - \\ else => try writer.print("%{}", .{@intFromEnum(self)}), + \\ else => try writer.print("%{d}", .{@intFromEnum(self)}), \\ } \\ } \\}; @@ -899,7 +895,8 @@ fn parseHexInt(text: []const u8) !u31 { } fn usageAndExit(arg0: []const u8, code: u8) noreturn { - std.io.getStdErr().writer().print( + const stderr = std.debug.lockStderrWriter(&.{}); + stderr.print( \\Usage: {s} \\ \\Generates Zig bindings for SPIR-V specifications found in the SPIRV-Headers diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig index 31095399d3ac3c938fcc649d8cfd6311402e32eb..4978611cc1c7436280ce1349d1afe0f4e7773b9d 100644 --- a/tools/gen_stubs.zig +++ b/tools/gen_stubs.zig @@ -333,7 +333,9 @@ pub fn main() !void { } } - const stdout = std.io.getStdOut().writer(); + var stdout_buffer: [2000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); + const stdout = &stdout_writer.interface; try stdout.writeAll( \\#ifdef PTR64 \\#define WEAK64 .weak @@ -533,6 +535,8 @@ pub fn main() !void { .all => {}, .single, .multi, .family, .time32 => try stdout.writeAll("#endif\n"), } + + try stdout.flush(); } fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: builtin.Endian) !void { diff --git a/tools/generate_JSONTestSuite.zig b/tools/generate_JSONTestSuite.zig index 42dc777e821252d0d9b77cd851df5de4c7ba24ba..56c6bc7261318a2fced5368519764b6631376f89 100644 --- a/tools/generate_JSONTestSuite.zig +++ b/tools/generate_JSONTestSuite.zig @@ -6,7 +6,9 @@ pub fn main() !void { var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; var allocator = gpa.allocator(); - var output = std.io.getStdOut().writer(); + var stdout_buffer: [2000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); + const output = &stdout_writer.interface; try output.writeAll( \\// This file was generated by _generate_JSONTestSuite.zig \\// These test cases are sourced from: https://github.com/nst/JSONTestSuite @@ -44,6 +46,8 @@ pub fn main() !void { try writeString(output, contents); try output.writeAll(");\n}\n"); } + + try output.flush(); } const i_structure_500_nested_arrays = "[" ** 500 ++ "]" ** 500; diff --git a/tools/generate_c_size_and_align_checks.zig b/tools/generate_c_size_and_align_checks.zig index 588deb4935dde7da0c40c923397faebadf3611b2..8c278407e48360def569bbc820495e591bc07588 100644 --- a/tools/generate_c_size_and_align_checks.zig +++ b/tools/generate_c_size_and_align_checks.zig @@ -42,20 +42,23 @@ pub fn main() !void { const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] }); const target = try std.zig.system.resolveTargetQuery(query); - const stdout = std.io.getStdOut().writer(); + var buffer: [2000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer); + const w = &stdout_writer.interface; inline for (@typeInfo(std.Target.CType).@"enum".fields) |field| { const c_type: std.Target.CType = @enumFromInt(field.value); - try stdout.print("_Static_assert(sizeof({0s}) == {1d}, \"sizeof({0s}) == {1d}\");\n", .{ + try w.print("_Static_assert(sizeof({0s}) == {1d}, \"sizeof({0s}) == {1d}\");\n", .{ cName(c_type), target.cTypeByteSize(c_type), }); - try stdout.print("_Static_assert(_Alignof({0s}) == {1d}, \"_Alignof({0s}) == {1d}\");\n", .{ + try w.print("_Static_assert(_Alignof({0s}) == {1d}, \"_Alignof({0s}) == {1d}\");\n", .{ cName(c_type), target.cTypeAlignment(c_type), }); - try stdout.print("_Static_assert(__alignof({0s}) == {1d}, \"__alignof({0s}) == {1d}\");\n\n", .{ + try w.print("_Static_assert(__alignof({0s}) == {1d}, \"__alignof({0s}) == {1d}\");\n\n", .{ cName(c_type), target.cTypePreferredAlignment(c_type), }); } + try w.flush(); } diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index 38726e6501e688f7561dd79db863bfc13944be42..1ee153c40c74c86277d37c9a2ad0c8d5d29ed88a 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -666,13 +666,16 @@ pub fn main() !void { const allocator = arena.allocator(); const args = try std.process.argsAlloc(allocator); - if (args.len < 3 or mem.eql(u8, args[1], "--help")) - usageAndExit(std.io.getStdErr(), args[0], 1); + if (args.len < 3 or mem.eql(u8, args[1], "--help")) { + usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2); + std.process.exit(1); + } const zig_exe = args[1]; const linux_path = args[2]; - var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer()); - const writer = buf_out.writer(); + var stdout_buffer: [2000]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); + const writer = &stdout_writer.interface; var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); defer linux_dir.close(); @@ -714,17 +717,16 @@ pub fn main() !void { } } - try buf_out.flush(); + try writer.flush(); } -fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { - file.writer().print( +fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { + try w.print( \\Usage: {s} /path/to/zig /path/to/linux \\Alternative Usage: zig run /path/to/git/zig/tools/generate_linux_syscalls.zig -- /path/to/zig /path/to/linux \\ \\Generates the list of Linux syscalls for each supported cpu arch, using the Linux development tree. \\Prints to stdout Zig code which you can use to replace the file lib/std/os/linux/syscalls.zig. \\ - , .{arg0}) catch std.process.exit(1); - std.process.exit(code); + , .{arg0}); } diff --git a/tools/lldb_pretty_printers.py b/tools/lldb_pretty_printers.py index 63bcb63111b3da4f89e7195ef91692467cedfbe2..99c1bf1ee599a1408a09c086c3088990d871d1ed 100644 --- a/tools/lldb_pretty_printers.py +++ b/tools/lldb_pretty_printers.py @@ -50,8 +50,6 @@ zig_keywords = { 'anyframe', 'anytype', 'asm', - 'async', - 'await', 'break', 'callconv', 'catch', @@ -88,7 +86,6 @@ zig_keywords = { 'try', 'union', 'unreachable', - 'usingnamespace', 'var', 'volatile', 'while', diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 3c2ef849521ac9bcf4be7c62de6b965d7e27ab30..8bf49ad93a1305e9733a43eee066a7950c99c158 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -634,25 +634,25 @@ pub fn main() anyerror!void { const allocator = arena.allocator(); const args = try std.process.argsAlloc(allocator); - if (args.len <= 1) { - usageAndExit(std.io.getStdErr(), args[0], 1); - } + var stdout_buffer: [4000]u8 = undefined; + var stdout_writer = fs.stdout().writerStreaming(&stdout_buffer); + const stdout = &stdout_writer.interface; + + if (args.len <= 1) printUsageAndExit(args[0]); + if (std.mem.eql(u8, args[1], "--help")) { - usageAndExit(std.io.getStdOut(), args[0], 0); - } - if (args.len < 3) { - usageAndExit(std.io.getStdErr(), args[0], 1); + printUsage(stdout, args[0]) catch std.process.exit(2); + stdout.flush() catch std.process.exit(2); + std.process.exit(0); } + if (args.len < 3) printUsageAndExit(args[0]); + const llvm_tblgen_exe = args[1]; - if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) { - usageAndExit(std.io.getStdErr(), args[0], 1); - } + if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) printUsageAndExit(args[0]); const llvm_src_root = args[2]; - if (std.mem.startsWith(u8, llvm_src_root, "-")) { - usageAndExit(std.io.getStdErr(), args[0], 1); - } + if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]); var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator); @@ -719,8 +719,6 @@ pub fn main() anyerror!void { // "W" and "Wl,". So we sort this list in order of descending priority. std.mem.sort(*json.ObjectMap, all_objects.items, {}, objectLessThan); - var buffered_stdout = std.io.bufferedWriter(std.io.getStdOut().writer()); - const stdout = buffered_stdout.writer(); try stdout.writeAll( \\// This file is generated by tools/update_clang_options.zig. \\// zig fmt: off @@ -815,7 +813,7 @@ pub fn main() anyerror!void { \\ ); - try buffered_stdout.flush(); + try stdout.flush(); } // 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 { return std.mem.lessThan(u8, a_key, b_key); } -fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { - file.writer().print( +fn printUsageAndExit(arg0: []const u8) noreturn { + printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2); + std.process.exit(1); +} + +fn printUsage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { + try w.print( \\Usage: {s} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project \\Alternative Usage: zig run /path/to/git/zig/tools/update_clang_options.zig -- /path/to/llvm-tblgen /path/to/git/llvm/llvm-project \\ \\Prints to stdout Zig code which you can use to replace the file src/clang_options_data.zig. \\ - , .{arg0}) catch std.process.exit(1); - std.process.exit(code); + , .{arg0}); } diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index cb6043f0c870ea8128fa611607e32bf687d86598..b54d74ca1c15d82c0be3bdfd213beb750f01247c 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -2082,8 +2082,8 @@ fn processOneTarget(job: Job) void { } fn usageAndExit(arg0: []const u8, code: u8) noreturn { - const stderr = std.io.getStdErr(); - stderr.writer().print( + const stderr = std.debug.lockStderrWriter(&.{}); + stderr.print( \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig [zig_name filter] \\ \\Updates lib/std/target/.zig from llvm/lib/Target//.td . diff --git a/tools/update_crc_catalog.zig b/tools/update_crc_catalog.zig index 5ccac15112411b2b4a6164da876b0c7527f7d7c8..1ae45cf1bcf7f80529818490dea22075dde2e800 100644 --- a/tools/update_crc_catalog.zig +++ b/tools/update_crc_catalog.zig @@ -11,14 +11,10 @@ pub fn main() anyerror!void { const arena = arena_state.allocator(); const args = try std.process.argsAlloc(arena); - if (args.len <= 1) { - usageAndExit(std.io.getStdErr(), args[0], 1); - } + if (args.len <= 1) printUsageAndExit(args[0]); const zig_src_root = args[1]; - if (mem.startsWith(u8, zig_src_root, "-")) { - usageAndExit(std.io.getStdErr(), args[0], 1); - } + if (mem.startsWith(u8, zig_src_root, "-")) printUsageAndExit(args[0]); var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{}); defer zig_src_dir.close(); @@ -193,10 +189,14 @@ pub fn main() anyerror!void { } } -fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { - file.writer().print( +fn printUsageAndExit(arg0: []const u8) noreturn { + printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2); + std.process.exit(1); +} + +fn printUsage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { + return w.print( \\Usage: {s} /path/git/zig \\ - , .{arg0}) catch std.process.exit(1); - std.process.exit(code); + , .{arg0}); }