diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ec48c4382..6dbdf0c93e 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 fe52457392..d260353c88 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 dfb440573f..335fe60bfb 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 395a6770a5..be5fe39446 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 b468f65493..3352d06a46 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 78f5c32384..dcf13e812d 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:
-
- - LLVM's lack of ability to optimize them.
- - Third-party debuggers' lack of ability to debug them.
- - The cancellation problem.
- - Async function pointers preventing the stack size from being known.
-
- 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
- If a call to {#syntax#}@import{#endsyntax#} is analyzed, the file being imported is analyzed.
- - If a type (including a file) is analyzed, all {#syntax#}comptime{#endsyntax#}, {#syntax#}usingnamespace{#endsyntax#}, and {#syntax#}export{#endsyntax#} declarations within it are analyzed.
+ - If a type (including a file) is analyzed, all {#syntax#}comptime{#endsyntax#} and {#syntax#}export{#endsyntax#} declarations within it are analyzed.
- If a type (including a file) is analyzed, and the compilation is for a {#link|test|Zig Test#}, and the module the type is within is the root module of the compilation, then all {#syntax#}test{#endsyntax#} declarations within it are also analyzed.
- If a reference to a named declaration (i.e. a usage of it) is analyzed, the declaration being referenced is analyzed. Declarations are order-independent, so this reference may be above or below the declaration being referenced, or even in another file entirely.
@@ -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.
-
- - See also {#link|Async Functions#}
-
- |
-
-
-
- {#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.
-
- - See also {#link|Async Functions#}
-
- |
-
{#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.
-
- - See also {#link|usingnamespace#}
-
- |
-
{#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 98fd498451..df38209c49 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 8730e46456..27ea1f689a 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 d3c9d019dd..91c3f3d158 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 82bb81733a..0000000000
--- 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 4dcbf95590..d9c001d925 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 0a65824341..dbf6e29af5 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 c89dafe002..f719e8cc15 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 5ebd89ec3d..ada6298a87 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 e45f6eabc6..c8695edd6b 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 02c9cfc830..183c557976 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 9b4f347de5..ddd66a3c9d 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 e064cfa345..939aede655 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 77e7bf1609..132a07c6c8 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 3e1ce6e2e1..693e9b4c70 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 2f4c26b0cc..0d26b59d24 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 bcd3a69a0c..3e383e6ad4 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 7d0d114841..eb8bf349f5 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 48bd55fd63..bc20cb791c 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 f27775d6af..6d4a368b4e 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 ab4784d9b0..75fe8b0a0e 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 3fd23dc300..864480e7bf 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 cfb75e4c5b..0b276b92f5 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 8344a16b25..903e0a2f71 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 b4fcd53f9d..4e1953233d 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 b69222fe03..840833fbcd 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 ce0d958ebd..baaf361a53 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 d5c93e4c5a..6cf819ccf3 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 434d0c0913..0bb427b651 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 7249685f7b..c20f4bbd90 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 b15ab127c7..0dd19ede3a 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 09b1f816c3..11008d2d61 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 6bfd40b6f0..ce23f63421 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 70f4bd8d82..9afb8debf2 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 c6226f82b5..0000000000
--- 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 6a08d590a1..0000000000
--- 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 50ffdb281b..0000000000
--- 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 8d007e9f84..0000000000
--- 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 14a31cd8c4..0000000000
--- 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 dceec739db..0000000000
--- 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 16aaec48ee..0000000000
--- 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 46ba88db53..2571fb3ded 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 bc29f15ce7..0000000000
--- 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 bc29f15ce7..0000000000
--- 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 4e6804449d..0000000000
--- 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 e1031ef401..0000000000
--- 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 4c0c7f1adb..0000000000
--- 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 ea28ec7ac5..0000000000
--- 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 8a0e16a2f8..0000000000
--- 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 1cde020a4c..27d3a4eaff 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 727e719cad..6bef7d380b 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 96931ee55b..14a5e8a24d 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 5f9c1409a3..a0a58067fc 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 (fmt_string.len > 0)
- std.fmt.invalidFmtError(fmt_string, self);
+ if (path.sub_path.len > 0) {
+ try std.zig.stringEscape(path.sub_path, writer);
+ }
+}
+
+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 963b6aefbf..28f8781dd1 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 9c9ba00f4e..6ea50f72e8 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 d9c098113f..0fa8a9a623 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 2fd4c7daae..0870000942 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 02a9c8279f..55e1bb0114 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 3987f038d9..356ea4e34e 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 45525aeeac..c408fa6b0c 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 9dfe0a6774..6f8c40b1d5 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 8439807b2a..38ebd4a20c 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 95aa6f5280..d6dec68806 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 c1df140374..52b5908693 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 5b634ca99a..91deb9ddc1 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 95f50df3e5..9fcbeacdd1 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 add94a47a0..b610562708 100644
--- a/lib/std/Uri.zig
+++ b/lib/std/Uri.zig
@@ -3,11 +3,9 @@
const std = @import("std.zig");
const testing = std.testing;
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const Writer = std.io.Writer;
-
const Uri = @This();
+const Allocator = std.mem.Allocator;
+const Writer = std.io.Writer;
scheme: []const u8,
user: ?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 12b9fa0f28..6bd425e8f5 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 a88b637ec0..99bebf09ab 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 e88b723439..a84f4a0b4f 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 1a4407e687..7864dfb775 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 2d8f1d1ed0..f0cf107582 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 aabb54de49..e652b10ce0 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 4514c8857f..445d103098 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 a3c289dfb8..6253341f36 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 f580c54546..b6c0ab20d4 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 8e74fe7899..7c2d5434fb 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 01a80d309c..00a9606f53 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 599686c14d..e2c2c2f78e 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 b7be0a3e81..eb08f5b409 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 33c0616898..0c51d56e30 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,
+
+ 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,
+ };
+ }
};
-/// 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));
- }
+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,
- const fields_info = args_type_info.@"struct".fields;
- if (fields_info.len > max_format_args) {
- @compileError("32 arguments max are supported per format call");
- }
+ pub const Mode = enum {
+ decimal,
+ binary,
+ octal,
+ hex,
+ scientific,
- @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 => {},
- }
+ pub fn base(mode: Mode) ?u8 {
+ return switch (mode) {
+ .decimal => 10,
+ .binary => 2,
+ .octal => 8,
+ .hex => 16,
+ .scientific => null,
+ };
}
+ };
+};
- 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);
- },
- };
-
- 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 ++ "'"),
- }
- }
-}
-
-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 {
}
}
-pub const ParseIntError = error{
- /// The result cannot fit in the type specified
- Overflow,
+/// Deprecated in favor of `Alt`.
+pub const Formatter = Alt;
- /// 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.?;
+/// 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 fn format(self: @This(), writer: *Writer, comptime fmt: []const u8) Writer.Error!void {
- try formatFn(self.data, writer, fmt);
+ 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.
+ Overflow,
+ /// The input was empty or contained an invalid character.
+ InvalidCharacter,
+};
+
/// 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 d50ed6f109..c3cc767a00 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| {
- 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);
+ 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);
}
- }
- 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| {
+ 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;
};
- }
- w.pos += i;
- return io_writer.consumeAll();
- }
+ 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);
+ }
+ 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;
+ return error.WriteFailed;
+ };
+ 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 };
- 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 };
- remaining_splat -= splat_buffer.len;
- len += 1;
- }
- if (remaining_splat > 0 and len < iovecs.len) {
- iovecs[len] = .{ .base = &splat_buffer, .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) |_| {
- if (iovecs.len - len == 0) break;
- iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].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 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;
+ }
+ if (remaining_splat > 0 and iovecs.len - len != 0) {
+ iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
+ len += 1;
+ }
+ },
+ else => for (0..splat) |_| {
+ iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len };
+ len += 1;
+ if (iovecs.len - len == 0) break;
},
},
- }
- 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 f17c1553a5..1cf4dc3c64 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 92902f1d72..ece7f79a74 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 2f5332be9f..51e4fe44a2 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 e3a76a06c1..130eae66f8 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 73721e810f..df4bb76f0f 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 51a4e2c318..d3eac58d93 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 6ef90641b1..61a9eeb5c3 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 914491f9d1..130d2803d6 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 8dcde79069..d27338fe5f 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 0000000000..3f2429c3ae
--- /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 0000000000..391b985357
--- /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 d26c18955b..518fdf2568 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.
///
-/// 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.
+///
+/// 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.
+///
+/// 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 c1371d3434..73d504c437 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;
-
- 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),
- },
- 2 => {
- if (fmt[0] == 'B' and fmt[1] == 'i') {
- return w.printByteSize(int_value, .binary, options);
- } else {
- invalidFmtError(fmt, value);
- }
- },
- else => invalidFmtError(fmt, 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(", ");
}
- comptime unreachable;
+ try w.writeAll(" }");
}
-pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void {
- return w.alignBufferOptions(@as(*const [1]u8, &c), options);
+// 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);
+ },
+ 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),
+ },
+ }
+ return printIntAny(w, value, base, case, 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);
-}
-
-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 {
- var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
-
- 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),
- }
+pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void {
+ return w.alignBufferOptions(@as(*const [1]u8, &c), options);
}
-pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void {
+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);
+}
+
+/// 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 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;
- 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;
+ 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;
+ 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 5ba2bb3c10..d9da1c4a0e 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 cb7efac2d9..fe6836f603 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 4cc9847e86..b6bd835f84 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 1e9cd0d9bc..fa17d9a16d 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 3fcb256f12..246c98817e 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 499c5c0cf9..4754bdb859 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 d54b930fae..a988e2c2c3 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 85a058af38..1836d6775b 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 2c51b9ddee..5bfb812bf3 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 a8bb42f3a6..33938dbd42 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 5a0fda52b2..3683eb2bcd 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 2e224e5db7..a5613536c6 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 dc3e3f0325..b81a916702 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 279a150799..160a9f2fba 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 0a4303b6f4..38a7675772 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 88a18094ac..02c927d566 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 cdc1973e04..4c68405c22 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 e437e2f66e..6526f590fa 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 33c2e7a548..a388072e74 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 b4cecefbd7..b78e4c323a 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 059383a8d3..db5b06828b 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 776fe615b3..993b8e28cd 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 7b29405cbf..58d16eef1d 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 a74b83fe81..68311463b0 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 c04eaf670c..7f2d3e86c5 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 4c6ec1294b..ef694f33ba 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 8d9346226a..ccdf65b1be 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 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 double quoted Zig string.
+pub fn fmtString(bytes: []const u8) std.fmt.Formatter([]const u8, stringEscape) {
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(
- \\" \\ hi \x07 \x11 \" derp '"
- , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
+/// 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 };
}
-/// 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 fmtString {
+ try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")});
+ try std.testing.expectFmt(
+ \\" \\ hi \x07 \x11 \" derp '"
+ , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")});
+}
+
+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 ++ "}");
- }
- },
- ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte),
- // Use hex escapes for rest any unprintable characters.
+ '\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 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' });
+ },
+ };
+}
+
+/// 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 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 04d9691df5..0e1b6fff5c 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 d7e262f4b2..f5113941c3 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 c2c8990591..2b6c61b138 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 628574349b..52900224cc 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 818362a371..5ef28fdfaf 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 b9684a585d..b398d5eb4c 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 34f8de9191..fc8b61a403 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 17643e6f1e..c53ec630ba 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 6693a7dbbf..bb66f76841 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 419b12d5ca..b3e0311cdd 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 21c9faee14..7c0a80c704 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 5d1de962bd..4dfab46203 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 27ebb6c1b7..7ba62eaf54 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 09c79d3562..de04ba41fe 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 b5fafe5aa8..792a83d70d 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 ab1727e745..dc0a65dc91 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 37dddb27f8..c9e38415a3 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 9fab03c679..83d5fd0919 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 78c42bded5..b1e13dbf40 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 b8e27f6bba..8ff199c02f 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 92b2dfd841..b2cb603f53 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 d1adbb6322..709415f3b1 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 531b71b4e8..e7c7461e5d 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 942a58684e..8471a1ad9e 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 9c0db8387a..08b163eebb 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 816d22f724..69cfb3ec66 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 4c279c77c8..92f6705e4e 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 cdbec43405..8c70a5b784 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 d623e5c700..199690fcac 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 96206b6c26..aed97c8754 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 6aff0eb105..26ee09cfbf 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 2776d3c860..d4a3d1598f 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 14ce169af6..0000000000
--- 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 e46bfb856a..0000000000
--- 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 88089c8488..0000000000
--- 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/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
deleted file mode 100644
index 12eb0c79a7..0000000000
--- 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 9872d9f4c8..0000000000
--- 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 5b7585a2ca..0000000000
--- 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/bits.zig b/src/arch/arm/bits.zig
deleted file mode 100644
index 0388ce8c63..0000000000
--- 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/powerpc/CodeGen.zig b/src/arch/powerpc/CodeGen.zig
deleted file mode 100644
index 4964fe19f4..0000000000
--- 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 6914e881a0..5aec8a91ab 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 4653b6d73b..43ccf00058 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 9ca4808f89..dd9529ec3d 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 238bbaee9e..9174a5850e 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 9df6255e52..5678b36ef9 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/x86/bits.zig b/src/arch/x86/bits.zig
deleted file mode 100644
index 4b69238c4f..0000000000
--- 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 1f67306598..486497a365 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 b99580f6ae..af7304709c 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 9cb78972cc..9ae7ce8fef 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 d0063c13f2..c9f11bd28e 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 415b211e6a..43d23af5fc 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 3aa172332d..c81dbfbe05 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/arch/aarch64/abi.zig b/src/codegen/aarch64/abi.zig
similarity index 89%
rename from src/arch/aarch64/abi.zig
rename to src/codegen/aarch64/abi.zig
index 99c67d6761..0cd0b389b1 100644
--- a/src/arch/aarch64/abi.zig
+++ b/src/codegen/aarch64/abi.zig
@@ -1,8 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");
-const bits = @import("bits.zig");
+const bits = @import("../../arch/aarch64/bits.zig");
const Register = bits.Register;
-const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
const Type = @import("../../Type.zig");
const Zcu = @import("../../Zcu.zig");
@@ -149,17 +148,3 @@ pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6,
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/abi.zig b/src/codegen/arm/abi.zig
similarity index 86%
rename from src/arch/arm/abi.zig
rename to src/codegen/arm/abi.zig
index b29bb523ab..23606d6145 100644
--- a/src/arch/arm/abi.zig
+++ b/src/codegen/arm/abi.zig
@@ -1,7 +1,5 @@
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");
@@ -163,25 +161,3 @@ fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 {
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/codegen/c.zig b/src/codegen/c.zig
index c8681ca525..61bd5259ae 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 9a35440eaf..3d670dce83 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 5d2d4c2a99..7fa2a7da43 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/arch/mips/abi.zig b/src/codegen/mips/abi.zig
similarity index 100%
rename from src/arch/mips/abi.zig
rename to src/codegen/mips/abi.zig
diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig
index e06d0c944f..f263e567e8 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 857d5bb03a..82ec05ebba 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/arch/wasm/abi.zig b/src/codegen/wasm/abi.zig
similarity index 100%
rename from src/arch/wasm/abi.zig
rename to src/codegen/wasm/abi.zig
diff --git a/src/deprecated.zig b/src/deprecated.zig
index 1f7d5c8c25..91c9ede27f 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 babf3af69e..4c602621ec 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 55d097b71b..6baa899087 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 f26f27732b..eebbb9cb73 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 36ca5faa25..d17baf8fa1 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 430ff59748..71cc6ccbc0 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 3aa9b10a39..de40b7279d 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 91f1b92b9e..b09a29257e 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 38570c43a6..094165b9c5 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 aaff0da9ba..6dced677a7 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);
-
- 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,
- });
- }
- 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),
- }),
- .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,
- });
- }
- 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,
- });
- }
- 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 libdl.
var args = std.ArrayList([]const u8).init(arena);
try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
+ try addLibcBottomHalfIncludes(comp, arena, &args);
+
+ 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,
+ });
+ }
+ }
+
+ {
+ // 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",
+ }),
+ });
+
+ 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,
+ });
+ }
+ }
+
+ {
+ // 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,
+ });
+ }
+ }
+
+ {
+ // 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 387dbe94dd..50b995c90b 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 0c282dbc4a..a1d28f9208 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 88195a9391..4b2a4dfaae 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 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 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 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 f33905610f..5b4e0b6608 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 388036b146..6c38d4c290 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 405a3a64e6..df89d50264 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 3ec05eb942..4e73f70206 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 500105673d..f0e3121417 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 58310a9272..99a40758bc 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,32 +437,31 @@ 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 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)});
+ 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)});
+ }
}
}
-}
+};
const std = @import("std");
const Allocator = mem.Allocator;
diff --git a/src/link/Elf/Merge.zig b/src/link/Elf/Merge.zig
index ec2a61579b..9e80907bef 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 be5b48c546..d202ba7f8a 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 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)});
+ }
+ }
+ }
+
+ 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 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) });
+ }
+ }
+ }
};
-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)});
- }
- }
-}
-
-pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
+pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) {
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) {
+pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.cies) {
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) {
+pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.fdes) {
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) {
+pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.groups) {
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];
- const atom_ptr = object.atom(atom_index) orelse continue;
- try bw.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(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 941bd374ed..e6b20152e6 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 2790afdd12..f27aebe5ba 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 8c3a1448bd..38692e795e 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 c969b6a3b0..78b93a3ec6 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 1586a0675d..671c0a80d8 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 68bd303407..99425457e0 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 44b2ced73d..e4b68e4305 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 6b73b041fa..0a66f337e6 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 c7436534ea..56c4d9faa2 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 eadde7ce6c..534f9766bb 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 4b3c611b4a..1aeeb5d214 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 b319df1652..b5f2344b7e 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 d625b8ead8..f074d740a7 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 7e100c059d..b41a0031bc 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 f7af39aa93..d950f791ff 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 19eb04b712..1329572dc8 100644
--- a/src/link/MachO/InternalObject.zig
+++ b/src/link/MachO/InternalObject.zig
@@ -836,48 +836,46 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool {
return false;
}
-const FormatContext = struct {
+const Format = struct {
self: *InternalObject,
macho_file: *MachO,
-};
-pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) {
- 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) {
- 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)});
+ 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(Format, Format.atoms) {
+ return .{ .data = .{
+ .self = self,
+ .macho_file = macho_file,
+ } };
+}
+
+pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
+ return .{ .data = .{
+ .self = self,
+ .macho_file = macho_file,
+ } };
}
const Section = struct {
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 4f344f536d..d4ac509a62 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 30eef438ae..7982dff6a4 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 9e8660d8cf..654e7c402c 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 b84cc4c8f2..5821c88ab7 100644
--- a/src/link/MachO/Thunk.zig
+++ b/src/link/MachO/Thunk.zig
@@ -61,35 +61,27 @@ 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 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 });
+ 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 });
+ }
}
-}
+};
const trampoline_size = 3 * @sizeOf(u32);
diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig
index a8afef13b0..5d1d4b3e3f 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 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 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 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 54d25fe6e3..023fad64c7 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 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)});
+ 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)});
+ }
}
}
-}
-pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) {
+ 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)});
+ }
+ }
+};
+
+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 17bd302d82..79ff4e1707 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 e057d8b166..f2e36fd876 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 3d77115604..5e766369ff 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 b40b770959..fde8553260 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 09e3ea1368..6ac4860103 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 c9a8a18843..93f7000401 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 084146acea..17e398f3af 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 fdd1e97eb3..d1cfa83cc1 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 100aa9c2f7..6dbbad62a2 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 f6556dd5dd..0f418dea30 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 038b0233a3..d8a6788a0f 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 4d3e29422b..e0a489ee40 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 5a0c829a50..ae674e43e5 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 df587fd365..85d4c22b48 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 d57e1e4adb..8c33a26939 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 e473088922..50d1b2e1a7 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 865b6939c2..301e0a219d 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 fe4e421dc3..c9a30bb167 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 e2dba338c3..db331512bd 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 ef2183dae8..83240d39b4 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 8457058d61..f1c50d438d 100644
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
diff --git a/test/behavior.zig b/test/behavior.zig
index 049278a69f..414ce2e00a 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 2921986cba..b3750c9d3d 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 e66ecd52db..0000000000
--- 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 c919922f03..0000000000
--- 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 c8166a06ab..bcd72d5176 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 5cfae1b35d..e509fcda35 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 6d00f4bba2..70edb900ff 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 ef3080e714..faa3a08141 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 03e757235a..4a66c56cea 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 3a0b254aa2..f19d90696b 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 aa6c5a78f4..0000000000
--- 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 21d4278e15..0000000000
--- 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 08dafa3c50..0000000000
--- 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 faa3f27dbe..0000000000
--- 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 6b2b14e51c..0000000000
--- 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 f06a5bb4f6..0000000000
--- 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 02e0f39e90..0000000000
--- 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 a1bd3c01e7..0000000000
--- 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 b4944eefc3..0000000000
--- 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 52b866afcf..0000000000
--- 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 9fadb992b4..0000000000
--- 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 804baed45f..0000000000
--- 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 fb3488ce95..0000000000
--- 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 97e29accd2..0000000000
--- 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 b3e7fabcea..0000000000
--- 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 082251c137..0000000000
--- 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 c02c20f495..0000000000
--- 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 b4193d4de1..0000000000
--- 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 f48863091b..0000000000
--- 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 dd853432b6..0000000000
--- 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 5963f1c0ec..0000000000
--- 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 294fd8c012..0000000000
--- 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 00fbcef537..0000000000
--- 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 6767ad4e76..0000000000
--- 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 efd611662a..0000000000
--- 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 c58f13b99d..0000000000
--- 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 47545584ea..0000000000
--- 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 26df1ae900..0000000000
--- 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 f8bf6d44c0..0000000000
--- 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 af288ab8ba..0000000000
--- 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 45577acb00..be63272f0c 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 3603163a2d..283e6d90c9 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 87f33b1c51..39a25e72de 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 84472effb5..7e2fe5742c 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 a26ab75423..4e78b7074a 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 171e4d3178..7c23b120f1 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 d3f6c85c37..1691764fbc 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 f0e2ea8d34..e492930031 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 892d0dd9b6..24392b25f7 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 bed4372b37..c9eb2be929 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 887e5ffd21..0754d39182 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 34a1f32dab..699134100e 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 ad5d3d124a..2d068d593e 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 bface3a383..ccb904581d 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 60fdcc8944..97049a1fc0 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 e12f3db080..247f78828e 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 84f4a69bcf..231558e3e9 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 f7fa8ff7e1..de41524563 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 51972e9232..9c427c7a96 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 607cd6805e..8f36a31b69 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 d1bc876071..c30cd50c6f 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 89388dca74..139593b2b0 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 726b2ca22a..ef31df2d5e 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 3e93513430..c2ff12761f 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 55219b8356..f7386db2a4 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 2ec483e3e5..9bba6bc038 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 8d3796b7c3..7623922d3d 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 6025f3fdae..51e078a82a 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 aaf865a0c8..2785a8360f 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 14b70441ba..f6dfbbea86 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 e5e38f88e0..80d861eea0 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 b9fa1226eb..9635f64a40 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 d2f784a5b0..9f6a7fe189 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 43800086af..30f3dd9789 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 635d36d510..a62ba23c5a 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 e9edcf9f4b..b02bec3500 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 068ff8d1a9..6537f90acf 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 041ebc3e50..da949d4fc1 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 293a6839a1..51f667fc15 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 facaf4a754..0000000000
--- 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 ba4464cefc..51d9b3a9b1 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 740e73a33e..7bb976e9e7 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 2c95c8993f..d477de2b78 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 1ad24a4147..de6cfe3ec7 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 ee69f33d2e..a036a64ab7 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 399a23d924..0000000000
--- 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 2835a51615..0000000000
--- 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 900ab6b51a..0000000000
--- 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 68ef6f6588..0000000000
--- 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 df988d2371..9ace58088a 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 6d08c1bf84..bbe956c365 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 074273ae21..d41ad95313 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 b91b4fb3bb..e30f5b36c1 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 2552045aed..054c4a6975 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 2b967b16d5..628b1a6900 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 dcf917c430..fb4a827e23 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 c9414081e3..3693e18d91 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,17 +1482,9 @@ const test_targets = blk: {
.target = .{
.cpu_arch = .aarch64,
.os_tag = .windows,
- .abi = .none,
+ .abi = .msvc,
},
},
- .{
- .target = .{
- .cpu_arch = .aarch64,
- .os_tag = .windows,
- .abi = .gnu,
- },
- .link_libc = true,
- },
.{
.target = .{
.cpu_arch = .aarch64,
@@ -1499,27 +1493,18 @@ const test_targets = blk: {
},
.link_libc = true,
},
-
.{
.target = .{
- .cpu_arch = .x86,
- .os_tag = .windows,
- .abi = .none,
- },
- },
- .{
- .target = .{
- .cpu_arch = .x86,
+ .cpu_arch = .aarch64,
.os_tag = .windows,
.abi = .gnu,
},
- .link_libc = true,
},
.{
.target = .{
- .cpu_arch = .x86,
+ .cpu_arch = .aarch64,
.os_tag = .windows,
- .abi = .msvc,
+ .abi = .gnu,
},
.link_libc = true,
},
@@ -1528,9 +1513,17 @@ const test_targets = blk: {
.target = .{
.cpu_arch = .thumb,
.os_tag = .windows,
- .abi = .none,
+ .abi = .msvc,
},
},
+ .{
+ .target = .{
+ .cpu_arch = .thumb,
+ .os_tag = .windows,
+ .abi = .msvc,
+ },
+ .link_libc = true,
+ },
// https://github.com/ziglang/zig/issues/24016
// .{
// .target = .{
@@ -1538,14 +1531,44 @@ const test_targets = blk: {
// .os_tag = .windows,
// .abi = .gnu,
// },
+ // },
+ // .{
+ // .target = .{
+ // .cpu_arch = .thumb,
+ // .os_tag = .windows,
+ // .abi = .gnu,
+ // },
// .link_libc = true,
// },
+
.{
.target = .{
- .cpu_arch = .thumb,
+ .cpu_arch = .x86,
.os_tag = .windows,
.abi = .msvc,
},
+ },
+ .{
+ .target = .{
+ .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,
},
@@ -1553,7 +1576,7 @@ const test_targets = blk: {
.target = .{
.cpu_arch = .x86_64,
.os_tag = .windows,
- .abi = .none,
+ .abi = .msvc,
},
.use_llvm = false,
.use_lld = false,
@@ -1562,23 +1585,14 @@ const test_targets = blk: {
.target = .{
.cpu_arch = .x86_64,
.os_tag = .windows,
- .abi = .gnu,
- },
- .use_llvm = false,
- .use_lld = false,
- },
- .{
- .target = .{
- .cpu_arch = .x86_64,
- .os_tag = .windows,
- .abi = .none,
+ .abi = .msvc,
},
},
.{
.target = .{
.cpu_arch = .x86_64,
.os_tag = .windows,
- .abi = .gnu,
+ .abi = .msvc,
},
.link_libc = true,
},
@@ -1586,7 +1600,23 @@ const test_targets = blk: {
.target = .{
.cpu_arch = .x86_64,
.os_tag = .windows,
- .abi = .msvc,
+ .abi = .gnu,
+ },
+ .use_llvm = false,
+ .use_lld = false,
+ },
+ .{
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .windows,
+ .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 6b3e898b9f..3f48ba39a8 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 af5ce9f983..8f9d1fe8cf 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 65bd19000d..7699ac702b 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 4b6bb87ae0..b81ee001b5 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 69f7cc33ff..a56194a9a8 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 2f989ed1a1..bcd757978a 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 0ba27c49a3..a772d99660 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 31095399d3..4978611cc1 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 42dc777e82..56c6bc7261 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 588deb4935..8c278407e4 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 38726e6501..1ee153c40c 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 63bcb63111..99c1bf1ee5 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 3c2ef84952..8bf49ad93a 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 cb6043f0c8..b54d74ca1c 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 5ccac15112..1ae45cf1bc 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});
}
|