From b92b55ab8e11614a587929bc66c023b9fe7cf7f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Sun, 23 Mar 2025 18:48:05 +0100
Subject: [PATCH 001/110] Update test build.zig.zon files to conform to the new
manifest rules
---
test/link/build.zig.zon | 3 ++-
test/standalone/build.zig.zon | 2 +-
test/standalone/dependencyFromBuildZig/build.zig.zon | 3 ++-
test/standalone/dependencyFromBuildZig/other/build.zig.zon | 3 ++-
4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/test/link/build.zig.zon b/test/link/build.zig.zon
index 16bba08c4e018dcd8c13aef2a2db6599411fdadc..ab44726091eddf978caeb4553f3ee69ceb22abfc 100644
--- a/test/link/build.zig.zon
+++ b/test/link/build.zig.zon
@@ -1,5 +1,6 @@
.{
- .name = "link_test_cases",
+ .name = .link_test_cases,
+ .fingerprint = 0x404f657576fec9f2,
.version = "0.0.0",
.dependencies = .{
.bss = .{
diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon
index 8cf899477f30bd0cbeb556a15b3f2cc1c67c2e95..afbe3fcfa8601f82e312c97a605a22faf8c53888 100644
--- a/test/standalone/build.zig.zon
+++ b/test/standalone/build.zig.zon
@@ -1,6 +1,6 @@
.{
.name = .standalone_test_cases,
- .fingerprint = 0xc0dbdf9c818957be,
+ .fingerprint = 0xc0dbdf9c3b92810b,
.version = "0.0.0",
.dependencies = .{
.simple = .{
diff --git a/test/standalone/dependencyFromBuildZig/build.zig.zon b/test/standalone/dependencyFromBuildZig/build.zig.zon
index 085ae2c80b45d4393640895dda8a8f9e4c39db35..fda6a098d8da887a5fb94f56b67672a4149bc20e 100644
--- a/test/standalone/dependencyFromBuildZig/build.zig.zon
+++ b/test/standalone/dependencyFromBuildZig/build.zig.zon
@@ -1,5 +1,6 @@
.{
- .name = "dependencyFromBuildZig",
+ .name = .dependencyFromBuildZig,
+ .fingerprint = 0xfd939a1eb8169080,
.version = "0.0.0",
.dependencies = .{
.other = .{
diff --git a/test/standalone/dependencyFromBuildZig/other/build.zig.zon b/test/standalone/dependencyFromBuildZig/other/build.zig.zon
index 204abdbbba3d5361a66a24ebc5c2454f437f9a7a..bb8fcb6fb4c433715b6b10360de445f6fe531477 100644
--- a/test/standalone/dependencyFromBuildZig/other/build.zig.zon
+++ b/test/standalone/dependencyFromBuildZig/other/build.zig.zon
@@ -1,5 +1,6 @@
.{
- .name = "other",
+ .name = .other,
+ .fingerprint = 0xd9583520a2405f6c,
.version = "0.0.0",
.dependencies = .{},
.paths = .{""},
--
2.54.0
From 00bc72b5ff01c7f8ceb4b58e82614e22a147ccc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Sun, 23 Mar 2025 20:38:41 +0100
Subject: [PATCH 002/110] Add standalone test case for passing options to
dependencies
---
test/standalone/build.zig.zon | 3 +
test/standalone/dependency_options/build.zig | 63 +++++++++++++++++++
.../dependency_options/build.zig.zon | 11 ++++
.../dependency_options/other/build.zig | 56 +++++++++++++++++
.../dependency_options/other/build.zig.zon | 7 +++
5 files changed, 140 insertions(+)
create mode 100644 test/standalone/dependency_options/build.zig
create mode 100644 test/standalone/dependency_options/build.zig.zon
create mode 100644 test/standalone/dependency_options/other/build.zig
create mode 100644 test/standalone/dependency_options/other/build.zig.zon
diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon
index afbe3fcfa8601f82e312c97a605a22faf8c53888..bdd059ab378d61669701ef930dc68e9715216053 100644
--- a/test/standalone/build.zig.zon
+++ b/test/standalone/build.zig.zon
@@ -181,6 +181,9 @@
.install_headers = .{
.path = "install_headers",
},
+ .dependency_options = .{
+ .path = "dependency_options",
+ },
.dependencyFromBuildZig = .{
.path = "dependencyFromBuildZig",
},
diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig
new file mode 100644
index 0000000000000000000000000000000000000000..8726f61d30fbf3f91b2b1053d7fc15039d34014d
--- /dev/null
+++ b/test/standalone/dependency_options/build.zig
@@ -0,0 +1,63 @@
+const std = @import("std");
+
+pub const Enum = enum { alfa, bravo, charlie };
+
+pub fn build(b: *std.Build) !void {
+ const test_step = b.step("test", "Test passing options to a dependency");
+ b.default_step = test_step;
+
+ const none_specified = b.dependency("other", .{});
+
+ const none_specified_mod = none_specified.module("dummy");
+ if (!none_specified_mod.resolved_target.?.query.eql(b.graph.host.query)) return error.TestFailed;
+ if (none_specified_mod.optimize.? != .Debug) return error.TestFailed;
+
+ const all_specified = b.dependency("other", .{
+ .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }),
+ .optimize = @as(std.builtin.OptimizeMode, .ReleaseSafe),
+ .bool = @as(bool, true),
+ .int = @as(i64, 123),
+ .float = @as(f64, 0.5),
+ .string = @as([]const u8, "abc"),
+ .string_list = @as([]const []const u8, &.{ "a", "b", "c" }),
+ .lazy_path = @as(std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
+ .lazy_path_list = @as([]const std.Build.LazyPath, &.{
+ .{ .cwd_relative = "a.txt" },
+ .{ .cwd_relative = "b.txt" },
+ .{ .cwd_relative = "c.txt" },
+ }),
+ .@"enum" = @as(Enum, .alfa),
+ //.enum_list = @as([]const Enum, &.{ .alfa, .bravo, .charlie }),
+ //.build_id = @as(std.zig.BuildId, .uuid),
+ });
+
+ const all_specified_mod = all_specified.module("dummy");
+ if (all_specified_mod.resolved_target.?.result.cpu.arch != .x86_64) return error.TestFailed;
+ if (all_specified_mod.resolved_target.?.result.os.tag != .windows) return error.TestFailed;
+ if (all_specified_mod.resolved_target.?.result.abi != .gnu) return error.TestFailed;
+ if (all_specified_mod.optimize.? != .ReleaseSafe) return error.TestFailed;
+
+ // Most supported option types are serialized to a string representation,
+ // so alternative representations of the same option value should resolve
+ // to the same cached dependency instance.
+ const all_specified_alt = b.dependency("other", .{
+ .target = @as(std.Target.Query, .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }),
+ .optimize = @as([]const u8, "ReleaseSafe"),
+ .bool = .true,
+ .int = @as([]const u8, "123"),
+ .float = @as(f16, 0.5),
+ .string = .abc,
+ .string_list = @as([]const []const u8, &.{ "a", "b", "c" }),
+ .lazy_path = @as(std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
+ .lazy_path_list = @as([]const std.Build.LazyPath, &.{
+ .{ .cwd_relative = "a.txt" },
+ .{ .cwd_relative = "b.txt" },
+ .{ .cwd_relative = "c.txt" },
+ }),
+ .@"enum" = @as([]const u8, "alfa"),
+ //.enum_list = @as([]const Enum, &.{ .alfa, .bravo, .charlie }),
+ //.build_id = @as(std.zig.BuildId, .uuid),
+ });
+
+ if (all_specified != all_specified_alt) return error.TestFailed;
+}
diff --git a/test/standalone/dependency_options/build.zig.zon b/test/standalone/dependency_options/build.zig.zon
new file mode 100644
index 0000000000000000000000000000000000000000..6788640a806501d253897380b88bb3c05cc482a6
--- /dev/null
+++ b/test/standalone/dependency_options/build.zig.zon
@@ -0,0 +1,11 @@
+.{
+ .name = .dependency_options,
+ .fingerprint = 0x3e3ce1c1f92ba47e,
+ .version = "0.0.0",
+ .dependencies = .{
+ .other = .{
+ .path = "other",
+ },
+ },
+ .paths = .{""},
+}
diff --git a/test/standalone/dependency_options/other/build.zig b/test/standalone/dependency_options/other/build.zig
new file mode 100644
index 0000000000000000000000000000000000000000..fe676a5b25a8a977bd68f5de0d0a6e60ea5d944f
--- /dev/null
+++ b/test/standalone/dependency_options/other/build.zig
@@ -0,0 +1,56 @@
+const std = @import("std");
+
+pub const Enum = enum { alfa, bravo, charlie };
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const expected_bool: bool = true;
+ const expected_int: i64 = 123;
+ const expected_float: f64 = 0.5;
+ const expected_string: []const u8 = "abc";
+ const expected_string_list: []const []const u8 = &.{ "a", "b", "c" };
+ const expected_lazy_path: std.Build.LazyPath = .{ .cwd_relative = "abc.txt" };
+ const expected_lazy_path_list: []const std.Build.LazyPath = &.{
+ .{ .cwd_relative = "a.txt" },
+ .{ .cwd_relative = "b.txt" },
+ .{ .cwd_relative = "c.txt" },
+ };
+ const expected_enum: Enum = .alfa;
+ const expected_enum_list: []const Enum = &.{ .alfa, .bravo, .charlie };
+ const expected_build_id: std.zig.BuildId = .uuid;
+
+ const @"bool" = b.option(bool, "bool", "bool") orelse expected_bool;
+ const int = b.option(i64, "int", "int") orelse expected_int;
+ const float = b.option(f64, "float", "float") orelse expected_float;
+ const string = b.option([]const u8, "string", "string") orelse expected_string;
+ const string_list = b.option([]const []const u8, "string_list", "string_list") orelse expected_string_list;
+ const lazy_path = b.option(std.Build.LazyPath, "lazy_path", "lazy_path") orelse expected_lazy_path;
+ const lazy_path_list = b.option([]const std.Build.LazyPath, "lazy_path_list", "lazy_path_list") orelse expected_lazy_path_list;
+ const @"enum" = b.option(Enum, "enum", "enum") orelse expected_enum;
+ const enum_list = b.option([]const Enum, "enum_list", "enum_list") orelse expected_enum_list;
+ const build_id = b.option(std.zig.BuildId, "build_id", "build_id") orelse expected_build_id;
+
+ if (@"bool" != expected_bool) return error.TestFailed;
+ if (int != expected_int) return error.TestFailed;
+ if (float != expected_float) return error.TestFailed;
+ if (!std.mem.eql(u8, string, expected_string)) return error.TestFailed;
+ if (string_list.len != expected_string_list.len) return error.TestFailed;
+ for (string_list, expected_string_list) |x, y| {
+ if (!std.mem.eql(u8, x, y)) return error.TestFailed;
+ }
+ if (!std.mem.eql(u8, lazy_path.cwd_relative, expected_lazy_path.cwd_relative)) return error.TestFailed;
+ for (lazy_path_list, expected_lazy_path_list) |x, y| {
+ if (!std.mem.eql(u8, x.cwd_relative, y.cwd_relative)) return error.TestFailed;
+ }
+ if (@"enum" != expected_enum) return error.TestFailed;
+ if (!std.mem.eql(Enum, enum_list, expected_enum_list)) return error.TestFailed;
+ if (!std.meta.eql(build_id, expected_build_id)) return error.TestFailed;
+
+ _ = b.addModule("dummy", .{
+ .root_source_file = b.path("build.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+}
diff --git a/test/standalone/dependency_options/other/build.zig.zon b/test/standalone/dependency_options/other/build.zig.zon
new file mode 100644
index 0000000000000000000000000000000000000000..d49a2cdcf86ac353887826f53ea0d530bf28ae80
--- /dev/null
+++ b/test/standalone/dependency_options/other/build.zig.zon
@@ -0,0 +1,7 @@
+.{
+ .name = .other,
+ .fingerprint = 0xd95835207bc8b630,
+ .version = "0.0.0",
+ .dependencies = .{},
+ .paths = .{""},
+}
--
2.54.0
From 5380e81924dd98e9717eaf09ae05e935952a78ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Sun, 23 Mar 2025 22:45:38 +0100
Subject: [PATCH 003/110] Support passing null to `b.dependency()`
Both null literals and optionals are supported.
---
lib/std/Build.zig | 179 +++++++++++--------
test/standalone/dependency_options/build.zig | 46 ++++-
2 files changed, 146 insertions(+), 79 deletions(-)
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index e65a71e12bc139da413d1988bf7ec6c7894367ef..e5b9e072f7cdbe52006e5c11a1f3b49943662771 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -408,104 +408,127 @@ fn createChildOnly(
return child;
}
-fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOptionsMap {
- var user_input_options = UserInputOptionsMap.init(allocator);
+fn userInputOptionsFromArgs(arena: Allocator, args: anytype) UserInputOptionsMap {
+ var map = UserInputOptionsMap.init(arena);
inline for (@typeInfo(@TypeOf(args)).@"struct".fields) |field| {
- const v = @field(args, field.name);
- const T = @TypeOf(v);
- switch (T) {
- Target.Query => {
- user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = v.zigTriple(allocator) catch @panic("OOM") },
- .used = false,
- }) catch @panic("OOM");
- user_input_options.put("cpu", .{
- .name = "cpu",
- .value = .{ .scalar = v.serializeCpuAlloc(allocator) catch @panic("OOM") },
- .used = false,
- }) catch @panic("OOM");
- },
- ResolvedTarget => {
- user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = v.query.zigTriple(allocator) catch @panic("OOM") },
- .used = false,
- }) catch @panic("OOM");
- user_input_options.put("cpu", .{
- .name = "cpu",
- .value = .{ .scalar = v.query.serializeCpuAlloc(allocator) catch @panic("OOM") },
- .used = false,
- }) catch @panic("OOM");
- },
- LazyPath => {
- user_input_options.put(field.name, .{
+ if (field.type == @Type(.null)) continue;
+ addUserInputOptionFromArg(arena, &map, field, field.type, @field(args, field.name));
+ }
+ return map;
+}
+
+fn addUserInputOptionFromArg(
+ arena: Allocator,
+ map: *UserInputOptionsMap,
+ field: std.builtin.Type.StructField,
+ comptime T: type,
+ /// If null, the value won't be added, but `T` will still be type-checked.
+ maybe_value: ?T,
+) void {
+ switch (T) {
+ Target.Query => return if (maybe_value) |v| {
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = v.zigTriple(arena) catch @panic("OOM") },
+ .used = false,
+ }) catch @panic("OOM");
+ map.put("cpu", .{
+ .name = "cpu",
+ .value = .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ ResolvedTarget => return if (maybe_value) |v| {
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") },
+ .used = false,
+ }) catch @panic("OOM");
+ map.put("cpu", .{
+ .name = "cpu",
+ .value = .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ LazyPath => return if (maybe_value) |v| {
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .lazy_path = v.dupeInner(arena) },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ []const LazyPath => return if (maybe_value) |v| {
+ var list = ArrayList(LazyPath).initCapacity(arena, v.len) catch @panic("OOM");
+ for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(arena));
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .lazy_path_list = list },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ []const u8 => return if (maybe_value) |v| {
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = v },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ []const []const u8 => return if (maybe_value) |v| {
+ var list = ArrayList([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
+ list.appendSliceAssumeCapacity(v);
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .list = list },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ else => switch (@typeInfo(T)) {
+ .bool => return if (maybe_value) |v| {
+ map.put(field.name, .{
.name = field.name,
- .value = .{ .lazy_path = v.dupeInner(allocator) },
+ .value = .{ .scalar = if (v) "true" else "false" },
.used = false,
}) catch @panic("OOM");
},
- []const LazyPath => {
- var list = ArrayList(LazyPath).initCapacity(allocator, v.len) catch @panic("OOM");
- for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(allocator));
- user_input_options.put(field.name, .{
+ .@"enum", .enum_literal => return if (maybe_value) |v| {
+ map.put(field.name, .{
.name = field.name,
- .value = .{ .lazy_path_list = list },
+ .value = .{ .scalar = @tagName(v) },
.used = false,
}) catch @panic("OOM");
},
- []const u8 => {
- user_input_options.put(field.name, .{
+ .comptime_int, .int => return if (maybe_value) |v| {
+ map.put(field.name, .{
.name = field.name,
- .value = .{ .scalar = v },
+ .value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") },
.used = false,
}) catch @panic("OOM");
},
- []const []const u8 => {
- var list = ArrayList([]const u8).initCapacity(allocator, v.len) catch @panic("OOM");
- list.appendSliceAssumeCapacity(v);
-
- user_input_options.put(field.name, .{
+ .comptime_float, .float => return if (maybe_value) |v| {
+ map.put(field.name, .{
.name = field.name,
- .value = .{ .list = list },
+ .value = .{ .scalar = std.fmt.allocPrint(arena, "{e}", .{v}) catch @panic("OOM") },
.used = false,
}) catch @panic("OOM");
},
- else => switch (@typeInfo(T)) {
- .bool => {
- user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = if (v) "true" else "false" },
- .used = false,
- }) catch @panic("OOM");
+ .null => unreachable,
+ .optional => |info| switch (@typeInfo(info.child)) {
+ .optional => {},
+ else => {
+ addUserInputOptionFromArg(
+ arena,
+ map,
+ field,
+ info.child,
+ maybe_value orelse null,
+ );
+ return;
},
- .@"enum", .enum_literal => {
- user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = @tagName(v) },
- .used = false,
- }) catch @panic("OOM");
- },
- .comptime_int, .int => {
- user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = std.fmt.allocPrint(allocator, "{d}", .{v}) catch @panic("OOM") },
- .used = false,
- }) catch @panic("OOM");
- },
- .comptime_float, .float => {
- user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = std.fmt.allocPrint(allocator, "{e}", .{v}) catch @panic("OOM") },
- .used = false,
- }) catch @panic("OOM");
- },
- else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
},
- }
+ else => {},
+ },
}
-
- return user_input_options;
+ @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(field.type));
}
const OrderedUserValue = union(enum) {
diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig
index 8726f61d30fbf3f91b2b1053d7fc15039d34014d..27ce63834d81deb365efaf8e3727ea8b669ec505 100644
--- a/test/standalone/dependency_options/build.zig
+++ b/test/standalone/dependency_options/build.zig
@@ -12,6 +12,29 @@ pub fn build(b: *std.Build) !void {
if (!none_specified_mod.resolved_target.?.query.eql(b.graph.host.query)) return error.TestFailed;
if (none_specified_mod.optimize.? != .Debug) return error.TestFailed;
+ // Passing null is the same as not specifying the option,
+ // so this should resolve to the same cached dependency instance.
+ const null_specified = b.dependency("other", .{
+ // Null literals
+ .target = null,
+ .optimize = null,
+ .bool = null,
+
+ // Optionals
+ .int = @as(?i64, null),
+ .float = @as(?f64, null),
+
+ // Optionals of the wrong type
+ .string = @as(?usize, null),
+ .@"enum" = @as(?bool, null),
+
+ // Non-defined option names
+ .this_option_does_not_exist = null,
+ .neither_does_this_one = @as(?[]const u8, null),
+ });
+
+ if (null_specified != none_specified) return error.TestFailed;
+
const all_specified = b.dependency("other", .{
.target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }),
.optimize = @as(std.builtin.OptimizeMode, .ReleaseSafe),
@@ -37,6 +60,27 @@ pub fn build(b: *std.Build) !void {
if (all_specified_mod.resolved_target.?.result.abi != .gnu) return error.TestFailed;
if (all_specified_mod.optimize.? != .ReleaseSafe) return error.TestFailed;
+ const all_specified_optional = b.dependency("other", .{
+ .target = @as(?std.Build.ResolvedTarget, b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu })),
+ .optimize = @as(?std.builtin.OptimizeMode, .ReleaseSafe),
+ .bool = @as(?bool, true),
+ .int = @as(?i64, 123),
+ .float = @as(?f64, 0.5),
+ .string = @as(?[]const u8, "abc"),
+ .string_list = @as(?[]const []const u8, &.{ "a", "b", "c" }),
+ .lazy_path = @as(?std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
+ .lazy_path_list = @as(?[]const std.Build.LazyPath, &.{
+ .{ .cwd_relative = "a.txt" },
+ .{ .cwd_relative = "b.txt" },
+ .{ .cwd_relative = "c.txt" },
+ }),
+ .@"enum" = @as(?Enum, .alfa),
+ //.enum_list = @as(?[]const Enum, &.{ .alfa, .bravo, .charlie }),
+ //.build_id = @as(?std.zig.BuildId, .uuid),
+ });
+
+ if (all_specified_optional != all_specified) return error.TestFailed;
+
// Most supported option types are serialized to a string representation,
// so alternative representations of the same option value should resolve
// to the same cached dependency instance.
@@ -59,5 +103,5 @@ pub fn build(b: *std.Build) !void {
//.build_id = @as(std.zig.BuildId, .uuid),
});
- if (all_specified != all_specified_alt) return error.TestFailed;
+ if (all_specified_alt != all_specified) return error.TestFailed;
}
--
2.54.0
From e7604bba3ef0654a882edb17d712d1beb2cefec9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Mon, 24 Mar 2025 13:22:08 +0100
Subject: [PATCH 004/110] Serialize float options using the hexadecimal format
This ensures no information is lost when the value is round-tripped.
---
lib/std/Build.zig | 2 +-
lib/std/Io/Writer.zig | 16 +++++++++++-----
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index e5b9e072f7cdbe52006e5c11a1f3b49943662771..1c73767009e7ee3f1b5b80dd2c03be0522a90cad 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -507,7 +507,7 @@ fn addUserInputOptionFromArg(
.comptime_float, .float => return if (maybe_value) |v| {
map.put(field.name, .{
.name = field.name,
- .value = .{ .scalar = std.fmt.allocPrint(arena, "{e}", .{v}) catch @panic("OOM") },
+ .value = .{ .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM") },
.used = false,
}) catch @panic("OOM");
},
diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig
index 09a1c8f81b4d477fb8a589fe7592bec1588ed310..1a717f0bca0e519a5439506abd4a466e709d824c 100644
--- a/lib/std/Io/Writer.zig
+++ b/lib/std/Io/Writer.zig
@@ -1563,17 +1563,23 @@ pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number)
}
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(switch (case) {
+ const v = switch (@TypeOf(value)) {
+ // comptime_float internally is a f128; this preserves precision.
+ comptime_float => @as(f128, value),
+ else => value,
+ };
+
+ if (std.math.signbit(v)) try w.writeByte('-');
+ if (std.math.isNan(v)) return w.writeAll(switch (case) {
.lower => "nan",
.upper => "NAN",
});
- if (std.math.isInf(value)) return w.writeAll(switch (case) {
+ if (std.math.isInf(v)) return w.writeAll(switch (case) {
.lower => "inf",
.upper => "INF",
});
- const T = @TypeOf(value);
+ const T = @TypeOf(v);
const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
const mantissa_bits = std.math.floatMantissaBits(T);
@@ -1583,7 +1589,7 @@ pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precisi
const exponent_mask = (1 << exponent_bits) - 1;
const exponent_bias = (1 << (exponent_bits - 1)) - 1;
- const as_bits: TU = @bitCast(value);
+ const as_bits: TU = @bitCast(v);
var mantissa = as_bits & mantissa_mask;
var exponent: i32 = @as(u16, @truncate((as_bits >> mantissa_bits) & exponent_mask));
--
2.54.0
From 1a9fae2a70371fdbd77446fd5173162bfa065624 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Mon, 24 Mar 2025 13:25:56 +0100
Subject: [PATCH 005/110] Dupe string options
---
lib/std/Build.zig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index 1c73767009e7ee3f1b5b80dd2c03be0522a90cad..21eb5196edbbc70b37805179d0245914c6a2fd16 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -469,13 +469,13 @@ fn addUserInputOptionFromArg(
[]const u8 => return if (maybe_value) |v| {
map.put(field.name, .{
.name = field.name,
- .value = .{ .scalar = v },
+ .value = .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") },
.used = false,
}) catch @panic("OOM");
},
[]const []const u8 => return if (maybe_value) |v| {
var list = ArrayList([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
- list.appendSliceAssumeCapacity(v);
+ for (v) |s| list.appendAssumeCapacity(arena.dupe(u8, s) catch @panic("OOM"));
map.put(field.name, .{
.name = field.name,
.value = .{ .list = list },
--
2.54.0
From fd5eba9358ebf2f498b7c35bae34267f06d070d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Mon, 24 Mar 2025 00:01:28 +0100
Subject: [PATCH 006/110] Coerce slice-like arguments passed to
`b.dependency()`
You can now pass string literals as options.
---
lib/std/Build.zig | 34 +++++++++++++
test/standalone/dependency_options/build.zig | 53 ++++++++++++++++----
2 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index 21eb5196edbbc70b37805179d0245914c6a2fd16..efff88b469f3af274526884fec4a8f6b66518800 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -511,6 +511,40 @@ fn addUserInputOptionFromArg(
.used = false,
}) catch @panic("OOM");
},
+ .pointer => |ptr_info| switch (ptr_info.size) {
+ .one => switch (@typeInfo(ptr_info.child)) {
+ .array => |array_info| {
+ comptime var slice_info = ptr_info;
+ slice_info.size = .slice;
+ slice_info.is_const = true;
+ slice_info.child = array_info.child;
+ slice_info.sentinel_ptr = null;
+ addUserInputOptionFromArg(
+ arena,
+ map,
+ field,
+ @Type(.{ .pointer = slice_info }),
+ maybe_value orelse null,
+ );
+ return;
+ },
+ else => {},
+ },
+ .slice => {
+ comptime var slice_info = ptr_info;
+ slice_info.is_const = true;
+ slice_info.sentinel_ptr = null;
+ addUserInputOptionFromArg(
+ arena,
+ map,
+ field,
+ @Type(.{ .pointer = slice_info }),
+ maybe_value orelse null,
+ );
+ return;
+ },
+ else => {},
+ },
.null => unreachable,
.optional => |info| switch (@typeInfo(info.child)) {
.optional => {},
diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig
index 27ce63834d81deb365efaf8e3727ea8b669ec505..351a82ccdbcac193eb8ae6058198fa0fdb616753 100644
--- a/test/standalone/dependency_options/build.zig
+++ b/test/standalone/dependency_options/build.zig
@@ -81,25 +81,56 @@ pub fn build(b: *std.Build) !void {
if (all_specified_optional != all_specified) return error.TestFailed;
+ const all_specified_literal = b.dependency("other", .{
+ .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }),
+ .optimize = .ReleaseSafe,
+ .bool = true,
+ .int = 123,
+ .float = 0.5,
+ .string = "abc",
+ .string_list = &[_][]const u8{ "a", "b", "c" },
+ .lazy_path = @as(std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
+ .lazy_path_list = &[_]std.Build.LazyPath{
+ .{ .cwd_relative = "a.txt" },
+ .{ .cwd_relative = "b.txt" },
+ .{ .cwd_relative = "c.txt" },
+ },
+ .@"enum" = .alfa,
+ //.enum_list = &[_]Enum{ .alfa, .bravo, .charlie },
+ //.build_id = @as(std.zig.BuildId, .uuid),
+ });
+
+ if (all_specified_literal != all_specified) return error.TestFailed;
+
+ var mut_string_buf = "abc".*;
+ const mut_string: []u8 = &mut_string_buf;
+ var mut_string_list_buf = [_][]const u8{ "a", "b", "c" };
+ const mut_string_list: [][]const u8 = &mut_string_list_buf;
+ var mut_lazy_path_list_buf = [_]std.Build.LazyPath{
+ .{ .cwd_relative = "a.txt" },
+ .{ .cwd_relative = "b.txt" },
+ .{ .cwd_relative = "c.txt" },
+ };
+ const mut_lazy_path_list: []std.Build.LazyPath = &mut_lazy_path_list_buf;
+ var mut_enum_list_buf = [_]Enum{ .alfa, .bravo, .charlie };
+ const mut_enum_list: []Enum = &mut_enum_list_buf;
+ _ = mut_enum_list;
+
// Most supported option types are serialized to a string representation,
// so alternative representations of the same option value should resolve
// to the same cached dependency instance.
const all_specified_alt = b.dependency("other", .{
.target = @as(std.Target.Query, .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }),
- .optimize = @as([]const u8, "ReleaseSafe"),
+ .optimize = "ReleaseSafe",
.bool = .true,
- .int = @as([]const u8, "123"),
+ .int = "123",
.float = @as(f16, 0.5),
- .string = .abc,
- .string_list = @as([]const []const u8, &.{ "a", "b", "c" }),
+ .string = mut_string,
+ .string_list = mut_string_list,
.lazy_path = @as(std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
- .lazy_path_list = @as([]const std.Build.LazyPath, &.{
- .{ .cwd_relative = "a.txt" },
- .{ .cwd_relative = "b.txt" },
- .{ .cwd_relative = "c.txt" },
- }),
- .@"enum" = @as([]const u8, "alfa"),
- //.enum_list = @as([]const Enum, &.{ .alfa, .bravo, .charlie }),
+ .lazy_path_list = mut_lazy_path_list,
+ .@"enum" = "alfa",
+ //.enum_list = mut_enum_list,
//.build_id = @as(std.zig.BuildId, .uuid),
});
--
2.54.0
From 2c1a349fb9bc9965e309257262665564cff64a79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Mon, 24 Mar 2025 00:14:25 +0100
Subject: [PATCH 007/110] Support passing enum slices to `b.dependency()`
---
lib/std/Build.zig | 35 +++++++++++++-------
test/standalone/dependency_options/build.zig | 9 +++--
2 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index efff88b469f3af274526884fec4a8f6b66518800..39aa0f1a4b5ee765e9a1ecc0acee853389749052 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -530,18 +530,29 @@ fn addUserInputOptionFromArg(
},
else => {},
},
- .slice => {
- comptime var slice_info = ptr_info;
- slice_info.is_const = true;
- slice_info.sentinel_ptr = null;
- addUserInputOptionFromArg(
- arena,
- map,
- field,
- @Type(.{ .pointer = slice_info }),
- maybe_value orelse null,
- );
- return;
+ .slice => switch (@typeInfo(ptr_info.child)) {
+ .@"enum" => return if (maybe_value) |v| {
+ var list = ArrayList([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
+ for (v) |tag| list.appendAssumeCapacity(@tagName(tag));
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .list = list },
+ .used = false,
+ }) catch @panic("OOM");
+ },
+ else => {
+ comptime var slice_info = ptr_info;
+ slice_info.is_const = true;
+ slice_info.sentinel_ptr = null;
+ addUserInputOptionFromArg(
+ arena,
+ map,
+ field,
+ @Type(.{ .pointer = slice_info }),
+ maybe_value orelse null,
+ );
+ return;
+ },
},
else => {},
},
diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig
index 351a82ccdbcac193eb8ae6058198fa0fdb616753..de7b7101554e4055d0f0c1be61443ec0eb60ef82 100644
--- a/test/standalone/dependency_options/build.zig
+++ b/test/standalone/dependency_options/build.zig
@@ -50,7 +50,7 @@ pub fn build(b: *std.Build) !void {
.{ .cwd_relative = "c.txt" },
}),
.@"enum" = @as(Enum, .alfa),
- //.enum_list = @as([]const Enum, &.{ .alfa, .bravo, .charlie }),
+ .enum_list = @as([]const Enum, &.{ .alfa, .bravo, .charlie }),
//.build_id = @as(std.zig.BuildId, .uuid),
});
@@ -75,7 +75,7 @@ pub fn build(b: *std.Build) !void {
.{ .cwd_relative = "c.txt" },
}),
.@"enum" = @as(?Enum, .alfa),
- //.enum_list = @as(?[]const Enum, &.{ .alfa, .bravo, .charlie }),
+ .enum_list = @as(?[]const Enum, &.{ .alfa, .bravo, .charlie }),
//.build_id = @as(?std.zig.BuildId, .uuid),
});
@@ -96,7 +96,7 @@ pub fn build(b: *std.Build) !void {
.{ .cwd_relative = "c.txt" },
},
.@"enum" = .alfa,
- //.enum_list = &[_]Enum{ .alfa, .bravo, .charlie },
+ .enum_list = &[_]Enum{ .alfa, .bravo, .charlie },
//.build_id = @as(std.zig.BuildId, .uuid),
});
@@ -114,7 +114,6 @@ pub fn build(b: *std.Build) !void {
const mut_lazy_path_list: []std.Build.LazyPath = &mut_lazy_path_list_buf;
var mut_enum_list_buf = [_]Enum{ .alfa, .bravo, .charlie };
const mut_enum_list: []Enum = &mut_enum_list_buf;
- _ = mut_enum_list;
// Most supported option types are serialized to a string representation,
// so alternative representations of the same option value should resolve
@@ -130,7 +129,7 @@ pub fn build(b: *std.Build) !void {
.lazy_path = @as(std.Build.LazyPath, .{ .cwd_relative = "abc.txt" }),
.lazy_path_list = mut_lazy_path_list,
.@"enum" = "alfa",
- //.enum_list = mut_enum_list,
+ .enum_list = mut_enum_list,
//.build_id = @as(std.zig.BuildId, .uuid),
});
--
2.54.0
From ca57115da7c4603dbcefce1dc9395617e28a86f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Mon, 24 Mar 2025 14:25:47 +0100
Subject: [PATCH 008/110] Support passing `std.zig.BuildId` to `b.dependency()`
---
lib/std/Build.zig | 7 +++++++
lib/std/zig.zig | 21 +++++++++++++++++++
test/standalone/dependency_options/build.zig | 12 +++++++----
.../dependency_options/other/build.zig | 3 +++
4 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index 39aa0f1a4b5ee765e9a1ecc0acee853389749052..d6b0e68f5d4abcf42d548a6bd40678c57b2b3942 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -450,6 +450,13 @@ fn addUserInputOptionFromArg(
.used = false,
}) catch @panic("OOM");
},
+ std.zig.BuildId => return if (maybe_value) |v| {
+ map.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM") },
+ .used = false,
+ }) catch @panic("OOM");
+ },
LazyPath => return if (maybe_value) |v| {
map.put(field.name, .{
.name = field.name,
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index 486947768d7933e5633c06485ad4f7c15b78b376..2039a4d8c0efff853d0c8695e4e930ddd71d5a2a 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -321,6 +321,27 @@ pub const BuildId = union(enum) {
try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
}
+
+ pub fn format(id: BuildId, writer: *std.io.Writer) std.io.Writer.Error!void {
+ switch (id) {
+ .none, .fast, .uuid, .sha1, .md5 => {
+ try writer.writeAll(@tagName(id));
+ },
+ .hexstring => |hs| {
+ try writer.print("0x{x}", .{hs.toSlice()});
+ },
+ }
+ }
+
+ test format {
+ try std.testing.expectFmt("none", "{f}", .{@as(BuildId, .none)});
+ try std.testing.expectFmt("fast", "{f}", .{@as(BuildId, .fast)});
+ try std.testing.expectFmt("uuid", "{f}", .{@as(BuildId, .uuid)});
+ try std.testing.expectFmt("sha1", "{f}", .{@as(BuildId, .sha1)});
+ try std.testing.expectFmt("md5", "{f}", .{@as(BuildId, .md5)});
+ try std.testing.expectFmt("0x", "{f}", .{BuildId.initHexString("")});
+ try std.testing.expectFmt("0x1234cdef", "{f}", .{BuildId.initHexString("\x12\x34\xcd\xef")});
+ }
};
pub const LtoMode = enum { none, full, thin };
diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig
index de7b7101554e4055d0f0c1be61443ec0eb60ef82..20e2db1fa24f551db53a30e4ad51305f73ca6a50 100644
--- a/test/standalone/dependency_options/build.zig
+++ b/test/standalone/dependency_options/build.zig
@@ -51,7 +51,8 @@ pub fn build(b: *std.Build) !void {
}),
.@"enum" = @as(Enum, .alfa),
.enum_list = @as([]const Enum, &.{ .alfa, .bravo, .charlie }),
- //.build_id = @as(std.zig.BuildId, .uuid),
+ .build_id = @as(std.zig.BuildId, .uuid),
+ .hex_build_id = std.zig.BuildId.initHexString("\x12\x34\xcd\xef"),
});
const all_specified_mod = all_specified.module("dummy");
@@ -76,7 +77,8 @@ pub fn build(b: *std.Build) !void {
}),
.@"enum" = @as(?Enum, .alfa),
.enum_list = @as(?[]const Enum, &.{ .alfa, .bravo, .charlie }),
- //.build_id = @as(?std.zig.BuildId, .uuid),
+ .build_id = @as(?std.zig.BuildId, .uuid),
+ .hex_build_id = @as(?std.zig.BuildId, .initHexString("\x12\x34\xcd\xef")),
});
if (all_specified_optional != all_specified) return error.TestFailed;
@@ -97,7 +99,8 @@ pub fn build(b: *std.Build) !void {
},
.@"enum" = .alfa,
.enum_list = &[_]Enum{ .alfa, .bravo, .charlie },
- //.build_id = @as(std.zig.BuildId, .uuid),
+ .build_id = .uuid,
+ .hex_build_id = std.zig.BuildId.initHexString("\x12\x34\xcd\xef"),
});
if (all_specified_literal != all_specified) return error.TestFailed;
@@ -130,7 +133,8 @@ pub fn build(b: *std.Build) !void {
.lazy_path_list = mut_lazy_path_list,
.@"enum" = "alfa",
.enum_list = mut_enum_list,
- //.build_id = @as(std.zig.BuildId, .uuid),
+ .build_id = "uuid",
+ .hex_build_id = "0x1234cdef",
});
if (all_specified_alt != all_specified) return error.TestFailed;
diff --git a/test/standalone/dependency_options/other/build.zig b/test/standalone/dependency_options/other/build.zig
index fe676a5b25a8a977bd68f5de0d0a6e60ea5d944f..c18f92f14d6c3adfffc2daa800b3a93b40866fbf 100644
--- a/test/standalone/dependency_options/other/build.zig
+++ b/test/standalone/dependency_options/other/build.zig
@@ -20,6 +20,7 @@ pub fn build(b: *std.Build) !void {
const expected_enum: Enum = .alfa;
const expected_enum_list: []const Enum = &.{ .alfa, .bravo, .charlie };
const expected_build_id: std.zig.BuildId = .uuid;
+ const expected_hex_build_id: std.zig.BuildId = .initHexString("\x12\x34\xcd\xef");
const @"bool" = b.option(bool, "bool", "bool") orelse expected_bool;
const int = b.option(i64, "int", "int") orelse expected_int;
@@ -31,6 +32,7 @@ pub fn build(b: *std.Build) !void {
const @"enum" = b.option(Enum, "enum", "enum") orelse expected_enum;
const enum_list = b.option([]const Enum, "enum_list", "enum_list") orelse expected_enum_list;
const build_id = b.option(std.zig.BuildId, "build_id", "build_id") orelse expected_build_id;
+ const hex_build_id = b.option(std.zig.BuildId, "hex_build_id", "hex_build_id") orelse expected_hex_build_id;
if (@"bool" != expected_bool) return error.TestFailed;
if (int != expected_int) return error.TestFailed;
@@ -47,6 +49,7 @@ pub fn build(b: *std.Build) !void {
if (@"enum" != expected_enum) return error.TestFailed;
if (!std.mem.eql(Enum, enum_list, expected_enum_list)) return error.TestFailed;
if (!std.meta.eql(build_id, expected_build_id)) return error.TestFailed;
+ if (!hex_build_id.eql(expected_hex_build_id)) return error.TestFailed;
_ = b.addModule("dummy", .{
.root_source_file = b.path("build.zig"),
--
2.54.0
From 3c046ab9d94c96632bdfd21ad20bea0613bfc1e2 Mon Sep 17 00:00:00 2001
From: IOKG04
Date: Tue, 22 Jul 2025 12:23:16 +0200
Subject: [PATCH 009/110] `[:x]T` coerces into `[*:x]T`
https://github.com/ziglang/zig/issues/9628
---
doc/langref/test_coerce_slices_arrays_and_pointers.zig | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/doc/langref/test_coerce_slices_arrays_and_pointers.zig b/doc/langref/test_coerce_slices_arrays_and_pointers.zig
index b2fdb6c787f844bd145484f0d18be7d5536d1c41..67b2687163f58bf78c07e0873d2d41e56d1e1502 100644
--- a/doc/langref/test_coerce_slices_arrays_and_pointers.zig
+++ b/doc/langref/test_coerce_slices_arrays_and_pointers.zig
@@ -67,4 +67,11 @@ test "*T to *[1]T" {
try expect(z[0] == 1234);
}
+// Sentinel-terminated slices can be coerced into sentinel-terminated pointers
+test "[:x]T to [*:x]T" {
+ const buf: [:0]const u8 = "hello";
+ const buf2: [*:0]const u8 = buf;
+ try expect(buf2[4] == 'o');
+}
+
// test
--
2.54.0
From a91b4aab734ec17273ca8ea5e4a8afabd6193107 Mon Sep 17 00:00:00 2001
From: IOKG04
Date: Tue, 22 Jul 2025 12:32:45 +0200
Subject: [PATCH 010/110] error return traces are *not* enabled for ReleaseSafe
https://github.com/ziglang/zig/issues/24232
---
doc/langref.html.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e8189e5c420ce42559b313b9b16090a5c8b94455..348f35444d84f57b25830cafadaefc03cf21cf1b 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -3215,7 +3215,7 @@ fn createFoo(param: i32) !Foo {
to increase their development pace.
- Error Return Traces are enabled by default in {#link|Debug#} and {#link|ReleaseSafe#} builds and disabled by default in {#link|ReleaseFast#} and {#link|ReleaseSmall#} builds.
+ Error Return Traces are enabled by default in {#link|Debug#} builds and disabled by default in {#link|ReleaseFast#}, {#link|ReleaseSafe#} and {#link|ReleaseSmall#} builds.
There are a few ways to activate this error return tracing feature:
--
2.54.0
From 84ae54fbe64a15301317716e7f901d81585332d5 Mon Sep 17 00:00:00 2001
From: IOKG04
Date: Tue, 22 Jul 2025 13:15:43 +0200
Subject: [PATCH 011/110] `@rem()` and `@mod()` take `denominator != 0`, not
just `denominator > 0`
https://github.com/ziglang/zig/issues/23635
I also added tests for `@rem()` with `denominator < 0` cause there were none before
I hope I added them in the correct place, if not I can change it ofc
---
doc/langref.html.in | 4 ++--
test/behavior/math.zig | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 348f35444d84f57b25830cafadaefc03cf21cf1b..139c19211e7200afad48443f8b5e1e24fa479542 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -5179,7 +5179,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}
Modulus division. For unsigned integers this is the same as
- {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}, otherwise the
+ {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator != 0{#endsyntax#}, otherwise the
operation will result in a {#link|Remainder Division by Zero#} when runtime safety checks are enabled.
@@ -5284,7 +5284,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}
Remainder division. For unsigned integers this is the same as
- {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}, otherwise the
+ {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator != 0{#endsyntax#}, otherwise the
operation will result in a {#link|Remainder Division by Zero#} when runtime safety checks are enabled.
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
index d51be481988bdd5737f3d3a66ac4202f4fff6ce9..8b541913b94706c6563791e8515d166496b58b2c 100644
--- a/test/behavior/math.zig
+++ b/test/behavior/math.zig
@@ -531,6 +531,8 @@ fn testIntDivision() !void {
try expect(rem(i32, 10, 12) == 10);
try expect(rem(i32, -14, 12) == -2);
try expect(rem(i32, -2, 12) == -2);
+ try expect(rem(i32, 118, -12) == 10);
+ try expect(rem(i32, -14, -12) == -2);
try expect(rem(i16, -118, 12) == -10);
try expect(divTrunc(i20, 20, -5) == -4);
--
2.54.0
From 799206a3ad68f1c4ddd7d65b04e17da1974a10ee Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 23 Jul 2025 17:49:03 -0700
Subject: [PATCH 012/110] std.Progress: support progress bar escape codes
---
lib/std/Progress.zig | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index 5ee5828970cf38698097a474821a41e14971329a..a4314a73bb891da27350943844b6f73fe81084ad 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -678,6 +678,9 @@ const save = "\x1b7";
const restore = "\x1b8";
const finish_sync = "\x1b[?2026l";
+const progress_remove = "\x1b]9;4;0\x07";
+const progress_pulsing = "\x1b]9;4;3\x07";
+
const TreeSymbol = enum {
/// ├─
tee,
@@ -760,7 +763,7 @@ fn clearWrittenWithEscapeCodes() anyerror!void {
if (noop_impl or !global_progress.need_clear) return;
global_progress.need_clear = false;
- try write(clear);
+ try write(clear ++ progress_remove);
}
/// U+25BA or ►
@@ -1203,6 +1206,20 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) struct { []u8, usize } {
i, const nl_n = computeNode(buf, i, 0, serialized, children, root_node_index);
if (global_progress.terminal_mode == .ansi_escape_codes) {
+ {
+ // Set progress state https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
+ const storage = &serialized.storage[@intFromEnum(root_node_index)];
+ const estimated_total = storage.estimated_total_count;
+ const completed_items = storage.completed_count;
+ if (estimated_total == 0) {
+ buf[i..][0..progress_pulsing.len].* = progress_pulsing.*;
+ i += progress_pulsing.len;
+ } else {
+ const percent = completed_items * 100 / estimated_total;
+ i += (std.fmt.bufPrint(buf[i..], "\x1b]9;4;1;{d}\x07", .{percent}) catch &.{}).len;
+ }
+ }
+
if (nl_n > 0) {
buf[i] = '\r';
i += 1;
--
2.54.0
From b22b9ebfe055f1a358447311605be8afa823287a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 25 Jul 2025 17:33:11 -0700
Subject: [PATCH 013/110] std.Progress: introduce Status
---
lib/compiler/build_runner.zig | 8 +++--
lib/std/Progress.zig | 65 +++++++++++++++++++++++++++++++----
2 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig
index 7402a4c66d2d12815941348408829856567ee74e..6b7266ee710b974833db34f1a33410b8be384ddd 100644
--- a/lib/compiler/build_runner.zig
+++ b/lib/compiler/build_runner.zig
@@ -696,8 +696,11 @@ fn runStepNames(
.failures, .none => true,
else => false,
};
- if (failure_count == 0 and failures_only) {
- return run.cleanExit();
+ if (failure_count == 0) {
+ std.Progress.setStatus(.success);
+ if (failures_only) return run.cleanExit();
+ } else {
+ std.Progress.setStatus(.failure);
}
const ttyconf = run.ttyconf;
@@ -1149,6 +1152,7 @@ fn workerMakeOneStep(
} else |err| switch (err) {
error.MakeFailed => {
@atomicStore(Step.State, &s.state, .failure, .seq_cst);
+ std.Progress.setStatus(.failure_working);
break :handle_result;
},
error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .seq_cst),
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index a4314a73bb891da27350943844b6f73fe81084ad..2806c1a09c2f29f21e485f2b6f74279752348ff4 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -25,6 +25,7 @@ redraw_event: std.Thread.ResetEvent,
/// Accessed atomically.
done: bool,
need_clear: bool,
+status: Status,
refresh_rate_ns: u64,
initial_delay_ns: u64,
@@ -47,6 +48,22 @@ node_freelist: Freelist,
/// value may at times temporarily exceed the node count.
node_end_index: u32,
+pub const Status = enum {
+ /// Indicates the application is progressing towards completion of a task.
+ /// Unless the application is interactive, this is the only status the
+ /// program will ever have!
+ working,
+ /// The application has completed an operation, and is now waiting for user
+ /// input rather than calling exit(0).
+ success,
+ /// The application encountered an error, and is now waiting for user input
+ /// rather than calling exit(1).
+ failure,
+ /// The application encountered at least one error, but is still working on
+ /// more tasks.
+ failure_working,
+};
+
const Freelist = packed struct(u32) {
head: Node.OptionalIndex,
/// Whenever `node_freelist` is added to, this generation is incremented
@@ -383,6 +400,7 @@ var global_progress: Progress = .{
.draw_buffer = undefined,
.done = false,
.need_clear = false,
+ .status = .working,
.node_parents = &node_parents_buffer,
.node_storage = &node_storage_buffer,
@@ -498,6 +516,11 @@ pub fn start(options: Options) Node {
return root_node;
}
+pub fn setStatus(new_status: Status) void {
+ if (noop_impl) return;
+ @atomicStore(Status, &global_progress.status, new_status, .monotonic);
+}
+
/// Returns whether a resize is needed to learn the terminal size.
fn wait(timeout_ns: u64) bool {
const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_|
@@ -679,7 +702,12 @@ const restore = "\x1b8";
const finish_sync = "\x1b[?2026l";
const progress_remove = "\x1b]9;4;0\x07";
+const @"progress_normal {d}" = "\x1b]9;4;1;{d}\x07";
+const @"progress_error {d}" = "\x1b]9;4;2;{d}\x07";
const progress_pulsing = "\x1b]9;4;3\x07";
+const progress_pulsing_error = "\x1b]9;4;2\x07";
+const progress_normal_100 = "\x1b]9;4;1;100\x07";
+const progress_error_100 = "\x1b]9;4;2;100\x07";
const TreeSymbol = enum {
/// ├─
@@ -1208,15 +1236,38 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) struct { []u8, usize } {
if (global_progress.terminal_mode == .ansi_escape_codes) {
{
// Set progress state https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
- const storage = &serialized.storage[@intFromEnum(root_node_index)];
+ const root_storage = &serialized.storage[0];
+ const storage = if (root_storage.name[0] != 0 or children[0].child == .none) root_storage else &serialized.storage[@intFromEnum(children[0].child)];
const estimated_total = storage.estimated_total_count;
const completed_items = storage.completed_count;
- if (estimated_total == 0) {
- buf[i..][0..progress_pulsing.len].* = progress_pulsing.*;
- i += progress_pulsing.len;
- } else {
- const percent = completed_items * 100 / estimated_total;
- i += (std.fmt.bufPrint(buf[i..], "\x1b]9;4;1;{d}\x07", .{percent}) catch &.{}).len;
+ const status = @atomicLoad(Status, &global_progress.status, .monotonic);
+ switch (status) {
+ .working => {
+ if (estimated_total == 0) {
+ buf[i..][0..progress_pulsing.len].* = progress_pulsing.*;
+ i += progress_pulsing.len;
+ } else {
+ const percent = completed_items * 100 / estimated_total;
+ i += (std.fmt.bufPrint(buf[i..], @"progress_normal {d}", .{percent}) catch &.{}).len;
+ }
+ },
+ .success => {
+ buf[i..][0..progress_remove.len].* = progress_remove.*;
+ i += progress_remove.len;
+ },
+ .failure => {
+ buf[i..][0..progress_error_100.len].* = progress_error_100.*;
+ i += progress_error_100.len;
+ },
+ .failure_working => {
+ if (estimated_total == 0) {
+ buf[i..][0..progress_pulsing_error.len].* = progress_pulsing_error.*;
+ i += progress_pulsing_error.len;
+ } else {
+ const percent = completed_items * 100 / estimated_total;
+ i += (std.fmt.bufPrint(buf[i..], @"progress_error {d}", .{percent}) catch &.{}).len;
+ }
+ },
}
}
--
2.54.0
From 413179ccfca32691504805d04ab1359104d22144 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Thu, 23 Jan 2025 23:15:44 +0100
Subject: [PATCH 014/110] std.Build: Deprecate `Step.Compile` APIs that mutate
the root module
Not only are `Step.Compile` methods like `linkLibC()` redundant because
`Module` exposes the same APIs, it also might not be immediately obvious
to users that these methods modify the underlying root module, which can
be a footgun and lead to unintended results if the module is exported to
package consumers or shared by multiple compile steps.
Using `compile.root_module.link_libc = true` makes it more clear to
users which of the compile step and the module owns which options.
---
lib/std/Build/Step/Compile.zig | 46 +++++++++++++++++++++++++++++++---
1 file changed, 42 insertions(+), 4 deletions(-)
diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig
index 356ea4e34e8b75b52f2a0c39b0f9e89a7efe074f..141d18a7bff1139718ce05024dd1a1ce5bc93bc5 100644
--- a/lib/std/Build/Step/Compile.zig
+++ b/lib/std/Build/Step/Compile.zig
@@ -681,10 +681,14 @@ pub fn producesImplib(compile: *Compile) bool {
return compile.isDll();
}
+/// Deprecated; use `compile.root_module.link_libc = true` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn linkLibC(compile: *Compile) void {
compile.root_module.link_libc = true;
}
+/// Deprecated; use `compile.root_module.link_libcpp = true` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn linkLibCpp(compile: *Compile) void {
compile.root_module.link_libcpp = true;
}
@@ -802,10 +806,14 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
};
}
+/// Deprecated; use `compile.root_module.linkSystemLibrary(name, .{})` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn linkSystemLibrary(compile: *Compile, name: []const u8) void {
return compile.root_module.linkSystemLibrary(name, .{});
}
+/// Deprecated; use `compile.root_module.linkSystemLibrary(name, options)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn linkSystemLibrary2(
compile: *Compile,
name: []const u8,
@@ -814,22 +822,26 @@ pub fn linkSystemLibrary2(
return compile.root_module.linkSystemLibrary(name, options);
}
+/// Deprecated; use `c.root_module.linkFramework(name, .{})` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn linkFramework(c: *Compile, name: []const u8) void {
c.root_module.linkFramework(name, .{});
}
-/// Handy when you have many C/C++ source files and want them all to have the same flags.
+/// Deprecated; use `compile.root_module.addCSourceFiles(options)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addCSourceFiles(compile: *Compile, options: Module.AddCSourceFilesOptions) void {
compile.root_module.addCSourceFiles(options);
}
+/// Deprecated; use `compile.root_module.addCSourceFile(source)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addCSourceFile(compile: *Compile, source: Module.CSourceFile) void {
compile.root_module.addCSourceFile(source);
}
-/// Resource files must have the extension `.rc`.
-/// Can be called regardless of target. The .rc file will be ignored
-/// if the target object format does not support embedded resources.
+/// Deprecated; use `compile.root_module.addWin32ResourceFile(source)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addWin32ResourceFile(compile: *Compile, source: Module.RcSourceFile) void {
compile.root_module.addWin32ResourceFile(source);
}
@@ -915,54 +927,80 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
}
+/// Deprecated; use `compile.root_module.addAssemblyFile(source)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
compile.root_module.addAssemblyFile(source);
}
+/// Deprecated; use `compile.root_module.addObjectFile(source)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addObjectFile(compile: *Compile, source: LazyPath) void {
compile.root_module.addObjectFile(source);
}
+/// Deprecated; use `compile.root_module.addObject(object)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addObject(compile: *Compile, object: *Compile) void {
compile.root_module.addObject(object);
}
+/// Deprecated; use `compile.root_module.linkLibrary(library)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn linkLibrary(compile: *Compile, library: *Compile) void {
compile.root_module.linkLibrary(library);
}
+/// Deprecated; use `compile.root_module.addAfterIncludePath(lazy_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addAfterIncludePath(compile: *Compile, lazy_path: LazyPath) void {
compile.root_module.addAfterIncludePath(lazy_path);
}
+/// Deprecated; use `compile.root_module.addSystemIncludePath(lazy_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addSystemIncludePath(compile: *Compile, lazy_path: LazyPath) void {
compile.root_module.addSystemIncludePath(lazy_path);
}
+/// Deprecated; use `compile.root_module.addIncludePath(lazy_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addIncludePath(compile: *Compile, lazy_path: LazyPath) void {
compile.root_module.addIncludePath(lazy_path);
}
+/// Deprecated; use `compile.root_module.addConfigHeader(config_header)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addConfigHeader(compile: *Compile, config_header: *Step.ConfigHeader) void {
compile.root_module.addConfigHeader(config_header);
}
+/// Deprecated; use `compile.root_module.addEmbedPath(lazy_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addEmbedPath(compile: *Compile, lazy_path: LazyPath) void {
compile.root_module.addEmbedPath(lazy_path);
}
+/// Deprecated; use `compile.root_module.addLibraryPath(directory_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addLibraryPath(compile: *Compile, directory_path: LazyPath) void {
compile.root_module.addLibraryPath(directory_path);
}
+/// Deprecated; use `compile.root_module.addRPath(directory_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addRPath(compile: *Compile, directory_path: LazyPath) void {
compile.root_module.addRPath(directory_path);
}
+/// Deprecated; use `compile.root_module.addSystemFrameworkPath(directory_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addSystemFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
compile.root_module.addSystemFrameworkPath(directory_path);
}
+/// Deprecated; use `compile.root_module.addFrameworkPath(directory_path)` instead.
+/// To be removed after 0.15.0 is tagged.
pub fn addFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
compile.root_module.addFrameworkPath(directory_path);
}
--
2.54.0
From 154bd2fd0597b23b4e4c95ad86d2b8c5274161e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?=
Date: Fri, 24 Jan 2025 00:00:12 +0100
Subject: [PATCH 015/110] Migrate from deprecated `Step.Compile` APIs
---
doc/langref/build.zig | 6 +-
doc/langref/build_c.zig | 12 +-
doc/langref/build_object.zig | 12 +-
test/link/elf.zig | 738 +++++++++++------------
test/link/link.zig | 6 +-
test/link/macho.zig | 246 ++++----
test/link/wasm/extern/build.zig | 2 +-
test/src/Cases.zig | 2 +-
test/src/RunTranslatedC.zig | 2 +-
test/standalone/c_embed_path/build.zig | 6 +-
test/standalone/extern/build.zig | 4 +-
test/standalone/issue_794/build.zig | 2 +-
test/standalone/stack_iterator/build.zig | 2 +-
test/tests.zig | 4 +-
14 files changed, 527 insertions(+), 517 deletions(-)
diff --git a/doc/langref/build.zig b/doc/langref/build.zig
index ca729b5b933dbc07aefd156212d8ece17f50de53..19e4b57c08c699666a61bab5d7fe1d1fb29737c4 100644
--- a/doc/langref/build.zig
+++ b/doc/langref/build.zig
@@ -4,8 +4,10 @@ pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "example",
- .root_source_file = b.path("example.zig"),
- .optimize = optimize,
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("example.zig"),
+ .optimize = optimize,
+ }),
});
b.default_step.dependOn(&exe.step);
}
diff --git a/doc/langref/build_c.zig b/doc/langref/build_c.zig
index 08f1683e9ffc187aa6188a2f9367799377301b10..dc8e5553fc72b0e4d2b9eb64cde68ac601ce25f6 100644
--- a/doc/langref/build_c.zig
+++ b/doc/langref/build_c.zig
@@ -4,15 +4,19 @@ pub fn build(b: *std.Build) void {
const lib = b.addLibrary(.{
.linkage = .dynamic,
.name = "mathtest",
- .root_source_file = b.path("mathtest.zig"),
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("mathtest.zig"),
+ }),
.version = .{ .major = 1, .minor = 0, .patch = 0 },
});
const exe = b.addExecutable(.{
.name = "test",
+ .root_module = b.createModule(.{
+ .link_libc = true,
+ }),
});
- exe.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
- exe.linkLibrary(lib);
- exe.linkSystemLibrary("c");
+ exe.root_module.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
+ exe.root_module.linkLibrary(lib);
b.default_step.dependOn(&exe.step);
diff --git a/doc/langref/build_object.zig b/doc/langref/build_object.zig
index c08644b0d6842d4925680bef86375857c8c7b399..c9a3588d9ba68708242731fe50c576205cf502ef 100644
--- a/doc/langref/build_object.zig
+++ b/doc/langref/build_object.zig
@@ -3,15 +3,19 @@ const std = @import("std");
pub fn build(b: *std.Build) void {
const obj = b.addObject(.{
.name = "base64",
- .root_source_file = b.path("base64.zig"),
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("base64.zig"),
+ }),
});
const exe = b.addExecutable(.{
.name = "test",
+ .root_module = b.createModule(.{
+ .link_libc = true,
+ }),
});
- exe.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
- exe.addObject(obj);
- exe.linkSystemLibrary("c");
+ exe.root_module.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
+ exe.root_module.addObject(obj);
b.installArtifact(exe);
}
diff --git a/test/link/elf.zig b/test/link/elf.zig
index f6dfbbea86fc3410c405415b340c079002406ec1..1d6de32f0d249e951c7a34e2c2f375f363efe118 100644
--- a/test/link/elf.zig
+++ b/test/link/elf.zig
@@ -210,8 +210,8 @@ fn testAbsSymbols(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.addObject(obj);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -235,7 +235,7 @@ fn testAsNeeded(b: *Build, opts: Options) *Step {
\\
,
});
- main_o.linkLibC();
+ main_o.root_module.link_libc = true;
const libfoo = addSharedLibrary(b, opts, .{ .name = "foo" });
addCSourceBytes(libfoo, "int foo() { return 42; }", &.{});
@@ -253,17 +253,17 @@ fn testAsNeeded(b: *Build, opts: Options) *Step {
const exe = addExecutable(b, opts, .{
.name = "test",
});
- exe.addObject(main_o);
- exe.linkSystemLibrary2("foo", .{ .needed = true });
- exe.addLibraryPath(libfoo.getEmittedBinDirectory());
- exe.addRPath(libfoo.getEmittedBinDirectory());
- exe.linkSystemLibrary2("bar", .{ .needed = true });
- exe.addLibraryPath(libbar.getEmittedBinDirectory());
- exe.addRPath(libbar.getEmittedBinDirectory());
- exe.linkSystemLibrary2("baz", .{ .needed = true });
- exe.addLibraryPath(libbaz.getEmittedBinDirectory());
- exe.addRPath(libbaz.getEmittedBinDirectory());
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkSystemLibrary("foo", .{ .needed = true });
+ exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory());
+ exe.root_module.addRPath(libfoo.getEmittedBinDirectory());
+ exe.root_module.linkSystemLibrary("bar", .{ .needed = true });
+ exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory());
+ exe.root_module.addRPath(libbar.getEmittedBinDirectory());
+ exe.root_module.linkSystemLibrary("baz", .{ .needed = true });
+ exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory());
+ exe.root_module.addRPath(libbaz.getEmittedBinDirectory());
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("42\n");
@@ -281,17 +281,17 @@ fn testAsNeeded(b: *Build, opts: Options) *Step {
const exe = addExecutable(b, opts, .{
.name = "test",
});
- exe.addObject(main_o);
- exe.linkSystemLibrary2("foo", .{ .needed = false });
- exe.addLibraryPath(libfoo.getEmittedBinDirectory());
- exe.addRPath(libfoo.getEmittedBinDirectory());
- exe.linkSystemLibrary2("bar", .{ .needed = false });
- exe.addLibraryPath(libbar.getEmittedBinDirectory());
- exe.addRPath(libbar.getEmittedBinDirectory());
- exe.linkSystemLibrary2("baz", .{ .needed = false });
- exe.addLibraryPath(libbaz.getEmittedBinDirectory());
- exe.addRPath(libbaz.getEmittedBinDirectory());
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkSystemLibrary("foo", .{ .needed = false });
+ exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory());
+ exe.root_module.addRPath(libfoo.getEmittedBinDirectory());
+ exe.root_module.linkSystemLibrary("bar", .{ .needed = false });
+ exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory());
+ exe.root_module.addRPath(libbar.getEmittedBinDirectory());
+ exe.root_module.linkSystemLibrary("baz", .{ .needed = false });
+ exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory());
+ exe.root_module.addRPath(libbaz.getEmittedBinDirectory());
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("42\n");
@@ -351,15 +351,15 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step {
,
.pic = false,
});
- main_o.linkLibC();
+ main_o.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{
.name = "main",
});
- exe.addObject(main_o);
- exe.addObject(b_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
exe.pie = false;
const run = addRunArtifact(exe);
@@ -384,7 +384,7 @@ fn testComdatElimination(b: *Build, opts: Options) *Step {
\\}
,
});
- a_o.linkLibCpp();
+ a_o.root_module.link_libcpp = true;
const main_o = addObject(b, opts, .{
.name = "main",
@@ -401,13 +401,13 @@ fn testComdatElimination(b: *Build, opts: Options) *Step {
\\}
,
});
- main_o.linkLibCpp();
+ main_o.root_module.link_libcpp = true;
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(a_o);
- exe.addObject(main_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(main_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(
@@ -420,9 +420,9 @@ fn testComdatElimination(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
- exe.addObject(a_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(
@@ -435,12 +435,12 @@ fn testComdatElimination(b: *Build, opts: Options) *Step {
{
const c_o = addObject(b, opts, .{ .name = "c" });
- c_o.addObject(main_o);
- c_o.addObject(a_o);
+ c_o.root_module.addObject(main_o);
+ c_o.root_module.addObject(a_o);
const exe = addExecutable(b, opts, .{ .name = "main3" });
- exe.addObject(c_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(c_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(
@@ -453,12 +453,12 @@ fn testComdatElimination(b: *Build, opts: Options) *Step {
{
const d_o = addObject(b, opts, .{ .name = "d" });
- d_o.addObject(a_o);
- d_o.addObject(main_o);
+ d_o.root_module.addObject(a_o);
+ d_o.root_module.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main4" });
- exe.addObject(d_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(d_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(
@@ -522,7 +522,7 @@ fn testCommonSymbols(b: *Build, opts: Options) *Step {
\\ printf("%d %d %d\n", foo, bar, baz);
\\}
, &.{"-fcommon"});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("0 5 42\n");
@@ -549,7 +549,7 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
,
.c_source_flags = &.{"-fcommon"},
});
- a_o.linkLibC();
+ a_o.root_module.link_libc = true;
const b_o = addObject(b, opts, .{
.name = "b",
@@ -575,16 +575,16 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
});
const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
- lib.addObject(b_o);
- lib.addObject(c_o);
- lib.addObject(d_o);
+ lib.root_module.addObject(b_o);
+ lib.root_module.addObject(c_o);
+ lib.root_module.addObject(d_o);
const exe = addExecutable(b, opts, .{
.name = "test",
});
- exe.addObject(a_o);
- exe.linkLibrary(lib);
- exe.linkLibC();
+ exe.root_module.addObject(a_o);
+ exe.root_module.linkLibrary(lib);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("5 0 0 -1\n");
@@ -603,15 +603,15 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
});
const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
- lib.addObject(b_o);
- lib.addObject(e_o);
+ lib.root_module.addObject(b_o);
+ lib.root_module.addObject(e_o);
const exe = addExecutable(b, opts, .{
.name = "test",
});
- exe.addObject(a_o);
- exe.linkLibrary(lib);
- exe.linkLibC();
+ exe.root_module.addObject(a_o);
+ exe.root_module.linkLibrary(lib);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("5 0 7 2\n");
@@ -641,8 +641,8 @@ fn testCopyrel(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("3 5\n");
@@ -679,8 +679,8 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step {
\\extern int bar;
\\int *get_bar() { return &bar; }
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
exe.pie = false;
const run = addRunArtifact(exe);
@@ -712,15 +712,15 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
,
.pic = false,
});
- obj.linkLibC();
+ obj.root_module.link_libc = true;
const exp_stdout = "5\n";
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj);
- exe.linkLibrary(a_so);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(a_so);
+ exe.root_module.link_libc = true;
exe.pie = false;
const run = addRunArtifact(exe);
@@ -737,9 +737,9 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj);
- exe.linkLibrary(b_so);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(b_so);
+ exe.root_module.link_libc = true;
exe.pie = false;
const run = addRunArtifact(exe);
@@ -756,9 +756,9 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj);
- exe.linkLibrary(c_so);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(c_so);
+ exe.root_module.link_libc = true;
exe.pie = false;
const run = addRunArtifact(exe);
@@ -793,7 +793,7 @@ fn testDsoPlt(b: *Build, opts: Options) *Step {
\\ real_hello();
\\}
, &.{});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "test" });
addCSourceBytes(exe,
@@ -806,8 +806,8 @@ fn testDsoPlt(b: *Build, opts: Options) *Step {
\\ hello();
\\}
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello WORLD\n");
@@ -825,7 +825,7 @@ fn testDsoUndef(b: *Build, opts: Options) *Step {
\\int bar = 5;
\\int baz() { return foo; }
, &.{});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const obj = addObject(b, opts, .{
.name = "obj",
@@ -833,18 +833,18 @@ fn testDsoUndef(b: *Build, opts: Options) *Step {
});
const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const exe = addExecutable(b, opts, .{ .name = "test" });
- exe.linkLibrary(dso);
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.linkLibrary(lib);
addCSourceBytes(exe,
\\extern int bar;
\\int main() {
\\ return bar - 5;
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -871,7 +871,7 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step {
\\ std.debug.print("foo={d}\n", .{foo()});
\\}
});
- a_o.linkLibC();
+ a_o.root_module.link_libc = true;
const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
\\#include
@@ -880,11 +880,11 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step {
\\ fprintf(stderr, "bar=%d\n", bar);
\\}
});
- b_o.linkLibC();
+ b_o.root_module.link_libc = true;
const c_o = addObject(b, opts, .{ .name = "c" });
- c_o.addObject(a_o);
- c_o.addObject(b_o);
+ c_o.root_module.addObject(a_o);
+ c_o.root_module.addObject(b_o);
const exe = addExecutable(b, opts, .{ .name = "test", .zig_source_bytes =
\\const std = @import("std");
@@ -895,8 +895,8 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step {
\\ printBar();
\\}
});
- exe.addObject(c_o);
- exe.linkLibC();
+ exe.root_module.addObject(c_o);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdErrEqual(
@@ -944,9 +944,9 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
});
const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
- lib.addObject(obj1);
- lib.addObject(obj2);
- lib.addObject(obj3);
+ lib.root_module.addObject(obj1);
+ lib.root_module.addObject(obj2);
+ lib.root_module.addObject(obj3);
const check = lib.checkObject();
check.checkInArchiveSymtab();
@@ -996,7 +996,7 @@ fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
\\}
,
});
- lib.addObject(obj1);
+ lib.root_module.addObject(obj1);
const exe = addExecutable(b, opts, .{
.name = "test",
@@ -1008,7 +1008,7 @@ fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(lib);
const run = addRunArtifact(exe);
run.expectStdErrEqual("44");
@@ -1023,7 +1023,7 @@ fn testEmptyObject(b: *Build, opts: Options) *Step {
const exe = addExecutable(b, opts, .{ .name = "test" });
addCSourceBytes(exe, "int main() { return 0; }", &.{});
addCSourceBytes(exe, "", &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -1052,8 +1052,8 @@ fn testEntryPoint(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(a_o);
- exe.addObject(b_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
exe.entry = .{ .symbol_name = "foo" };
const check = exe.checkObject();
@@ -1068,8 +1068,8 @@ fn testEntryPoint(b: *Build, opts: Options) *Step {
// cause an artifact collision taking the cached executable from the above
// step instead of generating a new one.
const exe = addExecutable(b, opts, .{ .name = "other" });
- exe.addObject(a_o);
- exe.addObject(b_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
exe.entry = .{ .symbol_name = "bar" };
const check = exe.checkObject();
@@ -1113,8 +1113,8 @@ fn testExportDynamic(b: *Build, opts: Options) *Step {
\\ return baz;
\\}
, &.{});
- exe.addObject(obj);
- exe.linkLibrary(dso);
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(dso);
exe.rdynamic = true;
const check = exe.checkObject();
@@ -1152,8 +1152,8 @@ fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
\\ foo();
\\}
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInDynamicSymtab();
@@ -1181,7 +1181,7 @@ fn testFuncAddress(b: *Build, opts: Options) *Step {
\\ assert(fn == ptr);
\\}
, &.{});
- exe.linkLibrary(dso);
+ exe.root_module.linkLibrary(dso);
exe.root_module.pic = false;
exe.pie = false;
@@ -1216,15 +1216,15 @@ fn testGcSections(b: *Build, opts: Options) *Step {
});
obj.link_function_sections = true;
obj.link_data_sections = true;
- obj.linkLibC();
- obj.linkLibCpp();
+ obj.root_module.link_libc = true;
+ obj.root_module.link_libcpp = true;
{
const exe = addExecutable(b, opts, .{ .name = "test" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = false;
- exe.linkLibC();
- exe.linkLibCpp();
+ exe.root_module.link_libc = true;
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2\n");
@@ -1252,10 +1252,10 @@ fn testGcSections(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "test" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = true;
- exe.linkLibC();
- exe.linkLibCpp();
+ exe.root_module.link_libc = true;
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2\n");
@@ -1321,7 +1321,7 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = false;
const run = addRunArtifact(exe);
@@ -1363,7 +1363,7 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = true;
const run = addRunArtifact(exe);
@@ -1427,7 +1427,7 @@ fn testIFuncAlias(b: *Build, opts: Options) *Step {
\\}
, &.{});
exe.root_module.pic = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -1467,9 +1467,9 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step {
\\ assert(foo == p);
\\}
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
- exe.linkSystemLibrary2("dl", .{});
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
+ exe.root_module.linkSystemLibrary("dl", .{});
exe.root_module.pic = false;
exe.pie = false;
@@ -1498,7 +1498,7 @@ fn testIFuncDso(b: *Build, opts: Options) *Step {
\\}
,
});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{
.name = "main",
@@ -1509,7 +1509,7 @@ fn testIFuncDso(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.linkLibrary(dso);
+ exe.root_module.linkLibrary(dso);
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
@@ -1540,7 +1540,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe, main_c, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
exe.link_z_lazy = true;
const run = addRunArtifact(exe);
@@ -1550,7 +1550,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "other" });
addCSourceBytes(exe, main_c, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
@@ -1576,7 +1576,7 @@ fn testIFuncExport(b: *Build, opts: Options) *Step {
\\ return real_foobar;
\\}
, &.{});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const check = dso.checkObject();
check.checkInDynamicSymtab();
@@ -1613,7 +1613,7 @@ fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
\\}
, &.{});
exe.root_module.pic = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("3\n");
@@ -1642,7 +1642,7 @@ fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
\\}
, &.{"-fno-plt"});
exe.root_module.pic = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
@@ -1669,7 +1669,7 @@ fn testIFuncStatic(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
exe.linkage = .static;
const run = addRunArtifact(exe);
@@ -1700,7 +1700,7 @@ fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
exe.linkage = .static;
exe.root_module.pic = true;
exe.pie = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
@@ -1733,7 +1733,7 @@ fn testImageBase(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
exe.image_base = 0x8000000;
const run = addRunArtifact(exe);
@@ -1779,7 +1779,7 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
\\void printFoo() { fprintf(stderr, "lib foo=%d\n", foo); }
,
});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const main = addExecutable(b, opts, .{
.name = "main",
@@ -1798,7 +1798,7 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
.strip = true, // TODO temp hack
});
main.pie = true;
- main.linkLibrary(dso);
+ main.root_module.linkLibrary(dso);
const run = addRunArtifact(main);
run.expectStdErrEqual(
@@ -1832,7 +1832,7 @@ fn testImportingDataStatic(b: *Build, opts: Options) *Step {
}, .{
.name = "a",
});
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const main = addExecutable(b, opts, .{
.name = "main",
@@ -1844,8 +1844,8 @@ fn testImportingDataStatic(b: *Build, opts: Options) *Step {
,
.strip = true, // TODO temp hack
});
- main.linkLibrary(lib);
- main.linkLibC();
+ main.root_module.linkLibrary(lib);
+ main.root_module.link_libc = true;
const run = addRunArtifact(main);
run.expectStdErrEqual("42\n");
@@ -1864,7 +1864,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((constructor(10000))) void init4() { printf("1"); }
,
});
- a_o.linkLibC();
+ a_o.root_module.link_libc = true;
const b_o = addObject(b, opts, .{
.name = "b",
@@ -1873,7 +1873,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((constructor(1000))) void init3() { printf("2"); }
,
});
- b_o.linkLibC();
+ b_o.root_module.link_libc = true;
const c_o = addObject(b, opts, .{
.name = "c",
@@ -1882,7 +1882,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((constructor)) void init1() { printf("3"); }
,
});
- c_o.linkLibC();
+ c_o.root_module.link_libc = true;
const d_o = addObject(b, opts, .{
.name = "d",
@@ -1891,7 +1891,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((constructor)) void init2() { printf("4"); }
,
});
- d_o.linkLibC();
+ d_o.root_module.link_libc = true;
const e_o = addObject(b, opts, .{
.name = "e",
@@ -1900,7 +1900,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((destructor(10000))) void fini4() { printf("5"); }
,
});
- e_o.linkLibC();
+ e_o.root_module.link_libc = true;
const f_o = addObject(b, opts, .{
.name = "f",
@@ -1909,7 +1909,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((destructor(1000))) void fini3() { printf("6"); }
,
});
- f_o.linkLibC();
+ f_o.root_module.link_libc = true;
const g_o = addObject(b, opts, .{
.name = "g",
@@ -1918,24 +1918,24 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step {
\\__attribute__((destructor)) void fini1() { printf("7"); }
,
});
- g_o.linkLibC();
+ g_o.root_module.link_libc = true;
const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes =
\\#include
\\__attribute__((destructor)) void fini2() { printf("8"); }
});
- h_o.linkLibC();
+ h_o.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe, "int main() { return 0; }", &.{});
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(c_o);
- exe.addObject(d_o);
- exe.addObject(e_o);
- exe.addObject(f_o);
- exe.addObject(g_o);
- exe.addObject(h_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(c_o);
+ exe.root_module.addObject(d_o);
+ exe.root_module.addObject(e_o);
+ exe.root_module.addObject(f_o);
+ exe.root_module.addObject(g_o);
+ exe.root_module.addObject(h_o);
if (opts.target.result.isGnuLibC()) {
// TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets
@@ -1970,7 +1970,7 @@ fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
\\}
, &.{});
dso.link_function_sections = true;
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const check = dso.checkObject();
check.checkInSymtab();
@@ -1986,8 +1986,8 @@ fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
\\void greet();
\\int main() { greet(); }
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world");
@@ -2021,7 +2021,7 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
\\}
, &.{});
exe.link_function_sections = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInSymtab();
@@ -2049,7 +2049,7 @@ fn testLargeBss(b: *Build, opts: Options) *Step {
\\ return arr[2000];
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
// Disabled to work around the ELF linker crashing.
// Can be reproduced on a x86_64-linux host by commenting out the line below.
exe.root_module.sanitize_c = .off;
@@ -2071,10 +2071,10 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
});
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(obj);
+ dso.root_module.addObject(obj);
const lib = addStaticLibrary(b, opts, .{ .name = "b" });
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const main_o = addObject(b, opts, .{
.name = "main",
@@ -2089,14 +2089,14 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
// https://github.com/ziglang/zig/issues/17450
// {
// const exe = addExecutable(b, opts, .{ .name = "main1"});
- // exe.addObject(main_o);
- // exe.linkSystemLibrary2("a", .{});
- // exe.addLibraryPath(dso.getEmittedBinDirectory());
- // exe.addRPath(dso.getEmittedBinDirectory());
- // exe.linkSystemLibrary2("b", .{});
- // exe.addLibraryPath(lib.getEmittedBinDirectory());
- // exe.addRPath(lib.getEmittedBinDirectory());
- // exe.linkLibC();
+ // exe.root_module.addObject(main_o);
+ // exe.root_module.linkSystemLibrary("a", .{});
+ // exe.root_module.addLibraryPath(dso.getEmittedBinDirectory());
+ // exe.root_module.addRPath(dso.getEmittedBinDirectory());
+ // exe.root_module.linkSystemLibrary("b", .{});
+ // exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
+ // exe.root_module.addRPath(lib.getEmittedBinDirectory());
+ // exe.root_module.link_libc = true;
// const check = exe.checkObject();
// check.checkInDynamicSection();
@@ -2106,14 +2106,14 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
- exe.linkSystemLibrary2("b", .{});
- exe.addLibraryPath(lib.getEmittedBinDirectory());
- exe.addRPath(lib.getEmittedBinDirectory());
- exe.linkSystemLibrary2("a", .{});
- exe.addLibraryPath(dso.getEmittedBinDirectory());
- exe.addRPath(dso.getEmittedBinDirectory());
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkSystemLibrary("b", .{});
+ exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
+ exe.root_module.addRPath(lib.getEmittedBinDirectory());
+ exe.root_module.linkSystemLibrary("a", .{});
+ exe.root_module.addLibraryPath(dso.getEmittedBinDirectory());
+ exe.root_module.addRPath(dso.getEmittedBinDirectory());
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInDynamicSection();
@@ -2149,14 +2149,14 @@ fn testLdScript(b: *Build, opts: Options) *Step {
\\ return bar() - baz();
\\}
, &.{});
- exe.linkSystemLibrary2("a", .{});
- exe.addLibraryPath(scripts.getDirectory());
- exe.addLibraryPath(scripts2.getDirectory());
- exe.addLibraryPath(bar.getEmittedBinDirectory());
- exe.addLibraryPath(baz.getEmittedBinDirectory());
- exe.addRPath(bar.getEmittedBinDirectory());
- exe.addRPath(baz.getEmittedBinDirectory());
- exe.linkLibC();
+ exe.root_module.linkSystemLibrary("a", .{});
+ exe.root_module.addLibraryPath(scripts.getDirectory());
+ exe.root_module.addLibraryPath(scripts2.getDirectory());
+ exe.root_module.addLibraryPath(bar.getEmittedBinDirectory());
+ exe.root_module.addLibraryPath(baz.getEmittedBinDirectory());
+ exe.root_module.addRPath(bar.getEmittedBinDirectory());
+ exe.root_module.addRPath(baz.getEmittedBinDirectory());
+ exe.root_module.link_libc = true;
exe.allow_so_scripts = true;
const run = addRunArtifact(exe);
@@ -2174,9 +2174,9 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe, "int main() { return 0; }", &.{});
- exe.linkSystemLibrary2("a", .{});
- exe.addLibraryPath(scripts.getDirectory());
- exe.linkLibC();
+ exe.root_module.linkSystemLibrary("a", .{});
+ exe.root_module.addLibraryPath(scripts.getDirectory());
+ exe.root_module.link_libc = true;
exe.allow_so_scripts = true;
// TODO: A future enhancement could make this error message also mention
@@ -2213,8 +2213,8 @@ fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.linkLibrary(so);
- exe.linkLibC();
+ exe.root_module.linkLibrary(so);
+ exe.root_module.link_libc = true;
exe.allow_so_scripts = true;
const run = addRunArtifact(exe);
@@ -2269,8 +2269,8 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
\\ return foo;
\\}
, &.{});
- exe.addObject(obj);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libc = true;
expectLinkErrors(exe, test_step, .{ .exact = &.{
"invalid ELF machine type: AARCH64",
@@ -2291,7 +2291,7 @@ fn testLinkingC(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello World!\n");
@@ -2320,8 +2320,8 @@ fn testLinkingCpp(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibC();
- exe.linkLibCpp();
+ exe.root_module.link_libc = true;
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello World!\n");
@@ -2364,7 +2364,7 @@ fn testLinkingObj(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
const run = addRunArtifact(exe);
run.expectStdErrEqual("84\n");
@@ -2389,7 +2389,7 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
\\}
,
});
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const exe = addExecutable(b, opts, .{
.name = "testlib",
@@ -2402,7 +2402,7 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(lib);
const run = addRunArtifact(exe);
run.expectStdErrEqual("0\n");
@@ -2452,7 +2452,7 @@ fn testMergeStrings(b: *Build, opts: Options) *Step {
\\char16_t *utf16_1 = u"foo";
\\char32_t *utf32_1 = U"foo";
, &.{"-O2"});
- obj1.linkLibC();
+ obj1.root_module.link_libc = true;
const obj2 = addObject(b, opts, .{ .name = "b.o" });
addCSourceBytes(obj2,
@@ -2481,12 +2481,12 @@ fn testMergeStrings(b: *Build, opts: Options) *Step {
\\ assert((void*)wide1 != (void*)utf16_1);
\\}
, &.{"-O2"});
- obj2.linkLibC();
+ obj2.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj1);
- exe.addObject(obj2);
- exe.linkLibC();
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj2);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -2520,8 +2520,8 @@ fn testMergeStrings2(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(obj1);
- exe.addObject(obj2);
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj2);
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -2537,11 +2537,11 @@ fn testMergeStrings2(b: *Build, opts: Options) *Step {
{
const obj3 = addObject(b, opts, .{ .name = "c" });
- obj3.addObject(obj1);
- obj3.addObject(obj2);
+ obj3.root_module.addObject(obj1);
+ obj3.root_module.addObject(obj2);
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(obj3);
+ exe.root_module.addObject(obj3);
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -2564,7 +2564,7 @@ fn testNoEhFrameHdr(b: *Build, opts: Options) *Step {
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe, "int main() { return 0; }", &.{});
exe.link_eh_frame_hdr = false;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInHeaders();
@@ -2586,7 +2586,7 @@ fn testPie(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
exe.root_module.pic = true;
exe.pie = true;
@@ -2617,7 +2617,7 @@ fn testPltGot(b: *Build, opts: Options) *Step {
\\ printf("Hello world\n");
\\}
, &.{});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe,
@@ -2626,9 +2626,9 @@ fn testPltGot(b: *Build, opts: Options) *Step {
\\void foo() { ignore(hello); }
\\int main() { hello(); }
, &.{});
- exe.linkLibrary(dso);
+ exe.root_module.linkLibrary(dso);
exe.root_module.pic = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
@@ -2647,7 +2647,7 @@ fn testPreinitArray(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
const check = exe.checkObject();
check.checkInDynamicSection();
@@ -2662,7 +2662,7 @@ fn testPreinitArray(b: *Build, opts: Options) *Step {
\\__attribute__((section(".preinit_array")))
\\void *preinit[] = { preinit_fn };
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInDynamicSection();
@@ -2710,15 +2710,15 @@ fn testRelocatableArchive(b: *Build, opts: Options) *Step {
});
const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
- lib.addObject(obj1);
- lib.addObject(obj2);
- lib.addObject(obj3);
+ lib.root_module.addObject(obj1);
+ lib.root_module.addObject(obj2);
+ lib.root_module.addObject(obj3);
const obj5 = addObject(b, opts, .{
.name = "obj5",
});
- obj5.addObject(obj4);
- obj5.linkLibrary(lib);
+ obj5.root_module.addObject(obj4);
+ obj5.root_module.linkLibrary(lib);
const check = obj5.checkObject();
check.checkInSymtab();
@@ -2744,7 +2744,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
\\}
,
});
- obj1.linkLibCpp();
+ obj1.root_module.link_libcpp = true;
const obj2 = addObject(b, opts, .{
.name = "obj2",
.cpp_source_bytes =
@@ -2754,7 +2754,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
\\}
,
});
- obj2.linkLibCpp();
+ obj2.root_module.link_libcpp = true;
const obj3 = addObject(b, opts, .{ .name = "obj3", .cpp_source_bytes =
\\#include
\\#include
@@ -2768,18 +2768,18 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
});
- obj3.linkLibCpp();
+ obj3.root_module.link_libcpp = true;
{
const obj = addObject(b, opts, .{ .name = "obj" });
- obj.addObject(obj1);
- obj.addObject(obj2);
- obj.linkLibCpp();
+ obj.root_module.addObject(obj1);
+ obj.root_module.addObject(obj2);
+ obj.root_module.link_libcpp = true;
const exe = addExecutable(b, opts, .{ .name = "test1" });
- exe.addObject(obj3);
- exe.addObject(obj);
- exe.linkLibCpp();
+ exe.root_module.addObject(obj3);
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("exception=Oh no!");
@@ -2788,14 +2788,14 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
{
// Flipping the order should not influence the end result.
const obj = addObject(b, opts, .{ .name = "obj" });
- obj.addObject(obj2);
- obj.addObject(obj1);
- obj.linkLibCpp();
+ obj.root_module.addObject(obj2);
+ obj.root_module.addObject(obj1);
+ obj.root_module.link_libcpp = true;
const exe = addExecutable(b, opts, .{ .name = "test2" });
- exe.addObject(obj3);
- exe.addObject(obj);
- exe.linkLibCpp();
+ exe.root_module.addObject(obj3);
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("exception=Oh no!");
@@ -2817,7 +2817,7 @@ fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
\\}
,
});
- obj1.linkLibCpp();
+ obj1.root_module.link_libcpp = true;
const obj2 = addObject(b, opts, .{
.name = "obj2",
.cpp_source_bytes =
@@ -2827,7 +2827,7 @@ fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
\\}
,
});
- obj2.linkLibCpp();
+ obj2.root_module.link_libcpp = true;
const obj3 = addObject(b, opts, .{
.name = "obj3",
.cpp_source_bytes =
@@ -2844,17 +2844,17 @@ fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
\\}
,
});
- obj3.linkLibCpp();
+ obj3.root_module.link_libcpp = true;
const obj = addObject(b, opts, .{ .name = "obj" });
- obj.addObject(obj1);
- obj.addObject(obj2);
- obj.addObject(obj3);
- obj.linkLibCpp();
+ obj.root_module.addObject(obj1);
+ obj.root_module.addObject(obj2);
+ obj.root_module.addObject(obj3);
+ obj.root_module.link_libcpp = true;
const exe = addExecutable(b, opts, .{ .name = "test2" });
- exe.addObject(obj);
- exe.linkLibCpp();
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("exception=Oh no!");
@@ -2880,7 +2880,7 @@ fn testRelocatableMergeStrings(b: *Build, opts: Options) *Step {
});
const obj2 = addObject(b, opts, .{ .name = "b" });
- obj2.addObject(obj1);
+ obj2.root_module.addObject(obj1);
const check = obj2.checkObject();
check.dumpSection(".rodata.str1.1");
@@ -2905,7 +2905,7 @@ fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step {
const obj2 = addObject(b, opts, .{
.name = "obj2",
});
- obj2.addObject(obj1);
+ obj2.root_module.addObject(obj1);
const check1 = obj1.checkObject();
check1.checkInHeaders();
@@ -2940,12 +2940,12 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- obj.linkLibC();
+ obj.root_module.link_libc = true;
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(obj);
- exe.linkLibrary(dso);
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(dso);
exe.pie = true;
const run = addRunArtifact(exe);
@@ -2965,8 +2965,8 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
// https://github.com/ziglang/zig/issues/17430
// {
// const exe = addExecutable(b, opts, .{ .name = "main2"});
- // exe.addObject(obj);
- // exe.linkLibrary(dso);
+ // exe.root_module.addObject(obj);
+ // exe.root_module.linkLibrary(dso);
// exe.pie = false;
// const run = addRunArtifact(exe);
@@ -2999,13 +2999,13 @@ fn testStrip(b: *Build, opts: Options) *Step {
\\}
,
});
- obj.linkLibC();
+ obj.root_module.link_libc = true;
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.root_module.strip = false;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInHeaders();
@@ -3016,9 +3016,9 @@ fn testStrip(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.root_module.strip = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInHeaders();
@@ -3074,7 +3074,7 @@ fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
{
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(obj);
+ dso.root_module.addObject(obj);
// dso.link_relax = true;
const check = dso.checkObject();
@@ -3086,7 +3086,7 @@ fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
// TODO add -Wl,--no-relax
// {
// const dso = addSharedLibrary(b, opts, .{ .name = "a"});
- // dso.addObject(obj);
+ // dso.root_module.addObject(obj);
// dso.link_relax = false;
// const check = dso.checkObject();
@@ -3128,8 +3128,8 @@ fn testTlsDso(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("5 3 5 3 5 3\n");
@@ -3159,7 +3159,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- main_o.linkLibC();
+ main_o.root_module.link_libc = true;
const a_o = addObject(b, opts, .{
.name = "a",
@@ -3184,17 +3184,17 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
const exp_stdout = "1 2 3 4 5 6\n";
const dso1 = addSharedLibrary(b, opts, .{ .name = "a" });
- dso1.addObject(a_o);
+ dso1.root_module.addObject(a_o);
const dso2 = addSharedLibrary(b, opts, .{ .name = "b" });
- dso2.addObject(b_o);
+ dso2.root_module.addObject(b_o);
// dso2.link_relax = false; // TODO
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
- exe.linkLibrary(dso1);
- exe.linkLibrary(dso2);
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkLibrary(dso1);
+ exe.root_module.linkLibrary(dso2);
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -3203,10 +3203,10 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
// exe.link_relax = false; // TODO
- exe.linkLibrary(dso1);
- exe.linkLibrary(dso2);
+ exe.root_module.linkLibrary(dso1);
+ exe.root_module.linkLibrary(dso2);
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -3216,9 +3216,9 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
// https://github.com/ziglang/zig/issues/17430 ??
// {
// const exe = addExecutable(b, opts, .{ .name = "main3"});
- // exe.addObject(main_o);
- // exe.linkLibrary(dso1);
- // exe.linkLibrary(dso2);
+ // exe.root_module.addObject(main_o);
+ // exe.root_module.linkLibrary(dso1);
+ // exe.root_module.linkLibrary(dso2);
// exe.linkage = .static;
// const run = addRunArtifact(exe);
@@ -3228,10 +3228,10 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
// {
// const exe = addExecutable(b, opts, .{ .name = "main4"});
- // exe.addObject(main_o);
+ // exe.root_module.addObject(main_o);
// // exe.link_relax = false; // TODO
- // exe.linkLibrary(dso1);
- // exe.linkLibrary(dso2);
+ // exe.root_module.linkLibrary(dso1);
+ // exe.root_module.linkLibrary(dso2);
// exe.linkage = .static;
// const run = addRunArtifact(exe);
@@ -3265,7 +3265,7 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
.c_source_flags = &.{"-fno-plt"},
.pic = true,
});
- obj.linkLibC();
+ obj.root_module.link_libc = true;
const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
addCSourceBytes(a_so,
@@ -3284,10 +3284,10 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(obj);
- exe.linkLibrary(a_so);
- exe.linkLibrary(b_so);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(a_so);
+ exe.root_module.linkLibrary(b_so);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2 3 4 5 6\n");
@@ -3296,10 +3296,10 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(obj);
- exe.linkLibrary(a_so);
- exe.linkLibrary(b_so);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.linkLibrary(a_so);
+ exe.root_module.linkLibrary(b_so);
+ exe.root_module.link_libc = true;
// exe.link_relax = false; // TODO
const run = addRunArtifact(exe);
@@ -3329,7 +3329,7 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- a_o.linkLibC();
+ a_o.root_module.link_libc = true;
const b_o = addObject(b, opts, .{
.name = "b",
@@ -3342,12 +3342,12 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
{
const dso = addSharedLibrary(b, opts, .{ .name = "a1" });
- dso.addObject(a_o);
+ dso.root_module.addObject(a_o);
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(b_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(b_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2 3\n");
@@ -3356,13 +3356,13 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
{
const dso = addSharedLibrary(b, opts, .{ .name = "a2" });
- dso.addObject(a_o);
+ dso.root_module.addObject(a_o);
// dso.link_relax = false; // TODO
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(b_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(b_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2 3\n");
@@ -3371,12 +3371,12 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
// {
// const dso = addSharedLibrary(b, opts, .{ .name = "a"});
- // dso.addObject(a_o);
+ // dso.root_module.addObject(a_o);
// dso.link_z_nodlopen = true;
// const exe = addExecutable(b, opts, .{ .name = "main"});
- // exe.addObject(b_o);
- // exe.linkLibrary(dso);
+ // exe.root_module.addObject(b_o);
+ // exe.root_module.linkLibrary(dso);
// const run = addRunArtifact(exe);
// run.expectStdOutEqual("1 2 3\n");
@@ -3385,13 +3385,13 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step {
// {
// const dso = addSharedLibrary(b, opts, .{ .name = "a"});
- // dso.addObject(a_o);
+ // dso.root_module.addObject(a_o);
// dso.link_relax = false;
// dso.link_z_nodlopen = true;
// const exe = addExecutable(b, opts, .{ .name = "main"});
- // exe.addObject(b_o);
- // exe.linkLibrary(dso);
+ // exe.root_module.addObject(b_o);
+ // exe.root_module.linkLibrary(dso);
// const run = addRunArtifact(exe);
// run.expectStdOutEqual("1 2 3\n");
@@ -3417,7 +3417,7 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
\\ printf("%d %d ", foo, bar);
\\}
, &.{});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const main_o = addObject(b, opts, .{
.name = "main",
@@ -3435,15 +3435,15 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
\\}
,
});
- main_o.linkLibC();
+ main_o.root_module.link_libc = true;
const exp_stdout = "0 0 3 5 7\n";
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -3452,9 +3452,9 @@ fn testTlsIe(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
// exe.link_relax = false; // TODO
const run = addRunArtifact(exe);
@@ -3500,17 +3500,17 @@ fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- c_o.linkLibC();
+ c_o.root_module.link_libc = true;
{
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(a_o);
- dso.addObject(b_o);
+ dso.root_module.addObject(a_o);
+ dso.root_module.addObject(b_o);
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(c_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(c_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("42 1 2 3\n");
@@ -3519,10 +3519,10 @@ fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(c_o);
- exe.linkLibC();
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(c_o);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("42 1 2 3\n");
@@ -3555,7 +3555,7 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
\\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[1023], y[0], y[1], y[1023]);
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
// Disabled to work around the ELF linker crashing.
// Can be reproduced on a x86_64-linux host by commenting out the line below.
exe.root_module.sanitize_c = .off;
@@ -3580,7 +3580,7 @@ fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
\\}
, &.{});
exe.root_module.pic = true;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2 3 0 5\n");
@@ -3609,7 +3609,7 @@ fn testTlsLd(b: *Build, opts: Options) *Step {
.c_source_flags = &.{"-ftls-model=local-dynamic"},
.pic = true,
});
- main_o.linkLibC();
+ main_o.root_module.link_libc = true;
const a_o = addObject(b, opts, .{
.name = "a",
@@ -3622,9 +3622,9 @@ fn testTlsLd(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
- exe.addObject(a_o);
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -3633,9 +3633,9 @@ fn testTlsLd(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
- exe.addObject(a_o);
- exe.linkLibC();
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.link_libc = true;
// exe.link_relax = false; // TODO
const run = addRunArtifact(exe);
@@ -3668,8 +3668,8 @@ fn testTlsLdDso(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("1 2\n");
@@ -3699,7 +3699,7 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
.c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
.pic = true,
});
- a_o.linkLibC();
+ a_o.root_module.link_libc = true;
const b_o = addObject(b, opts, .{
.name = "b",
@@ -3710,9 +3710,9 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.linkLibC();
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("3 5 3 5\n");
@@ -3721,9 +3721,9 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.linkLibC();
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.link_libc = true;
// exe.link_relax = false; // TODO
const run = addRunArtifact(exe);
@@ -3756,7 +3756,7 @@ fn testTlsNoPic(b: *Build, opts: Options) *Step {
\\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
, &.{});
exe.root_module.pic = false;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("3 5 3 5\n");
@@ -3784,7 +3784,7 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
\\ return NULL;
\\}
, &.{});
- dso.linkLibC();
+ dso.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe,
@@ -3811,8 +3811,8 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
\\ pthread_join(thread, NULL);
\\}
, &.{});
- exe.addRPath(dso.getEmittedBinDirectory());
- exe.linkLibC();
+ exe.root_module.addRPath(dso.getEmittedBinDirectory());
+ exe.root_module.link_libc = true;
exe.root_module.pic = true;
const run = addRunArtifact(exe);
@@ -3842,14 +3842,14 @@ fn testTlsPic(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- obj.linkLibC();
+ obj.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe,
\\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3;
, &.{});
- exe.addObject(obj);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("3 5 3 5\n");
@@ -3889,14 +3889,14 @@ fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- c_o.linkLibC();
+ c_o.root_module.link_libc = true;
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(c_o);
- exe.linkLibC();
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(c_o);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("42\n");
@@ -3905,13 +3905,13 @@ fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
{
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(a_o);
- dso.addObject(b_o);
+ dso.root_module.addObject(a_o);
+ dso.root_module.addObject(b_o);
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(c_o);
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.addObject(c_o);
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("42\n");
@@ -3939,7 +3939,7 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(
@@ -3969,8 +3969,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
\\ return foo;
\\}
, &.{});
- exe.linkLibrary(dylib);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dylib);
+ exe.root_module.link_libc = true;
expectLinkErrors(exe, test_step, .{
.contains = "error: failed to parse shared library: BadMagic",
@@ -3993,7 +3993,7 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {
,
.c_source_flags = &.{"-ffunction-sections"},
});
- obj1.linkLibC();
+ obj1.root_module.link_libc = true;
const obj2 = addObject(b, opts, .{
.name = "b",
@@ -4007,12 +4007,12 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {
,
.c_source_flags = &.{"-ffunction-sections"},
});
- obj2.linkLibC();
+ obj2.root_module.link_libc = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj1);
- exe.addObject(obj2);
- exe.linkLibC();
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj2);
+ exe.root_module.link_libc = true;
expectLinkErrors(exe, test_step, .{ .exact = &.{
"error: undefined symbol: foo",
@@ -4037,12 +4037,12 @@ fn testWeakExports(b: *Build, opts: Options) *Step {
,
.pic = true,
});
- obj.linkLibC();
+ obj.root_module.link_libc = true;
{
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(obj);
- dso.linkLibC();
+ dso.root_module.addObject(obj);
+ dso.root_module.link_libc = true;
const check = dso.checkObject();
check.checkInDynamicSymtab();
@@ -4052,8 +4052,8 @@ fn testWeakExports(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj);
- exe.linkLibC();
+ exe.root_module.addObject(obj);
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInDynamicSymtab();
@@ -4084,8 +4084,8 @@ fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
\\int bar();
\\int main() { printf("bar=%d\n", bar()); }
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("bar=-1\n");
@@ -4100,8 +4100,8 @@ fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
\\int bar();
\\int main() { printf("bar=%d\n", bar()); }
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("bar=5\n");
@@ -4122,7 +4122,7 @@ fn testZNow(b: *Build, opts: Options) *Step {
{
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(obj);
+ dso.root_module.addObject(obj);
const check = dso.checkObject();
check.checkInDynamicSection();
@@ -4132,7 +4132,7 @@ fn testZNow(b: *Build, opts: Options) *Step {
{
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(obj);
+ dso.root_module.addObject(obj);
dso.link_z_lazy = true;
const check = dso.checkObject();
@@ -4150,7 +4150,7 @@ fn testZStackSize(b: *Build, opts: Options) *Step {
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe, "int main() { return 0; }", &.{});
exe.stack_size = 0x800000;
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const check = exe.checkObject();
check.checkInHeaders();
@@ -4202,8 +4202,8 @@ fn testZText(b: *Build, opts: Options) *Step {
});
const dso = addSharedLibrary(b, opts, .{ .name = "a" });
- dso.addObject(a_o);
- dso.addObject(b_o);
+ dso.root_module.addObject(a_o);
+ dso.root_module.addObject(b_o);
dso.link_z_notext = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
@@ -4214,8 +4214,8 @@ fn testZText(b: *Build, opts: Options) *Step {
\\ printf("%d\n", fnn());
\\}
, &.{});
- exe.linkLibrary(dso);
- exe.linkLibC();
+ exe.root_module.linkLibrary(dso);
+ exe.root_module.link_libc = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("3\n");
diff --git a/test/link/link.zig b/test/link/link.zig
index 5d1a02f23e69b31ed125332e35c94e33ffc64976..601247e87702ebb019db48967f03df5dd6530be8 100644
--- a/test/link/link.zig
+++ b/test/link/link.zig
@@ -140,20 +140,20 @@ pub fn addRunArtifact(comp: *Compile) *Run {
pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
const b = comp.step.owner;
const file = WriteFile.create(b).add("a.c", bytes);
- comp.addCSourceFile(.{ .file = file, .flags = flags });
+ comp.root_module.addCSourceFile(.{ .file = file, .flags = flags });
}
pub fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
const b = comp.step.owner;
const file = WriteFile.create(b).add("a.cpp", bytes);
- comp.addCSourceFile(.{ .file = file, .flags = flags });
+ comp.root_module.addCSourceFile(.{ .file = file, .flags = flags });
}
pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
const b = comp.step.owner;
const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
const file = WriteFile.create(b).add("a.s", actual_bytes);
- comp.addAssemblyFile(file);
+ comp.root_module.addAssemblyFile(file);
}
pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
diff --git a/test/link/macho.zig b/test/link/macho.zig
index 80d861eea093423ab216ce5962847d7644bdabce..422fc89a568a54f7a0c6bdb5e61fd1bfce269289 100644
--- a/test/link/macho.zig
+++ b/test/link/macho.zig
@@ -127,7 +127,7 @@ fn testDeadStrip(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "no_dead_strip" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = false;
const check = exe.checkObject();
@@ -156,7 +156,7 @@ fn testDeadStrip(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "yes_dead_strip" });
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = true;
const check = exe.checkObject();
@@ -206,7 +206,7 @@ fn testDuplicateDefinitions(b: *Build, opts: Options) *Step {
\\ strong();
\\}
});
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
expectLinkErrors(exe, test_step, .{ .exact = &.{
"error: duplicate symbol definition: _strong",
@@ -235,7 +235,7 @@ fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkFramework("Cocoa", .{});
const check = exe.checkObject();
@@ -254,7 +254,7 @@ fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkFramework("Cocoa", .{});
exe.dead_strip_dylibs = true;
@@ -350,7 +350,7 @@ fn testEmptyObject(b: *Build, opts: Options) *Step {
\\ printf("Hello world!");
\\}
});
- exe.addObject(empty);
+ exe.root_module.addObject(empty);
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world!");
@@ -451,7 +451,7 @@ fn testEntryPointDylib(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
, &.{});
- exe.linkLibrary(dylib);
+ exe.root_module.linkLibrary(dylib);
exe.entry = .{ .symbol_name = "_bootstrap" };
exe.forceUndefinedSymbol("_my_main");
@@ -604,11 +604,11 @@ fn testHeaderWeakFlags(b: *Build, opts: Options) *Step {
});
const lib = addSharedLibrary(b, opts, .{ .name = "a" });
- lib.addObject(obj1);
+ lib.root_module.addObject(obj1);
{
const exe = addExecutable(b, opts, .{ .name = "main1", .c_source_bytes = "int main() { return 0; }" });
- exe.addObject(obj1);
+ exe.root_module.addObject(obj1);
const check = exe.checkObject();
check.checkInHeaders();
@@ -642,8 +642,8 @@ fn testHeaderWeakFlags(b: *Build, opts: Options) *Step {
}
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.linkLibrary(lib);
- exe.addObject(obj);
+ exe.root_module.linkLibrary(lib);
+ exe.root_module.addObject(obj);
const check = exe.checkObject();
check.checkInHeaders();
@@ -665,7 +665,7 @@ fn testHeaderWeakFlags(b: *Build, opts: Options) *Step {
\\_main:
\\ ret
});
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(lib);
const check = exe.checkObject();
check.checkInHeaders();
@@ -910,7 +910,7 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
\\}
,
});
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const exe = addExecutable(b, opts, .{
.name = "testlib",
@@ -923,7 +923,7 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
\\}
,
});
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(lib);
const run = addRunArtifact(exe);
run.expectStdErrEqual("0\n");
@@ -1051,28 +1051,28 @@ fn testMergeLiteralsX64(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(main_o);
runWithChecks(test_step, exe);
}
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(b_o);
- exe.addObject(a_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(main_o);
runWithChecks(test_step, exe);
}
{
const c_o = addObject(b, opts, .{ .name = "c" });
- c_o.addObject(a_o);
- c_o.addObject(b_o);
- c_o.addObject(main_o);
+ c_o.root_module.addObject(a_o);
+ c_o.root_module.addObject(b_o);
+ c_o.root_module.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main3" });
- exe.addObject(c_o);
+ exe.root_module.addObject(c_o);
runWithChecks(test_step, exe);
}
@@ -1167,28 +1167,28 @@ fn testMergeLiteralsArm64(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(main_o);
runWithChecks(test_step, exe);
}
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(b_o);
- exe.addObject(a_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(main_o);
runWithChecks(test_step, exe);
}
{
const c_o = addObject(b, opts, .{ .name = "c" });
- c_o.addObject(a_o);
- c_o.addObject(b_o);
- c_o.addObject(main_o);
+ c_o.root_module.addObject(a_o);
+ c_o.root_module.addObject(b_o);
+ c_o.root_module.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main3" });
- exe.addObject(c_o);
+ exe.root_module.addObject(c_o);
runWithChecks(test_step, exe);
}
@@ -1259,9 +1259,9 @@ fn testMergeLiteralsArm642(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(main_o);
const check = exe.checkObject();
check.dumpSection("__TEXT,__const");
@@ -1335,17 +1335,17 @@ fn testMergeLiteralsAlignment(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(main_o);
runWithChecks(test_step, exe);
}
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(b_o);
- exe.addObject(a_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(main_o);
runWithChecks(test_step, exe);
}
@@ -1414,27 +1414,27 @@ fn testMergeLiteralsObjc(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
- exe.addObject(a_o);
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(a_o);
exe.root_module.linkFramework("Foundation", .{});
runWithChecks(test_step, exe);
}
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(a_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkFramework("Foundation", .{});
runWithChecks(test_step, exe);
}
{
const b_o = addObject(b, opts, .{ .name = "b" });
- b_o.addObject(a_o);
- b_o.addObject(main_o);
+ b_o.root_module.addObject(a_o);
+ b_o.root_module.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main3" });
- exe.addObject(b_o);
+ exe.root_module.addObject(b_o);
exe.root_module.linkFramework("Foundation", .{});
runWithChecks(test_step, exe);
}
@@ -1610,7 +1610,7 @@ fn testObjcpp(b: *Build, opts: Options) *Step {
\\@end
});
foo_o.root_module.addIncludePath(foo_h.dirname());
- foo_o.linkLibCpp();
+ foo_o.root_module.link_libcpp = true;
const exe = addExecutable(b, opts, .{ .name = "main", .objcpp_source_bytes =
\\#import "Foo.h"
@@ -1628,8 +1628,8 @@ fn testObjcpp(b: *Build, opts: Options) *Step {
\\}
});
exe.root_module.addIncludePath(foo_h.dirname());
- exe.addObject(foo_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(foo_o);
+ exe.root_module.link_libcpp = true;
exe.root_module.linkFramework("Foundation", .{});
const run = addRunArtifact(exe);
@@ -1693,7 +1693,7 @@ fn testReexportsZig(b: *Build, opts: Options) *Step {
\\ return bar() - foo();
\\}
});
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(lib);
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -1711,7 +1711,7 @@ fn testRelocatable(b: *Build, opts: Options) *Step {
\\ throw std::runtime_error("Oh no!");
\\}
});
- a_o.linkLibCpp();
+ a_o.root_module.link_libcpp = true;
const b_o = addObject(b, opts, .{ .name = "b", .cpp_source_bytes =
\\extern int try_me();
@@ -1733,19 +1733,19 @@ fn testRelocatable(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
});
- main_o.linkLibCpp();
+ main_o.root_module.link_libcpp = true;
const exp_stdout = "exception=Oh no!";
{
const c_o = addObject(b, opts, .{ .name = "c" });
- c_o.addObject(a_o);
- c_o.addObject(b_o);
+ c_o.root_module.addObject(a_o);
+ c_o.root_module.addObject(b_o);
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
- exe.addObject(c_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(c_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -1754,13 +1754,13 @@ fn testRelocatable(b: *Build, opts: Options) *Step {
{
const d_o = addObject(b, opts, .{ .name = "d" });
- d_o.addObject(a_o);
- d_o.addObject(b_o);
- d_o.addObject(main_o);
+ d_o.root_module.addObject(a_o);
+ d_o.root_module.addObject(b_o);
+ d_o.root_module.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(d_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(d_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -1805,12 +1805,12 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step {
});
const c_o = addObject(b, opts, .{ .name = "c" });
- c_o.addObject(a_o);
- c_o.addObject(b_o);
- c_o.addObject(main_o);
+ c_o.root_module.addObject(a_o);
+ c_o.root_module.addObject(b_o);
+ c_o.root_module.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(c_o);
+ exe.root_module.addObject(c_o);
const run = addRunArtifact(exe);
run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") });
@@ -1833,10 +1833,10 @@ fn testSearchStrategy(b: *Build, opts: Options) *Step {
});
const liba = addStaticLibrary(b, opts, .{ .name = "a" });
- liba.addObject(obj);
+ liba.root_module.addObject(obj);
const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
- dylib.addObject(obj);
+ dylib.root_module.addObject(obj);
const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
\\#include
@@ -1850,7 +1850,7 @@ fn testSearchStrategy(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .mode_first });
exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
@@ -1869,7 +1869,7 @@ fn testSearchStrategy(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .paths_first });
exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
@@ -1924,9 +1924,9 @@ fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "test" });
- exe.addObject(obj1);
- exe.addObject(obj2);
- exe.addObject(main_o);
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj2);
+ exe.root_module.addObject(main_o);
const run = b.addRunArtifact(exe);
run.skip_foreign_checks = true;
@@ -1951,9 +1951,9 @@ fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "test" });
- exe.addObject(obj1);
- exe.addObject(obj3);
- exe.addObject(main_o);
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj3);
+ exe.root_module.addObject(main_o);
const run = b.addRunArtifact(exe);
run.skip_foreign_checks = true;
@@ -2031,9 +2031,9 @@ fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(obj1);
- exe.addObject(obj2);
- exe.addObject(main_o);
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj2);
+ exe.root_module.addObject(main_o);
const run = addRunArtifact(exe);
run.expectStdOutEqual("All your codebase are belong to us.\n");
@@ -2054,9 +2054,9 @@ fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(obj1);
- exe.addObject(obj2);
- exe.addObject(main_o);
+ exe.root_module.addObject(obj1);
+ exe.root_module.addObject(obj2);
+ exe.root_module.addObject(main_o);
const check = exe.checkObject();
check.checkInHeaders();
@@ -2102,9 +2102,9 @@ fn testSymbolStabs(b: *Build, opts: Options) *Step {
});
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(a_o);
- exe.addObject(b_o);
- exe.addObject(main_o);
+ exe.root_module.addObject(a_o);
+ exe.root_module.addObject(b_o);
+ exe.root_module.addObject(main_o);
const run = addRunArtifact(exe);
run.expectStdOutEqual("foo=42,bar=24");
@@ -2299,7 +2299,7 @@ fn testTlsPointers(b: *Build, opts: Options) *Step {
\\}
});
bar_o.root_module.addIncludePath(foo_h.dirname());
- bar_o.linkLibCpp();
+ bar_o.root_module.link_libcpp = true;
const baz_o = addObject(b, opts, .{ .name = "baz", .cpp_source_bytes =
\\#include "foo.h"
@@ -2309,7 +2309,7 @@ fn testTlsPointers(b: *Build, opts: Options) *Step {
\\}
});
baz_o.root_module.addIncludePath(foo_h.dirname());
- baz_o.linkLibCpp();
+ baz_o.root_module.link_libcpp = true;
const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
\\extern int bar();
@@ -2321,13 +2321,13 @@ fn testTlsPointers(b: *Build, opts: Options) *Step {
\\}
});
main_o.root_module.addIncludePath(foo_h.dirname());
- main_o.linkLibCpp();
+ main_o.root_module.link_libcpp = true;
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(bar_o);
- exe.addObject(baz_o);
- exe.addObject(main_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(bar_o);
+ exe.root_module.addObject(baz_o);
+ exe.root_module.addObject(main_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -2445,7 +2445,7 @@ fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkSystemLibrary("a", .{});
exe.root_module.linkSystemLibrary("b", .{});
exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
@@ -2474,7 +2474,7 @@ fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
+ exe.root_module.addObject(main_o);
exe.root_module.linkSystemLibrary("b", .{});
exe.root_module.linkSystemLibrary("a", .{});
exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
@@ -2510,14 +2510,14 @@ fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step {
const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "static int foo = 42;" });
const lib = addStaticLibrary(b, opts, .{ .name = "a" });
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
{
const exe = addExecutable(b, opts, .{ .name = "main3" });
- exe.addObject(main_o);
- exe.addObject(obj);
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(obj);
exe.discard_local_symbols = true;
const run = addRunArtifact(exe);
@@ -2532,8 +2532,8 @@ fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main4" });
- exe.addObject(main_o);
- exe.linkLibrary(lib);
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkLibrary(lib);
exe.discard_local_symbols = true;
const run = addRunArtifact(exe);
@@ -2555,14 +2555,14 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "int foo = 42;" });
const lib = addStaticLibrary(b, opts, .{ .name = "a" });
- lib.addObject(obj);
+ lib.root_module.addObject(obj);
const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
- exe.addObject(main_o);
- exe.linkLibrary(lib);
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkLibrary(lib);
exe.forceUndefinedSymbol("_foo");
const run = addRunArtifact(exe);
@@ -2577,8 +2577,8 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
- exe.addObject(main_o);
- exe.linkLibrary(lib);
+ exe.root_module.addObject(main_o);
+ exe.root_module.linkLibrary(lib);
exe.forceUndefinedSymbol("_foo");
exe.link_gc_sections = true;
@@ -2594,8 +2594,8 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main3" });
- exe.addObject(main_o);
- exe.addObject(obj);
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(obj);
const run = addRunArtifact(exe);
run.expectExitCode(0);
@@ -2609,8 +2609,8 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
{
const exe = addExecutable(b, opts, .{ .name = "main4" });
- exe.addObject(main_o);
- exe.addObject(obj);
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(obj);
exe.link_gc_sections = true;
const run = addRunArtifact(exe);
@@ -2642,7 +2642,7 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step {
\\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()});
\\}
});
- exe.addObject(obj);
+ exe.root_module.addObject(obj);
// TODO order should match across backends if possible
if (opts.use_llvm) {
@@ -2764,7 +2764,7 @@ fn testUnwindInfo(b: *Build, opts: Options) *Step {
\\}
});
main_o.root_module.addIncludePath(all_h.dirname());
- main_o.linkLibCpp();
+ main_o.root_module.link_libcpp = true;
const simple_string_o = addObject(b, opts, .{ .name = "simple_string", .cpp_source_bytes =
\\#include "all.h"
@@ -2799,7 +2799,7 @@ fn testUnwindInfo(b: *Build, opts: Options) *Step {
\\}
});
simple_string_o.root_module.addIncludePath(all_h.dirname());
- simple_string_o.linkLibCpp();
+ simple_string_o.root_module.link_libcpp = true;
const simple_string_owner_o = addObject(b, opts, .{ .name = "simple_string_owner", .cpp_source_bytes =
\\#include "all.h"
@@ -2816,7 +2816,7 @@ fn testUnwindInfo(b: *Build, opts: Options) *Step {
\\}
});
simple_string_owner_o.root_module.addIncludePath(all_h.dirname());
- simple_string_owner_o.linkLibCpp();
+ simple_string_owner_o.root_module.link_libcpp = true;
const exp_stdout =
\\Constructed: a
@@ -2828,10 +2828,10 @@ fn testUnwindInfo(b: *Build, opts: Options) *Step {
;
const exe = addExecutable(b, opts, .{ .name = "main" });
- exe.addObject(main_o);
- exe.addObject(simple_string_o);
- exe.addObject(simple_string_owner_o);
- exe.linkLibCpp();
+ exe.root_module.addObject(main_o);
+ exe.root_module.addObject(simple_string_o);
+ exe.root_module.addObject(simple_string_owner_o);
+ exe.root_module.link_libcpp = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual(exp_stdout);
@@ -2896,7 +2896,7 @@ fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
});
- exe.addObject(a_o);
+ exe.root_module.addObject(a_o);
const run = addRunArtifact(exe);
run.expectStdOutEqual("4\n");
@@ -2948,7 +2948,7 @@ fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step {
\\ return 0;
\\}
});
- exe.addObject(a_o);
+ exe.root_module.addObject(a_o);
const run = addRunArtifact(exe);
run.expectStdOutEqual("4\n");
@@ -3052,7 +3052,7 @@ fn testWeakBind(b: *Build, opts: Options) *Step {
\\ .quad 0
\\ .quad _weak_internal_tlv$tlv$init
});
- exe.linkLibrary(lib);
+ exe.root_module.linkLibrary(lib);
{
const check = exe.checkObject();
diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig
index 4976c97b316a1ca9313308a843c249cb5287b68e..74036d486d992d1d4f3d0fabb2a93b5ac70279e6 100644
--- a/test/link/wasm/extern/build.zig
+++ b/test/link/wasm/extern/build.zig
@@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }),
}),
});
- exe.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} });
+ exe.root_module.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} });
exe.use_llvm = false;
exe.use_lld = false;
diff --git a/test/src/Cases.zig b/test/src/Cases.zig
index 522fe6b38557eb9cf75c8149055abffc875bf791..bd93599171e987a833504a2434be683dcab6cb5e 100644
--- a/test/src/Cases.zig
+++ b/test/src/Cases.zig
@@ -560,7 +560,7 @@ pub fn lowerToTranslateCSteps(
.root_module = translate_c.createModule(),
});
run_exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});
- run_exe.linkLibC();
+ run_exe.root_module.link_libc = true;
const run = b.addRunArtifact(run_exe);
run.step.name = b.fmt("{s} run", .{annotated_case_name});
run.expectStdOutEqual(output);
diff --git a/test/src/RunTranslatedC.zig b/test/src/RunTranslatedC.zig
index 528df69c4b72d9a92d2af7a37764ed0267bd49dc..537c49dcd5ab60a1bb6444988bbf264367b4de5c 100644
--- a/test/src/RunTranslatedC.zig
+++ b/test/src/RunTranslatedC.zig
@@ -89,7 +89,7 @@ pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void {
.root_module = translate_c.createModule(),
});
exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});
- exe.linkLibC();
+ exe.root_module.link_libc = true;
const run = b.addRunArtifact(exe);
run.step.name = b.fmt("{s} run", .{annotated_case_name});
if (!case.allow_warnings) {
diff --git a/test/standalone/c_embed_path/build.zig b/test/standalone/c_embed_path/build.zig
index a314847ba6f196bb0bbd6dbfe5359ca36cbd1f69..246e18b3f0dec9a657ef98297f7a105cc1d8dd88 100644
--- a/test/standalone/c_embed_path/build.zig
+++ b/test/standalone/c_embed_path/build.zig
@@ -13,12 +13,12 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
}),
});
- exe.addCSourceFile(.{
+ exe.root_module.addCSourceFile(.{
.file = b.path("test.c"),
.flags = &.{"-std=c23"},
});
- exe.linkLibC();
- exe.addEmbedPath(b.path("data"));
+ exe.root_module.link_libc = true;
+ exe.root_module.addEmbedPath(b.path("data"));
const run_c_cmd = b.addRunArtifact(exe);
run_c_cmd.expectExitCode(0);
diff --git a/test/standalone/extern/build.zig b/test/standalone/extern/build.zig
index 3c22f77f2a916ca380dcc86679c20e30d022ce2e..178fa76d41fe2805edde6ddbc1789119d79307e5 100644
--- a/test/standalone/extern/build.zig
+++ b/test/standalone/extern/build.zig
@@ -31,8 +31,8 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
}) });
- test_exe.addObject(obj);
- test_exe.linkLibrary(shared);
+ test_exe.root_module.addObject(obj);
+ test_exe.root_module.linkLibrary(shared);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig
index 4b0c089f97ee382630e274c9f46f42c0f19e2911..0f3f0a16f74f2d8914cdb1f1e2107cb3e5a84ae0 100644
--- a/test/standalone/issue_794/build.zig
+++ b/test/standalone/issue_794/build.zig
@@ -8,7 +8,7 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("main.zig"),
.target = b.graph.host,
}) });
- test_artifact.addIncludePath(b.path("a_directory"));
+ test_artifact.root_module.addIncludePath(b.path("a_directory"));
// TODO: actually check the output
_ = test_artifact.getEmittedBin();
diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig
index 878859312b23500c104a5a36b064a0e34cddf6ac..8d2c448215ee993262cc397247021e9bae269d6f 100644
--- a/test/standalone/stack_iterator/build.zig
+++ b/test/standalone/stack_iterator/build.zig
@@ -109,7 +109,7 @@ pub fn build(b: *std.Build) void {
// .use_llvm = true,
// });
- // exe.linkLibrary(c_shared_lib);
+ // exe.root_module.linkLibrary(c_shared_lib);
// const run_cmd = b.addRunArtifact(exe);
// test_step.dependOn(&run_cmd.step);
diff --git a/test/tests.zig b/test/tests.zig
index caec19d556e0ae7cb78b2de9a49a7230f6c90298..a12312d278398e46c8b5a89d18c8251f0935b4f0 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -2371,10 +2371,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
} else "";
const use_pic = if (test_target.pic == true) "-pic" else "";
- for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path));
+ for (options.include_paths) |include_path| these_tests.root_module.addIncludePath(b.path(include_path));
if (target.os.tag == .windows) {
- for (options.windows_libs) |lib| these_tests.linkSystemLibrary(lib);
+ for (options.windows_libs) |lib| these_tests.root_module.linkSystemLibrary(lib, .{});
}
const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{
--
2.54.0
From 68cfa736dfd38cc151af1f9e1b0edb3041bc237c Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sat, 26 Jul 2025 06:23:31 -0400
Subject: [PATCH 016/110] x86_64: fix switch on mod result
Closes #24541
---
src/arch/x86_64/CodeGen.zig | 17 ++++++++---------
test/behavior/switch.zig | 10 ++++++++++
2 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 89a23d351442202188bc95d0fbceab66ce00f765..a7d771853ce7c7a0f3cb3d1f62230303ddb24df5 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -1103,11 +1103,7 @@ const FormatAirData = struct {
inst: Air.Inst.Index,
};
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");
+ data.self.air.writeInst(w, data.inst, data.self.pt, data.self.liveness);
}
fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) {
return .{ .data = .{ .self = self, .inst = inst } };
@@ -179300,10 +179296,13 @@ fn lowerSwitchBr(
} else undefined;
const table_start: u31 = @intCast(cg.mir_table.items.len);
{
- const condition_index_reg = if (condition_index.isRegister())
- condition_index.getReg().?
- else
- try cg.copyToTmpRegister(.usize, condition_index);
+ const condition_index_reg = condition_index_reg: {
+ if (condition_index.isRegister()) {
+ const condition_index_reg = condition_index.getReg().?;
+ if (condition_index_reg.isClass(.general_purpose)) break :condition_index_reg condition_index_reg;
+ }
+ break :condition_index_reg try cg.copyToTmpRegister(.usize, condition_index);
+ };
const condition_index_lock = cg.register_manager.lockReg(condition_index_reg);
defer if (condition_index_lock) |lock| cg.register_manager.unlockReg(lock);
try cg.truncateRegister(condition_ty, condition_index_reg);
diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig
index 813530b361ef38c0795f9fc7b8e889e5e119daab..764080b9376aa8cf111750e2eec04ce5c243015c 100644
--- a/test/behavior/switch.zig
+++ b/test/behavior/switch.zig
@@ -1072,3 +1072,13 @@ test "switch on a signed value smaller than the smallest prong value" {
else => {},
}
}
+
+test "switch on 8-bit mod result" {
+ var x: u8 = undefined;
+ x = 16;
+ switch (x % 4) {
+ 0 => {},
+ 1, 2, 3 => return error.TestFailed,
+ else => unreachable,
+ }
+}
--
2.54.0
From 3194a4d22b755393296a4db4c85d7b54ea83e96d Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sat, 26 Jul 2025 07:30:56 -0400
Subject: [PATCH 017/110] x86_64: fix dst create alloc reg clobbering src
Closes #24390
---
src/arch/x86_64/CodeGen.zig | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index a7d771853ce7c7a0f3cb3d1f62230303ddb24df5..d1000702943cc0f2032cd7a766f3480520c01874 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -191920,18 +191920,15 @@ const Select = struct {
error.InvalidInstruction => {
const fixes = @tagName(mir_tag[0]);
const fixes_blank = std.mem.indexOfScalar(u8, fixes, '_').?;
- return s.cg.fail(
- "invalid instruction: '{s}{s}{s} {s} {s} {s} {s}'",
- .{
- fixes[0..fixes_blank],
- @tagName(mir_tag[1]),
- fixes[fixes_blank + 1 ..],
- @tagName(mir_ops[0]),
- @tagName(mir_ops[1]),
- @tagName(mir_ops[2]),
- @tagName(mir_ops[3]),
- },
- );
+ return s.cg.fail("invalid instruction: '{s}{s}{s} {s} {s} {s} {s}'", .{
+ fixes[0..fixes_blank],
+ @tagName(mir_tag[1]),
+ fixes[fixes_blank + 1 ..],
+ @tagName(mir_ops[0]),
+ @tagName(mir_ops[1]),
+ @tagName(mir_ops[2]),
+ @tagName(mir_ops[3]),
+ });
},
else => |e| return e,
};
@@ -194423,6 +194420,18 @@ fn select(
while (true) for (pattern.src[0..src_temps.len], src_temps) |src_pattern, *src_temp| {
if (try src_pattern.convert(src_temp, cg)) break;
} else break;
+ var src_locks: [s_src_temps.len][2]?RegisterLock = @splat(@splat(null));
+ for (src_locks[0..src_temps.len], src_temps) |*locks, src_temp| {
+ const regs: [2]Register = switch (src_temp.tracking(cg).short) {
+ else => continue,
+ .register => |reg| .{ reg, .none },
+ .register_pair => |regs| regs,
+ };
+ for (regs, locks) |reg, *lock| {
+ if (reg == .none) continue;
+ lock.* = cg.register_manager.lockRegIndex(RegisterManager.indexOfRegIntoTracked(reg) orelse continue);
+ }
+ }
@memcpy(s_src_temps[0..src_temps.len], src_temps);
std.mem.swap(Temp, &s_src_temps[pattern.commute[0]], &s_src_temps[pattern.commute[1]]);
@@ -194441,6 +194450,7 @@ fn select(
}
assert(s.top == 0);
+ for (src_locks) |locks| for (locks) |lock| if (lock) |reg| cg.register_manager.unlockReg(reg);
for (tmp_locks) |locks| for (locks) |lock| if (lock) |reg| cg.register_manager.unlockReg(reg);
for (dst_locks) |locks| for (locks) |lock| if (lock) |reg| cg.register_manager.unlockReg(reg);
caller_preserved: {
--
2.54.0
From c9ce1debe7cb59b63c00f56bb0d233eafc04dfd9 Mon Sep 17 00:00:00 2001
From: mlugg
Date: Fri, 25 Jul 2025 18:59:35 +0100
Subject: [PATCH 018/110] Sema: exclude sentinel from source array length in
pointer cast to slice
Resolves: #24569
---
src/Sema.zig | 22 ++++++++++++++--------
test/behavior/ptrcast.zig | 10 ++++++++++
2 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index 788107f786ed5021e51c425af8358099d9bfc0dc..93740589bcb007accc6023f17435f298fd8ec60e 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -22483,11 +22483,18 @@ fn ptrCastFull(
.slice => {},
.many, .c, .one => break :len null,
}
- // `null` means the operand is a runtime-known slice (so the length is runtime-known).
- const opt_src_len: ?u64 = switch (src_info.flags.size) {
- .one => 1,
- .slice => src_len: {
- const operand_val = try sema.resolveValue(operand) orelse break :src_len null;
+ // A `null` length means the operand is a runtime-known slice (so the length is runtime-known).
+ // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels.
+ const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) {
+ .one => src: {
+ const true_child: Type = .fromInterned(src_info.child);
+ break :src switch (true_child.zigTypeTag(zcu)) {
+ .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) },
+ else => .{ true_child, 1 },
+ };
+ },
+ .slice => src: {
+ const operand_val = try sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null };
if (operand_val.isUndef(zcu)) break :len .undef;
const slice_val = switch (operand_ty.zigTypeTag(zcu)) {
.optional => operand_val.optionalValue(zcu) orelse break :len .undef,
@@ -22496,14 +22503,13 @@ fn ptrCastFull(
};
const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern())));
if (slice_len_resolved.isUndef(zcu)) break :len .undef;
- break :src_len slice_len_resolved.toUnsignedInt(zcu);
+ break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) };
},
.many, .c => {
return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)});
},
};
const dest_elem_ty: Type = .fromInterned(dest_info.child);
- const src_elem_ty: Type = .fromInterned(src_info.child);
if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) {
break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice;
}
@@ -22519,7 +22525,7 @@ fn ptrCastFull(
const bytes = src_len * src_elem_size;
const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) {
.slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
- .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}),
+ .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}),
else => unreachable,
};
break :len .{ .constant = dest_len };
diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig
index 678fd68cbb906a6e6013a8a2d43e2b710a5b01dc..7b6e6edbb857aae3ba6ae6d91347b2b334927c2c 100644
--- a/test/behavior/ptrcast.zig
+++ b/test/behavior/ptrcast.zig
@@ -552,3 +552,13 @@ test "@ptrCast single-item pointer to slice of bytes" {
try comptime S.doTheTest(void, &{});
try comptime S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
}
+
+test "@ptrCast array pointer removing sentinel" {
+ const in: *const [4:0]u8 = &.{ 1, 2, 3, 4 };
+ const out: []const i8 = @ptrCast(in);
+ comptime assert(out.len == 4);
+ comptime assert(out[0] == 1);
+ comptime assert(out[1] == 2);
+ comptime assert(out[2] == 3);
+ comptime assert(out[3] == 4);
+}
--
2.54.0
From 7c349da49c73817139444570d3082ac1f0c93078 Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Fri, 25 Jul 2025 15:41:43 -0400
Subject: [PATCH 019/110] aarch64: implement complex switch prongs
---
src/codegen/aarch64/Select.zig | 161 +++++++++++++++++++++-----------
test/behavior/inline_switch.zig | 1 -
test/behavior/switch.zig | 1 -
3 files changed, 106 insertions(+), 57 deletions(-)
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index f7da48d847388db12505e55bd81b98872cdb4789..0a50cb6c9dc2297e3b68458a9d8c0246963729e9 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -4328,7 +4328,6 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
};
var cond_mat: ?Value.Materialize = null;
var cond_reg: Register = undefined;
- var temp_reg: Register = undefined;
var cases_it = switch_br.iterateCases();
while (cases_it.next()) |case| {
const next_label = isel.instructions.items.len;
@@ -4342,11 +4341,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
if (cond_mat == null) {
var cond_vi = try isel.use(switch_br.operand);
cond_mat = try cond_vi.matReg(isel);
- const temp_ra = try isel.allocIntReg();
- cond_reg, temp_reg = switch (cond_int_info.bits) {
+ cond_reg = switch (cond_int_info.bits) {
else => unreachable,
- 1...32 => .{ cond_mat.?.ra.w(), temp_ra.w() },
- 33...64 => .{ cond_mat.?.ra.x(), temp_ra.x() },
+ 1...32 => cond_mat.?.ra.w(),
+ 33...64 => cond_mat.?.ra.x(),
};
}
if (case.ranges.len == 0 and case.items.len == 1 and Constant.fromInterned(
@@ -4387,17 +4385,45 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
) else high_bigint.toInt(i64) catch
return isel.fail("too big case range end: {f}", .{isel.fmtConstant(high_val)});
+ const adjusted_ra = switch (low_int) {
+ 0 => cond_mat.?.ra,
+ else => try isel.allocIntReg(),
+ };
+ defer if (adjusted_ra != cond_mat.?.ra) isel.freeReg(adjusted_ra);
+ const adjusted_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => adjusted_ra.w(),
+ 33...64 => adjusted_ra.x(),
+ };
const delta_int = high_int -% low_int;
- if (case_range_index > 0) {
- return isel.fail("case range", .{});
- } else if (case.items.len > 0) {
- return isel.fail("case range", .{});
+ if (case_range_index | case.items.len > 0) {
+ if (std.math.cast(u5, delta_int)) |pos_imm| try isel.emit(.ccmp(
+ adjusted_reg,
+ .{ .immediate = pos_imm },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ if (case_range_index > 0) .hi else .ne,
+ )) else if (std.math.cast(u5, -delta_int)) |neg_imm| try isel.emit(.ccmn(
+ adjusted_reg,
+ .{ .immediate = neg_imm },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ if (case_range_index > 0) .hi else .ne,
+ )) else {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.ccmp(
+ cond_reg,
+ .{ .register = imm_reg },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ if (case_range_index > 0) .hi else .ne,
+ ));
+ try isel.movImmediate(imm_reg, @bitCast(delta_int));
+ }
} else {
- const adjusted_reg = switch (low_int) {
- 0 => cond_reg,
- else => temp_reg,
- };
-
if (std.math.cast(u12, delta_int)) |pos_imm| try isel.emit(.subs(
zero_reg,
adjusted_reg,
@@ -4421,41 +4447,55 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
adjusted_reg,
.{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
)) else {
- try isel.movImmediate(temp_reg, @bitCast(delta_int));
- try isel.emit(.subs(zero_reg, adjusted_reg, .{ .register = temp_reg }));
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.subs(zero_reg, adjusted_reg, .{ .register = imm_reg }));
+ try isel.movImmediate(imm_reg, @bitCast(delta_int));
}
+ }
- switch (low_int) {
- 0 => {},
- else => {
- if (std.math.cast(u12, low_int)) |pos_imm| try isel.emit(.sub(
- adjusted_reg,
- cond_reg,
- .{ .immediate = pos_imm },
- )) else if (std.math.cast(u12, -low_int)) |neg_imm| try isel.emit(.add(
- adjusted_reg,
- cond_reg,
- .{ .immediate = neg_imm },
- )) else if (if (@as(i12, @truncate(low_int)) == 0)
- std.math.cast(u12, low_int >> 12)
- else
- null) |pos_imm_lsr_12| try isel.emit(.sub(
- adjusted_reg,
- cond_reg,
- .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
- )) else if (if (@as(i12, @truncate(-low_int)) == 0)
- std.math.cast(u12, -low_int >> 12)
- else
- null) |neg_imm_lsr_12| try isel.emit(.add(
- adjusted_reg,
- cond_reg,
- .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
- )) else {
- try isel.movImmediate(temp_reg, @bitCast(low_int));
- try isel.emit(.subs(adjusted_reg, cond_reg, .{ .register = temp_reg }));
- }
- },
- }
+ switch (low_int) {
+ 0 => {},
+ else => {
+ if (std.math.cast(u12, low_int)) |pos_imm| try isel.emit(.sub(
+ adjusted_reg,
+ cond_reg,
+ .{ .immediate = pos_imm },
+ )) else if (std.math.cast(u12, -low_int)) |neg_imm| try isel.emit(.add(
+ adjusted_reg,
+ cond_reg,
+ .{ .immediate = neg_imm },
+ )) else if (if (@as(i12, @truncate(low_int)) == 0)
+ std.math.cast(u12, low_int >> 12)
+ else
+ null) |pos_imm_lsr_12| try isel.emit(.sub(
+ adjusted_reg,
+ cond_reg,
+ .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
+ )) else if (if (@as(i12, @truncate(-low_int)) == 0)
+ std.math.cast(u12, -low_int >> 12)
+ else
+ null) |neg_imm_lsr_12| try isel.emit(.add(
+ adjusted_reg,
+ cond_reg,
+ .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
+ )) else {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.sub(adjusted_reg, cond_reg, .{ .register = imm_reg }));
+ try isel.movImmediate(imm_reg, @bitCast(low_int));
+ }
+ },
}
}
var case_item_index = case.items.len;
@@ -4483,13 +4523,20 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
.{ .n = false, .z = true, .c = false, .v = false },
.ne,
)) else {
- try isel.movImmediate(temp_reg, @bitCast(item_int));
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
try isel.emit(.ccmp(
cond_reg,
- .{ .register = temp_reg },
+ .{ .register = imm_reg },
.{ .n = false, .z = true, .c = false, .v = false },
.ne,
));
+ try isel.movImmediate(imm_reg, @bitCast(item_int));
}
} else {
if (std.math.cast(u12, item_int)) |pos_imm| try isel.emit(.subs(
@@ -4515,16 +4562,20 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
cond_reg,
.{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
)) else {
- try isel.movImmediate(temp_reg, @bitCast(item_int));
- try isel.emit(.subs(zero_reg, cond_reg, .{ .register = temp_reg }));
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.subs(zero_reg, cond_reg, .{ .register = imm_reg }));
+ try isel.movImmediate(imm_reg, @bitCast(item_int));
}
}
}
}
- if (cond_mat) |mat| {
- try mat.finish(isel);
- isel.freeReg(temp_reg.alias);
- }
+ if (cond_mat) |mat| try mat.finish(isel);
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
.@"try", .try_cold => {
diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig
index a6f664ded8b3f11f904d254a63c8facffa977937..57444d22a4c323e2a64eead53e37c2fda148189a 100644
--- a/test/behavior/inline_switch.zig
+++ b/test/behavior/inline_switch.zig
@@ -105,7 +105,6 @@ test "inline else enum" {
}
test "inline else int with gaps" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig
index 813530b361ef38c0795f9fc7b8e889e5e119daab..a7173be1ed346c325b6850e67584dfdefc49799b 100644
--- a/test/behavior/switch.zig
+++ b/test/behavior/switch.zig
@@ -8,7 +8,6 @@ const minInt = std.math.minInt;
const maxInt = std.math.maxInt;
test "switch with numbers" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
--
2.54.0
From 1274254c48ee105623c513dfc01451fee2912c5b Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Fri, 25 Jul 2025 16:38:17 -0400
Subject: [PATCH 020/110] aarch64: implement stack probing
---
src/Package/Module.zig | 2 +-
src/codegen/aarch64/Select.zig | 73 +++++++++++++++++++++-----------
src/codegen/aarch64/encoding.zig | 2 +
src/target.zig | 10 +++--
4 files changed, 59 insertions(+), 28 deletions(-)
diff --git a/src/Package/Module.zig b/src/Package/Module.zig
index d829b397baf67d0215afd85551d5da486f72b05f..1c941f51f44a8c0d6c6e1029ef122b9110de5d8a 100644
--- a/src/Package/Module.zig
+++ b/src/Package/Module.zig
@@ -250,7 +250,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
};
const stack_check = b: {
- if (!target_util.supportsStackProbing(target)) {
+ if (!target_util.supportsStackProbing(target, zig_backend)) {
if (options.inherited.stack_check == true)
return error.StackCheckUnsupportedByTarget;
break :b false;
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index 0a50cb6c9dc2297e3b68458a9d8c0246963729e9..b0b1297a932f119595fc9bfa3b85b2079cd0155f 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -6693,8 +6693,8 @@ pub fn layout(
wip_mir_log.debug("{f}:\n", .{nav.fqn.fmt(ip)});
const stack_size: u24 = @intCast(InternPool.Alignment.@"16".forward(isel.stack_size));
- const stack_size_low: u12 = @truncate(stack_size >> 0);
- const stack_size_high: u12 = @truncate(stack_size >> 12);
+ const stack_size_lo: u12 = @truncate(stack_size >> 0);
+ const stack_size_hi: u12 = @truncate(stack_size >> 12);
var saves_buf: [10 + 8 + 8 + 2 + 8]struct {
class: enum { integer, vector },
@@ -6881,28 +6881,53 @@ pub fn layout(
}
}
+ try isel.emit(.add(.fp, .sp, .{ .immediate = frame_record_offset }));
const scratch_reg: Register = if (isel.stack_align == .@"16")
.sp
- else if (stack_size == 0)
+ else if (stack_size == 0 and frame_record_offset == 0)
.fp
else
- .x9;
- try isel.emit(.add(.fp, .sp, .{ .immediate = frame_record_offset }));
- if (stack_size_high > 0) try isel.emit(.sub(scratch_reg, .sp, .{
- .shifted_immediate = .{ .immediate = stack_size_high, .lsl = .@"12" },
- }));
- if (stack_size_low > 0) try isel.emit(.sub(
- scratch_reg,
- if (stack_size_high > 0) scratch_reg else .sp,
- .{ .immediate = stack_size_low },
- ));
- if (isel.stack_align != .@"16") {
- try isel.emit(.@"and"(.sp, scratch_reg, .{ .immediate = .{
- .N = .doubleword,
- .immr = -%isel.stack_align.toLog2Units(),
- .imms = ~isel.stack_align.toLog2Units(),
- } }));
+ .ip0;
+ if (mod.stack_check) {
+ if (stack_size_hi > 2) {
+ try isel.movImmediate(.ip1, stack_size_hi);
+ const loop_label = isel.instructions.items.len;
+ try isel.emit(.sub(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = 1, .lsl = .@"12" },
+ }));
+ try isel.emit(.sub(.ip1, .ip1, .{ .immediate = 1 }));
+ try isel.emit(.ldr(.xzr, .{ .base = .sp }));
+ try isel.emit(.cbnz(.ip1, -@as(i21, @intCast(
+ (isel.instructions.items.len - loop_label) << 2,
+ ))));
+ } else for (0..stack_size_hi) |_| {
+ try isel.emit(.sub(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = 1, .lsl = .@"12" },
+ }));
+ try isel.emit(.ldr(.xzr, .{ .base = .sp }));
+ }
+ if (stack_size_lo > 0) try isel.emit(.sub(
+ scratch_reg,
+ .sp,
+ .{ .immediate = stack_size_lo },
+ )) else if (scratch_reg.alias == Register.Alias.ip0)
+ try isel.emit(.add(scratch_reg, .sp, .{ .immediate = 0 }));
+ } else {
+ if (stack_size_hi > 0) try isel.emit(.sub(scratch_reg, .sp, .{
+ .shifted_immediate = .{ .immediate = stack_size_hi, .lsl = .@"12" },
+ }));
+ if (stack_size_lo > 0) try isel.emit(.sub(
+ scratch_reg,
+ if (stack_size_hi > 0) scratch_reg else .sp,
+ .{ .immediate = stack_size_lo },
+ )) else if (scratch_reg.alias == Register.Alias.ip0 and stack_size_hi == 0)
+ try isel.emit(.add(scratch_reg, .sp, .{ .immediate = 0 }));
}
+ if (isel.stack_align != .@"16") try isel.emit(.@"and"(.sp, scratch_reg, .{ .immediate = .{
+ .N = .doubleword,
+ .immr = -%isel.stack_align.toLog2Units(),
+ .imms = ~isel.stack_align.toLog2Units(),
+ } }));
wip_mir_log.debug("", .{});
}
@@ -6947,17 +6972,17 @@ pub fn layout(
save_index += 1;
} else save_index += 1;
}
- if (isel.stack_align != .@"16" or (stack_size_low > 0 and stack_size_high > 0)) {
+ if (isel.stack_align != .@"16" or (stack_size_lo > 0 and stack_size_hi > 0)) {
try isel.emit(switch (frame_record_offset) {
0 => .add(.sp, .fp, .{ .immediate = 0 }),
else => |offset| .sub(.sp, .fp, .{ .immediate = offset }),
});
} else {
- if (stack_size_high > 0) try isel.emit(.add(.sp, .sp, .{
- .shifted_immediate = .{ .immediate = stack_size_high, .lsl = .@"12" },
+ if (stack_size_hi > 0) try isel.emit(.add(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = stack_size_hi, .lsl = .@"12" },
}));
- if (stack_size_low > 0) try isel.emit(.add(.sp, .sp, .{
- .immediate = stack_size_low,
+ if (stack_size_lo > 0) try isel.emit(.add(.sp, .sp, .{
+ .immediate = stack_size_lo,
}));
}
wip_mir_log.debug("{f}:\n", .{nav.fqn.fmt(ip)});
diff --git a/src/codegen/aarch64/encoding.zig b/src/codegen/aarch64/encoding.zig
index 1697b2957b46d96979728351573007ad1165de42..727b88c7290aa8b989fa9d81908152bce74dbb3c 100644
--- a/src/codegen/aarch64/encoding.zig
+++ b/src/codegen/aarch64/encoding.zig
@@ -151,6 +151,7 @@ pub const Register = struct {
pub const wzr: Register = .{ .alias = .zr, .format = .{ .integer = .word } };
pub const wsp: Register = .{ .alias = .sp, .format = .{ .integer = .word } };
+ pub const ip = x16;
pub const ip0 = x16;
pub const ip1 = x17;
pub const fp = x29;
@@ -774,6 +775,7 @@ pub const Register = struct {
ffr,
+ pub const ip: Alias = .r16;
pub const ip0: Alias = .r16;
pub const ip1: Alias = .r17;
pub const fp: Alias = .r29;
diff --git a/src/target.zig b/src/target.zig
index e59c3eda0530f8d576aa675020a5f01664c95d87..5896af7b2a6d22204bfb413f38d5269ee7e41c53 100644
--- a/src/target.zig
+++ b/src/target.zig
@@ -248,9 +248,13 @@ pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool {
return false;
}
-pub fn supportsStackProbing(target: *const std.Target) bool {
- return target.os.tag != .windows and target.os.tag != .uefi and
- (target.cpu.arch == .x86 or target.cpu.arch == .x86_64);
+pub fn supportsStackProbing(target: *const std.Target, backend: std.builtin.CompilerBackend) bool {
+ return switch (backend) {
+ .stage2_aarch64, .stage2_x86_64 => true,
+ .stage2_llvm => target.os.tag != .windows and target.os.tag != .uefi and
+ (target.cpu.arch == .x86 or target.cpu.arch == .x86_64),
+ else => false,
+ };
}
pub fn supportsStackProtector(target: *const std.Target, backend: std.builtin.CompilerBackend) bool {
--
2.54.0
From 69abc945e45af3f447f9ee07d426d1ec40cf3f15 Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sat, 26 Jul 2025 03:09:55 -0400
Subject: [PATCH 021/110] aarch64: implement some safety checks
Closes #24553
---
src/Compilation.zig | 10 +-
src/arch/x86_64/CodeGen.zig | 6 +-
src/arch/x86_64/Emit.zig | 11 +-
src/codegen/aarch64.zig | 7 +-
src/codegen/aarch64/Assemble.zig | 32 +-
src/codegen/aarch64/Mir.zig | 139 ++-
src/codegen/aarch64/Select.zig | 806 ++++++++++++++----
src/codegen/aarch64/instructions.zon | 190 +++++
src/target.zig | 6 +-
test/behavior/error.zig | 3 -
test/behavior/return_address.zig | 1 -
test/cases/array_in_anon_struct.zig | 2 +-
...conv_interrupt_on_unsupported_platform.zig | 4 +-
.../compile_errors/error_set_membership.zig | 2 +-
.../compile_errors/function_ptr_alignment.zig | 2 +-
.../issue_15572_break_on_inline_while.zig | 2 +-
.../switch_on_non_err_union.zig | 2 +-
test/cases/pic_freestanding.zig | 2 +-
test/cases/safety/@alignCast misaligned.zig | 2 +-
.../@enumFromInt - no matching tag value.zig | 2 +-
...numFromInt truncated bits - exhaustive.zig | 2 +-
...FromInt truncated bits - nonexhaustive.zig | 2 +-
...rCast error not present in destination.zig | 2 +-
...ast error union casted to disjoint set.zig | 2 +-
test/cases/safety/@intCast to u0.zig | 2 +-
...at cannot fit - boundary case - i0 max.zig | 2 +-
...at cannot fit - boundary case - i0 min.zig | 2 +-
...annot fit - boundary case - signed max.zig | 2 +-
...annot fit - boundary case - signed min.zig | 2 +-
...at cannot fit - boundary case - u0 max.zig | 2 +-
...at cannot fit - boundary case - u0 min.zig | 2 +-
...not fit - boundary case - unsigned max.zig | 2 +-
...not fit - boundary case - unsigned min.zig | 2 +-
...annot fit - boundary case - vector max.zig | 2 +-
...annot fit - boundary case - vector min.zig | 2 +-
...oat cannot fit - negative out of range.zig | 2 +-
...loat cannot fit - negative to unsigned.zig | 2 +-
...oat cannot fit - positive out of range.zig | 2 +-
...o to non-optional byte-aligned pointer.zig | 2 +-
...t address zero to non-optional pointer.zig | 2 +-
.../@ptrFromInt with misaligned address.zig | 2 +-
.../@tagName on corrupted enum value.zig | 2 +-
.../@tagName on corrupted union value.zig | 2 +-
.../array slice sentinel mismatch vector.zig | 2 +-
.../safety/array slice sentinel mismatch.zig | 2 +-
test/cases/safety/bad union field access.zig | 2 +-
test/cases/safety/calling panic.zig | 2 +-
...ast []u8 to bigger slice of wrong size.zig | 2 +-
...er to global error and no code matches.zig | 2 +-
...mpty slice with sentinel out of bounds.zig | 2 +-
.../exact division failure - vectors.zig | 2 +-
test/cases/safety/exact division failure.zig | 2 +-
test/cases/safety/for_len_mismatch.zig | 2 +-
test/cases/safety/for_len_mismatch_three.zig | 2 +-
.../ignored expression integer overflow.zig | 2 +-
.../safety/integer addition overflow.zig | 2 +-
.../integer division by zero - vectors.zig | 2 +-
.../cases/safety/integer division by zero.zig | 2 +-
.../integer multiplication overflow.zig | 2 +-
.../safety/integer negation overflow.zig | 2 +-
.../safety/integer subtraction overflow.zig | 2 +-
test/cases/safety/memcpy_alias.zig | 2 +-
test/cases/safety/memcpy_len_mismatch.zig | 2 +-
test/cases/safety/memmove_len_mismatch.zig | 2 +-
.../safety/memset_array_undefined_bytes.zig | 2 +-
.../safety/memset_array_undefined_large.zig | 2 +-
.../safety/memset_slice_undefined_bytes.zig | 2 +-
.../safety/memset_slice_undefined_large.zig | 2 +-
test/cases/safety/modrem by zero.zig | 2 +-
test/cases/safety/modulus by zero.zig | 2 +-
test/cases/safety/noreturn returned.zig | 2 +-
.../optional unwrap operator on C pointer.zig | 2 +-
...tional unwrap operator on null pointer.zig | 2 +-
.../cases/safety/optional_empty_error_set.zig | 2 +-
.../out of bounds array slice by length.zig | 2 +-
.../safety/out of bounds slice access.zig | 2 +-
...r casting null to non-optional pointer.zig | 2 +-
...inter casting to null function pointer.zig | 2 +-
.../pointer slice sentinel mismatch.zig | 2 +-
.../safety/remainder division by zero.zig | 2 +-
.../safety/shift left by huge amount.zig | 2 +-
.../safety/shift right by huge amount.zig | 2 +-
...ed integer division overflow - vectors.zig | 2 +-
.../signed integer division overflow.zig | 2 +-
...in cast to unsigned integer - widening.zig | 2 +-
...ot fitting in cast to unsigned integer.zig | 2 +-
.../safety/signed shift left overflow.zig | 2 +-
.../safety/signed shift right overflow.zig | 2 +-
.../safety/signed-unsigned vector cast.zig | 2 +-
...ice by length sentinel mismatch on lhs.zig | 2 +-
...ice by length sentinel mismatch on rhs.zig | 2 +-
.../slice sentinel mismatch - floats.zig | 2 +-
... sentinel mismatch - optional pointers.zig | 2 +-
.../safety/slice slice sentinel mismatch.zig | 2 +-
...ice start index greater than end index.zig | 2 +-
...h sentinel out of bounds - runtime len.zig | 2 +-
.../slice with sentinel out of bounds.zig | 2 +-
test/cases/safety/slice_cast_change_len_0.zig | 2 +-
test/cases/safety/slice_cast_change_len_1.zig | 2 +-
test/cases/safety/slice_cast_change_len_2.zig | 2 +-
.../slicing null C pointer - runtime len.zig | 2 +-
test/cases/safety/slicing null C pointer.zig | 2 +-
...else on corrupt enum value - one prong.zig | 2 +-
...tch else on corrupt enum value - union.zig | 2 +-
.../switch else on corrupt enum value.zig | 2 +-
.../safety/switch on corrupted enum value.zig | 2 +-
.../switch on corrupted union value.zig | 2 +-
test/cases/safety/truncating vector cast.zig | 2 +-
test/cases/safety/unreachable.zig | 2 +-
...ast to signed integer - same bit count.zig | 2 +-
.../safety/unsigned shift left overflow.zig | 2 +-
.../safety/unsigned shift right overflow.zig | 2 +-
.../safety/unsigned-signed vector cast.zig | 2 +-
test/cases/safety/unwrap error switch.zig | 2 +-
test/cases/safety/unwrap error.zig | 2 +-
...e does not fit in shortening cast - u0.zig | 2 +-
.../value does not fit in shortening cast.zig | 2 +-
.../vector integer addition overflow.zig | 2 +-
...vector integer multiplication overflow.zig | 2 +-
.../vector integer negation overflow.zig | 2 +-
.../vector integer subtraction overflow.zig | 2 +-
test/cases/safety/zero casted to error.zig | 2 +-
.../taking_pointer_of_global_tagged_union.zig | 2 +-
test/src/Cases.zig | 6 +-
124 files changed, 1078 insertions(+), 365 deletions(-)
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 4ed2e8c0ca344238af7f94b436c1e430b5d3f69b..3796ed6acc53ccc82cbbbfac112eb8e7e32b13a0 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -1816,10 +1816,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
if (options.skip_linker_dependencies) break :s .none;
const want = options.want_compiler_rt orelse is_exe_or_dyn_lib;
if (!want) break :s .none;
- if (have_zcu) {
+ if (have_zcu and target_util.canBuildLibCompilerRt(target, use_llvm, build_options.have_llvm and use_llvm)) {
if (output_mode == .Obj) break :s .zcu;
- if (target.ofmt == .coff and target_util.zigBackend(target, use_llvm) == .stage2_x86_64)
- break :s if (is_exe_or_dyn_lib) .dyn_lib else .zcu;
+ if (switch (target_util.zigBackend(target, use_llvm)) {
+ else => false,
+ .stage2_aarch64, .stage2_x86_64 => target.ofmt == .coff,
+ }) break :s if (is_exe_or_dyn_lib) .dyn_lib else .zcu;
}
if (is_exe_or_dyn_lib) break :s .lib;
break :s .obj;
@@ -1854,7 +1856,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
const want_ubsan_rt = options.want_ubsan_rt orelse (can_build_ubsan_rt and any_sanitize_c == .full and is_exe_or_dyn_lib);
if (!want_ubsan_rt) break :s .none;
if (options.skip_linker_dependencies) break :s .none;
- if (have_zcu) break :s .zcu;
+ if (have_zcu and target_util.canBuildLibUbsanRt(target, use_llvm, build_options.have_llvm and use_llvm)) break :s .zcu;
if (is_exe_or_dyn_lib) break :s .lib;
break :s .obj;
};
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 89a23d351442202188bc95d0fbceab66ce00f765..70de2991437696a236ea4b35f1b69570d3137b28 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -168141,7 +168141,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unused,
.unused,
},
- .dst_temps = .{ .{ .cc = .b }, .unused },
+ .dst_temps = .{ .{ .cc = .be }, .unused },
.clobbers = .{ .eflags = true },
.each = .{ .once = &.{
.{ ._, ._, .lea, .tmp1p, .lea(.tmp0), ._, ._ },
@@ -168165,7 +168165,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unused,
.unused,
},
- .dst_temps = .{ .{ .cc = .b }, .unused },
+ .dst_temps = .{ .{ .cc = .be }, .unused },
.clobbers = .{ .eflags = true },
.each = .{ .once = &.{
.{ ._, ._, .lea, .tmp1p, .lea(.tmp0), ._, ._ },
@@ -168189,7 +168189,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unused,
.unused,
},
- .dst_temps = .{ .{ .cc = .b }, .unused },
+ .dst_temps = .{ .{ .cc = .be }, .unused },
.clobbers = .{ .eflags = true },
.each = .{ .once = &.{
.{ ._, ._, .lea, .tmp1p, .lea(.tmp0), ._, ._ },
diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
index da15dc6bfbe29a0c23f68ebf212d8eb2bc3807e4..49c67620d543d7aac587968f61ec911c4b6fdc01 100644
--- a/src/arch/x86_64/Emit.zig
+++ b/src/arch/x86_64/Emit.zig
@@ -168,11 +168,12 @@ pub fn emitMir(emit: *Emit) Error!void {
else if (emit.bin_file.cast(.macho)) |macho_file|
macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
- else if (emit.bin_file.cast(.coff)) |coff_file| sym_index: {
- const atom = coff_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
- return emit.fail("{s} creating lazy symbol", .{@errorName(err)});
- break :sym_index coff_file.getAtom(atom).getSymbolIndex().?;
- } else if (emit.bin_file.cast(.plan9)) |p9_file|
+ else if (emit.bin_file.cast(.coff)) |coff_file|
+ if (coff_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym)) |atom|
+ coff_file.getAtom(atom).getSymbolIndex().?
+ else |err|
+ return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
+ else if (emit.bin_file.cast(.plan9)) |p9_file|
p9_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
else
diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig
index f4b02a13c82dd21aa1991d6003a2a4ec0062680f..4cb3e8ecc8398119d72446cad4aa74eede4c7038 100644
--- a/src/codegen/aarch64.zig
+++ b/src/codegen/aarch64.zig
@@ -47,6 +47,7 @@ pub fn generate(
.literals = .empty,
.nav_relocs = .empty,
.uav_relocs = .empty,
+ .lazy_relocs = .empty,
.global_relocs = .empty,
.literal_relocs = .empty,
@@ -101,8 +102,8 @@ pub fn generate(
};
switch (passed_vi.parent(&isel)) {
.unallocated => if (!mod.strip) {
- var part_it = arg_vi.parts(&isel);
- const first_passed_part_vi = part_it.next() orelse passed_vi;
+ var part_it = passed_vi.parts(&isel);
+ const first_passed_part_vi = part_it.next().?;
const hint_ra = first_passed_part_vi.hint(&isel).?;
passed_vi.setParent(&isel, .{ .stack_slot = if (hint_ra.isVector())
isel.va_list.__vr_top.withOffset(@as(i8, -16) *
@@ -167,6 +168,7 @@ pub fn generate(
.literals = &.{},
.nav_relocs = &.{},
.uav_relocs = &.{},
+ .lazy_relocs = &.{},
.global_relocs = &.{},
.literal_relocs = &.{},
};
@@ -174,6 +176,7 @@ pub fn generate(
mir.literals = try isel.literals.toOwnedSlice(gpa);
mir.nav_relocs = try isel.nav_relocs.toOwnedSlice(gpa);
mir.uav_relocs = try isel.uav_relocs.toOwnedSlice(gpa);
+ mir.lazy_relocs = try isel.lazy_relocs.toOwnedSlice(gpa);
mir.global_relocs = try isel.global_relocs.toOwnedSlice(gpa);
mir.literal_relocs = try isel.literal_relocs.toOwnedSlice(gpa);
return mir;
diff --git a/src/codegen/aarch64/Assemble.zig b/src/codegen/aarch64/Assemble.zig
index 080df667d75ee73d84a3262cf0f80f14dbaeb1ca..494e012d80e2dc0662f796e7c445be6fd1d272bf 100644
--- a/src/codegen/aarch64/Assemble.zig
+++ b/src/codegen/aarch64/Assemble.zig
@@ -6,14 +6,19 @@ pub const Operand = union(enum) {
};
pub fn nextInstruction(as: *Assemble) !?Instruction {
- @setEvalBranchQuota(37_000);
+ @setEvalBranchQuota(42_000);
comptime var ct_token_buf: [token_buf_len]u8 = undefined;
var token_buf: [token_buf_len]u8 = undefined;
const original_source = while (true) {
const original_source = as.source;
const source_token = try as.nextToken(&token_buf, .{});
- if (source_token.len == 0) return null;
- if (source_token[0] != '\n') break original_source;
+ switch (source_token.len) {
+ 0 => return null,
+ else => switch (source_token[0]) {
+ else => break original_source,
+ '\n', ';' => {},
+ },
+ }
};
log.debug(
\\.
@@ -52,7 +57,13 @@ pub fn nextInstruction(as: *Assemble) !?Instruction {
std.zig.fmtString(source_token),
});
if (pattern_token.len == 0) {
- if (source_token.len > 0 and source_token[0] != '\n') break :next_pattern;
+ switch (source_token.len) {
+ 0 => {},
+ else => switch (source_token[0]) {
+ else => break :next_pattern,
+ '\n', ';' => {},
+ },
+ }
const encode = @field(Instruction, @tagName(instruction.encode[0]));
const Encode = @TypeOf(encode);
var args: std.meta.ArgsTuple(Encode) = undefined;
@@ -65,7 +76,7 @@ pub fn nextInstruction(as: *Assemble) !?Instruction {
const symbol = &@field(symbols, symbol_name);
symbol.* = zonCast(SymbolSpec, @field(instruction.symbols, symbol_name), .{}).parse(source_token) orelse break :next_pattern;
log.debug("{s} = {any}", .{ symbol_name, symbol.* });
- } else if (!std.ascii.eqlIgnoreCase(pattern_token, source_token)) break :next_pattern;
+ } else if (!toUpperEqlAssertUpper(source_token, pattern_token)) break :next_pattern;
}
}
log.debug("'{s}' not matched...", .{instruction.pattern});
@@ -125,6 +136,15 @@ fn zonCast(comptime Result: type, zon_value: anytype, symbols: anytype) Result {
}
}
+fn toUpperEqlAssertUpper(lhs: []const u8, rhs: []const u8) bool {
+ if (lhs.len != rhs.len) return false;
+ for (lhs, rhs) |l, r| {
+ assert(!std.ascii.isLower(r));
+ if (std.ascii.toUpper(l) != r) return false;
+ }
+ return true;
+}
+
const token_buf_len = "v31.b[15]".len;
fn nextToken(as: *Assemble, buf: *[token_buf_len]u8, comptime opts: struct {
operands: bool = false,
@@ -134,7 +154,7 @@ fn nextToken(as: *Assemble, buf: *[token_buf_len]u8, comptime opts: struct {
while (true) c: switch (as.source[0]) {
0 => return as.source[0..0],
'\t', '\n' + 1...'\r', ' ' => as.source = as.source[1..],
- '\n', '!', '#', ',', '[', ']' => {
+ '\n', '!', '#', ',', ';', '[', ']' => {
defer as.source = as.source[1..];
return as.source[0..1];
},
diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig
index 1446238888b0383ee9028aa3d323bc9b6fc0d278..b6598b7ea7b1ed97a747328973010a8aaacf3af2 100644
--- a/src/codegen/aarch64/Mir.zig
+++ b/src/codegen/aarch64/Mir.zig
@@ -4,6 +4,7 @@ epilogue: []const Instruction,
literals: []const u32,
nav_relocs: []const Reloc.Nav,
uav_relocs: []const Reloc.Uav,
+lazy_relocs: []const Reloc.Lazy,
global_relocs: []const Reloc.Global,
literal_relocs: []const Reloc.Literal,
@@ -21,8 +22,13 @@ pub const Reloc = struct {
reloc: Reloc,
};
+ pub const Lazy = struct {
+ symbol: link.File.LazySymbol,
+ reloc: Reloc,
+ };
+
pub const Global = struct {
- global: [*:0]const u8,
+ name: [*:0]const u8,
reloc: Reloc,
};
@@ -38,6 +44,7 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
gpa.free(mir.literals);
gpa.free(mir.nav_relocs);
gpa.free(mir.uav_relocs);
+ gpa.free(mir.lazy_relocs);
gpa.free(mir.global_relocs);
gpa.free(mir.literal_relocs);
mir.* = undefined;
@@ -119,16 +126,37 @@ pub fn emit(
body_end - Instruction.size * (1 + uav_reloc.reloc.label),
uav_reloc.reloc.addend,
);
+ for (mir.lazy_relocs) |lazy_reloc| try emitReloc(
+ lf,
+ zcu,
+ func.owner_nav,
+ if (lf.cast(.elf)) |ef|
+ ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ else if (lf.cast(.macho)) |mf|
+ mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ else if (lf.cast(.coff)) |cf|
+ if (cf.getOrCreateAtomForLazySymbol(pt, lazy_reloc.symbol)) |atom|
+ cf.getAtom(atom).getSymbolIndex().?
+ else |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ else
+ return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
+ mir.body[lazy_reloc.reloc.label],
+ body_end - Instruction.size * (1 + lazy_reloc.reloc.label),
+ lazy_reloc.reloc.addend,
+ );
for (mir.global_relocs) |global_reloc| try emitReloc(
lf,
zcu,
func.owner_nav,
if (lf.cast(.elf)) |ef|
- try ef.getGlobalSymbol(std.mem.span(global_reloc.global), null)
+ try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null)
else if (lf.cast(.macho)) |mf|
- try mf.getGlobalSymbol(std.mem.span(global_reloc.global), null)
+ try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null)
else if (lf.cast(.coff)) |cf|
- try cf.getGlobalSymbol(std.mem.span(global_reloc.global), "compiler_rt")
+ try cf.getGlobalSymbol(std.mem.span(global_reloc.name), "compiler_rt")
else
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
mir.body[global_reloc.reloc.label],
@@ -172,35 +200,6 @@ fn emitReloc(
const gpa = zcu.gpa;
switch (instruction.decode()) {
else => unreachable,
- .branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
- const zo = ef.zigObjectPtr().?;
- const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
- const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
- .b => .JUMP26,
- .bl => .CALL26,
- };
- try atom.addReloc(gpa, .{
- .r_offset = offset,
- .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
- .r_addend = @bitCast(addend),
- }, zo);
- } else if (lf.cast(.macho)) |mf| {
- const zo = mf.getZigObject().?;
- const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
- try atom.addReloc(mf, .{
- .tag = .@"extern",
- .offset = offset,
- .target = sym_index,
- .addend = @bitCast(addend),
- .type = .branch,
- .meta = .{
- .pcrel = true,
- .has_subtractor = false,
- .length = 2,
- .symbolnum = @intCast(sym_index),
- },
- });
- },
.data_processing_immediate => |decoded| if (lf.cast(.elf)) |ef| {
const zo = ef.zigObjectPtr().?;
const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
@@ -259,6 +258,80 @@ fn emitReloc(
},
}
},
+ .branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
+ const zo = ef.zigObjectPtr().?;
+ const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
+ const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
+ .b => .JUMP26,
+ .bl => .CALL26,
+ };
+ try atom.addReloc(gpa, .{
+ .r_offset = offset,
+ .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_addend = @bitCast(addend),
+ }, zo);
+ } else if (lf.cast(.macho)) |mf| {
+ const zo = mf.getZigObject().?;
+ const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
+ try atom.addReloc(mf, .{
+ .tag = .@"extern",
+ .offset = offset,
+ .target = sym_index,
+ .addend = @bitCast(addend),
+ .type = .branch,
+ .meta = .{
+ .pcrel = true,
+ .has_subtractor = false,
+ .length = 2,
+ .symbolnum = @intCast(sym_index),
+ },
+ });
+ },
+ .load_store => |decoded| if (lf.cast(.elf)) |ef| {
+ const zo = ef.zigObjectPtr().?;
+ const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
+ const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) {
+ .integer => |integer| switch (integer.decode()) {
+ .unallocated, .prfm => unreachable,
+ .strb, .ldrb, .ldrsb => .LDST8_ABS_LO12_NC,
+ .strh, .ldrh, .ldrsh => .LDST16_ABS_LO12_NC,
+ .ldrsw => .LDST32_ABS_LO12_NC,
+ inline .str, .ldr => |encoded| switch (encoded.sf) {
+ .word => .LDST32_ABS_LO12_NC,
+ .doubleword => .LDST64_ABS_LO12_NC,
+ },
+ },
+ .vector => |vector| switch (vector.group.opc1.decode(vector.group.size)) {
+ .byte => .LDST8_ABS_LO12_NC,
+ .half => .LDST16_ABS_LO12_NC,
+ .single => .LDST32_ABS_LO12_NC,
+ .double => .LDST64_ABS_LO12_NC,
+ .quad => .LDST128_ABS_LO12_NC,
+ .scalable, .predicate => unreachable,
+ },
+ };
+ try atom.addReloc(gpa, .{
+ .r_offset = offset,
+ .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_addend = @bitCast(addend),
+ }, zo);
+ } else if (lf.cast(.macho)) |mf| {
+ const zo = mf.getZigObject().?;
+ const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
+ try atom.addReloc(mf, .{
+ .tag = .@"extern",
+ .offset = offset,
+ .target = sym_index,
+ .addend = @bitCast(addend),
+ .type = .pageoff,
+ .meta = .{
+ .pcrel = false,
+ .has_subtractor = false,
+ .length = 2,
+ .symbolnum = @intCast(sym_index),
+ },
+ });
+ },
}
}
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index b0b1297a932f119595fc9bfa3b85b2079cd0155f..6ceb3f3a59b0f1679a2b921782d8a4f025b4b712 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -22,6 +22,7 @@ instructions: std.ArrayListUnmanaged(codegen.aarch64.encoding.Instruction),
literals: std.ArrayListUnmanaged(u32),
nav_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Nav),
uav_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Uav),
+lazy_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Lazy),
global_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Global),
literal_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Literal),
@@ -50,11 +51,11 @@ pub const Block = struct {
std.math.maxInt(@typeInfo(Air.Inst.Index).@"enum".tag_type),
);
- fn branch(block: *const Block, isel: *Select) !void {
- if (isel.instructions.items.len > block.target_label) {
- try isel.emit(.b(@intCast((isel.instructions.items.len + 1 - block.target_label) << 2)));
+ fn branch(target_block: *const Block, isel: *Select) !void {
+ if (isel.instructions.items.len > target_block.target_label) {
+ try isel.emit(.b(@intCast((isel.instructions.items.len + 1 - target_block.target_label) << 2)));
}
- try isel.merge(&block.live_registers, .{});
+ try isel.merge(&target_block.live_registers, .{});
}
};
@@ -84,12 +85,12 @@ pub const Loop = struct {
pub const empty_list: u32 = std.math.maxInt(u32);
- fn branch(loop: *Loop, isel: *Select) !void {
+ fn branch(target_loop: *Loop, isel: *Select) !void {
try isel.instructions.ensureUnusedCapacity(isel.pt.zcu.gpa, 1);
- const repeat_list_tail = loop.repeat_list;
- loop.repeat_list = @intCast(isel.instructions.items.len);
+ const repeat_list_tail = target_loop.repeat_list;
+ target_loop.repeat_list = @intCast(isel.instructions.items.len);
isel.instructions.appendAssumeCapacity(@bitCast(repeat_list_tail));
- try isel.merge(&loop.live_registers, .{});
+ try isel.merge(&target_loop.live_registers, .{});
}
};
@@ -108,6 +109,7 @@ pub fn deinit(isel: *Select) void {
isel.literals.deinit(gpa);
isel.nav_relocs.deinit(gpa);
isel.uav_relocs.deinit(gpa);
+ isel.lazy_relocs.deinit(gpa);
isel.global_relocs.deinit(gpa);
isel.literal_relocs.deinit(gpa);
@@ -864,7 +866,7 @@ pub fn finishAnalysis(isel: *Select) !void {
}
}
-pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
+pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
const zcu = isel.pt.zcu;
const ip = &zcu.intern_pool;
const gpa = zcu.gpa;
@@ -946,7 +948,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .add, .add_optimized, .add_wrap, .sub, .sub_optimized, .sub_wrap => |air_tag| {
+ .add, .add_safe, .add_optimized, .add_wrap, .sub, .sub_safe, .sub_optimized, .sub_wrap => |air_tag| {
if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
defer res_vi.value.deref(isel);
@@ -954,13 +956,16 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isRuntimeFloat()) try res_vi.value.addOrSubtract(isel, ty, try isel.use(bin_op.lhs), switch (air_tag) {
else => unreachable,
- .add, .add_wrap => .add,
- .sub, .sub_wrap => .sub,
- }, try isel.use(bin_op.rhs), .{ .wrap = switch (air_tag) {
- else => unreachable,
- .add, .sub => false,
- .add_wrap, .sub_wrap => true,
- } }) else switch (ty.floatBits(isel.target)) {
+ .add, .add_safe, .add_wrap => .add,
+ .sub, .sub_safe, .sub_wrap => .sub,
+ }, try isel.use(bin_op.rhs), .{
+ .overflow = switch (air_tag) {
+ else => unreachable,
+ .add, .sub => .@"unreachable",
+ .add_safe, .sub_safe => .{ .panic = .integer_overflow },
+ .add_wrap, .sub_wrap => .wrap,
+ },
+ }) else switch (ty.floatBits(isel.target)) {
else => unreachable,
16, 32, 64 => |bits| {
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
@@ -1021,7 +1026,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (air_tag) {
+ .name = switch (air_tag) {
else => unreachable,
.add, .add_optimized => switch (bits) {
else => unreachable,
@@ -1336,7 +1341,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__mulhf3",
32 => "__mulsf3",
@@ -1379,6 +1384,143 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .mul_safe => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
+ defer res_vi.value.deref(isel);
+
+ const bin_op = air.data(air.inst_index).bin_op;
+ const ty = isel.air.typeOf(bin_op.lhs, ip);
+ if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
+ const int_info = ty.intInfo(zcu);
+ switch (int_info.signedness) {
+ .signed => switch (int_info.bits) {
+ 0 => unreachable,
+ 1 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(.orr(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.@"b."(
+ .invert(.ne),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.ands(.wzr, lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
+ },
+ .unsigned => switch (int_info.bits) {
+ 0 => unreachable,
+ 1 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(.@"and"(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 2...16 => |bits| {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.@"b."(
+ .eq,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.ands(.wzr, res_ra.w(), .{ .immediate = .{
+ .N = .word,
+ .immr = @intCast(32 - bits),
+ .imms = @intCast(32 - bits - 1),
+ } }));
+ try isel.emit(.madd(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 17...32 => |bits| {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.@"b."(
+ .eq,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.ands(.xzr, res_ra.x(), .{ .immediate = .{
+ .N = .doubleword,
+ .immr = @intCast(64 - bits),
+ .imms = @intCast(64 - bits - 1),
+ } }));
+ try isel.emit(.umaddl(res_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 33...63 => |bits| {
+ const lo64_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ const hi64_ra = hi64_ra: {
+ const lo64_lock = isel.tryLockReg(lo64_ra);
+ defer lo64_lock.unlock(isel);
+ break :hi64_ra try isel.allocIntReg();
+ };
+ defer isel.freeReg(hi64_ra);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.cbz(
+ hi64_ra.x(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.orr(hi64_ra.x(), hi64_ra.x(), .{ .shifted_register = .{
+ .register = lo64_ra.x(),
+ .shift = .{ .lsr = @intCast(bits) },
+ } }));
+ try isel.emit(.madd(lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
+ try isel.emit(.umulh(hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 64 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(.madd(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
+ const hi64_ra = try isel.allocIntReg();
+ defer isel.freeReg(hi64_ra);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.cbz(
+ hi64_ra.x(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.umulh(hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 65...128 => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
+ else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.mul_sat => |air_tag| {
if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
defer res_vi.value.deref(isel);
@@ -1674,7 +1816,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__divhf3",
32 => "__divsf3",
@@ -1813,7 +1955,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (int_info.signedness) {
+ .name = switch (int_info.signedness) {
.signed => "__divti3",
.unsigned => "__udivti3",
},
@@ -1917,7 +2059,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
else => unreachable,
.div_trunc, .div_trunc_optimized => {
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__trunch",
32 => "truncf",
@@ -1931,7 +2073,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
},
.div_floor, .div_floor_optimized => {
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__floorh",
32 => "floorf",
@@ -1946,7 +2088,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
.div_exact, .div_exact_optimized => {},
}
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__divhf3",
32 => "__divsf3",
@@ -2046,7 +2188,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__fmodh",
32 => "fmodf",
@@ -2212,7 +2354,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (air_tag) {
+ .name = switch (air_tag) {
else => unreachable,
.max => switch (bits) {
else => unreachable,
@@ -2284,7 +2426,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
else => unreachable,
.add_with_overflow => .add,
.sub_with_overflow => .sub,
- }, rhs_vi, .{ .wrap = true, .overflow_ra = try overflow_vi.?.defReg(isel) orelse .zr });
+ }, rhs_vi, .{
+ .overflow = if (try overflow_vi.?.defReg(isel)) |overflow_ra| .{ .ra = overflow_ra } else .wrap,
+ });
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -3092,7 +3236,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = "memcpy",
+ .name = "memcpy",
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -3119,7 +3263,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = "memcpy",
+ .name = "memcpy",
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -3139,19 +3283,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
.block => {
const ty_pl = air.data(air.inst_index).ty_pl;
const extra = isel.air.extraData(Air.Block, ty_pl.payload);
-
- if (ty_pl.ty != .noreturn_type) {
- isel.blocks.putAssumeCapacityNoClobber(air.inst_index, .{
- .live_registers = isel.live_registers,
- .target_label = @intCast(isel.instructions.items.len),
- });
- }
- try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
- if (ty_pl.ty != .noreturn_type) {
- const block_entry = isel.blocks.pop().?;
- assert(block_entry.key == air.inst_index);
- if (isel.live_values.fetchRemove(air.inst_index)) |result_vi| result_vi.value.deref(isel);
- }
+ try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast(
+ isel.air.extra.items[extra.end..][0..extra.data.body_len],
+ ));
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
.loop => {
@@ -3175,11 +3309,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
// IT'S DOM TIME!!!
- for (isel.blocks.values(), 0..) |*block, dom_index| {
+ for (isel.blocks.values(), 0..) |*dom_block, dom_index| {
if (@as(u1, @truncate(isel.dom.items[
loop.dom + dom_index / @bitSizeOf(DomInt)
] >> @truncate(dom_index))) == 0) continue;
- var live_reg_it = block.live_registers.iterator();
+ var live_reg_it = dom_block.live_registers.iterator();
while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) {
_ => |live_vi| try live_vi.mat(isel),
.allocating => unreachable,
@@ -3211,8 +3345,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
},
.br => {
const br = air.data(air.inst_index).br;
- const block = isel.blocks.getPtr(br.block_inst).?;
- try block.branch(isel);
+ try isel.blocks.getPtr(br.block_inst).?.branch(isel);
if (isel.live_values.get(br.block_inst)) |dst_vi| try dst_vi.move(isel, br.operand);
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -3224,6 +3357,22 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try isel.emit(.brk(0xf000));
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .ret_addr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |addr_vi| unused: {
+ defer addr_vi.value.deref(isel);
+ const addr_ra = try addr_vi.value.defReg(isel) orelse break :unused;
+ try isel.emit(.ldr(addr_ra.x(), .{ .unsigned_offset = .{ .base = .fp, .offset = 8 } }));
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .frame_addr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |addr_vi| unused: {
+ defer addr_vi.value.deref(isel);
+ const addr_ra = try addr_vi.value.defReg(isel) orelse break :unused;
+ try isel.emit(.orr(addr_ra.x(), .xzr, .{ .register = .fp }));
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.call => {
const pl_op = air.data(air.inst_index).pl_op;
const extra = isel.air.extraData(Air.Call, pl_op.payload);
@@ -3312,7 +3461,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
var param_part_it = passed_vi.parts(isel);
var arg_part_it = arg_vi.parts(isel);
if (arg_part_it.only()) |_| {
- try isel.values.ensureUnusedCapacity(isel.pt.zcu.gpa, param_part_it.remaining);
+ try isel.values.ensureUnusedCapacity(gpa, param_part_it.remaining);
arg_vi.setParts(isel, param_part_it.remaining);
while (param_part_it.next()) |param_part_vi| _ = arg_vi.addPart(
isel,
@@ -3659,7 +3808,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (air_tag) {
+ .name = switch (air_tag) {
else => unreachable,
.sqrt => switch (bits) {
else => unreachable,
@@ -3751,7 +3900,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (air_tag) {
+ .name = switch (air_tag) {
else => unreachable,
.sin => switch (bits) {
else => unreachable,
@@ -4239,7 +4388,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__cmphf2",
32 => "__cmpsf2",
@@ -4629,6 +4778,14 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try isel.emit(.nop());
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .dbg_inline_block => {
+ const ty_pl = air.data(air.inst_index).ty_pl;
+ const extra = isel.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+ try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast(
+ isel.air.extra.items[extra.end..][0..extra.data.body_len],
+ ));
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => {
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -4724,7 +4881,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = "memcpy",
+ .name = "memcpy",
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -4816,10 +4973,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
const ptr_ty = isel.air.typeOf(bin_op.lhs, ip);
const ptr_info = ptr_ty.ptrInfo(zcu);
if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed store", .{});
- if (bin_op.rhs.toInterned()) |rhs_val| if (ip.isUndef(rhs_val)) {
- if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
- break :air_tag;
- };
+ if (bin_op.rhs.toInterned()) |rhs_val| if (ip.isUndef(rhs_val))
+ break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
const src_vi = try isel.use(bin_op.rhs);
const size = src_vi.size(isel);
@@ -4833,8 +4988,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
});
try ptr_mat.finish(isel);
- if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
- break :air_tag;
+ break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
else => {},
};
@@ -4843,7 +4997,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = "memcpy",
+ .name = "memcpy",
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -4906,7 +5060,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (dst_bits) {
+ .name = switch (dst_bits) {
else => unreachable,
16 => switch (src_bits) {
else => unreachable,
@@ -5060,6 +5214,108 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .intcast_safe => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
+ defer dst_vi.value.deref(isel);
+
+ const ty_op = air.data(air.inst_index).ty_op;
+ const dst_ty = ty_op.ty.toType();
+ const dst_int_info = dst_ty.intInfo(zcu);
+ const src_ty = isel.air.typeOf(ty_op.operand, ip);
+ const src_int_info = src_ty.intInfo(zcu);
+ const can_be_negative = dst_int_info.signedness == .signed and
+ src_int_info.signedness == .signed;
+ const panic_id: Zcu.SimplePanicId = panic_id: switch (dst_ty.zigTypeTag(zcu)) {
+ else => unreachable,
+ .int => .integer_out_of_bounds,
+ .@"enum" => {
+ if (!dst_ty.isNonexhaustiveEnum(zcu)) {
+ return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
+ }
+ break :panic_id .invalid_enum_value;
+ },
+ };
+ if (dst_ty.toIntern() == src_ty.toIntern()) {
+ try dst_vi.value.move(isel, ty_op.operand);
+ } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 64) {
+ const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
+ const src_vi = try isel.use(ty_op.operand);
+ const dst_active_bits = dst_int_info.bits - @intFromBool(dst_int_info.signedness == .signed);
+ const src_active_bits = src_int_info.bits - @intFromBool(src_int_info.signedness == .signed);
+ if ((dst_int_info.signedness != .unsigned or src_int_info.signedness != .signed) and dst_active_bits >= src_active_bits) {
+ const src_mat = try src_vi.matReg(isel);
+ try isel.emit(if (can_be_negative and dst_active_bits > 32 and src_active_bits <= 32)
+ .sbfm(dst_ra.x(), src_mat.ra.x(), .{
+ .N = .doubleword,
+ .immr = 0,
+ .imms = @intCast(src_int_info.bits - 1),
+ })
+ else switch (src_int_info.bits) {
+ else => unreachable,
+ 1...32 => .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }),
+ 33...64 => .orr(dst_ra.x(), .xzr, .{ .register = src_mat.ra.x() }),
+ });
+ try src_mat.finish(isel);
+ } else {
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(panic_id);
+ try isel.emit(.@"b."(
+ .eq,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ if (can_be_negative) {
+ const src_mat = src_mat: {
+ const dst_lock = isel.lockReg(dst_ra);
+ defer dst_lock.unlock(isel);
+ break :src_mat try src_vi.matReg(isel);
+ };
+ try isel.emit(switch (src_int_info.bits) {
+ else => unreachable,
+ 1...32 => .subs(.wzr, dst_ra.w(), .{ .register = src_mat.ra.w() }),
+ 33...64 => .subs(.xzr, dst_ra.x(), .{ .register = src_mat.ra.x() }),
+ });
+ try isel.emit(switch (@max(dst_int_info.bits, src_int_info.bits)) {
+ else => unreachable,
+ 1...32 => .sbfm(dst_ra.w(), src_mat.ra.w(), .{
+ .N = .word,
+ .immr = 0,
+ .imms = @intCast(dst_int_info.bits - 1),
+ }),
+ 33...64 => .sbfm(dst_ra.x(), src_mat.ra.x(), .{
+ .N = .doubleword,
+ .immr = 0,
+ .imms = @intCast(dst_int_info.bits - 1),
+ }),
+ });
+ try src_mat.finish(isel);
+ } else {
+ const src_mat = try src_vi.matReg(isel);
+ try isel.emit(switch (@min(dst_int_info.bits, src_int_info.bits)) {
+ else => unreachable,
+ 1...32 => .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }),
+ 33...64 => .orr(dst_ra.x(), .xzr, .{ .register = src_mat.ra.x() }),
+ });
+ const active_bits = @min(dst_active_bits, src_active_bits);
+ try isel.emit(switch (src_int_info.bits) {
+ else => unreachable,
+ 1...32 => .ands(.wzr, src_mat.ra.w(), .{ .immediate = .{
+ .N = .word,
+ .immr = @intCast(32 - active_bits),
+ .imms = @intCast(32 - active_bits - 1),
+ } }),
+ 33...64 => .ands(.xzr, src_mat.ra.x(), .{ .immediate = .{
+ .N = .doubleword,
+ .immr = @intCast(64 - active_bits),
+ .imms = @intCast(64 - active_bits - 1),
+ } }),
+ });
+ try src_mat.finish(isel);
+ }
+ }
+ } else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.trunc => |air_tag| {
if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
defer dst_vi.value.deref(isel);
@@ -5832,7 +6088,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (dst_int_info.bits) {
+ .name = switch (dst_int_info.bits) {
else => unreachable,
1...32 => switch (dst_int_info.signedness) {
.signed => switch (src_bits) {
@@ -5972,7 +6228,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (src_int_info.bits) {
+ .name = switch (src_int_info.bits) {
else => unreachable,
1...32 => switch (src_int_info.signedness) {
.signed => switch (dst_bits) {
@@ -6055,14 +6311,20 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .memset => |air_tag| {
+ .memset, .memset_safe => |air_tag| {
const bin_op = air.data(air.inst_index).bin_op;
const dst_ty = isel.air.typeOf(bin_op.lhs, ip);
const dst_info = dst_ty.ptrInfo(zcu);
const fill_byte: union(enum) { constant: u8, value: Air.Inst.Ref } = fill_byte: {
- if (bin_op.rhs.toInterned()) |fill_val|
+ if (bin_op.rhs.toInterned()) |fill_val| {
+ if (ip.isUndef(fill_val)) switch (air_tag) {
+ else => unreachable,
+ .memset => break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
+ .memset_safe => break :fill_byte .{ .constant = 0xaa },
+ };
if (try isel.hasRepeatedByteRepr(.fromInterned(fill_val))) |fill_byte|
break :fill_byte .{ .constant = fill_byte };
+ }
switch (dst_ty.elemType2(zcu).abiSize(zcu)) {
0 => unreachable,
1 => break :fill_byte .{ .value = bin_op.rhs },
@@ -6121,8 +6383,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
.c => unreachable,
}
- if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
- break :air_tag;
+ break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty) }),
}
@@ -6133,7 +6394,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = "memset",
+ .name = "memset",
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -6179,7 +6440,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = @tagName(air_tag),
+ .name = @tagName(air_tag),
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -6268,6 +6529,72 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .error_name => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |name_vi| unused: {
+ defer name_vi.value.deref(isel);
+ var ptr_part_it = name_vi.value.field(.slice_const_u8_sentinel_0, 0, 8);
+ const ptr_part_vi = try ptr_part_it.only(isel);
+ const ptr_part_ra = try ptr_part_vi.?.defReg(isel);
+ var len_part_it = name_vi.value.field(.slice_const_u8_sentinel_0, 8, 8);
+ const len_part_vi = try len_part_it.only(isel);
+ const len_part_ra = try len_part_vi.?.defReg(isel);
+ if (ptr_part_ra == null and len_part_ra == null) break :unused;
+
+ const un_op = air.data(air.inst_index).un_op;
+ const err_vi = try isel.use(un_op);
+ const err_mat = try err_vi.matReg(isel);
+ const ptr_ra = try isel.allocIntReg();
+ defer isel.freeReg(ptr_ra);
+ const start_ra, const end_ra = range_ras: {
+ const name_lock: RegLock = if (len_part_ra != null) if (ptr_part_ra) |name_ptr_ra|
+ isel.tryLockReg(name_ptr_ra)
+ else
+ .empty else .empty;
+ defer name_lock.unlock(isel);
+ break :range_ras .{ try isel.allocIntReg(), try isel.allocIntReg() };
+ };
+ defer {
+ isel.freeReg(start_ra);
+ isel.freeReg(end_ra);
+ }
+ if (len_part_ra) |name_len_ra| try isel.emit(.sub(
+ name_len_ra.w(),
+ end_ra.w(),
+ .{ .register = start_ra.w() },
+ ));
+ if (ptr_part_ra) |name_ptr_ra| try isel.emit(.add(
+ name_ptr_ra.x(),
+ ptr_ra.x(),
+ .{ .extended_register = .{
+ .register = start_ra.w(),
+ .extend = .{ .uxtw = 0 },
+ } },
+ ));
+ if (len_part_ra) |_| try isel.emit(.sub(end_ra.w(), end_ra.w(), .{ .immediate = 1 }));
+ try isel.emit(.ldp(start_ra.w(), end_ra.w(), .{ .base = start_ra.x() }));
+ try isel.emit(.add(start_ra.x(), ptr_ra.x(), .{ .extended_register = .{
+ .register = err_mat.ra.w(),
+ .extend = switch (zcu.errorSetBits()) {
+ else => unreachable,
+ 1...8 => .{ .uxtb = 2 },
+ 9...16 => .{ .uxth = 2 },
+ 17...32 => .{ .uxtw = 2 },
+ },
+ } }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.adrp(ptr_ra.x(), 0));
+ try err_mat.finish(isel);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.aggregate_init => {
if (isel.live_values.fetchRemove(air.inst_index)) |agg_vi| {
defer agg_vi.value.deref(isel);
@@ -6362,7 +6689,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = "memcpy",
+ .name = "memcpy",
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
@@ -6478,7 +6805,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
- .global = switch (bits) {
+ .name = switch (bits) {
else => unreachable,
16 => "__fmah",
32 => "fmaf",
@@ -6559,6 +6886,32 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .cmp_lt_errors_len => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |is_vi| unused: {
+ defer is_vi.value.deref(isel);
+ const is_ra = try is_vi.value.defReg(isel) orelse break :unused;
+ try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(.ls)));
+
+ const un_op = air.data(air.inst_index).un_op;
+ const err_vi = try isel.use(un_op);
+ const err_mat = try err_vi.matReg(isel);
+ const ptr_ra = try isel.allocIntReg();
+ defer isel.freeReg(ptr_ra);
+ try isel.emit(.subs(.wzr, err_mat.ra.w(), .{ .register = ptr_ra.w() }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.ldr(ptr_ra.w(), .{ .base = ptr_ra.x() }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.adrp(ptr_ra.x(), 0));
+ try err_mat.finish(isel);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.runtime_nav_ptr => {
if (isel.live_values.fetchRemove(air.inst_index)) |ptr_vi| unused: {
defer ptr_vi.value.deref(isel);
@@ -6567,19 +6920,19 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
const ty_nav = air.data(air.inst_index).ty_nav;
if (ZigType.fromInterned(ip.getNav(ty_nav.nav).typeOf(ip)).isFnOrHasRuntimeBits(zcu)) switch (true) {
false => {
- try isel.nav_relocs.append(zcu.gpa, .{
+ try isel.nav_relocs.append(gpa, .{
.nav = ty_nav.nav,
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.adr(ptr_ra.x(), 0));
},
true => {
- try isel.nav_relocs.append(zcu.gpa, .{
+ try isel.nav_relocs.append(gpa, .{
.nav = ty_nav.nav,
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 }));
- try isel.nav_relocs.append(zcu.gpa, .{
+ try isel.nav_relocs.append(gpa, .{
.nav = ty_nav.nav,
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
@@ -6589,9 +6942,6 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .add_safe,
- .sub_safe,
- .mul_safe,
.inferred_alloc,
.inferred_alloc_comptime,
.int_from_float_safe,
@@ -6822,6 +7172,9 @@ pub fn layout(
saves_len += 1;
saves_size += 8;
deferred_gr = null;
+ } else switch (@as(u1, @truncate(saved_gra_len))) {
+ 0 => {},
+ 1 => saves_size += 8,
}
save_ra = if (mod.strip) incoming.ngrn else CallAbiIterator.ngrn_start;
while (save_ra != if (have_va) CallAbiIterator.ngrn_end else incoming.ngrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
@@ -6844,42 +7197,42 @@ pub fn layout(
{
wip_mir_log.debug("{f}:", .{nav.fqn.fmt(ip)});
var save_index: usize = 0;
- while (save_index < saves.len) {
- if (save_index + 2 <= saves.len and saves[save_index + 0].class == saves[save_index + 1].class and
- saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset)
- {
- try isel.emit(.stp(
- saves[save_index + 0].register,
- saves[save_index + 1].register,
- switch (saves[save_index + 0].offset) {
- 0 => .{ .pre_index = .{
- .base = .sp,
- .index = @intCast(-@as(i11, saves_size)),
- } },
- else => |offset| .{ .signed_offset = .{
- .base = .sp,
- .offset = @intCast(offset),
- } },
- },
- ));
- save_index += 2;
- } else {
- try isel.emit(.str(
- saves[save_index].register,
- switch (saves[save_index].offset) {
- 0 => .{ .pre_index = .{
- .base = .sp,
- .index = @intCast(-@as(i11, saves_size)),
- } },
- else => |offset| .{ .unsigned_offset = .{
- .base = .sp,
- .offset = @intCast(offset),
- } },
- },
- ));
- save_index += 1;
- }
- }
+ while (save_index < saves.len) if (save_index + 2 <= saves.len and
+ saves[save_index + 0].class == saves[save_index + 1].class and
+ saves[save_index + 0].size == saves[save_index + 1].size and
+ saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset)
+ {
+ try isel.emit(.stp(
+ saves[save_index + 0].register,
+ saves[save_index + 1].register,
+ switch (saves[save_index + 0].offset) {
+ 0 => .{ .pre_index = .{
+ .base = .sp,
+ .index = @intCast(-@as(i11, saves_size)),
+ } },
+ else => |offset| .{ .signed_offset = .{
+ .base = .sp,
+ .offset = @intCast(offset),
+ } },
+ },
+ ));
+ save_index += 2;
+ } else {
+ try isel.emit(.str(
+ saves[save_index].register,
+ switch (saves[save_index].offset) {
+ 0 => .{ .pre_index = .{
+ .base = .sp,
+ .index = @intCast(-@as(i11, saves_size)),
+ } },
+ else => |offset| .{ .unsigned_offset = .{
+ .base = .sp,
+ .offset = @intCast(offset),
+ } },
+ },
+ ));
+ save_index += 1;
+ };
try isel.emit(.add(.fp, .sp, .{ .immediate = frame_record_offset }));
const scratch_reg: Register = if (isel.stack_align == .@"16")
@@ -7053,11 +7406,43 @@ fn fmtConstant(isel: *Select, constant: Constant) @typeInfo(@TypeOf(Constant.fmt
return constant.fmtValue(isel.pt);
}
+fn block(
+ isel: *Select,
+ air_inst_index: Air.Inst.Index,
+ res_ty: ZigType,
+ air_body: []const Air.Inst.Index,
+) !void {
+ if (res_ty.toIntern() != .noreturn_type) {
+ isel.blocks.putAssumeCapacityNoClobber(air_inst_index, .{
+ .live_registers = isel.live_registers,
+ .target_label = @intCast(isel.instructions.items.len),
+ });
+ }
+ try isel.body(air_body);
+ if (res_ty.toIntern() != .noreturn_type) {
+ const block_entry = isel.blocks.pop().?;
+ assert(block_entry.key == air_inst_index);
+ if (isel.live_values.fetchRemove(air_inst_index)) |result_vi| result_vi.value.deref(isel);
+ }
+}
+
fn emit(isel: *Select, instruction: codegen.aarch64.encoding.Instruction) !void {
wip_mir_log.debug(" | {f}", .{instruction});
try isel.instructions.append(isel.pt.zcu.gpa, instruction);
}
+fn emitPanic(isel: *Select, panic_id: Zcu.SimplePanicId) !void {
+ const zcu = isel.pt.zcu;
+ try isel.nav_relocs.append(zcu.gpa, .{
+ .nav = switch (zcu.intern_pool.indexToKey(zcu.builtin_decl_values.get(panic_id.toBuiltin()))) {
+ else => unreachable,
+ inline .@"extern", .func => |func| func.owner_nav,
+ },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+}
+
fn emitLiteral(isel: *Select, bytes: []const u8) !void {
const words: []align(1) const u32 = @ptrCast(bytes);
const literals = try isel.literals.addManyAsSlice(isel.pt.zcu.gpa, words.len);
@@ -8104,6 +8489,32 @@ pub const Value = struct {
}
}
+ const AddOrSubtractOptions = struct {
+ overflow: Overflow,
+
+ const Overflow = union(enum) {
+ @"unreachable",
+ panic: Zcu.SimplePanicId,
+ wrap,
+ ra: Register.Alias,
+
+ fn defCond(overflow: Overflow, isel: *Select, cond: codegen.aarch64.encoding.ConditionCode) !void {
+ switch (overflow) {
+ .@"unreachable" => unreachable,
+ .panic => |panic_id| {
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(panic_id);
+ try isel.emit(.@"b."(
+ cond.invert(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ },
+ .wrap => {},
+ .ra => |overflow_ra| try isel.emit(.csinc(overflow_ra.w(), .wzr, .wzr, cond.invert())),
+ }
+ }
+ };
+ };
fn addOrSubtract(
res_vi: Value.Index,
isel: *Select,
@@ -8111,19 +8522,21 @@ pub const Value = struct {
lhs_vi: Value.Index,
op: codegen.aarch64.encoding.Instruction.AddSubtractOp,
rhs_vi: Value.Index,
- opts: struct {
- wrap: bool,
- overflow_ra: Register.Alias = .zr,
- },
+ opts: AddOrSubtractOptions,
) !void {
- assert(opts.wrap or opts.overflow_ra == .zr);
const zcu = isel.pt.zcu;
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(op), isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(op), isel.fmtType(ty) });
var part_offset = res_vi.size(isel);
- var need_wrap = opts.wrap;
- var need_carry = opts.overflow_ra != .zr;
+ var need_wrap = switch (opts.overflow) {
+ .@"unreachable" => false,
+ .panic, .wrap, .ra => true,
+ };
+ var need_carry = switch (opts.overflow) {
+ .@"unreachable", .wrap => false,
+ .panic, .ra => true,
+ };
while (part_offset > 0) : (need_wrap = false) {
const part_size = @min(part_offset, 8);
part_offset -= part_size;
@@ -8133,48 +8546,87 @@ pub const Value = struct {
const unwrapped_res_part_ra = unwrapped_res_part_ra: {
if (!need_wrap) break :unwrapped_res_part_ra wrapped_res_part_ra;
if (int_info.bits % 32 == 0) {
- if (opts.overflow_ra != .zr) try isel.emit(.csinc(opts.overflow_ra.w(), .wzr, .wzr, .invert(switch (int_info.signedness) {
+ try opts.overflow.defCond(isel, switch (int_info.signedness) {
.signed => .vs,
.unsigned => switch (op) {
.add => .cs,
.sub => .cc,
},
- })));
+ });
break :unwrapped_res_part_ra wrapped_res_part_ra;
}
- const wrapped_part_ra, const unwrapped_part_ra = if (opts.overflow_ra != .zr) part_ra: {
- switch (op) {
- .add => {},
- .sub => switch (int_info.signedness) {
- .signed => {},
- .unsigned => {
- try isel.emit(.csinc(opts.overflow_ra.w(), .wzr, .wzr, .invert(.cc)));
- break :part_ra .{ wrapped_res_part_ra, wrapped_res_part_ra };
- },
+ need_carry = false;
+ const wrapped_part_ra, const unwrapped_part_ra = part_ra: switch (opts.overflow) {
+ .@"unreachable" => unreachable,
+ .panic, .ra => switch (int_info.signedness) {
+ .signed => {
+ try opts.overflow.defCond(isel, .ne);
+ const wrapped_part_ra = switch (wrapped_res_part_ra) {
+ else => |res_part_ra| res_part_ra,
+ .zr => try isel.allocIntReg(),
+ };
+ errdefer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
+ const unwrapped_part_ra = unwrapped_part_ra: {
+ const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
+ else => |res_part_ra| isel.lockReg(res_part_ra),
+ .zr => .empty,
+ };
+ defer wrapped_res_part_lock.unlock(isel);
+ break :unwrapped_part_ra try isel.allocIntReg();
+ };
+ errdefer isel.freeReg(unwrapped_part_ra);
+ switch (part_size) {
+ else => unreachable,
+ 1...4 => try isel.emit(.subs(.wzr, wrapped_part_ra.w(), .{ .register = unwrapped_part_ra.w() })),
+ 5...8 => try isel.emit(.subs(.xzr, wrapped_part_ra.x(), .{ .register = unwrapped_part_ra.x() })),
+ }
+ break :part_ra .{ wrapped_part_ra, unwrapped_part_ra };
},
- }
- try isel.emit(.csinc(opts.overflow_ra.w(), .wzr, .wzr, .invert(.ne)));
- const wrapped_part_ra = switch (wrapped_res_part_ra) {
- else => |res_part_ra| res_part_ra,
- .zr => try isel.allocIntReg(),
- };
- errdefer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
- const unwrapped_part_ra = unwrapped_part_ra: {
- const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
- else => |res_part_ra| isel.lockReg(res_part_ra),
- .zr => .empty,
- };
- defer wrapped_res_part_lock.unlock(isel);
- break :unwrapped_part_ra try isel.allocIntReg();
- };
- errdefer isel.freeReg(unwrapped_part_ra);
- switch (part_size) {
- else => unreachable,
- 1...4 => try isel.emit(.subs(.wzr, wrapped_part_ra.w(), .{ .register = unwrapped_part_ra.w() })),
- 5...8 => try isel.emit(.subs(.xzr, wrapped_part_ra.x(), .{ .register = unwrapped_part_ra.x() })),
- }
- break :part_ra .{ wrapped_part_ra, unwrapped_part_ra };
- } else .{ wrapped_res_part_ra, wrapped_res_part_ra };
+ .unsigned => {
+ const unwrapped_part_ra = unwrapped_part_ra: {
+ const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
+ else => |res_part_ra| isel.lockReg(res_part_ra),
+ .zr => .empty,
+ };
+ defer wrapped_res_part_lock.unlock(isel);
+ break :unwrapped_part_ra try isel.allocIntReg();
+ };
+ errdefer isel.freeReg(unwrapped_part_ra);
+ const bit: u6 = @truncate(int_info.bits);
+ switch (opts.overflow) {
+ .@"unreachable", .wrap => unreachable,
+ .panic => |panic_id| {
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(panic_id);
+ try isel.emit(.tbz(
+ switch (bit) {
+ 0, 32 => unreachable,
+ 1...31 => unwrapped_part_ra.w(),
+ 33...63 => unwrapped_part_ra.x(),
+ },
+ bit,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ },
+ .ra => |overflow_ra| try isel.emit(switch (bit) {
+ 0, 32 => unreachable,
+ 1...31 => .ubfm(overflow_ra.w(), unwrapped_part_ra.w(), .{
+ .N = .word,
+ .immr = bit,
+ .imms = bit,
+ }),
+ 33...63 => .ubfm(overflow_ra.x(), unwrapped_part_ra.x(), .{
+ .N = .doubleword,
+ .immr = bit,
+ .imms = bit,
+ }),
+ }),
+ }
+ break :part_ra .{ wrapped_res_part_ra, unwrapped_part_ra };
+ },
+ },
+ .wrap => .{ wrapped_res_part_ra, wrapped_res_part_ra },
+ };
defer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
errdefer if (unwrapped_part_ra != wrapped_res_part_ra) isel.freeReg(unwrapped_part_ra);
if (wrapped_part_ra != .zr) try isel.emit(switch (part_size) {
@@ -8650,41 +9102,15 @@ pub const Value = struct {
expected_live_registers: *const LiveRegisters,
) !void {
try vi.liveIn(isel, src_ra, expected_live_registers);
- const offset_from_parent: i65, const parent_vi = vi.valueParent(isel);
+ const offset_from_parent, const parent_vi = vi.valueParent(isel);
switch (parent_vi.parent(isel)) {
.unallocated => {},
- .stack_slot => |stack_slot| {
- const offset = stack_slot.offset + offset_from_parent;
- try isel.emit(switch (vi.size(isel)) {
- else => unreachable,
- 1 => if (src_ra.isVector()) .str(src_ra.b(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }) else .strb(src_ra.w(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }),
- 2 => if (src_ra.isVector()) .str(src_ra.h(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }) else .strh(src_ra.w(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }),
- 4 => .str(if (src_ra.isVector()) src_ra.s() else src_ra.w(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }),
- 8 => .str(if (src_ra.isVector()) src_ra.d() else src_ra.x(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }),
- 16 => .str(src_ra.q(), .{ .unsigned_offset = .{
- .base = stack_slot.base.x(),
- .offset = @intCast(offset),
- } }),
- });
- },
+ .stack_slot => |stack_slot| if (stack_slot.base != Register.Alias.fp) try isel.storeReg(
+ src_ra,
+ vi.size(isel),
+ stack_slot.base,
+ @as(i65, stack_slot.offset) + offset_from_parent,
+ ),
else => unreachable,
}
try vi.spillReg(isel, src_ra, 0, expected_live_registers);
@@ -9631,14 +10057,18 @@ pub const Value = struct {
var base_ptr = ip.indexToKey(base).ptr;
const eu_ty = ip.indexToKey(base_ptr.ty).ptr_type.child;
const payload_ty = ip.indexToKey(eu_ty).error_union_type.payload_type;
- base_ptr.byte_offset += codegen.errUnionPayloadOffset(.fromInterned(payload_ty), zcu);
+ base_ptr.byte_offset += codegen.errUnionPayloadOffset(.fromInterned(payload_ty), zcu) + ptr.byte_offset;
+ continue :constant_key .{ .ptr = base_ptr };
+ },
+ .opt_payload => |base| {
+ var base_ptr = ip.indexToKey(base).ptr;
+ base_ptr.byte_offset += ptr.byte_offset;
continue :constant_key .{ .ptr = base_ptr };
},
- .opt_payload => |base| continue :constant_key .{ .ptr = ip.indexToKey(base).ptr },
.field => |field| {
var base_ptr = ip.indexToKey(field.base).ptr;
const agg_ty: ZigType = .fromInterned(ip.indexToKey(base_ptr.ty).ptr_type.child);
- base_ptr.byte_offset += agg_ty.structFieldOffset(@intCast(field.index), zcu);
+ base_ptr.byte_offset += agg_ty.structFieldOffset(@intCast(field.index), zcu) + ptr.byte_offset;
continue :constant_key .{ .ptr = base_ptr };
},
.comptime_alloc, .comptime_field, .arr_elem => unreachable,
diff --git a/src/codegen/aarch64/instructions.zon b/src/codegen/aarch64/instructions.zon
index 85de19605094155b462837b754951e308cffbc10..48b8eaa21ff858083428bc545258201308147dac 100644
--- a/src/codegen/aarch64/instructions.zon
+++ b/src/codegen/aarch64/instructions.zon
@@ -213,6 +213,63 @@
},
.encode = .{ .ands, .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } },
},
+ // C6.2.16 ASR (register)
+ .{
+ .pattern = "ASR , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .asrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "ASR , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .asrv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.17 ASR (immediate)
+ .{
+ .pattern = "ASR , , #",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } },
+ },
+ .encode = .{ .sbfm, .Wd, .Wn, .{ .N = .word, .immr = .shift, .imms = 31 } },
+ },
+ .{
+ .pattern = "ASR , , #",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } },
+ },
+ .encode = .{ .sbfm, .Xd, .Xn, .{ .N = .doubleword, .immr = .shift, .imms = 63 } },
+ },
+ // C6.2.18 ASRV
+ .{
+ .pattern = "ASRV , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .asrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "ASRV , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .asrv, .Xd, .Xn, .Xm },
+ },
// C6.2.35 BLR
.{
.pattern = "BLR ",
@@ -681,6 +738,82 @@
},
.encode = .{ .ldr, .Xt, .{ .unsigned_offset = .{ .base = .Xn, .offset = .pimm } } },
},
+ // C6.2.212 LSL (register)
+ .{
+ .pattern = "LSL , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lslv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSL , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lslv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.214 LSLV
+ .{
+ .pattern = "LSLV , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lslv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSLV , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lslv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.215 LSR (register)
+ .{
+ .pattern = "LSR , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lsrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSR , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lsrv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.217 LSRV
+ .{
+ .pattern = "LSRV , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lsrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSRV , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lsrv, .Xd, .Xn, .Xm },
+ },
// C6.2.220 MOV (to/from SP)
.{
.pattern = "MOV WSP, ",
@@ -964,6 +1097,63 @@
},
.encode = .{ .ret, .Xn },
},
+ // C6.2.261 ROR (immediate)
+ .{
+ .pattern = "ROR , , #",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Ws = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } },
+ },
+ .encode = .{ .extr, .Wd, .Ws, .Ws, .shift },
+ },
+ .{
+ .pattern = "ROR , , #",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xs = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } },
+ },
+ .encode = .{ .extr, .Xd, .Xs, .Xs, .shift },
+ },
+ // C6.2.262 ROR (register)
+ .{
+ .pattern = "ROR , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .rorv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "ROR , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .rorv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.263 RORV
+ .{
+ .pattern = "RORV , , ",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .rorv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "RORV , , ",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .rorv, .Xd, .Xn, .Xm },
+ },
// C6.2.268 SBFM
.{
.pattern = "SBFM , , #, #",
diff --git a/src/target.zig b/src/target.zig
index 5896af7b2a6d22204bfb413f38d5269ee7e41c53..ad83414c23c079ddf23f802efa1a0e116ab9263f 100644
--- a/src/target.zig
+++ b/src/target.zig
@@ -351,7 +351,7 @@ pub fn defaultCompilerRtOptimizeMode(target: *const std.Target) std.builtin.Opti
}
}
-pub fn canBuildLibCompilerRt(target: *const std.Target, use_llvm: bool, comptime have_llvm: bool) bool {
+pub fn canBuildLibCompilerRt(target: *const std.Target, use_llvm: bool, have_llvm: bool) bool {
switch (target.os.tag) {
.plan9 => return false,
else => {},
@@ -373,7 +373,7 @@ pub fn canBuildLibCompilerRt(target: *const std.Target, use_llvm: bool, comptime
};
}
-pub fn canBuildLibUbsanRt(target: *const std.Target, use_llvm: bool, comptime have_llvm: bool) bool {
+pub fn canBuildLibUbsanRt(target: *const std.Target, use_llvm: bool, have_llvm: bool) bool {
switch (target.cpu.arch) {
.spirv32, .spirv64 => return false,
// Remove this once https://github.com/ziglang/zig/issues/23715 is fixed
@@ -382,6 +382,7 @@ pub fn canBuildLibUbsanRt(target: *const std.Target, use_llvm: bool, comptime ha
}
return switch (zigBackend(target, use_llvm)) {
.stage2_llvm => true,
+ .stage2_wasm => false,
.stage2_x86_64 => switch (target.ofmt) {
.elf, .macho => true,
else => have_llvm,
@@ -860,6 +861,7 @@ pub fn zigBackend(target: *const std.Target, use_llvm: bool) std.builtin.Compile
pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, comptime feature: Feature) bool {
return switch (feature) {
.panic_fn => switch (backend) {
+ .stage2_aarch64,
.stage2_c,
.stage2_llvm,
.stage2_x86_64,
diff --git a/test/behavior/error.zig b/test/behavior/error.zig
index eff2cf855fbcf56d9b50cfb59347bcc048b865e5..4665178808e201b10688335e33390f8ab7b64a9f 100644
--- a/test/behavior/error.zig
+++ b/test/behavior/error.zig
@@ -590,7 +590,6 @@ test "error union comptime caching" {
}
test "@errorName" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -605,7 +604,6 @@ fn gimmeItBroke() anyerror {
}
test "@errorName sentinel length matches slice length" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -883,7 +881,6 @@ test "catch within a function that calls no errorable functions" {
}
test "error from comptime string" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/return_address.zig b/test/behavior/return_address.zig
index ba342956b330c9db7613c413e7870f3f8e4216a4..d7fb76d3b09459e157f9b17013fbcec6bef20b01 100644
--- a/test/behavior/return_address.zig
+++ b/test/behavior/return_address.zig
@@ -6,7 +6,6 @@ fn retAddr() usize {
}
test "return address" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/cases/array_in_anon_struct.zig b/test/cases/array_in_anon_struct.zig
index 5961b3f72326c4a29c1e5112acf3d833f3fb3c06..8c4f5ea0514f37d5f1d4fadb1dda0ce34b66484d 100644
--- a/test/cases/array_in_anon_struct.zig
+++ b/test/cases/array_in_anon_struct.zig
@@ -19,4 +19,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig
index 8bbc3154a13a5024dc1cff627f8457216f87ea18..5f42b7d9af72bea55c6287bf446d7b300d1e9379 100644
--- a/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig
+++ b/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig
@@ -7,5 +7,5 @@ export fn entry3() callconv(.avr_interrupt) void {}
// target=aarch64-linux-none
//
// :1:30: error: calling convention 'x86_64_interrupt' only available on architectures 'x86_64'
-// :1:30: error: calling convention 'x86_interrupt' only available on architectures 'x86'
-// :1:30: error: calling convention 'avr_interrupt' only available on architectures 'avr'
+// :2:30: error: calling convention 'x86_interrupt' only available on architectures 'x86'
+// :3:30: error: calling convention 'avr_interrupt' only available on architectures 'avr'
diff --git a/test/cases/compile_errors/error_set_membership.zig b/test/cases/compile_errors/error_set_membership.zig
index 67826f4db975fe458f48f82aaf7a1052f98b1e8e..a146bd39bab16e8e3cf48a82b09fadd49504a32b 100644
--- a/test/cases/compile_errors/error_set_membership.zig
+++ b/test/cases/compile_errors/error_set_membership.zig
@@ -25,7 +25,7 @@ pub fn main() Error!void {
// error
// backend=stage2
-// target=native
+// target=x86_64-linux
//
// :23:29: error: expected type 'error{InvalidCharacter}', found '@typeInfo(@typeInfo(@TypeOf(tmp.fooey)).@"fn".return_type.?).error_union.error_set'
// :23:29: note: 'error.InvalidDirection' not a member of destination error set
diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig
index cf97e61f40147ef636bd7536d9a6cab0783a6b71..fd8aec06d05dcbebb2759d89b5c8d2d06ddd1b1e 100644
--- a/test/cases/compile_errors/function_ptr_alignment.zig
+++ b/test/cases/compile_errors/function_ptr_alignment.zig
@@ -10,7 +10,7 @@ comptime {
// error
// backend=stage2
-// target=native
+// target=x86_64-linux
//
// :8:41: error: expected type '*align(2) const fn () void', found '*const fn () void'
// :8:41: note: pointer alignment '1' cannot cast into pointer alignment '2'
diff --git a/test/cases/compile_errors/issue_15572_break_on_inline_while.zig b/test/cases/compile_errors/issue_15572_break_on_inline_while.zig
index f264e695c0e3dda0eb4e57ec2ba25a96c70dcc60..69d5c11eab9c43f7c4804fcfe75bf5b19fe944d1 100644
--- a/test/cases/compile_errors/issue_15572_break_on_inline_while.zig
+++ b/test/cases/compile_errors/issue_15572_break_on_inline_while.zig
@@ -15,6 +15,6 @@ pub fn main() void {
// error
// backend=stage2
-// target=native
+// target=x86_64-linux
//
// :9:28: error: incompatible types: 'builtin.Type.EnumField' and 'void'
diff --git a/test/cases/compile_errors/switch_on_non_err_union.zig b/test/cases/compile_errors/switch_on_non_err_union.zig
index 87624b21dc278996891757db58db7f4b447cbf10..e79a181e62c6046afaf7a207e32d399dfcf4b239 100644
--- a/test/cases/compile_errors/switch_on_non_err_union.zig
+++ b/test/cases/compile_errors/switch_on_non_err_union.zig
@@ -6,6 +6,6 @@ pub fn main() void {
// error
// backend=stage2
-// target=native
+// target=x86_64-linux
//
// :2:23: error: expected error union type, found 'bool'
diff --git a/test/cases/pic_freestanding.zig b/test/cases/pic_freestanding.zig
index 86e37662e2247c7ca18a3759c847c6d27a796bed..eda1399887341b38eb6056cc0d0c56167bdf1b8c 100644
--- a/test/cases/pic_freestanding.zig
+++ b/test/cases/pic_freestanding.zig
@@ -1,7 +1,7 @@
const builtin = @import("builtin");
const std = @import("std");
-fn _start() callconv(.naked) void {}
+pub fn _start() callconv(.naked) void {}
comptime {
@export(&_start, .{ .name = if (builtin.cpu.arch.isMIPS()) "__start" else "_start" });
diff --git a/test/cases/safety/@alignCast misaligned.zig b/test/cases/safety/@alignCast misaligned.zig
index e523a9d1204e6689e95e184b3a0ecfac3a661221..017c46a98da9d873223c2f1bc182a0fb6d6a0459 100644
--- a/test/cases/safety/@alignCast misaligned.zig
+++ b/test/cases/safety/@alignCast misaligned.zig
@@ -22,4 +22,4 @@ fn foo(bytes: []u8) u32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@enumFromInt - no matching tag value.zig b/test/cases/safety/@enumFromInt - no matching tag value.zig
index 0021a4d3976c1976308c8ec848d5e4d1b6a1c0c2..7953b93358443f5cd34c1afc4a7719bcb2fc9a1a 100644
--- a/test/cases/safety/@enumFromInt - no matching tag value.zig
+++ b/test/cases/safety/@enumFromInt - no matching tag value.zig
@@ -23,4 +23,4 @@ fn baz(_: Foo) void {}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@enumFromInt truncated bits - exhaustive.zig b/test/cases/safety/@enumFromInt truncated bits - exhaustive.zig
index 92065c4892a9e72e9a23369d588e2a82ff070047..ace1e08d11eb54e4a088ff6a5a978a1d56fe6c6f 100644
--- a/test/cases/safety/@enumFromInt truncated bits - exhaustive.zig
+++ b/test/cases/safety/@enumFromInt truncated bits - exhaustive.zig
@@ -20,4 +20,4 @@ pub fn main() u8 {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@enumFromInt truncated bits - nonexhaustive.zig b/test/cases/safety/@enumFromInt truncated bits - nonexhaustive.zig
index 25959c9ffd8909d9de39e8264cfbdd2ee957b942..8f20081610c7f620f6722a50576f38052794dbf5 100644
--- a/test/cases/safety/@enumFromInt truncated bits - nonexhaustive.zig
+++ b/test/cases/safety/@enumFromInt truncated bits - nonexhaustive.zig
@@ -20,4 +20,4 @@ pub fn main() u8 {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@errorCast error not present in destination.zig b/test/cases/safety/@errorCast error not present in destination.zig
index 74e81f2a6040d65cb8263318299cadb5dcb59fee..a121d3e6e8e07857023bb7f73da52b60d4df08a2 100644
--- a/test/cases/safety/@errorCast error not present in destination.zig
+++ b/test/cases/safety/@errorCast error not present in destination.zig
@@ -18,4 +18,4 @@ fn foo(set1: Set1) Set2 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@errorCast error union casted to disjoint set.zig b/test/cases/safety/@errorCast error union casted to disjoint set.zig
index 2696a037c7da292391962f750ebc7c0ea7a2590d..a84f61d8e5d14218a3e1901fd62e1985cd85a68b 100644
--- a/test/cases/safety/@errorCast error union casted to disjoint set.zig
+++ b/test/cases/safety/@errorCast error union casted to disjoint set.zig
@@ -17,4 +17,4 @@ fn foo() anyerror!i32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intCast to u0.zig b/test/cases/safety/@intCast to u0.zig
index 4394f63f54aa612637351df474e68f295ed77b3a..219f42f2137f996253f18b757a2eb92d4dd93490 100644
--- a/test/cases/safety/@intCast to u0.zig
+++ b/test/cases/safety/@intCast to u0.zig
@@ -19,4 +19,4 @@ fn bar(one: u1, not_zero: i32) void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 max.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 max.zig
index 38ec595b454eab9f1b5daf9df7803c3794650f72..70f0cebb93c8b782cc19d0969cdeddab730a236e 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 max.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 max.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 min.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 min.zig
index 97a651855bcba1c3f34790775fb78cc52ba6ca9c..bc35aa6e231b1a127d37f9cf4cbd55c83ea36ada 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 min.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - i0 min.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - signed max.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - signed max.zig
index cc19ee84ff4ce1c8f7d8b555ac2a25716795137f..56e87423a14093ce940ad0c3b271d27725b5563e 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - signed max.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - signed max.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - signed min.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - signed min.zig
index abc95e396a7966a428b2376b32766c5711663eed..61704a873326cbe8fb875019aa8e6ab1a2ca9cd2 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - signed min.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - signed min.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 max.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 max.zig
index f488d0291f1b78a272b95fab12b5d7a904456e78..361a528498946b4c75f486a349bce12964633823 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 max.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 max.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 min.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 min.zig
index 8d459e1a5ca42750e91e5c70e3f94ba8eaa2878c..5706d192d5186dae4e4683a5cee6499d9b7b4ea9 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 min.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - u0 min.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned max.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned max.zig
index 95122abc8c1574987c64c0c89e0f530018052acb..842aaaa1dec0aae0aa23b729ee8bda99bd0a6e50 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned max.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned max.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned min.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned min.zig
index fbc7cf18ac27d4588626e6c6f9911faa2e13c5e0..c1e8af2f5e1f320febf72f50d03c8f46e7e89fba 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned min.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - unsigned min.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - vector max.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - vector max.zig
index 35b4c91509421460fe6b23c6f8288e276f4a0322..96e1c5594ef387ccf73a0fa016c5bb9389acd7aa 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - vector max.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - vector max.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - boundary case - vector min.zig b/test/cases/safety/@intFromFloat cannot fit - boundary case - vector min.zig
index 94ad5097728307141e8232d91ae140560764afb3..cf17014d3febb2f28c780b27fd12effaea32cda7 100644
--- a/test/cases/safety/@intFromFloat cannot fit - boundary case - vector min.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - boundary case - vector min.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - negative out of range.zig b/test/cases/safety/@intFromFloat cannot fit - negative out of range.zig
index 80edbdfd3cc24979af610701db80f4716ebd007e..23d9f87ac1ee771a54ff3496b5cdc7934ddb7717 100644
--- a/test/cases/safety/@intFromFloat cannot fit - negative out of range.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - negative out of range.zig
@@ -17,4 +17,4 @@ fn bar(a: f32) i8 {
fn baz(_: i8) void {}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - negative to unsigned.zig b/test/cases/safety/@intFromFloat cannot fit - negative to unsigned.zig
index ee0c0402737b671d40eb2a2ab533f57eb7ba5b6e..9d28ee0aaa1d25d8ae9eefa3cd92fec02ae6ad82 100644
--- a/test/cases/safety/@intFromFloat cannot fit - negative to unsigned.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - negative to unsigned.zig
@@ -17,4 +17,4 @@ fn bar(a: f32) u8 {
fn baz(_: u8) void {}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@intFromFloat cannot fit - positive out of range.zig b/test/cases/safety/@intFromFloat cannot fit - positive out of range.zig
index c526a70047664480d4a61197c2cfc0ff4ae6167c..2e76a9b2535f886ddc7cc7dca0904515cf37f5eb 100644
--- a/test/cases/safety/@intFromFloat cannot fit - positive out of range.zig
+++ b/test/cases/safety/@intFromFloat cannot fit - positive out of range.zig
@@ -17,4 +17,4 @@ fn bar(a: f32) u8 {
fn baz(_: u8) void {}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@ptrFromInt address zero to non-optional byte-aligned pointer.zig b/test/cases/safety/@ptrFromInt address zero to non-optional byte-aligned pointer.zig
index 4944a239e2a98cff9918870b3f598a1f5cf894f0..eb45f357dd83a69cd0aec289944488b351923b7b 100644
--- a/test/cases/safety/@ptrFromInt address zero to non-optional byte-aligned pointer.zig
+++ b/test/cases/safety/@ptrFromInt address zero to non-optional byte-aligned pointer.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@ptrFromInt address zero to non-optional pointer.zig b/test/cases/safety/@ptrFromInt address zero to non-optional pointer.zig
index a217de3073b4fbfcf9e01a53bfdaafbde3dcbf37..308f97ad12e16adff6c53e0372b617ff398d707a 100644
--- a/test/cases/safety/@ptrFromInt address zero to non-optional pointer.zig
+++ b/test/cases/safety/@ptrFromInt address zero to non-optional pointer.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@ptrFromInt with misaligned address.zig b/test/cases/safety/@ptrFromInt with misaligned address.zig
index b95c1b320fccbb30dde40062fbaaf67c49adacd0..1383a4c3c36326416226e752d8fa4ad5126975c1 100644
--- a/test/cases/safety/@ptrFromInt with misaligned address.zig
+++ b/test/cases/safety/@ptrFromInt with misaligned address.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/@tagName on corrupted enum value.zig b/test/cases/safety/@tagName on corrupted enum value.zig
index df6a3f45e0e677a3d7bc659439bfb87dc405d010..450d0ee2e05d6e61625ea1738bce53f63ea83e60 100644
--- a/test/cases/safety/@tagName on corrupted enum value.zig
+++ b/test/cases/safety/@tagName on corrupted enum value.zig
@@ -23,4 +23,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/@tagName on corrupted union value.zig b/test/cases/safety/@tagName on corrupted union value.zig
index 7b856e57b9116dd13bf9c8eae148d633c798786a..b61a72420ed60ff8a2af5246ff63f3bf8ffa56b2 100644
--- a/test/cases/safety/@tagName on corrupted union value.zig
+++ b/test/cases/safety/@tagName on corrupted union value.zig
@@ -24,4 +24,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/array slice sentinel mismatch vector.zig b/test/cases/safety/array slice sentinel mismatch vector.zig
index 55ff4b3e39bff6bc078309ad1af814a62c9cffa0..f374f1b9d598a6d0772db7c80a89d2f17be78ada 100644
--- a/test/cases/safety/array slice sentinel mismatch vector.zig
+++ b/test/cases/safety/array slice sentinel mismatch vector.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/array slice sentinel mismatch.zig b/test/cases/safety/array slice sentinel mismatch.zig
index ab7a513b3998797f032ca00fc3813e8edc7d5557..deb43250ec4b7b3005284aca08165de0cf830631 100644
--- a/test/cases/safety/array slice sentinel mismatch.zig
+++ b/test/cases/safety/array slice sentinel mismatch.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/bad union field access.zig b/test/cases/safety/bad union field access.zig
index 14ebb1f344ec1b5010572919b27ee2d809266b3f..a2778237c4eb15e9576c05246725a1b18b95e1ff 100644
--- a/test/cases/safety/bad union field access.zig
+++ b/test/cases/safety/bad union field access.zig
@@ -24,4 +24,4 @@ fn bar(f: *Foo) void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/calling panic.zig b/test/cases/safety/calling panic.zig
index 7b8a478be3b0ab67baa8de2926c1862d6bfefe04..7ac512eadc07da72f8b118397c610d88a310fcd8 100644
--- a/test/cases/safety/calling panic.zig
+++ b/test/cases/safety/calling panic.zig
@@ -13,4 +13,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/cast []u8 to bigger slice of wrong size.zig b/test/cases/safety/cast []u8 to bigger slice of wrong size.zig
index b6b8e89bf9aa5e76f6d54f4632eb29eb3d952c7b..65dda7875130ca24281130f90d2e3a236235f815 100644
--- a/test/cases/safety/cast []u8 to bigger slice of wrong size.zig
+++ b/test/cases/safety/cast []u8 to bigger slice of wrong size.zig
@@ -18,4 +18,4 @@ fn widenSlice(slice: []align(1) const u8) []align(1) const i32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/cast integer to global error and no code matches.zig b/test/cases/safety/cast integer to global error and no code matches.zig
index fa0474a88cb4a0f3339f89a5a5bcd3afbaa5a875..2b9cadf811dfb5ac31eb69306b170e09d0a4ef8b 100644
--- a/test/cases/safety/cast integer to global error and no code matches.zig
+++ b/test/cases/safety/cast integer to global error and no code matches.zig
@@ -16,4 +16,4 @@ fn bar(x: u16) anyerror {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/empty slice with sentinel out of bounds.zig b/test/cases/safety/empty slice with sentinel out of bounds.zig
index 51846f894f6140db8e81649590b2751ded9b171f..2d9494826d42e0f1c826ae7aa45fb2dc5e5f33f8 100644
--- a/test/cases/safety/empty slice with sentinel out of bounds.zig
+++ b/test/cases/safety/empty slice with sentinel out of bounds.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/exact division failure - vectors.zig b/test/cases/safety/exact division failure - vectors.zig
index 30d5dcf11a5b3232b02617c275d03f0538cbe846..398ae7a4cd9e76eebe0c8661e85149bc4ee474f1 100644
--- a/test/cases/safety/exact division failure - vectors.zig
+++ b/test/cases/safety/exact division failure - vectors.zig
@@ -20,4 +20,4 @@ fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/exact division failure.zig b/test/cases/safety/exact division failure.zig
index be86853c77babec86e417a7ceedd2372c3b9bbae..0831bb4e09d8ad21937f795ffd82c12d771301ec 100644
--- a/test/cases/safety/exact division failure.zig
+++ b/test/cases/safety/exact division failure.zig
@@ -18,4 +18,4 @@ fn divExact(a: i32, b: i32) i32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/for_len_mismatch.zig b/test/cases/safety/for_len_mismatch.zig
index 8841f11aa7cb37654dd7cf297ec6f8f14bf15b84..55bb7bf8b5a4ec0e1c870ca5ab80de3676941316 100644
--- a/test/cases/safety/for_len_mismatch.zig
+++ b/test/cases/safety/for_len_mismatch.zig
@@ -22,4 +22,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/for_len_mismatch_three.zig b/test/cases/safety/for_len_mismatch_three.zig
index 4efe18d3cd0a20147c613c905112f829939f3498..b4256b0eee5b899874d3f4090a38bbdb4a512402 100644
--- a/test/cases/safety/for_len_mismatch_three.zig
+++ b/test/cases/safety/for_len_mismatch_three.zig
@@ -21,4 +21,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/ignored expression integer overflow.zig b/test/cases/safety/ignored expression integer overflow.zig
index 10890108540d7b8ef3c46bb80a6a150e0e2eaf57..859c615e42feb8c87f334863143ed13ebfcc38f3 100644
--- a/test/cases/safety/ignored expression integer overflow.zig
+++ b/test/cases/safety/ignored expression integer overflow.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/integer addition overflow.zig b/test/cases/safety/integer addition overflow.zig
index 499e8b10159f5e70250f56cf9bd985d76951faa9..119800c686eaf5d7d83f5e3262744afc72bf283b 100644
--- a/test/cases/safety/integer addition overflow.zig
+++ b/test/cases/safety/integer addition overflow.zig
@@ -20,4 +20,4 @@ fn add(a: u16, b: u16) u16 {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/integer division by zero - vectors.zig b/test/cases/safety/integer division by zero - vectors.zig
index 63e77a0dd4d0376ecfeab37bdb9f4a4248cff8f4..d3ddfa06c6b95b8009038b522563c8151043c65a 100644
--- a/test/cases/safety/integer division by zero - vectors.zig
+++ b/test/cases/safety/integer division by zero - vectors.zig
@@ -19,4 +19,4 @@ fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/integer division by zero.zig b/test/cases/safety/integer division by zero.zig
index e8eba5c4f0bfa33127664fb065610582d058c234..dc12dde34396410e52e05c11d989b7d30a038876 100644
--- a/test/cases/safety/integer division by zero.zig
+++ b/test/cases/safety/integer division by zero.zig
@@ -17,4 +17,4 @@ fn div0(a: i32, b: i32) i32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/integer multiplication overflow.zig b/test/cases/safety/integer multiplication overflow.zig
index f7f4148a155619774978a4512d7df6c545abdf37..4380ec6d519d3fd761f184972c720bfa791901f8 100644
--- a/test/cases/safety/integer multiplication overflow.zig
+++ b/test/cases/safety/integer multiplication overflow.zig
@@ -18,4 +18,4 @@ fn mul(a: u16, b: u16) u16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/integer negation overflow.zig b/test/cases/safety/integer negation overflow.zig
index cfdfed04299e20f871baa941fabc043f63f8439e..1c6610ae6f5168cd84a7c9f65e1ba8ffe55f0165 100644
--- a/test/cases/safety/integer negation overflow.zig
+++ b/test/cases/safety/integer negation overflow.zig
@@ -18,4 +18,4 @@ fn neg(a: i16) i16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/integer subtraction overflow.zig b/test/cases/safety/integer subtraction overflow.zig
index 14e9131c3b9aff14bfea95083dbc574fad8c0046..9211c877e475ee6ac715179977302f1167ab4ed5 100644
--- a/test/cases/safety/integer subtraction overflow.zig
+++ b/test/cases/safety/integer subtraction overflow.zig
@@ -18,4 +18,4 @@ fn sub(a: u16, b: u16) u16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memcpy_alias.zig b/test/cases/safety/memcpy_alias.zig
index f7a1a160246670a9234042b5cfb4d354e6a99864..62c30ec4596974223591354969dbdf70ad73a745 100644
--- a/test/cases/safety/memcpy_alias.zig
+++ b/test/cases/safety/memcpy_alias.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memcpy_len_mismatch.zig b/test/cases/safety/memcpy_len_mismatch.zig
index 0ef22b959c2b493ac9d3805359fd325e2c85b9b2..aa9b3fd63fbe14219003fe2385b070130df101e9 100644
--- a/test/cases/safety/memcpy_len_mismatch.zig
+++ b/test/cases/safety/memcpy_len_mismatch.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memmove_len_mismatch.zig b/test/cases/safety/memmove_len_mismatch.zig
index 881af9f336649dc0f54c49d4a8abb40e6def194a..fa2259712270a3332d28d2d2474d98a625eed038 100644
--- a/test/cases/safety/memmove_len_mismatch.zig
+++ b/test/cases/safety/memmove_len_mismatch.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memset_array_undefined_bytes.zig b/test/cases/safety/memset_array_undefined_bytes.zig
index 20a65d65d62a5272f634e70e2f538351cb610427..47865a8def94abc508c48ff5687bb9f00d893feb 100644
--- a/test/cases/safety/memset_array_undefined_bytes.zig
+++ b/test/cases/safety/memset_array_undefined_bytes.zig
@@ -15,4 +15,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memset_array_undefined_large.zig b/test/cases/safety/memset_array_undefined_large.zig
index a52bfecbf029f6bdbb3464ad8a6845f773345c56..10f57521cf9598adeb782b894d1887341b818ab4 100644
--- a/test/cases/safety/memset_array_undefined_large.zig
+++ b/test/cases/safety/memset_array_undefined_large.zig
@@ -15,4 +15,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memset_slice_undefined_bytes.zig b/test/cases/safety/memset_slice_undefined_bytes.zig
index fb67999306299ab2164715ff2ef4137a6c16ac8f..4d76bbc414e31f8e69784be4fabb87bd6b34f244 100644
--- a/test/cases/safety/memset_slice_undefined_bytes.zig
+++ b/test/cases/safety/memset_slice_undefined_bytes.zig
@@ -17,4 +17,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/memset_slice_undefined_large.zig b/test/cases/safety/memset_slice_undefined_large.zig
index 166557240c7027b71bcdde7cb8e9386ea2b4a444..e404e35226d4b7c7a2c59cd08a48572cbf61cef0 100644
--- a/test/cases/safety/memset_slice_undefined_large.zig
+++ b/test/cases/safety/memset_slice_undefined_large.zig
@@ -17,4 +17,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/modrem by zero.zig b/test/cases/safety/modrem by zero.zig
index 35b7e37e3ac9ed8066b8a9498bf8658ecb088801..fac10065edff231a0ea4df2f25cccdb260b43036 100644
--- a/test/cases/safety/modrem by zero.zig
+++ b/test/cases/safety/modrem by zero.zig
@@ -17,4 +17,4 @@ fn div0(a: u32, b: u32) u32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/modulus by zero.zig b/test/cases/safety/modulus by zero.zig
index cdeab00dbc530dc40d6a3b25e65be2c53cf3af40..1c0c8ba3a9a0771d9bfd8a4888a249aca058d3eb 100644
--- a/test/cases/safety/modulus by zero.zig
+++ b/test/cases/safety/modulus by zero.zig
@@ -17,4 +17,4 @@ fn mod0(a: i32, b: i32) i32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/noreturn returned.zig b/test/cases/safety/noreturn returned.zig
index c92fb08e62cbe7a7473cd0e89cdc5c619121d51c..b91a6def49bfcf9a3adf9e50c0296ae7cb3f711d 100644
--- a/test/cases/safety/noreturn returned.zig
+++ b/test/cases/safety/noreturn returned.zig
@@ -20,4 +20,4 @@ pub fn main() void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/optional unwrap operator on C pointer.zig b/test/cases/safety/optional unwrap operator on C pointer.zig
index 98135cfae4930936569f45b8543a25b5de07b999..4deb62bc2594c3d593a9d3b27bab2831f862b637 100644
--- a/test/cases/safety/optional unwrap operator on C pointer.zig
+++ b/test/cases/safety/optional unwrap operator on C pointer.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/optional unwrap operator on null pointer.zig b/test/cases/safety/optional unwrap operator on null pointer.zig
index 6ac54e6bd0e963a86992ea8cfbb8967b78257bcf..97d07626f5a66aa41e05a9de0a963d2d1870066e 100644
--- a/test/cases/safety/optional unwrap operator on null pointer.zig
+++ b/test/cases/safety/optional unwrap operator on null pointer.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/optional_empty_error_set.zig b/test/cases/safety/optional_empty_error_set.zig
index dbe39d00c3a551144f1bfc4825ba949e39c82f7b..1ee1690d517bed54d490e00bbd34018cb3cf01c7 100644
--- a/test/cases/safety/optional_empty_error_set.zig
+++ b/test/cases/safety/optional_empty_error_set.zig
@@ -19,4 +19,4 @@ fn foo() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/out of bounds array slice by length.zig b/test/cases/safety/out of bounds array slice by length.zig
index 325749a5eb2b264a57f0adc752618712a9705eb6..df613b1d53582afe321cc5ed32a541d1489a4023 100644
--- a/test/cases/safety/out of bounds array slice by length.zig
+++ b/test/cases/safety/out of bounds array slice by length.zig
@@ -17,4 +17,4 @@ fn foo(a: u32) u32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/out of bounds slice access.zig b/test/cases/safety/out of bounds slice access.zig
index f4f34a203fa53f57d8e4910fc24b5b9fc84b45a1..d5ebf6d5311c5b2c13b1dc1337428fb4b4bd9213 100644
--- a/test/cases/safety/out of bounds slice access.zig
+++ b/test/cases/safety/out of bounds slice access.zig
@@ -18,4 +18,4 @@ fn bar(a: []const i32) i32 {
fn baz(_: i32) void {}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/pointer casting null to non-optional pointer.zig b/test/cases/safety/pointer casting null to non-optional pointer.zig
index 33da071e7331305bfeeef3d4c2363e3f22097bcd..ef61f162b4f6ed4c3722aa4993591a6e928928a4 100644
--- a/test/cases/safety/pointer casting null to non-optional pointer.zig
+++ b/test/cases/safety/pointer casting null to non-optional pointer.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/pointer casting to null function pointer.zig b/test/cases/safety/pointer casting to null function pointer.zig
index a57e71cb8f013ff6c6a03bbfddd9bd1b9c147b2c..1ce1ebc266487dc144c0dbcdaf94eed2a20b0f3b 100644
--- a/test/cases/safety/pointer casting to null function pointer.zig
+++ b/test/cases/safety/pointer casting to null function pointer.zig
@@ -20,4 +20,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/pointer slice sentinel mismatch.zig b/test/cases/safety/pointer slice sentinel mismatch.zig
index 519b04b9168c172d5096a5f8b8ef8644b8f04df2..a400c0bc35c96a83c57e069d18d41e94b21794d1 100644
--- a/test/cases/safety/pointer slice sentinel mismatch.zig
+++ b/test/cases/safety/pointer slice sentinel mismatch.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/remainder division by zero.zig b/test/cases/safety/remainder division by zero.zig
index 2d938a2fd6f4d3c3d61c08c030b7613f30aa3323..3749c3d5d7e8a6a9222b425b9e5a45f005013b85 100644
--- a/test/cases/safety/remainder division by zero.zig
+++ b/test/cases/safety/remainder division by zero.zig
@@ -17,4 +17,4 @@ fn rem0(a: i32, b: i32) i32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/shift left by huge amount.zig b/test/cases/safety/shift left by huge amount.zig
index 374b03d123782eddfac54c4dcc57aef395ca82bd..b6b88ba8701426d780f72be10ab6e28ab9a3fa3e 100644
--- a/test/cases/safety/shift left by huge amount.zig
+++ b/test/cases/safety/shift left by huge amount.zig
@@ -19,4 +19,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/shift right by huge amount.zig b/test/cases/safety/shift right by huge amount.zig
index 173e6fcd7efce62e259a362bb0c134aba99832cb..664e2b54730bab71f50e99f1e59630a80fd9da3f 100644
--- a/test/cases/safety/shift right by huge amount.zig
+++ b/test/cases/safety/shift right by huge amount.zig
@@ -19,4 +19,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/signed integer division overflow - vectors.zig b/test/cases/safety/signed integer division overflow - vectors.zig
index 0de062094dfaec92011591809971c6d36015859e..7a696c4b4d06b1b17e59b94cb1fe202503d89506 100644
--- a/test/cases/safety/signed integer division overflow - vectors.zig
+++ b/test/cases/safety/signed integer division overflow - vectors.zig
@@ -20,4 +20,4 @@ fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/signed integer division overflow.zig b/test/cases/safety/signed integer division overflow.zig
index 0d67f72649666b9916503440f5add9cb9b22cd57..acbb3d4e1622351b6a94a0f47360653df5f7de70 100644
--- a/test/cases/safety/signed integer division overflow.zig
+++ b/test/cases/safety/signed integer division overflow.zig
@@ -18,4 +18,4 @@ fn div(a: i16, b: i16) i16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig
index fa0eec94c0166261954a2afa5bf9fa68cda82275..f47083d4df4595584b7376c79cacd79681271f4c 100644
--- a/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig
+++ b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig
index 6ce662cdc75e4bef7cceea87c5fac4f51b6db5b6..881b3c1631d027b8d0487b0f33527e8000315603 100644
--- a/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig
+++ b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig
@@ -17,4 +17,4 @@ fn unsigned_cast(x: i32) u32 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/signed shift left overflow.zig b/test/cases/safety/signed shift left overflow.zig
index 54a51e0ccdfd99cbfa1fbe9eb2b33759afdcfb37..0cab01ca023b25bbbc383e4e730cf63078ddda97 100644
--- a/test/cases/safety/signed shift left overflow.zig
+++ b/test/cases/safety/signed shift left overflow.zig
@@ -18,4 +18,4 @@ fn shl(a: i16, b: u4) i16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/signed shift right overflow.zig b/test/cases/safety/signed shift right overflow.zig
index 1a0c5973c9f4251dab07c7515174eabf42a935f7..9fe3fe78737caf4e687864194c46992da3ea497b 100644
--- a/test/cases/safety/signed shift right overflow.zig
+++ b/test/cases/safety/signed shift right overflow.zig
@@ -18,4 +18,4 @@ fn shr(a: i16, b: u4) i16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/signed-unsigned vector cast.zig b/test/cases/safety/signed-unsigned vector cast.zig
index 919562b06cbd89b25086c7b08863a332d4ebbb05..22d4073694831c614cdd3c1925eae3c7ed944369 100644
--- a/test/cases/safety/signed-unsigned vector cast.zig
+++ b/test/cases/safety/signed-unsigned vector cast.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/slice by length sentinel mismatch on lhs.zig b/test/cases/safety/slice by length sentinel mismatch on lhs.zig
index c66a968d4ba584f1b51abcb97b1d501fd7bd9716..85785ce769d486d2741f03a90bfb7048f74d15b6 100644
--- a/test/cases/safety/slice by length sentinel mismatch on lhs.zig
+++ b/test/cases/safety/slice by length sentinel mismatch on lhs.zig
@@ -15,4 +15,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice by length sentinel mismatch on rhs.zig b/test/cases/safety/slice by length sentinel mismatch on rhs.zig
index a4a2189a9cf0ad07fc496d08085371916ab663c4..64fe818d1e05703378764970e03a90e9bde8cdc4 100644
--- a/test/cases/safety/slice by length sentinel mismatch on rhs.zig
+++ b/test/cases/safety/slice by length sentinel mismatch on rhs.zig
@@ -15,4 +15,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice sentinel mismatch - floats.zig b/test/cases/safety/slice sentinel mismatch - floats.zig
index be63272f0ce04b4aaa3893b037ff33f48618c2c1..b31855ab4294792aca0230db90dfb2f211ca5892 100644
--- a/test/cases/safety/slice sentinel mismatch - floats.zig
+++ b/test/cases/safety/slice sentinel mismatch - floats.zig
@@ -17,4 +17,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/slice sentinel mismatch - optional pointers.zig b/test/cases/safety/slice sentinel mismatch - optional pointers.zig
index 38ab78b2c1c9b6a916ff2cccf64c4f084fc183a9..4337fe448d2ccbf190f8a35fba708dd46e443282 100644
--- a/test/cases/safety/slice sentinel mismatch - optional pointers.zig
+++ b/test/cases/safety/slice sentinel mismatch - optional pointers.zig
@@ -17,4 +17,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice slice sentinel mismatch.zig b/test/cases/safety/slice slice sentinel mismatch.zig
index 51d4c16596b2f603889df53375220cbac08ce677..76224f966d9c8388023d84948cd3f6eb88cdd6ea 100644
--- a/test/cases/safety/slice slice sentinel mismatch.zig
+++ b/test/cases/safety/slice slice sentinel mismatch.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice start index greater than end index.zig b/test/cases/safety/slice start index greater than end index.zig
index a6dde3ac63a00b4124f3e209091547780ce67832..684020b8a739d52cbf69da2ee00c89f8904db98a 100644
--- a/test/cases/safety/slice start index greater than end index.zig
+++ b/test/cases/safety/slice start index greater than end index.zig
@@ -21,4 +21,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice with sentinel out of bounds - runtime len.zig b/test/cases/safety/slice with sentinel out of bounds - runtime len.zig
index 7039f541e33ecfdfee8c4063354f3fa0372aaa91..b9ef281144c68643c347ddceb1c66842521cff44 100644
--- a/test/cases/safety/slice with sentinel out of bounds - runtime len.zig
+++ b/test/cases/safety/slice with sentinel out of bounds - runtime len.zig
@@ -20,4 +20,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice with sentinel out of bounds.zig b/test/cases/safety/slice with sentinel out of bounds.zig
index 8439e8c7379801908c3a3cbe82a4794001a6c604..f07d393a0ed13bd498c71b6ecc001ad4d419c3aa 100644
--- a/test/cases/safety/slice with sentinel out of bounds.zig
+++ b/test/cases/safety/slice with sentinel out of bounds.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice_cast_change_len_0.zig b/test/cases/safety/slice_cast_change_len_0.zig
index d32bdfc920802307052395094c78d3015ea9d1d9..96d94cfad5c6061a77afde8edf490512f5031d9d 100644
--- a/test/cases/safety/slice_cast_change_len_0.zig
+++ b/test/cases/safety/slice_cast_change_len_0.zig
@@ -24,4 +24,4 @@ const std = @import("std");
// run
// backend=stage2,llvm
-// target=x86_64-linux
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice_cast_change_len_1.zig b/test/cases/safety/slice_cast_change_len_1.zig
index 5d3728bcdc2d4c9effb9d5dbec1277f23544ef4b..21a1d695587539c29d77f2e4898aec7b3b6ce29c 100644
--- a/test/cases/safety/slice_cast_change_len_1.zig
+++ b/test/cases/safety/slice_cast_change_len_1.zig
@@ -24,4 +24,4 @@ const std = @import("std");
// run
// backend=stage2,llvm
-// target=x86_64-linux
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slice_cast_change_len_2.zig b/test/cases/safety/slice_cast_change_len_2.zig
index 3a25d27504dfab8cedad63e9a41f46debd11f330..5da254e9035f812018b3952affc069adb2f02a5c 100644
--- a/test/cases/safety/slice_cast_change_len_2.zig
+++ b/test/cases/safety/slice_cast_change_len_2.zig
@@ -24,4 +24,4 @@ const std = @import("std");
// run
// backend=stage2,llvm
-// target=x86_64-linux
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slicing null C pointer - runtime len.zig b/test/cases/safety/slicing null C pointer - runtime len.zig
index 763553b04aed736a990a100d3c2a0cc5dda8b3af..831224edee4201f9f6f52f0779a90777c2dadb05 100644
--- a/test/cases/safety/slicing null C pointer - runtime len.zig
+++ b/test/cases/safety/slicing null C pointer - runtime len.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/slicing null C pointer.zig b/test/cases/safety/slicing null C pointer.zig
index a928fd585f9d0df1e8788be44bf86460d41668b5..53da877c59acd58023baf80aa6f7273749bf8435 100644
--- a/test/cases/safety/slicing null C pointer.zig
+++ b/test/cases/safety/slicing null C pointer.zig
@@ -17,4 +17,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/switch else on corrupt enum value - one prong.zig b/test/cases/safety/switch else on corrupt enum value - one prong.zig
index 73f6ed9dc8a0175b69328b52bb55351a1f703cd5..b2ef933080483579da8a0062dc7e01780be3200a 100644
--- a/test/cases/safety/switch else on corrupt enum value - one prong.zig
+++ b/test/cases/safety/switch else on corrupt enum value - one prong.zig
@@ -21,4 +21,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/switch else on corrupt enum value - union.zig b/test/cases/safety/switch else on corrupt enum value - union.zig
index 77dacd86c65d14cf59d17f84ffd441ff573a433a..933f7995a59fd3788c0d85ac0ebb9180102a54ec 100644
--- a/test/cases/safety/switch else on corrupt enum value - union.zig
+++ b/test/cases/safety/switch else on corrupt enum value - union.zig
@@ -26,4 +26,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/switch else on corrupt enum value.zig b/test/cases/safety/switch else on corrupt enum value.zig
index 228e3c70eca17acfb375fc2ea27d8e48ea643d8e..300de27e932cbc2384e69fc88cc2cff53942e3ff 100644
--- a/test/cases/safety/switch else on corrupt enum value.zig
+++ b/test/cases/safety/switch else on corrupt enum value.zig
@@ -20,4 +20,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/switch on corrupted enum value.zig b/test/cases/safety/switch on corrupted enum value.zig
index 4d46d2e7a7dce78ebf5f5ee4dc068ca342c2242d..74ec3a4057b7b85cd383577b5f374d9192ef9d7c 100644
--- a/test/cases/safety/switch on corrupted enum value.zig
+++ b/test/cases/safety/switch on corrupted enum value.zig
@@ -24,4 +24,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/switch on corrupted union value.zig b/test/cases/safety/switch on corrupted union value.zig
index 0f622dcbd80b73a025b010dfbcb12c4232558122..cede4feb0467d9357fd6459b87d1087ec3f1b62f 100644
--- a/test/cases/safety/switch on corrupted union value.zig
+++ b/test/cases/safety/switch on corrupted union value.zig
@@ -24,4 +24,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/truncating vector cast.zig b/test/cases/safety/truncating vector cast.zig
index 9b222e691843d3ebe224bba1f0828fdde19773fd..f6271a094ec45ca3b77f53f9bde601829496e0f3 100644
--- a/test/cases/safety/truncating vector cast.zig
+++ b/test/cases/safety/truncating vector cast.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/unreachable.zig b/test/cases/safety/unreachable.zig
index fc1e886540aad4c12d6c46b3b55bf660e30b8388..1094123cba469db5e3b0ff30d0c2daf7c37dc16c 100644
--- a/test/cases/safety/unreachable.zig
+++ b/test/cases/safety/unreachable.zig
@@ -12,4 +12,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig
index 185cde9973b1c61de9e49a34681fdf0ac5b40cc9..7f27c5fcd57acdb3fb2f08a35e16303f6e821c35 100644
--- a/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig
+++ b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig
@@ -16,4 +16,4 @@ pub fn main() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/unsigned shift left overflow.zig b/test/cases/safety/unsigned shift left overflow.zig
index e2f58f0f3bfd85f3182622838769245035dc3f17..1098a80c8e94535971025e8d99e903c513c6431b 100644
--- a/test/cases/safety/unsigned shift left overflow.zig
+++ b/test/cases/safety/unsigned shift left overflow.zig
@@ -18,4 +18,4 @@ fn shl(a: u16, b: u4) u16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/unsigned shift right overflow.zig b/test/cases/safety/unsigned shift right overflow.zig
index 6ded52098d34461b7a4886b87a53d837db998edd..e9ad8571b620e093d03a2565dd2fa4308f9abd84 100644
--- a/test/cases/safety/unsigned shift right overflow.zig
+++ b/test/cases/safety/unsigned shift right overflow.zig
@@ -18,4 +18,4 @@ fn shr(a: u16, b: u4) u16 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/unsigned-signed vector cast.zig b/test/cases/safety/unsigned-signed vector cast.zig
index 6501643b363505c7f31fa0d9ebc7058d5560a39d..5b3b58d928b2b9c2817d633efcd64da68356a43d 100644
--- a/test/cases/safety/unsigned-signed vector cast.zig
+++ b/test/cases/safety/unsigned-signed vector cast.zig
@@ -18,4 +18,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/unwrap error switch.zig b/test/cases/safety/unwrap error switch.zig
index b3194bd2e0d0b607b28b11199cfaae9ce2dece95..a1a148cfd99a70d66af44f8bd1bacf2e6a1fe22d 100644
--- a/test/cases/safety/unwrap error switch.zig
+++ b/test/cases/safety/unwrap error switch.zig
@@ -18,4 +18,4 @@ fn bar() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/unwrap error.zig b/test/cases/safety/unwrap error.zig
index 9fe7d437bc4eb069e9c92237801866148eb231a9..dd157c87218b3a870831c655e31319df5abab9b6 100644
--- a/test/cases/safety/unwrap error.zig
+++ b/test/cases/safety/unwrap error.zig
@@ -16,4 +16,4 @@ fn bar() !void {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/value does not fit in shortening cast - u0.zig b/test/cases/safety/value does not fit in shortening cast - u0.zig
index f29df8d8af2c5f21671da62d0ac9d64794ae94cb..9d77b3f1c803331050762640f3171f283d80b857 100644
--- a/test/cases/safety/value does not fit in shortening cast - u0.zig
+++ b/test/cases/safety/value does not fit in shortening cast - u0.zig
@@ -18,4 +18,4 @@ fn shorten_cast(x: u8) u0 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/value does not fit in shortening cast.zig b/test/cases/safety/value does not fit in shortening cast.zig
index 415ac95dbb3061ce4b39c30ec8cf66738e70acb6..9b6af3996736f42708a5ffd6123ad9017c296838 100644
--- a/test/cases/safety/value does not fit in shortening cast.zig
+++ b/test/cases/safety/value does not fit in shortening cast.zig
@@ -18,4 +18,4 @@ fn shorten_cast(x: i32) i8 {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/safety/vector integer addition overflow.zig b/test/cases/safety/vector integer addition overflow.zig
index db08d8b241d98c8bdfabb12f8fe7849f3275435f..64f6e238e0661000f4a0030324a7a2839107dfdf 100644
--- a/test/cases/safety/vector integer addition overflow.zig
+++ b/test/cases/safety/vector integer addition overflow.zig
@@ -19,4 +19,4 @@ fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/vector integer multiplication overflow.zig b/test/cases/safety/vector integer multiplication overflow.zig
index 61176fd482012c7cdf888d5e3d8fcff8c6314d26..69d0dde16d08f83b1d5a26b3fa4f0ccc1529ca76 100644
--- a/test/cases/safety/vector integer multiplication overflow.zig
+++ b/test/cases/safety/vector integer multiplication overflow.zig
@@ -19,4 +19,4 @@ fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/vector integer negation overflow.zig b/test/cases/safety/vector integer negation overflow.zig
index f1f36ff294eb2e772a1509f58695fd5bb1433116..72182cfdfc2dc52eb01c2c00b397e0df8f4ffac1 100644
--- a/test/cases/safety/vector integer negation overflow.zig
+++ b/test/cases/safety/vector integer negation overflow.zig
@@ -19,4 +19,4 @@ fn neg(a: @Vector(4, i16)) @Vector(4, i16) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/vector integer subtraction overflow.zig b/test/cases/safety/vector integer subtraction overflow.zig
index 9ba942469c0ca814f520562ec2fc68a5afc32690..8ac2c6d75632a9e98cb84164e6b4cc6b39a08fc7 100644
--- a/test/cases/safety/vector integer subtraction overflow.zig
+++ b/test/cases/safety/vector integer subtraction overflow.zig
@@ -19,4 +19,4 @@ fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/cases/safety/zero casted to error.zig b/test/cases/safety/zero casted to error.zig
index 1ffa995260b197dc48f472eb8e25e52ae3ab1275..7a02ec2b711821fbe6f3508100403e7ccc545e1b 100644
--- a/test/cases/safety/zero casted to error.zig
+++ b/test/cases/safety/zero casted to error.zig
@@ -16,4 +16,4 @@ fn bar(x: u16) anyerror {
}
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux,aarch64-linux
diff --git a/test/cases/taking_pointer_of_global_tagged_union.zig b/test/cases/taking_pointer_of_global_tagged_union.zig
index accb22667dfb741c0d6695355e06213e44c7dfb3..adc71d81c00771f68284b28fbc2f3a9652d07824 100644
--- a/test/cases/taking_pointer_of_global_tagged_union.zig
+++ b/test/cases/taking_pointer_of_global_tagged_union.zig
@@ -23,4 +23,4 @@ pub fn main() !void {
// run
// backend=stage2,llvm
-// target=native
+// target=x86_64-linux
diff --git a/test/src/Cases.zig b/test/src/Cases.zig
index bd93599171e987a833504a2434be683dcab6cb5e..60a564bc1610b634f5eb439b3a0a628b2a91c2a8 100644
--- a/test/src/Cases.zig
+++ b/test/src/Cases.zig
@@ -436,7 +436,7 @@ fn addFromDirInner(
const target = &resolved_target.result;
for (backends) |backend| {
if (backend == .stage2 and
- target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64)
+ target.cpu.arch != .aarch64 and target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64)
{
// Other backends don't support new liveness format
continue;
@@ -447,10 +447,6 @@ fn addFromDirInner(
// Rosetta has issues with ZLD
continue;
}
- if (backend == .stage2 and target.ofmt == .coff) {
- // COFF linker has bitrotted
- continue;
- }
const next = ctx.cases.items.len;
try ctx.cases.append(.{
--
2.54.0
From 7894703ee74a915206143f0b3efea082f999bb86 Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sat, 26 Jul 2025 21:39:43 -0400
Subject: [PATCH 022/110] aarch64: implement more optional/error union/union
support
---
lib/compiler_rt.zig | 2 +-
src/codegen/aarch64/Select.zig | 370 +++++++++++++++++++--
test/behavior/decl_literals.zig | 2 -
test/behavior/error.zig | 1 -
test/behavior/field_parent_ptr.zig | 2 -
test/behavior/inline_switch.zig | 1 -
test/behavior/optional.zig | 2 -
test/behavior/struct.zig | 5 -
test/behavior/switch.zig | 4 -
test/behavior/switch_on_captured_error.zig | 1 -
test/behavior/union.zig | 18 -
test/behavior/union_with_members.zig | 1 -
test/behavior/while.zig | 1 -
13 files changed, 342 insertions(+), 68 deletions(-)
diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig
index 17e9e04da7f73b180001bfab583de0550a68f734..b8723c56eeb228ec4641e4b22c6fbdd254ed933a 100644
--- a/lib/compiler_rt.zig
+++ b/lib/compiler_rt.zig
@@ -240,7 +240,7 @@ comptime {
_ = @import("compiler_rt/udivmodti4.zig");
// extra
- if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/os_version_check.zig");
+ _ = @import("compiler_rt/os_version_check.zig");
_ = @import("compiler_rt/emutls.zig");
_ = @import("compiler_rt/arm.zig");
_ = @import("compiler_rt/aulldiv.zig");
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index 6ceb3f3a59b0f1679a2b921782d8a4f025b4b712..f84088624af358e38b17efd7d9ed97aaba316004 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -584,7 +584,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
air_body_index += 1;
},
- .@"try", .try_cold, .try_ptr, .try_ptr_cold => {
+ .@"try", .try_cold => {
const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
const extra = isel.air.extraData(Air.Try, pl_op.payload);
@@ -596,6 +596,18 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
air_inst_index = air_body[air_body_index];
continue :air_tag air_tags[@intFromEnum(air_inst_index)];
},
+ .try_ptr, .try_ptr_cold => {
+ const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
+ const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
+
+ try isel.analyzeUse(extra.data.ptr);
+ try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
+ try isel.def_order.putNoClobber(gpa, air_inst_index, {});
+
+ air_body_index += 1;
+ air_inst_index = air_body[air_body_index];
+ continue :air_tag air_tags[@intFromEnum(air_inst_index)];
+ },
.ret, .ret_safe, .ret_load => {
const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
isel.returns = true;
@@ -4760,17 +4772,62 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const error_set_part_vi = try error_set_part_it.only(isel);
const error_set_part_mat = try error_set_part_vi.?.matReg(isel);
try isel.emit(.cbz(
- switch (error_set_part_vi.?.size(isel)) {
- else => unreachable,
- 1...4 => error_set_part_mat.ra.w(),
- 5...8 => error_set_part_mat.ra.x(),
- },
+ error_set_part_mat.ra.w(),
@intCast((isel.instructions.items.len + 1 - cont_label) << 2),
));
try error_set_part_mat.finish(isel);
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .try_ptr, .try_ptr_cold => {
+ const ty_pl = air.data(air.inst_index).ty_pl;
+ const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
+ const error_union_ty = isel.air.typeOf(extra.data.ptr, ip).childType(zcu);
+ const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
+ const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
+
+ const error_union_ptr_vi = try isel.use(extra.data.ptr);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
+ defer payload_ptr_vi.value.deref(isel);
+ switch (codegen.errUnionPayloadOffset(ty_pl.ty.toType().childType(zcu), zcu)) {
+ 0 => try payload_ptr_vi.value.move(isel, extra.data.ptr),
+ else => |payload_offset| {
+ const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
+ const lo12: u12 = @truncate(payload_offset >> 0);
+ const hi12: u12 = @intCast(payload_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ payload_ptr_ra.x(),
+ if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ },
+ }
+ }
+
+ const cont_label = isel.instructions.items.len;
+ const cont_live_registers = isel.live_registers;
+ try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
+ try isel.merge(&cont_live_registers, .{});
+
+ const error_set_ra = try isel.allocIntReg();
+ defer isel.freeReg(error_set_ra);
+ try isel.loadReg(
+ error_set_ra,
+ ZigType.fromInterned(error_union_info.error_set_type).abiSize(zcu),
+ .unsigned,
+ error_union_ptr_mat.ra,
+ codegen.errUnionErrorOffset(payload_ty, zcu),
+ );
+ try error_union_ptr_mat.finish(isel);
+ try isel.emit(.cbz(
+ error_set_ra.w(),
+ @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
+ ));
+
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.dbg_stmt => {
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -5403,14 +5460,6 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .optional_payload_ptr => {
- if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| {
- defer dst_vi.value.deref(isel);
- const ty_op = air.data(air.inst_index).ty_op;
- try dst_vi.value.move(isel, ty_op.operand);
- }
- if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
- },
.optional_payload => {
if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| unused: {
defer payload_vi.value.deref(isel);
@@ -5429,6 +5478,37 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .optional_payload_ptr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ try payload_ptr_vi.value.move(isel, ty_op.operand);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .optional_payload_ptr_set => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const opt_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
+ if (!opt_ty.optionalReprIsPayload(zcu)) {
+ const opt_ptr_vi = try isel.use(ty_op.operand);
+ const opt_ptr_mat = try opt_ptr_vi.matReg(isel);
+ const has_value_ra = try isel.allocIntReg();
+ defer isel.freeReg(has_value_ra);
+ try isel.storeReg(
+ has_value_ra,
+ 1,
+ opt_ptr_mat.ra,
+ opt_ty.optionalChild(zcu).abiSize(zcu),
+ );
+ try opt_ptr_mat.finish(isel);
+ try isel.emit(.movz(has_value_ra.w(), 1, .{ .lsl = .@"0" }));
+ }
+ try payload_ptr_vi.value.move(isel, ty_op.operand);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.wrap_optional => {
if (isel.live_values.fetchRemove(air.inst_index)) |opt_vi| unused: {
defer opt_vi.value.deref(isel);
@@ -5486,6 +5566,93 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .unwrap_errunion_payload_ptr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ switch (codegen.errUnionPayloadOffset(ty_op.ty.toType().childType(zcu), zcu)) {
+ 0 => try payload_ptr_vi.value.move(isel, ty_op.operand),
+ else => |payload_offset| {
+ const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
+ const error_union_ptr_vi = try isel.use(ty_op.operand);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ const lo12: u12 = @truncate(payload_offset >> 0);
+ const hi12: u12 = @intCast(payload_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ payload_ptr_ra.x(),
+ if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ try error_union_ptr_mat.finish(isel);
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .unwrap_errunion_err_ptr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |error_ptr_vi| unused: {
+ defer error_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ switch (codegen.errUnionErrorOffset(
+ isel.air.typeOf(ty_op.operand, ip).childType(zcu).errorUnionPayload(zcu),
+ zcu,
+ )) {
+ 0 => try error_ptr_vi.value.move(isel, ty_op.operand),
+ else => |error_offset| {
+ const error_ptr_ra = try error_ptr_vi.value.defReg(isel) orelse break :unused;
+ const error_union_ptr_vi = try isel.use(ty_op.operand);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ const lo12: u12 = @truncate(error_offset >> 0);
+ const hi12: u12 = @intCast(error_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ error_ptr_ra.x(),
+ if (lo12 > 0) error_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(error_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ try error_union_ptr_mat.finish(isel);
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .errunion_payload_ptr_set => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const payload_ty = ty_op.ty.toType().childType(zcu);
+ const error_union_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
+ const error_set_size = error_union_ty.errorUnionSet(zcu).abiSize(zcu);
+ const error_union_ptr_vi = try isel.use(ty_op.operand);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ if (error_set_size > 0) try isel.storeReg(
+ .zr,
+ error_set_size,
+ error_union_ptr_mat.ra,
+ codegen.errUnionErrorOffset(payload_ty, zcu),
+ );
+ switch (codegen.errUnionPayloadOffset(payload_ty, zcu)) {
+ 0 => {
+ try error_union_ptr_mat.finish(isel);
+ try payload_ptr_vi.value.move(isel, ty_op.operand);
+ },
+ else => |payload_offset| {
+ const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
+ const lo12: u12 = @truncate(payload_offset >> 0);
+ const hi12: u12 = @intCast(payload_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ payload_ptr_ra.x(),
+ if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ try error_union_ptr_mat.finish(isel);
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.wrap_errunion_payload => {
if (isel.live_values.fetchRemove(air.inst_index)) |error_union_vi| {
defer error_union_vi.value.deref(isel);
@@ -5672,6 +5839,32 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .set_union_tag => {
+ const bin_op = air.data(air.inst_index).bin_op;
+ const union_ty = isel.air.typeOf(bin_op.lhs, ip).childType(zcu);
+ const union_layout = union_ty.unionGetLayout(zcu);
+ const tag_vi = try isel.use(bin_op.rhs);
+ const union_ptr_vi = try isel.use(bin_op.lhs);
+ const union_ptr_mat = try union_ptr_vi.matReg(isel);
+ try tag_vi.store(isel, isel.air.typeOf(bin_op.rhs, ip), union_ptr_mat.ra, .{
+ .offset = union_layout.tagOffset(),
+ });
+ try union_ptr_mat.finish(isel);
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .get_union_tag => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |tag_vi| {
+ defer tag_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const union_ty = isel.air.typeOf(ty_op.operand, ip);
+ const union_layout = union_ty.unionGetLayout(zcu);
+ const union_vi = try isel.use(ty_op.operand);
+ var tag_part_it = union_vi.field(union_ty, union_layout.tagOffset(), union_layout.tag_size);
+ const tag_part_vi = try tag_part_it.only(isel);
+ try tag_vi.value.copy(isel, ty_op.ty.toType(), tag_part_vi.?);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
.slice => {
if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {
defer slice_vi.value.deref(isel);
@@ -6541,8 +6734,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (ptr_part_ra == null and len_part_ra == null) break :unused;
const un_op = air.data(air.inst_index).un_op;
- const err_vi = try isel.use(un_op);
- const err_mat = try err_vi.matReg(isel);
+ const error_vi = try isel.use(un_op);
+ const error_mat = try error_vi.matReg(isel);
const ptr_ra = try isel.allocIntReg();
defer isel.freeReg(ptr_ra);
const start_ra, const end_ra = range_ras: {
@@ -6573,7 +6766,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (len_part_ra) |_| try isel.emit(.sub(end_ra.w(), end_ra.w(), .{ .immediate = 1 }));
try isel.emit(.ldp(start_ra.w(), end_ra.w(), .{ .base = start_ra.x() }));
try isel.emit(.add(start_ra.x(), ptr_ra.x(), .{ .extended_register = .{
- .register = err_mat.ra.w(),
+ .register = error_mat.ra.w(),
.extend = switch (zcu.errorSetBits()) {
else => unreachable,
1...8 => .{ .uxtb = 2 },
@@ -6591,7 +6784,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.adrp(ptr_ra.x(), 0));
- try err_mat.finish(isel);
+ try error_mat.finish(isel);
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -6893,11 +7086,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(.ls)));
const un_op = air.data(air.inst_index).un_op;
- const err_vi = try isel.use(un_op);
- const err_mat = try err_vi.matReg(isel);
+ const error_vi = try isel.use(un_op);
+ const error_mat = try error_vi.matReg(isel);
const ptr_ra = try isel.allocIntReg();
defer isel.freeReg(ptr_ra);
- try isel.emit(.subs(.wzr, err_mat.ra.w(), .{ .register = ptr_ra.w() }));
+ try isel.emit(.subs(.wzr, error_mat.ra.w(), .{ .register = ptr_ra.w() }));
try isel.lazy_relocs.append(gpa, .{
.symbol = .{ .kind = .const_data, .ty = .anyerror_type },
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
@@ -6908,7 +7101,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.adrp(ptr_ra.x(), 0));
- try err_mat.finish(isel);
+ try error_mat.finish(isel);
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -9529,8 +9722,14 @@ pub const Value = struct {
} },
},
.struct_type => {
- const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
const loaded_struct = ip.loadStructType(ty.toIntern());
+ switch (loaded_struct.layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{
+ .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
+ },
+ }
+ const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
if (loaded_struct.field_types.len > Value.max_parts and
(std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
@@ -9638,6 +9837,77 @@ pub const Value = struct {
if (part.is_vector) subpart_vi.setIsVector(isel);
}
},
+ .union_type => {
+ const loaded_union = ip.loadUnionType(ty.toIntern());
+ switch (loaded_union.flagsUnordered(ip).layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{ .int_type = .{
+ .signedness = .unsigned,
+ .bits = @intCast(ty.bitSize(zcu)),
+ } },
+ }
+ const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
+ if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
+ const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
+ const alignment = vi.alignment(isel);
+ const tag_offset = union_layout.tagOffset();
+ const payload_offset = union_layout.payloadOffset();
+ const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness };
+ var parts: [2]Part = undefined;
+ var parts_len: Value.PartsLen = 0;
+ var field_end: u64 = 0;
+ for (0..2) |field_index| {
+ const field: enum { tag, payload } = switch (field_index) {
+ 0 => if (tag_offset < payload_offset) .tag else .payload,
+ 1 => if (tag_offset < payload_offset) .payload else .tag,
+ else => unreachable,
+ };
+ const field_size, const field_begin = switch (field) {
+ .tag => .{ union_layout.tag_size, tag_offset },
+ .payload => .{ union_layout.payload_size, payload_offset },
+ };
+ if (field_begin >= offset + size) break;
+ if (field_size == 0) continue;
+ field_end = field_begin + field_size;
+ if (field_end <= offset) continue;
+ const field_signedness = field_signedness: switch (field) {
+ .tag => {
+ if (offset >= field_begin and offset + size <= field_begin + field_size) {
+ ty = .fromInterned(loaded_union.enum_tag_ty);
+ ty_size = field_size;
+ offset -= field_begin;
+ continue :type_key ip.indexToKey(loaded_union.enum_tag_ty);
+ }
+ break :field_signedness ip.indexToKey(loaded_union.loadTagType(ip).tag_ty).int_type.signedness;
+ },
+ .payload => null,
+ };
+ if (parts_len > 0) combine: {
+ const prev_part = &parts[parts_len - 1];
+ const combined_size = field_end - prev_part.offset;
+ if (combined_size > @as(u64, 1) << @min(
+ min_part_log2_stride,
+ alignment.toLog2Units(),
+ @ctz(prev_part.offset),
+ )) break :combine;
+ prev_part.size = combined_size;
+ prev_part.signedness = null;
+ continue;
+ }
+ parts[parts_len] = .{
+ .offset = field_begin,
+ .size = field_size,
+ .signedness = field_signedness,
+ };
+ parts_len += 1;
+ }
+ vi.setParts(isel, parts_len);
+ for (parts[0..parts_len]) |part| {
+ const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
+ if (part.signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
+ }
+ },
.opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
.enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
.error_set_type,
@@ -10075,7 +10345,11 @@ pub const Value = struct {
};
},
.slice => |slice| switch (offset) {
- 0 => continue :constant_key .{ .ptr = ip.indexToKey(slice.ptr).ptr },
+ 0 => continue :constant_key switch (ip.indexToKey(slice.ptr)) {
+ else => unreachable,
+ .undef => |undef| .{ .undef = undef },
+ .ptr => |ptr| .{ .ptr = ptr },
+ },
else => {
assert(offset == @divExact(isel.target.ptrBitWidth(), 8));
offset = 0;
@@ -11128,16 +11402,14 @@ pub const CallAbiIterator = struct {
{
const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
const offset = codegen.errUnionErrorOffset(payload_ty, zcu);
- const size = error_set_ty.abiSize(zcu);
- const end = offset % 8 + size;
+ const end = offset % 8 + error_set_ty.abiSize(zcu);
const part_index: usize = @intCast(offset / 8);
sizes[part_index] = @max(sizes[part_index], @min(end, 8));
if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
}
{
const offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
- const size = payload_ty.abiSize(zcu);
- const end = offset % 8 + size;
+ const end = offset % 8 + payload_ty.abiSize(zcu);
const part_index: usize = @intCast(offset / 8);
sizes[part_index] = @max(sizes[part_index], @min(end, 8));
if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
@@ -11181,8 +11453,14 @@ pub const CallAbiIterator = struct {
=> unreachable,
},
.struct_type => {
- const size = wip_vi.size(isel);
const loaded_struct = ip.loadStructType(ty.toIntern());
+ switch (loaded_struct.layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{
+ .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
+ },
+ }
+ const size = wip_vi.size(isel);
if (size <= 16 * 4) homogeneous_aggregate: {
const fdt = homogeneousStructBaseType(zcu, &loaded_struct) orelse break :homogeneous_aggregate;
const parts_len = @shrExact(size, fdt.log2Size());
@@ -11267,6 +11545,40 @@ pub const CallAbiIterator = struct {
else => it.indirect(isel, wip_vi),
}
},
+ .union_type => {
+ const loaded_union = ip.loadUnionType(ty.toIntern());
+ switch (loaded_union.flagsUnordered(ip).layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{ .int_type = .{
+ .signedness = .unsigned,
+ .bits = @intCast(ty.bitSize(zcu)),
+ } },
+ }
+ switch (wip_vi.size(isel)) {
+ 0 => unreachable,
+ 1...8 => it.integer(isel, wip_vi),
+ 9...16 => {
+ const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
+ var sizes: [2]u64 = @splat(0);
+ {
+ const offset = union_layout.tagOffset();
+ const end = offset % 8 + union_layout.tag_size;
+ const part_index: usize = @intCast(offset / 8);
+ sizes[part_index] = @max(sizes[part_index], @min(end, 8));
+ if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
+ }
+ {
+ const offset = union_layout.payloadOffset();
+ const end = offset % 8 + union_layout.payload_size;
+ const part_index: usize = @intCast(offset / 8);
+ sizes[part_index] = @max(sizes[part_index], @min(end, 8));
+ if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
+ }
+ it.integers(isel, wip_vi, sizes);
+ },
+ else => it.indirect(isel, wip_vi),
+ }
+ },
.opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
.enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
.error_set_type,
diff --git a/test/behavior/decl_literals.zig b/test/behavior/decl_literals.zig
index 169c705a6b72f79bac808086b57109b115b83005..f96f46177179af58eee8e419648e5fdc7568a185 100644
--- a/test/behavior/decl_literals.zig
+++ b/test/behavior/decl_literals.zig
@@ -33,7 +33,6 @@ test "decl literal with pointer" {
}
test "call decl literal with optional" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -74,7 +73,6 @@ test "call decl literal" {
}
test "call decl literal with error union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
const S = struct {
diff --git a/test/behavior/error.zig b/test/behavior/error.zig
index 4665178808e201b10688335e33390f8ab7b64a9f..ae99c0a7e8346dff6ad8c3b21f128cdd7fcab140 100644
--- a/test/behavior/error.zig
+++ b/test/behavior/error.zig
@@ -943,7 +943,6 @@ test "optional error set function parameter" {
}
test "returning an error union containing a type with no runtime bits" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig
index 59742cf3f625a0d2a092f0c331e86b8af641f9c7..742b3060595cc7515316a4945d779ab551cfe10d 100644
--- a/test/behavior/field_parent_ptr.zig
+++ b/test/behavior/field_parent_ptr.zig
@@ -587,7 +587,6 @@ test "@fieldParentPtr extern struct last zero-bit field" {
}
test "@fieldParentPtr unaligned packed struct" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -726,7 +725,6 @@ test "@fieldParentPtr unaligned packed struct" {
}
test "@fieldParentPtr aligned packed struct" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig
index 57444d22a4c323e2a64eead53e37c2fda148189a..a1efe7ab022e1de44dc0e0e0411d915348523999 100644
--- a/test/behavior/inline_switch.zig
+++ b/test/behavior/inline_switch.zig
@@ -43,7 +43,6 @@ test "inline switch enums" {
const U = union(E) { a: void, b: u2, c: u3, d: u4 };
test "inline switch unions" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig
index 3a63dfb1acd84528617f4e5b07b3aad5189d4ff8..11d4ee053701cc01c55ff75b53134fd787293f96 100644
--- a/test/behavior/optional.zig
+++ b/test/behavior/optional.zig
@@ -319,7 +319,6 @@ test "assigning to an unwrapped optional field in an inline loop" {
}
test "coerce an anon struct literal to optional struct" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -447,7 +446,6 @@ test "optional pointer to zero bit optional payload" {
}
test "optional pointer to zero bit error union payload" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig
index ba69ae0990a8bdd9dca62f97ffaff66c1c7a25e6..3c4c4d7f80957904163706faf6ed0b56f1f821ba 100644
--- a/test/behavior/struct.zig
+++ b/test/behavior/struct.zig
@@ -797,7 +797,6 @@ test "fn with C calling convention returns struct by value" {
}
test "non-packed struct with u128 entry in union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1026,7 +1025,6 @@ test "packed struct with undefined initializers" {
}
test "for loop over pointers to struct, getting field from struct pointer" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -1093,7 +1091,6 @@ test "anon init through error unions and optionals" {
}
test "anon init through optional" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1113,7 +1110,6 @@ test "anon init through optional" {
}
test "anon init through error union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1398,7 +1394,6 @@ test "struct has only one reference" {
}
test "no dependency loop on pointer to optional struct" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig
index a7173be1ed346c325b6850e67584dfdefc49799b..77b3321ba31cb92ee6f28e083cc0ff227e976089 100644
--- a/test/behavior/switch.zig
+++ b/test/behavior/switch.zig
@@ -299,7 +299,6 @@ fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void {
}
test "switch on enum using pointer capture" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -360,7 +359,6 @@ fn testSwitchHandleAllCasesRange(x: u8) u8 {
}
test "switch on union with some prongs capturing" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -975,8 +973,6 @@ test "switch prong captures range" {
}
test "prong with inline call to unreachable" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
const U = union(enum) {
void: void,
bool: bool,
diff --git a/test/behavior/switch_on_captured_error.zig b/test/behavior/switch_on_captured_error.zig
index 75a4280d6222ce2861a08efd3847a00d5147fefb..9aae1c7fbe0108b0c290639d6026e9ba488ac0c7 100644
--- a/test/behavior/switch_on_captured_error.zig
+++ b/test/behavior/switch_on_captured_error.zig
@@ -6,7 +6,6 @@ const expectEqual = std.testing.expectEqual;
const builtin = @import("builtin");
test "switch on error union catch capture" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
diff --git a/test/behavior/union.zig b/test/behavior/union.zig
index bbace0f70691b2c971b42db9e119c35306c1fda3..fb05b9edbb0acee15e9e5622f2f9fdef82ce298f 100644
--- a/test/behavior/union.zig
+++ b/test/behavior/union.zig
@@ -160,7 +160,6 @@ test "unions embedded in aggregate types" {
}
test "constant tagged union with payload" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -263,7 +262,6 @@ fn testComparison() !void {
}
test "comparison between union and enum literal" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -279,7 +277,6 @@ const TheUnion = union(TheTag) {
C: i32,
};
test "cast union to tag type of union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -300,7 +297,6 @@ test "union field access gives the enum values" {
}
test "cast tag type of union to union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -316,7 +312,6 @@ const Value2 = union(Letter2) {
};
test "implicit cast union to its tag type" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -495,7 +490,6 @@ test "initialize global array of union" {
}
test "update the tag value for zero-sized unions" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -734,7 +728,6 @@ test "union with only 1 field casted to its enum type which has enum value speci
}
test "@intFromEnum works on unions" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -848,7 +841,6 @@ test "@unionInit stored to a const" {
}
test "@unionInit can modify a union type" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -871,7 +863,6 @@ test "@unionInit can modify a union type" {
}
test "@unionInit can modify a pointer value" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -990,7 +981,6 @@ test "function call result coerces from tagged union to the tag" {
}
test "switching on non exhaustive union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -1176,7 +1166,6 @@ test "comptime equality of extern unions with same tag" {
}
test "union tag is set when initiated as a temporary value at runtime" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1216,7 +1205,6 @@ test "extern union most-aligned field is smaller" {
}
test "return an extern union from C calling convention" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1248,7 +1236,6 @@ test "return an extern union from C calling convention" {
}
test "noreturn field in union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -1481,8 +1468,6 @@ test "reinterpreting enum value inside packed union" {
}
test "access the tag of a global tagged union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
const U = union(enum) {
a,
b: u8,
@@ -2111,7 +2096,6 @@ test "runtime union init, most-aligned field != largest" {
}
test "copied union field doesn't alias source" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -2334,8 +2318,6 @@ test "assign global tagged union" {
}
test "set mutable union by switching on same union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
const U = union(enum) {
foo,
bar: usize,
diff --git a/test/behavior/union_with_members.zig b/test/behavior/union_with_members.zig
index 288d47d9cbdccfa57772986b0b0d6f0c9cdb68e8..9303ac14da1e6d4d3de23633ea38f496134ce2d4 100644
--- a/test/behavior/union_with_members.zig
+++ b/test/behavior/union_with_members.zig
@@ -17,7 +17,6 @@ const ET = union(enum) {
};
test "enum with members" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/while.zig b/test/behavior/while.zig
index db3d299b55d800a04c83283654c697ba236ffa61..d6323babf556339ea000a9de4b523ba4be51f3b2 100644
--- a/test/behavior/while.zig
+++ b/test/behavior/while.zig
@@ -344,7 +344,6 @@ test "else continue outer while" {
}
test "try terminating an infinite loop" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
--
2.54.0
From 04614d6ea17fff69ead42223c35a257da25462de Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 26 Jul 2025 13:43:17 -0700
Subject: [PATCH 023/110] std.Io.Reader: add rebase to the vtable
This eliminates a footgun and special case handling with fixed buffers,
as well as allowing decompression streams to keep a window in the output
buffer.
---
lib/std/Io.zig | 2 +-
lib/std/Io/Reader.zig | 73 ++++++++++++++--------------
lib/std/compress/zstd/Decompress.zig | 30 ++++++++++--
3 files changed, 63 insertions(+), 42 deletions(-)
diff --git a/lib/std/Io.zig b/lib/std/Io.zig
index 1ab5d13cab9644fc635d99984956a94d526fe929..1511f0dcadd06f9748f06151f36954a2ed0dadba 100644
--- a/lib/std/Io.zig
+++ b/lib/std/Io.zig
@@ -757,7 +757,7 @@ pub fn Poller(comptime StreamEnum: type) type {
const unused = r.buffer[r.end..];
if (unused.len >= min_len) return unused;
}
- if (r.seek > 0) r.rebase();
+ if (r.seek > 0) r.rebase(r.buffer.len) catch unreachable;
{
var list: std.ArrayListUnmanaged(u8) = .{
.items = r.buffer[0..r.end],
diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig
index fa05f0275b77f9a95184e3cbea7ef9ee8a103fa4..da9e01dd2c43e65d7522f4afa8d10e506cf4a2f6 100644
--- a/lib/std/Io/Reader.zig
+++ b/lib/std/Io/Reader.zig
@@ -67,6 +67,18 @@ pub const VTable = struct {
///
/// This function is only called when `buffer` is empty.
discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
+
+ /// Ensures `capacity` more data can be buffered without rebasing.
+ ///
+ /// Asserts `capacity` is within buffer capacity, or that the stream ends
+ /// within `capacity` bytes.
+ ///
+ /// Only called when `capacity` cannot fit into the unused capacity of
+ /// `buffer`.
+ ///
+ /// The default implementation moves buffered data to the start of
+ /// `buffer`, setting `seek` to zero, and cannot fail.
+ rebase: *const fn (r: *Reader, capacity: usize) RebaseError!void = defaultRebase,
};
pub const StreamError = error{
@@ -97,6 +109,10 @@ pub const ShortError = error{
ReadFailed,
};
+pub const RebaseError = error{
+ EndOfStream,
+};
+
pub const failing: Reader = .{
.vtable = &.{
.stream = failingStream,
@@ -122,6 +138,7 @@ pub fn fixed(buffer: []const u8) Reader {
.vtable = &.{
.stream = endingStream,
.discard = endingDiscard,
+ .rebase = endingRebase,
},
// This cast is safe because all potential writes to it will instead
// return `error.EndOfStream`.
@@ -780,11 +797,8 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
@branchHint(.likely);
return buffer[seek .. end + 1];
}
- if (r.vtable.stream == &endingStream) {
- // Protect the `@constCast` of `fixed`.
- return error.EndOfStream;
- }
- r.rebase();
+ // TODO take a parameter for max search length rather than relying on buffer capacity
+ try rebase(r, r.buffer.len);
while (r.buffer.len - r.end != 0) {
const end_cap = r.buffer[r.end..];
var writer: Writer = .fixed(end_cap);
@@ -1050,11 +1064,7 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
};
if (r.seek + n <= r.end) return;
};
- if (r.vtable.stream == &endingStream) {
- // Protect the `@constCast` of `fixed`.
- return error.EndOfStream;
- }
- rebaseCapacity(r, n);
+ try rebase(r, n);
var writer: Writer = .{
.buffer = r.buffer,
.vtable = &.{ .drain = Writer.fixedDrain },
@@ -1074,7 +1084,7 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
///
/// Asserts buffer capacity is at least 1.
pub fn fillMore(r: *Reader) Error!void {
- rebaseCapacity(r, 1);
+ try rebase(r, 1);
var writer: Writer = .{
.buffer = r.buffer,
.end = r.end,
@@ -1251,7 +1261,7 @@ pub fn takeLeb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result {
pub fn expandTotalCapacity(r: *Reader, allocator: Allocator, n: usize) Allocator.Error!void {
if (n <= r.buffer.len) return;
- if (r.seek > 0) rebase(r);
+ if (r.seek > 0) rebase(r, r.buffer.len);
var list: ArrayList(u8) = .{
.items = r.buffer[0..r.end],
.capacity = r.buffer.len,
@@ -1297,37 +1307,20 @@ fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Resu
}
}
-/// Left-aligns data such that `r.seek` becomes zero.
-///
-/// If `r.seek` is not already zero then `buffer` is mutated, making it illegal
-/// to call this function with a const-casted `buffer`, such as in the case of
-/// `fixed`. This issue can be avoided:
-/// * in implementations, by attempting a read before a rebase, in which
-/// case the read will return `error.EndOfStream`, preventing the rebase.
-/// * in usage, by copying into a mutable buffer before initializing `fixed`.
-pub fn rebase(r: *Reader) void {
- if (r.seek == 0) return;
+/// Ensures `capacity` more data can be buffered without rebasing.
+pub fn rebase(r: *Reader, capacity: usize) RebaseError!void {
+ if (r.end + capacity <= r.buffer.len) return;
+ return r.vtable.rebase(r, capacity);
+}
+
+pub fn defaultRebase(r: *Reader, capacity: usize) RebaseError!void {
+ if (r.end <= r.buffer.len - capacity) return;
const data = r.buffer[r.seek..r.end];
@memmove(r.buffer[0..data.len], data);
r.seek = 0;
r.end = data.len;
}
-/// Ensures `capacity` more data can be buffered without rebasing, by rebasing
-/// if necessary.
-///
-/// Asserts `capacity` is within the buffer capacity.
-///
-/// If the rebase occurs then `buffer` is mutated, making it illegal to call
-/// this function with a const-casted `buffer`, such as in the case of `fixed`.
-/// This issue can be avoided:
-/// * in implementations, by attempting a read before a rebase, in which
-/// case the read will return `error.EndOfStream`, preventing the rebase.
-/// * in usage, by copying into a mutable buffer before initializing `fixed`.
-pub fn rebaseCapacity(r: *Reader, capacity: usize) void {
- if (r.end > r.buffer.len - capacity) rebase(r);
-}
-
/// Advances the stream and decreases the size of the storage buffer by `n`,
/// returning the range of bytes no longer accessible by `r`.
///
@@ -1683,6 +1676,12 @@ fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
return error.EndOfStream;
}
+fn endingRebase(r: *Reader, capacity: usize) RebaseError!void {
+ _ = r;
+ _ = capacity;
+ return error.EndOfStream;
+}
+
fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
_ = r;
_ = w;
diff --git a/lib/std/compress/zstd/Decompress.zig b/lib/std/compress/zstd/Decompress.zig
index b831fe7fb44056196ec2faebe3594c2f74813cb6..b13a2dcf7a70b3b0aedf81978f43ccee0c20ca5f 100644
--- a/lib/std/compress/zstd/Decompress.zig
+++ b/lib/std/compress/zstd/Decompress.zig
@@ -31,7 +31,12 @@ pub const Options = struct {
/// Verifying checksums is not implemented yet and will cause a panic if
/// you set this to true.
verify_checksum: bool = false,
- /// Affects the minimum capacity of the provided buffer.
+
+ /// The output buffer is asserted to have capacity for `window_len` plus
+ /// `zstd.block_size_max`.
+ ///
+ /// If `window_len` is too small, then some streams will fail to decompress
+ /// with `error.OutputBufferUndersize`.
window_len: u32 = zstd.default_window_len,
};
@@ -69,8 +74,10 @@ pub const Error = error{
WindowSizeUnknown,
};
-/// If buffer that is written to is not big enough, some streams will fail with
-/// `error.OutputBufferUndersize`. A safe value is `zstd.default_window_len * 2`.
+/// When connecting `reader` to a `Writer`, `buffer` should be empty, and
+/// `Writer.buffer` capacity has requirements based on `Options.window_len`.
+///
+/// Otherwise, `buffer` has those requirements.
pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
return .{
.input = input,
@@ -78,7 +85,10 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
.verify_checksum = options.verify_checksum,
.window_len = options.window_len,
.reader = .{
- .vtable = &.{ .stream = stream },
+ .vtable = &.{
+ .stream = stream,
+ .rebase = rebase,
+ },
.buffer = buffer,
.seek = 0,
.end = 0,
@@ -86,6 +96,18 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
};
}
+fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {
+ const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
+ assert(capacity <= r.buffer.len - d.window_len);
+ assert(r.end + capacity > r.buffer.len);
+ const buffered = r.buffer[0..r.end];
+ const discard = buffered.len - d.window_len;
+ const keep = buffered[discard..];
+ @memmove(r.buffer[0..keep.len], keep);
+ r.end = keep.len;
+ r.seek -= discard;
+}
+
fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
const in = d.input;
--
2.54.0
From e12dc4947c411d25c4c56d887e62d0e2b9addcb8 Mon Sep 17 00:00:00 2001
From: A cursed quail
Date: Sat, 26 Jul 2025 10:32:17 -0500
Subject: [PATCH 024/110] std.zig: fmtId returns a FormatId
Changes fmtId to return the FormatId type directly, and renames the
FormatId.render function to FormatId.format, so it can be used in a
format expression directly.
Why? Since `render` is private, you can't create functions that wrap
`fmtId` or `fmtIdFlags`, since you can't name the return type of those
functions outside of std itself.
The current setup _might_ be intentional? In which case I can live with
it, but I figured I'd make a small contrib to upstream zig :)
---
lib/std/zig.zig | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index 2039a4d8c0efff853d0c8695e4e930ddd71d5a2a..a692a63795a7f2f5fb0871b66f8db93aa0365ffd 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -385,23 +385,23 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![
/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
///
/// See also `fmtIdFlags`.
-pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
- return .{ .data = .{ .bytes = bytes, .flags = .{} } };
+pub fn fmtId(bytes: []const u8) FormatId {
+ return .{ .bytes = bytes, .flags = .{} };
}
/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
///
/// 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 fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) FormatId {
+ return .{ .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 fmtIdPU(bytes: []const u8) FormatId {
+ return .{ .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 } } };
+pub fn fmtIdP(bytes: []const u8) FormatId {
+ return .{ .bytes = bytes, .flags = .{ .allow_primitive = true } };
}
test fmtId {
@@ -447,7 +447,7 @@ pub const FormatId = struct {
};
/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
- fn render(ctx: FormatId, writer: *Writer) Writer.Error!void {
+ pub fn format(ctx: FormatId, writer: *Writer) Writer.Error!void {
const bytes = ctx.bytes;
if (isValidId(bytes) and
(ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and
--
2.54.0
From 771523c67534fc47800608eb720886e9f53da7b4 Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sun, 27 Jul 2025 06:50:20 -0400
Subject: [PATCH 025/110] aarch64: implement var args
---
src/codegen/aarch64.zig | 52 ++--
src/codegen/aarch64/Select.zig | 495 +++++++++++++++++++++++++------
src/codegen/aarch64/encoding.zig | 48 +--
test/behavior/var_args.zig | 4 -
4 files changed, 455 insertions(+), 144 deletions(-)
diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig
index 4cb3e8ecc8398119d72446cad4aa74eede4c7038..2904d36b7f21522b2bb0ca5d2e441dc1b5016aaf 100644
--- a/src/codegen/aarch64.zig
+++ b/src/codegen/aarch64.zig
@@ -19,8 +19,12 @@ pub fn generate(
) !Mir {
const zcu = pt.zcu;
const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
const func = zcu.funcInfo(func_index);
- const func_type = zcu.intern_pool.indexToKey(func.ty).func_type;
+ const func_zir = func.zir_body_inst.resolveFull(ip).?;
+ const file = zcu.fileByIndex(func_zir.file);
+ const named_params_len = file.zir.?.getParamBody(func_zir.inst).len;
+ const func_type = ip.indexToKey(func.ty).func_type;
assert(liveness.* == null);
const mod = zcu.navFileScope(func.owner_nav).mod.?;
@@ -61,23 +65,32 @@ pub fn generate(
.values = .empty,
};
defer isel.deinit();
+ const is_sysv = !isel.target.os.tag.isDarwin() and isel.target.os.tag != .windows;
+ const is_sysv_var_args = is_sysv and func_type.is_var_args;
const air_main_body = air.getMainBody();
var param_it: Select.CallAbiIterator = .init;
const air_args = for (air_main_body, 0..) |air_inst_index, body_index| {
if (air.instructions.items(.tag)[@intFromEnum(air_inst_index)] != .arg) break air_main_body[0..body_index];
- const param_ty = air.instructions.items(.data)[@intFromEnum(air_inst_index)].arg.ty.toType();
- const param_vi = try param_it.param(&isel, param_ty);
+ const arg = air.instructions.items(.data)[@intFromEnum(air_inst_index)].arg;
+ const param_ty = arg.ty.toType();
+ const param_vi = param_vi: {
+ if (arg.zir_param_index >= named_params_len) {
+ assert(func_type.is_var_args);
+ if (!is_sysv) break :param_vi try param_it.nonSysvVarArg(&isel, param_ty);
+ }
+ break :param_vi try param_it.param(&isel, param_ty);
+ };
tracking_log.debug("${d} <- %{d}", .{ @intFromEnum(param_vi.?), @intFromEnum(air_inst_index) });
try isel.live_values.putNoClobber(gpa, air_inst_index, param_vi.?);
} else unreachable;
const saved_gra_start = if (mod.strip) param_it.ngrn else Select.CallAbiIterator.ngrn_start;
- const saved_gra_end = if (func_type.is_var_args) Select.CallAbiIterator.ngrn_end else param_it.ngrn;
+ const saved_gra_end = if (is_sysv_var_args) Select.CallAbiIterator.ngrn_end else param_it.ngrn;
const saved_gra_len = @intFromEnum(saved_gra_end) - @intFromEnum(saved_gra_start);
const saved_vra_start = if (mod.strip) param_it.nsrn else Select.CallAbiIterator.nsrn_start;
- const saved_vra_end = if (func_type.is_var_args) Select.CallAbiIterator.nsrn_end else param_it.nsrn;
+ const saved_vra_end = if (is_sysv_var_args) Select.CallAbiIterator.nsrn_end else param_it.nsrn;
const saved_vra_len = @intFromEnum(saved_vra_end) - @intFromEnum(saved_vra_start);
const frame_record = 2;
@@ -85,11 +98,16 @@ pub fn generate(
.base = .fp,
.offset = 8 * std.mem.alignForward(u7, frame_record + saved_gra_len, 2),
};
- isel.va_list = .{
- .__stack = named_stack_args.withOffset(param_it.nsaa),
- .__gr_top = named_stack_args,
- .__vr_top = .{ .base = .fp, .offset = 0 },
- };
+ const stack_var_args = named_stack_args.withOffset(param_it.nsaa);
+ const gr_top = named_stack_args;
+ const vr_top: Select.Value.Indirect = .{ .base = .fp, .offset = 0 };
+ isel.va_list = if (is_sysv) .{ .sysv = .{
+ .__stack = stack_var_args,
+ .__gr_top = gr_top,
+ .__vr_top = vr_top,
+ .__gr_offs = @as(i32, @intFromEnum(Select.CallAbiIterator.ngrn_end) - @intFromEnum(param_it.ngrn)) * -8,
+ .__vr_offs = @as(i32, @intFromEnum(Select.CallAbiIterator.nsrn_end) - @intFromEnum(param_it.nsrn)) * -16,
+ } } else .{ .other = stack_var_args };
// translate arg locations from caller-based to callee-based
for (air_args) |air_inst_index| {
@@ -106,11 +124,9 @@ pub fn generate(
const first_passed_part_vi = part_it.next().?;
const hint_ra = first_passed_part_vi.hint(&isel).?;
passed_vi.setParent(&isel, .{ .stack_slot = if (hint_ra.isVector())
- isel.va_list.__vr_top.withOffset(@as(i8, -16) *
- (@intFromEnum(saved_vra_end) - @intFromEnum(hint_ra)))
+ vr_top.withOffset(@as(i8, -16) * (@intFromEnum(saved_vra_end) - @intFromEnum(hint_ra)))
else
- isel.va_list.__gr_top.withOffset(@as(i8, -8) *
- (@intFromEnum(saved_gra_end) - @intFromEnum(hint_ra))) });
+ gr_top.withOffset(@as(i8, -8) * (@intFromEnum(saved_gra_end) - @intFromEnum(hint_ra))) });
},
.stack_slot => |stack_slot| {
assert(stack_slot.base == .sp);
@@ -152,13 +168,7 @@ pub fn generate(
isel.verify(true);
const prologue = isel.instructions.items.len;
- const epilogue = try isel.layout(
- param_it,
- func_type.is_var_args,
- saved_gra_len,
- saved_vra_len,
- mod,
- );
+ const epilogue = try isel.layout(param_it, is_sysv_var_args, saved_gra_len, saved_vra_len, mod);
const instructions = try isel.instructions.toOwnedSlice(gpa);
var mir: Mir = .{
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index f84088624af358e38b17efd7d9ed97aaba316004..0b60f26b02113a3be885ad2d4869617e8dbd127b 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -28,10 +28,15 @@ literal_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Literal),
// Stack Frame
returns: bool,
-va_list: struct {
- __stack: Value.Indirect,
- __gr_top: Value.Indirect,
- __vr_top: Value.Indirect,
+va_list: union(enum) {
+ other: Value.Indirect,
+ sysv: struct {
+ __stack: Value.Indirect,
+ __gr_top: Value.Indirect,
+ __vr_top: Value.Indirect,
+ __gr_offs: i32,
+ __vr_offs: i32,
+ },
},
stack_size: u24,
stack_align: InternPool.Alignment,
@@ -408,13 +413,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
air_body_index += 1;
},
- .breakpoint,
- .dbg_stmt,
- .dbg_empty_stmt,
- .dbg_var_ptr,
- .dbg_var_val,
- .dbg_arg_inline,
- => {
+ .breakpoint, .dbg_stmt, .dbg_empty_stmt, .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline, .c_va_end => {
air_body_index += 1;
air_inst_index = air_body[air_body_index];
continue :air_tag air_tags[@intFromEnum(air_inst_index)];
@@ -428,23 +427,43 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
const extra = isel.air.extraData(Air.Call, pl_op.payload);
const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]);
isel.saved_registers.insert(.lr);
+ const callee_ty = isel.air.typeOf(pl_op.operand, ip);
+ const func_info = switch (ip.indexToKey(callee_ty.toIntern())) {
+ else => unreachable,
+ .func_type => |func_type| func_type,
+ .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type,
+ };
try isel.analyzeUse(pl_op.operand);
var param_it: CallAbiIterator = .init;
- for (args) |arg| {
+ for (args, 0..) |arg, arg_index| {
const restore_values_len = isel.values.items.len;
defer isel.values.shrinkRetainingCapacity(restore_values_len);
- const param_vi = try param_it.param(isel, isel.air.typeOf(arg, ip)) orelse continue;
- const param_parent = param_vi.parent(isel);
- switch (switch (param_parent) {
- .unallocated, .stack_slot => param_parent,
+ const param_vi = param_vi: {
+ const param_ty = isel.air.typeOf(arg, ip);
+ if (arg_index >= func_info.param_types.len) {
+ assert(func_info.is_var_args);
+ switch (isel.va_list) {
+ .other => break :param_vi try param_it.nonSysvVarArg(isel, param_ty),
+ .sysv => {},
+ }
+ }
+ break :param_vi try param_it.param(isel, param_ty);
+ } orelse continue;
+ defer param_vi.deref(isel);
+ const passed_vi = switch (param_vi.parent(isel)) {
+ .unallocated, .stack_slot => param_vi,
.value, .constant => unreachable,
- .address => |address_vi| address_vi.parent(isel),
- }) {
+ .address => |address_vi| address_vi,
+ };
+ switch (passed_vi.parent(isel)) {
.unallocated => {},
.stack_slot => |stack_slot| {
assert(stack_slot.base == .sp);
- isel.stack_size = @max(isel.stack_size, stack_slot.offset);
+ isel.stack_size = @max(
+ isel.stack_size,
+ stack_slot.offset + @as(u24, @intCast(passed_vi.size(isel))),
+ );
},
.value, .constant, .address => unreachable,
}
@@ -802,7 +821,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
air_inst_index = air_body[air_body_index];
continue :air_tag air_tags[@intFromEnum(air_inst_index)];
},
- .set_err_return_trace, .c_va_end => {
+ .set_err_return_trace => {
const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
try isel.analyzeUse(un_op);
@@ -2474,6 +2493,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .inferred_alloc, .inferred_alloc_comptime => unreachable,
.assembly => {
const ty_pl = air.data(air.inst_index).ty_pl;
const extra = isel.air.extraData(Air.Asm, ty_pl.payload);
@@ -3389,6 +3409,12 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const pl_op = air.data(air.inst_index).pl_op;
const extra = isel.air.extraData(Air.Call, pl_op.payload);
const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]);
+ const callee_ty = isel.air.typeOf(pl_op.operand, ip);
+ const func_info = switch (ip.indexToKey(callee_ty.toIntern())) {
+ else => unreachable,
+ .func_type => |func_type| func_type,
+ .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type,
+ };
try call.prepareReturn(isel);
const maybe_def_ret_vi = isel.live_values.fetchRemove(air.inst_index);
@@ -3455,41 +3481,50 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
ret_addr_vi.hint(isel).?,
);
var param_it: CallAbiIterator = .init;
- for (args) |arg| {
- const param_vi = try param_it.param(isel, isel.air.typeOf(arg, ip)) orelse continue;
+ for (args, 0..) |arg, arg_index| {
+ const param_ty = isel.air.typeOf(arg, ip);
+ const param_vi = param_vi: {
+ if (arg_index >= func_info.param_types.len) {
+ assert(func_info.is_var_args);
+ switch (isel.va_list) {
+ .other => break :param_vi try param_it.nonSysvVarArg(isel, param_ty),
+ .sysv => {},
+ }
+ }
+ break :param_vi try param_it.param(isel, param_ty);
+ } orelse continue;
defer param_vi.deref(isel);
const arg_vi = try isel.use(arg);
- const passed_vi = switch (param_vi.parent(isel)) {
- .unallocated, .stack_slot => param_vi,
- .value, .constant => unreachable,
- .address => |address_vi| {
- try call.paramAddress(isel, arg_vi, address_vi.hint(isel).?);
- continue;
+ switch (param_vi.parent(isel)) {
+ .unallocated => if (param_vi.hint(isel)) |param_ra| {
+ try call.paramLiveOut(isel, arg_vi, param_ra);
+ } else {
+ var param_part_it = param_vi.parts(isel);
+ var arg_part_it = arg_vi.parts(isel);
+ if (arg_part_it.only()) |_| {
+ try isel.values.ensureUnusedCapacity(gpa, param_part_it.remaining);
+ arg_vi.setParts(isel, param_part_it.remaining);
+ while (param_part_it.next()) |param_part_vi| _ = arg_vi.addPart(
+ isel,
+ param_part_vi.get(isel).offset_from_parent,
+ param_part_vi.size(isel),
+ );
+ param_part_it = param_vi.parts(isel);
+ arg_part_it = arg_vi.parts(isel);
+ }
+ while (param_part_it.next()) |param_part_vi| {
+ const arg_part_vi = arg_part_it.next().?;
+ assert(arg_part_vi.get(isel).offset_from_parent ==
+ param_part_vi.get(isel).offset_from_parent);
+ assert(arg_part_vi.size(isel) == param_part_vi.size(isel));
+ try call.paramLiveOut(isel, arg_part_vi, param_part_vi.hint(isel).?);
+ }
},
- };
- if (passed_vi.hint(isel)) |param_ra| {
- try call.paramLiveOut(isel, arg_vi, param_ra);
- } else {
- var param_part_it = passed_vi.parts(isel);
- var arg_part_it = arg_vi.parts(isel);
- if (arg_part_it.only()) |_| {
- try isel.values.ensureUnusedCapacity(gpa, param_part_it.remaining);
- arg_vi.setParts(isel, param_part_it.remaining);
- while (param_part_it.next()) |param_part_vi| _ = arg_vi.addPart(
- isel,
- param_part_vi.get(isel).offset_from_parent,
- param_part_vi.size(isel),
- );
- param_part_it = passed_vi.parts(isel);
- arg_part_it = arg_vi.parts(isel);
- }
- while (param_part_it.next()) |param_part_vi| {
- const arg_part_vi = arg_part_it.next().?;
- assert(arg_part_vi.get(isel).offset_from_parent ==
- param_part_vi.get(isel).offset_from_parent);
- assert(arg_part_vi.size(isel) == param_part_vi.size(isel));
- try call.paramLiveOut(isel, arg_part_vi, param_part_vi.hint(isel).?);
- }
+ .stack_slot => |stack_slot| try arg_vi.store(isel, param_ty, stack_slot.base, .{
+ .offset = @intCast(stack_slot.offset),
+ }),
+ .value, .constant => unreachable,
+ .address => |address_vi| try call.paramAddress(isel, arg_vi, address_vi.hint(isel).?),
}
}
try call.finishParams(isel);
@@ -4828,9 +4863,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .dbg_stmt => {
- if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
- },
+ .dbg_stmt => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
.dbg_empty_stmt => {
try isel.emit(.nop());
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@@ -7079,6 +7112,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
+ .wasm_memory_size, .wasm_memory_grow => unreachable,
.cmp_lt_errors_len => {
if (isel.live_values.fetchRemove(air.inst_index)) |is_vi| unused: {
defer is_vi.value.deref(isel);
@@ -7135,16 +7169,266 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .inferred_alloc,
- .inferred_alloc_comptime,
- .int_from_float_safe,
- .int_from_float_optimized_safe,
- .wasm_memory_size,
- .wasm_memory_grow,
- .work_item_id,
- .work_group_size,
- .work_group_id,
- => unreachable,
+ .c_va_arg => {
+ const maybe_arg_vi = isel.live_values.fetchRemove(air.inst_index);
+ defer if (maybe_arg_vi) |arg_vi| arg_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const ty = ty_op.ty.toType();
+ var param_it: CallAbiIterator = .init;
+ const param_vi = try param_it.param(isel, ty);
+ defer param_vi.?.deref(isel);
+ const passed_vi = switch (param_vi.?.parent(isel)) {
+ .unallocated => param_vi.?,
+ .stack_slot, .value, .constant => unreachable,
+ .address => |address_vi| address_vi,
+ };
+ const passed_size: u5 = @intCast(passed_vi.alignment(isel).forward(passed_vi.size(isel)));
+ const passed_is_vector = passed_vi.isVector(isel);
+
+ const va_list_ptr_vi = try isel.use(ty_op.operand);
+ const va_list_ptr_mat = try va_list_ptr_vi.matReg(isel);
+ const offs_ra = try isel.allocIntReg();
+ defer isel.freeReg(offs_ra);
+ const stack_ra = try isel.allocIntReg();
+ defer isel.freeReg(stack_ra);
+
+ var part_vis: [2]Value.Index = undefined;
+ var arg_part_ras: [2]?Register.Alias = @splat(null);
+ const parts_len = parts_len: {
+ var parts_len: u2 = 0;
+ var part_it = passed_vi.parts(isel);
+ while (part_it.next()) |part_vi| : (parts_len += 1) {
+ part_vis[parts_len] = part_vi;
+ const arg_vi = maybe_arg_vi orelse continue;
+ const part_offset, const part_size = part_vi.position(isel);
+ var arg_part_it = arg_vi.value.field(ty, part_offset, part_size);
+ const arg_part_vi = try arg_part_it.only(isel);
+ arg_part_ras[parts_len] = try arg_part_vi.?.defReg(isel);
+ }
+ break :parts_len parts_len;
+ };
+
+ const done_label = isel.instructions.items.len;
+ try isel.emit(.str(stack_ra.x(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = 0,
+ } }));
+ try isel.emit(switch (parts_len) {
+ else => unreachable,
+ 1 => if (arg_part_ras[0]) |arg_part_ra| switch (part_vis[0].size(isel)) {
+ else => unreachable,
+ 1 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.b(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsb(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ .unsigned => .ldrb(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ },
+ 2 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.h(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsh(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ .unsigned => .ldrh(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ },
+ 4 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.s() else arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ 8 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.d() else arg_part_ra.x(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ 16 => .ldr(arg_part_ra.q(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ } else .add(stack_ra.x(), stack_ra.x(), .{ .immediate = passed_size }),
+ 2 => if (arg_part_ras[0] != null or arg_part_ras[1] != null) .ldp(
+ @as(Register.Alias, arg_part_ras[0] orelse .zr).x(),
+ @as(Register.Alias, arg_part_ras[1] orelse .zr).x(),
+ .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } },
+ ) else .add(stack_ra.x(), stack_ra.x(), .{ .immediate = passed_size }),
+ });
+ try isel.emit(.ldr(stack_ra.x(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = 0,
+ } }));
+ switch (isel.va_list) {
+ .other => {},
+ .sysv => {
+ const stack_label = isel.instructions.items.len;
+ try isel.emit(.b(
+ @intCast((isel.instructions.items.len + 1 - done_label) << 2),
+ ));
+ switch (parts_len) {
+ else => unreachable,
+ 1 => if (arg_part_ras[0]) |arg_part_ra| try isel.emit(switch (part_vis[0].size(isel)) {
+ else => unreachable,
+ 1 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.b(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsb(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ .unsigned => .ldrb(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ },
+ 2 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.h(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsh(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ .unsigned => .ldrh(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ },
+ 4 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.s() else arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ 8 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.d() else arg_part_ra.x(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ 16 => .ldr(arg_part_ra.q(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ }),
+ 2 => if (arg_part_ras[0] != null or arg_part_ras[1] != null) {
+ try isel.emit(.ldp(
+ @as(Register.Alias, arg_part_ras[0] orelse .zr).x(),
+ @as(Register.Alias, arg_part_ras[1] orelse .zr).x(),
+ .{ .base = stack_ra.x() },
+ ));
+ try isel.emit(.add(stack_ra.x(), stack_ra.x(), .{ .extended_register = .{
+ .register = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }));
+ },
+ }
+ try isel.emit(.ldr(stack_ra.x(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = if (passed_is_vector) 16 else 8,
+ } }));
+ try isel.emit(.@"b."(
+ .gt,
+ @intCast((isel.instructions.items.len + 1 - stack_label) << 2),
+ ));
+ try isel.emit(.str(stack_ra.w(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = if (passed_is_vector) 28 else 24,
+ } }));
+ try isel.emit(.adds(stack_ra.w(), offs_ra.w(), .{ .immediate = passed_size }));
+ try isel.emit(.tbz(
+ offs_ra.w(),
+ 31,
+ @intCast((isel.instructions.items.len + 1 - stack_label) << 2),
+ ));
+ try isel.emit(.ldr(offs_ra.w(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = if (passed_is_vector) 28 else 24,
+ } }));
+ },
+ }
+ try va_list_ptr_mat.finish(isel);
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .c_va_copy => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |va_list_vi| {
+ defer va_list_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const va_list_ptr_vi = try isel.use(ty_op.operand);
+ const va_list_ptr_mat = try va_list_ptr_vi.matReg(isel);
+ _ = try va_list_vi.value.load(isel, ty_op.ty.toType(), va_list_ptr_mat.ra, .{});
+ try va_list_ptr_mat.finish(isel);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .c_va_end => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
+ .c_va_start => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |va_list_vi| {
+ defer va_list_vi.value.deref(isel);
+ const ty = air.data(air.inst_index).ty;
+ switch (isel.va_list) {
+ .other => |va_list| if (try va_list_vi.value.defReg(isel)) |va_list_ra| try isel.emit(.add(
+ va_list_ra.x(),
+ va_list.base.x(),
+ .{ .immediate = @intCast(va_list.offset) },
+ )),
+ .sysv => |va_list| {
+ var vr_offs_it = va_list_vi.value.field(ty, 28, 4);
+ const vr_offs_vi = try vr_offs_it.only(isel);
+ if (try vr_offs_vi.?.defReg(isel)) |vr_offs_ra| try isel.movImmediate(
+ vr_offs_ra.w(),
+ @as(u32, @bitCast(va_list.__vr_offs)),
+ );
+ var gr_offs_it = va_list_vi.value.field(ty, 24, 4);
+ const gr_offs_vi = try gr_offs_it.only(isel);
+ if (try gr_offs_vi.?.defReg(isel)) |gr_offs_ra| try isel.movImmediate(
+ gr_offs_ra.w(),
+ @as(u32, @bitCast(va_list.__gr_offs)),
+ );
+ var vr_top_it = va_list_vi.value.field(ty, 16, 8);
+ const vr_top_vi = try vr_top_it.only(isel);
+ if (try vr_top_vi.?.defReg(isel)) |vr_top_ra| try isel.emit(.add(
+ vr_top_ra.x(),
+ va_list.__vr_top.base.x(),
+ .{ .immediate = @intCast(va_list.__vr_top.offset) },
+ ));
+ var gr_top_it = va_list_vi.value.field(ty, 8, 8);
+ const gr_top_vi = try gr_top_it.only(isel);
+ if (try gr_top_vi.?.defReg(isel)) |gr_top_ra| try isel.emit(.add(
+ gr_top_ra.x(),
+ va_list.__gr_top.base.x(),
+ .{ .immediate = @intCast(va_list.__gr_top.offset) },
+ ));
+ var stack_it = va_list_vi.value.field(ty, 0, 8);
+ const stack_vi = try stack_it.only(isel);
+ if (try stack_vi.?.defReg(isel)) |stack_ra| try isel.emit(.add(
+ stack_ra.x(),
+ va_list.__stack.base.x(),
+ .{ .immediate = @intCast(va_list.__stack.offset) },
+ ));
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .work_item_id, .work_group_size, .work_group_id => unreachable,
}
assert(air.body_index == 0);
}
@@ -7225,7 +7509,7 @@ pub fn verify(isel: *Select, check_values: bool) void {
pub fn layout(
isel: *Select,
incoming: CallAbiIterator,
- have_va: bool,
+ is_sysv_var_args: bool,
saved_gra_len: u7,
saved_vra_len: u7,
mod: *const Package.Module,
@@ -7236,8 +7520,6 @@ pub fn layout(
wip_mir_log.debug("{f}:\n", .{nav.fqn.fmt(ip)});
const stack_size: u24 = @intCast(InternPool.Alignment.@"16".forward(isel.stack_size));
- const stack_size_lo: u12 = @truncate(stack_size >> 0);
- const stack_size_hi: u12 = @truncate(stack_size >> 12);
var saves_buf: [10 + 8 + 8 + 2 + 8]struct {
class: enum { integer, vector },
@@ -7315,7 +7597,7 @@ pub fn layout(
// incoming vr arguments
save_ra = if (mod.strip) incoming.nsrn else CallAbiIterator.nsrn_start;
- while (save_ra != if (have_va) CallAbiIterator.nsrn_end else incoming.nsrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
+ while (save_ra != if (is_sysv_var_args) CallAbiIterator.nsrn_end else incoming.nsrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
saves_size = std.mem.alignForward(u10, saves_size, 16);
saves_buf[saves_len] = .{
.class = .vector,
@@ -7370,7 +7652,7 @@ pub fn layout(
1 => saves_size += 8,
}
save_ra = if (mod.strip) incoming.ngrn else CallAbiIterator.ngrn_start;
- while (save_ra != if (have_va) CallAbiIterator.ngrn_end else incoming.ngrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
+ while (save_ra != if (is_sysv_var_args) CallAbiIterator.ngrn_end else incoming.ngrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
saves_size = std.mem.alignForward(u10, saves_size, 8);
saves_buf[saves_len] = .{
.class = .integer,
@@ -7434,6 +7716,8 @@ pub fn layout(
.fp
else
.ip0;
+ const stack_size_lo: u12 = @truncate(stack_size >> 0);
+ const stack_size_hi: u12 = @truncate(stack_size >> 12);
if (mod.stack_check) {
if (stack_size_hi > 2) {
try isel.movImmediate(.ip1, stack_size_hi);
@@ -7481,6 +7765,7 @@ pub fn layout(
if (isel.returns) {
try isel.emit(.ret(.lr));
var save_index: usize = 0;
+ var first_offset: ?u10 = null;
while (save_index < saves.len) {
if (save_index + 2 <= saves.len and saves[save_index + 1].needs_restore and
saves[save_index + 0].class == saves[save_index + 1].class and
@@ -7489,46 +7774,51 @@ pub fn layout(
try isel.emit(.ldp(
saves[save_index + 0].register,
saves[save_index + 1].register,
- switch (saves[save_index + 0].offset) {
- 0 => .{ .post_index = .{
+ if (first_offset) |offset| .{ .signed_offset = .{
+ .base = .sp,
+ .offset = @intCast(saves[save_index + 0].offset - offset),
+ } } else form: {
+ first_offset = @intCast(saves[save_index + 0].offset);
+ break :form .{ .post_index = .{
.base = .sp,
- .index = @intCast(saves_size),
- } },
- else => |offset| .{ .signed_offset = .{
- .base = .sp,
- .offset = @intCast(offset),
- } },
+ .index = @intCast(saves_size - first_offset.?),
+ } };
},
));
save_index += 2;
} else if (saves[save_index].needs_restore) {
try isel.emit(.ldr(
saves[save_index].register,
- switch (saves[save_index].offset) {
- 0 => .{ .post_index = .{
+ if (first_offset) |offset| .{ .unsigned_offset = .{
+ .base = .sp,
+ .offset = saves[save_index + 0].offset - offset,
+ } } else form: {
+ const offset = saves[save_index + 0].offset;
+ first_offset = offset;
+ break :form .{ .post_index = .{
.base = .sp,
- .index = @intCast(saves_size),
- } },
- else => |offset| .{ .unsigned_offset = .{
- .base = .sp,
- .offset = @intCast(offset),
- } },
+ .index = @intCast(saves_size - offset),
+ } };
},
));
save_index += 1;
} else save_index += 1;
}
- if (isel.stack_align != .@"16" or (stack_size_lo > 0 and stack_size_hi > 0)) {
- try isel.emit(switch (frame_record_offset) {
- 0 => .add(.sp, .fp, .{ .immediate = 0 }),
- else => |offset| .sub(.sp, .fp, .{ .immediate = offset }),
- });
+ const offset = stack_size + first_offset.?;
+ const offset_lo: u12 = @truncate(offset >> 0);
+ const offset_hi: u12 = @truncate(offset >> 12);
+ if (isel.stack_align != .@"16" or (offset_lo > 0 and offset_hi > 0)) {
+ const fp_offset = @as(i11, first_offset.?) - frame_record_offset;
+ try isel.emit(if (fp_offset >= 0)
+ .add(.sp, .fp, .{ .immediate = @intCast(fp_offset) })
+ else
+ .sub(.sp, .fp, .{ .immediate = @intCast(-fp_offset) }));
} else {
- if (stack_size_hi > 0) try isel.emit(.add(.sp, .sp, .{
- .shifted_immediate = .{ .immediate = stack_size_hi, .lsl = .@"12" },
+ if (offset_hi > 0) try isel.emit(.add(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = offset_hi, .lsl = .@"12" },
}));
- if (stack_size_lo > 0) try isel.emit(.add(.sp, .sp, .{
- .immediate = stack_size_lo,
+ if (offset_lo > 0) try isel.emit(.add(.sp, .sp, .{
+ .immediate = offset_lo,
}));
}
wip_mir_log.debug("{f}:\n", .{nav.fqn.fmt(ip)});
@@ -9493,6 +9783,11 @@ pub const Value = struct {
return it.vi;
}
+ pub fn peek(it: PartIterator) ?Value.Index {
+ var it_mut = it;
+ return it_mut.next();
+ }
+
pub fn only(it: PartIterator) ?Value.Index {
return if (it.remaining == 1) it.vi else null;
}
@@ -11607,6 +11902,16 @@ pub const CallAbiIterator = struct {
return wip_vi.ref(isel);
}
+ pub fn nonSysvVarArg(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
+ const ngrn = it.ngrn;
+ defer it.ngrn = ngrn;
+ it.ngrn = ngrn_end;
+ const nsrn = it.nsrn;
+ defer it.nsrn = nsrn;
+ it.nsrn = nsrn_end;
+ return it.param(isel, ty);
+ }
+
pub fn ret(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
const wip_vi = try it.param(isel, ty) orelse return null;
switch (wip_vi.parent(isel)) {
diff --git a/src/codegen/aarch64/encoding.zig b/src/codegen/aarch64/encoding.zig
index 727b88c7290aa8b989fa9d81908152bce74dbb3c..6ddf46b625ca2fbc51d47b0a5e99d4c7deb1d925 100644
--- a/src/codegen/aarch64/encoding.zig
+++ b/src/codegen/aarch64/encoding.zig
@@ -10089,18 +10089,6 @@ pub const Instruction = packed union {
},
} } } };
},
- .signed_offset => |signed_offset| {
- assert(signed_offset.base.format.integer == .doubleword);
- return .{ .load_store = .{ .register_pair_offset = .{ .integer = .{
- .ldp = .{
- .Rt = t1.alias.encode(.{}),
- .Rn = signed_offset.base.alias.encode(.{ .sp = true }),
- .Rt2 = t2.alias.encode(.{}),
- .imm7 = @intCast(@shrExact(signed_offset.offset, @as(u2, 2) + @intFromEnum(sf))),
- .sf = sf,
- },
- } } } };
- },
.pre_index => |pre_index| {
assert(pre_index.base.format.integer == .doubleword);
return .{ .load_store = .{ .register_pair_pre_indexed = .{ .integer = .{
@@ -10113,6 +10101,18 @@ pub const Instruction = packed union {
},
} } } };
},
+ .signed_offset => |signed_offset| {
+ assert(signed_offset.base.format.integer == .doubleword);
+ return .{ .load_store = .{ .register_pair_offset = .{ .integer = .{
+ .ldp = .{
+ .Rt = t1.alias.encode(.{}),
+ .Rn = signed_offset.base.alias.encode(.{ .sp = true }),
+ .Rt2 = t2.alias.encode(.{}),
+ .imm7 = @intCast(@shrExact(signed_offset.offset, @as(u2, 2) + @intFromEnum(sf))),
+ .sf = sf,
+ },
+ } } } };
+ },
.base => |base| continue :form .{ .signed_offset = .{ .base = base } },
}
},
@@ -11473,18 +11473,6 @@ pub const Instruction = packed union {
},
} } } };
},
- .signed_offset => |signed_offset| {
- assert(signed_offset.base.format.integer == .doubleword);
- return .{ .load_store = .{ .register_pair_offset = .{ .integer = .{
- .stp = .{
- .Rt = t1.alias.encode(.{}),
- .Rn = signed_offset.base.alias.encode(.{ .sp = true }),
- .Rt2 = t2.alias.encode(.{}),
- .imm7 = @intCast(@shrExact(signed_offset.offset, @as(u2, 2) + @intFromEnum(sf))),
- .sf = sf,
- },
- } } } };
- },
.pre_index => |pre_index| {
assert(pre_index.base.format.integer == .doubleword);
return .{ .load_store = .{ .register_pair_pre_indexed = .{ .integer = .{
@@ -11497,6 +11485,18 @@ pub const Instruction = packed union {
},
} } } };
},
+ .signed_offset => |signed_offset| {
+ assert(signed_offset.base.format.integer == .doubleword);
+ return .{ .load_store = .{ .register_pair_offset = .{ .integer = .{
+ .stp = .{
+ .Rt = t1.alias.encode(.{}),
+ .Rn = signed_offset.base.alias.encode(.{ .sp = true }),
+ .Rt2 = t2.alias.encode(.{}),
+ .imm7 = @intCast(@shrExact(signed_offset.offset, @as(u2, 2) + @intFromEnum(sf))),
+ .sf = sf,
+ },
+ } } } };
+ },
.base => |base| continue :form .{ .signed_offset = .{ .base = base } },
}
},
diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig
index 36cebd5d77828ff73962f5b30ef634c57055d558..bd06404149a45eb7e11c52d44ccd470654283076 100644
--- a/test/behavior/var_args.zig
+++ b/test/behavior/var_args.zig
@@ -92,7 +92,6 @@ fn doNothingWithFirstArg(args: anytype) void {
}
test "simple variadic function" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
@@ -154,7 +153,6 @@ test "simple variadic function" {
}
test "coerce reference to var arg" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
@@ -234,7 +232,6 @@ test "variadic functions" {
}
test "copy VaList" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -269,7 +266,6 @@ test "copy VaList" {
}
test "unused VaList arg" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
--
2.54.0
From b26e732bd0a33161b079202e9df9dda4b918b2bb Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sun, 27 Jul 2025 08:00:57 -0400
Subject: [PATCH 026/110] aarch64: fix error union constants
---
src/codegen/aarch64/Select.zig | 87 ++++++++++++++++++++++++----------
test/behavior/enum.zig | 1 -
test/behavior/error.zig | 12 -----
test/behavior/while.zig | 2 -
4 files changed, 62 insertions(+), 40 deletions(-)
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index 0b60f26b02113a3be885ad2d4869617e8dbd127b..d030eab47182b80e2af6c8f8c5ba8dd457afb2eb 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -10414,11 +10414,12 @@ pub const Value = struct {
} },
.error_union => |error_union| {
const error_union_type = ip.indexToKey(error_union.ty).error_union_type;
+ const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
- if (!ip.isNoReturn(error_union_type.error_set_type) and
- offset == codegen.errUnionErrorOffset(payload_ty, zcu))
- {
- offset = 0;
+ const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
+ const error_set_size = error_set_ty.abiSize(zcu);
+ if (offset >= error_set_offset and offset + size <= error_set_offset + error_set_size) {
+ offset -= error_set_offset;
continue :constant_key switch (error_union.val) {
.err_name => |err_name| .{ .err = .{
.ty = error_union_type.error_set_type,
@@ -10430,15 +10431,18 @@ pub const Value = struct {
} },
};
}
- assert(payload_ty.hasRuntimeBitsIgnoreComptime(zcu));
- offset -= @intCast(codegen.errUnionPayloadOffset(payload_ty, zcu));
- switch (error_union.val) {
- .err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
- .payload => |payload| {
- constant = payload;
- constant_key = ip.indexToKey(payload);
- continue :constant_key constant_key;
- },
+ const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
+ const payload_size = payload_ty.abiSize(zcu);
+ if (offset >= payload_offset and offset + size <= payload_offset + payload_size) {
+ offset -= payload_offset;
+ switch (error_union.val) {
+ .err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
+ .payload => |payload| {
+ constant = payload;
+ constant_key = ip.indexToKey(payload);
+ continue :constant_key constant_key;
+ },
+ }
}
},
.enum_tag => |enum_tag| continue :constant_key .{ .int = ip.indexToKey(enum_tag.int).int },
@@ -10975,7 +10979,17 @@ fn hasRepeatedByteRepr(isel: *Select, constant: Constant) error{OutOfMemory}!?u8
fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMemory}!bool {
const zcu = isel.pt.zcu;
const ip = &zcu.intern_pool;
- switch (ip.indexToKey(constant.toIntern())) {
+ if (try isel.writeKeyToMemory(ip.indexToKey(constant.toIntern()), buffer)) return true;
+ constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false,
+ };
+ return true;
+}
+fn writeKeyToMemory(isel: *Select, constant_key: InternPool.Key, buffer: []u8) error{OutOfMemory}!bool {
+ const zcu = isel.pt.zcu;
+ const ip = &zcu.intern_pool;
+ switch (constant_key) {
.int_type,
.ptr_type,
.array_type,
@@ -10997,6 +11011,37 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
.empty_enum_value,
.memoized_call,
=> unreachable, // not a runtime value
+ .err => |err| {
+ const error_int = ip.getErrorValueIfExists(err.name).?;
+ switch (buffer.len) {
+ else => unreachable,
+ inline 1...4 => |size| std.mem.writeInt(
+ @Type(.{ .int = .{ .signedness = .unsigned, .bits = 8 * size } }),
+ buffer[0..size],
+ @intCast(error_int),
+ isel.target.cpu.arch.endian(),
+ ),
+ }
+ },
+ .error_union => |error_union| {
+ const error_union_type = ip.indexToKey(error_union.ty).error_union_type;
+ const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
+ const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
+ const error_set = buffer[@intCast(codegen.errUnionErrorOffset(payload_ty, zcu))..][0..@intCast(error_set_ty.abiSize(zcu))];
+ switch (error_union.val) {
+ .err_name => |err_name| if (!try isel.writeKeyToMemory(.{ .err = .{
+ .ty = error_set_ty.toIntern(),
+ .name = err_name,
+ } }, error_set)) return false,
+ .payload => |payload| {
+ if (!try isel.writeToMemory(
+ .fromInterned(payload),
+ buffer[@intCast(codegen.errUnionPayloadOffset(payload_ty, zcu))..][0..@intCast(payload_ty.abiSize(zcu))],
+ )) return false;
+ @memset(error_set, 0);
+ },
+ }
+ },
.opt => |opt| {
const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu));
switch (opt.val) {
@@ -11008,7 +11053,6 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) buffer[child_size] = @intFromBool(true);
},
}
- return true;
},
.aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) {
else => unreachable,
@@ -11027,9 +11071,8 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
elem_offset += elem_size;
},
}
- return true;
},
- .vector_type => {},
+ .vector_type => return false,
.struct_type => {
const loaded_struct = ip.loadStructType(aggregate.ty);
switch (loaded_struct.layout) {
@@ -11052,9 +11095,8 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
}), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false;
field_offset += field_size;
}
- return true;
},
- .@"extern", .@"packed" => {},
+ .@"extern", .@"packed" => return false,
}
},
.tuple_type => |tuple_type| {
@@ -11071,15 +11113,10 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
}), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false;
field_offset += field_size;
}
- return true;
},
},
- else => {},
+ else => return false,
}
- constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false,
- };
return true;
}
diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig
index 2d9d41d7b296d865dbc713618e8f7389d201f0dc..d719a611e6a5319d6374606066f3c16616ea1238 100644
--- a/test/behavior/enum.zig
+++ b/test/behavior/enum.zig
@@ -926,7 +926,6 @@ test "enum literal casting to tagged union" {
const Bar = enum { A, B, C, D };
test "enum literal casting to error union with payload enum" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var bar: error{B}!Bar = undefined;
diff --git a/test/behavior/error.zig b/test/behavior/error.zig
index ae99c0a7e8346dff6ad8c3b21f128cdd7fcab140..4ce94bb43b9f2b6a25b20f8b490368639ecf7825 100644
--- a/test/behavior/error.zig
+++ b/test/behavior/error.zig
@@ -145,14 +145,11 @@ test "implicit cast to optional to error union to return result loc" {
}
test "fn returning empty error set can be passed as fn returning any error" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
entry();
comptime entry();
}
test "fn returning empty error set can be passed as fn returning any error - pointer" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
entryPtr();
@@ -404,7 +401,6 @@ fn intLiteral(str: []const u8) !?i64 {
}
test "nested error union function call in optional unwrap" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -482,7 +478,6 @@ test "optional error set is the same size as error set" {
}
test "nested catch" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
const S = struct {
@@ -698,7 +693,6 @@ test "coerce error set to the current inferred error set" {
}
test "error union payload is properly aligned" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -757,7 +751,6 @@ test "simple else prong allowed even when all errors handled" {
}
test "pointer to error union payload" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -845,7 +838,6 @@ test "alignment of wrapping an error union payload" {
}
test "compare error union and error set" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var a: anyerror = error.Foo;
@@ -1034,8 +1026,6 @@ test "errorCast to adhoc inferred error set" {
}
test "@errorCast from error set to error union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
const S = struct {
fn doTheTest(set: error{ A, B }) error{A}!i32 {
return @errorCast(set);
@@ -1046,8 +1036,6 @@ test "@errorCast from error set to error union" {
}
test "@errorCast from error union to error union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-
const S = struct {
fn doTheTest(set: error{ A, B }!i32) error{A}!i32 {
return @errorCast(set);
diff --git a/test/behavior/while.zig b/test/behavior/while.zig
index d6323babf556339ea000a9de4b523ba4be51f3b2..7a177d56905c751c15a1c923181a78a09a55d746 100644
--- a/test/behavior/while.zig
+++ b/test/behavior/while.zig
@@ -174,7 +174,6 @@ test "while with optional as condition with else" {
}
test "while with error union condition" {
- 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;
@@ -306,7 +305,6 @@ test "while optional 2 break statements and an else" {
}
test "while error 2 break statements and an else" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
--
2.54.0
From bb29846732ce94d4492e824bd5252279b5220593 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 27 Jul 2025 16:38:43 -0700
Subject: [PATCH 027/110] std.compress.xz: eliminate dependency on
std.Io.bitReader
---
lib/std/compress/xz.zig | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/lib/std/compress/xz.zig b/lib/std/compress/xz.zig
index c8bc964543884dd55b7778b6bb52c5d2ce9013c1..6c99e9f4275fc6d41b39bfa8f6519f31bcbe8aed 100644
--- a/lib/std/compress/xz.zig
+++ b/lib/std/compress/xz.zig
@@ -12,17 +12,11 @@ pub const Check = enum(u4) {
};
fn readStreamFlags(reader: anytype, check: *Check) !void {
- var bit_reader = std.io.bitReader(.little, reader);
-
- const reserved1 = try bit_reader.readBitsNoEof(u8, 8);
- if (reserved1 != 0)
- return error.CorruptInput;
-
- check.* = @as(Check, @enumFromInt(try bit_reader.readBitsNoEof(u4, 4)));
-
- const reserved2 = try bit_reader.readBitsNoEof(u4, 4);
- if (reserved2 != 0)
- return error.CorruptInput;
+ const reserved1 = try reader.readByte();
+ if (reserved1 != 0) return error.CorruptInput;
+ const byte = try reader.readByte();
+ if ((byte >> 4) != 0) return error.CorruptInput;
+ check.* = @enumFromInt(@as(u4, @truncate(byte)));
}
pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) {
--
2.54.0
From dea3ed7f59347e87a1b8fa237202873988084ae8 Mon Sep 17 00:00:00 2001
From: Ivan
Date: Mon, 28 Jul 2025 07:10:23 +0000
Subject: [PATCH 028/110] build: fix error in standalone test when using
`--release`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Carl Åstholm
---
test/standalone/dependency_options/build.zig | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig
index 20e2db1fa24f551db53a30e4ad51305f73ca6a50..95cde3c8913fc3d8c33b07391bc0a77611623e33 100644
--- a/test/standalone/dependency_options/build.zig
+++ b/test/standalone/dependency_options/build.zig
@@ -10,7 +10,14 @@ pub fn build(b: *std.Build) !void {
const none_specified_mod = none_specified.module("dummy");
if (!none_specified_mod.resolved_target.?.query.eql(b.graph.host.query)) return error.TestFailed;
- if (none_specified_mod.optimize.? != .Debug) return error.TestFailed;
+ const expected_optimize: std.builtin.OptimizeMode = switch (b.release_mode) {
+ .off => .Debug,
+ .any => unreachable,
+ .fast => .ReleaseFast,
+ .safe => .ReleaseSafe,
+ .small => .ReleaseSmall,
+ };
+ if (none_specified_mod.optimize.? != expected_optimize) return error.TestFailed;
// Passing null is the same as not specifying the option,
// so this should resolve to the same cached dependency instance.
--
2.54.0
From 2dea904d5a1602674d9cd147f8a5f2797dd00c40 Mon Sep 17 00:00:00 2001
From: IOKG04
Date: Mon, 28 Jul 2025 15:15:49 +0200
Subject: [PATCH 029/110] `.strong`, not `.Strong`
https://github.com/ziglang/zig/pull/24537#issuecomment-3124556900
---
doc/langref.html.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 139c19211e7200afad48443f8b5e1e24fa479542..6cf44ff78477797898b802c2c086192d10ca4c25 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4840,7 +4840,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
This builtin can be called from a {#link|comptime#} block to conditionally export symbols.
When ptr points to a function with the C calling convention and
- {#syntax#}options.linkage{#endsyntax#} is {#syntax#}.Strong{#endsyntax#}, this is equivalent to
+ {#syntax#}options.linkage{#endsyntax#} is {#syntax#}.strong{#endsyntax#}, this is equivalent to
the {#syntax#}export{#endsyntax#} keyword used on a function:
{#code|export_builtin.zig#}
--
2.54.0
From c334956a54c691ad7e76341193d2d46df18090ef Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Sun, 27 Jul 2025 19:55:05 -0400
Subject: [PATCH 030/110] aarch64: workaround some optional/union issues
---
lib/std/builtin.zig | 9 +-
lib/std/start.zig | 1 -
src/codegen/aarch64/Select.zig | 322 ++++++++++++------
test/behavior/array.zig | 2 -
test/behavior/defer.zig | 2 -
test/behavior/optional.zig | 1 -
test/behavior/pointers.zig | 1 -
test/behavior/slice.zig | 1 -
test/behavior/struct.zig | 1 -
.../struct_contains_slice_of_itself.zig | 2 -
test/behavior/switch.zig | 4 -
test/behavior/switch_prong_err_enum.zig | 1 -
test/behavior/try.zig | 1 -
test/behavior/union.zig | 21 --
14 files changed, 220 insertions(+), 149 deletions(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 54376426e262df1e35f05ad44cb2fef03fd4e0b8..f79bc2fe72099758a9b1b6c065631e9b3d318f85 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -895,8 +895,8 @@ pub const VaList = switch (builtin.cpu.arch) {
.windows => *u8,
.ios, .macos, .tvos, .watchos, .visionos => *u8,
else => switch (builtin.zig_backend) {
- .stage2_aarch64 => VaListAarch64,
- else => @compileError("disabled due to miscompilations"),
+ else => VaListAarch64,
+ .stage2_llvm => @compileError("disabled due to miscompilations"),
},
},
.arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) {
@@ -921,7 +921,10 @@ pub const VaList = switch (builtin.cpu.arch) {
.wasm32, .wasm64 => *anyopaque,
.x86 => *u8,
.x86_64 => switch (builtin.os.tag) {
- .windows => @compileError("disabled due to miscompilations"), // *u8,
+ .windows => switch (builtin.zig_backend) {
+ else => *u8,
+ .stage2_llvm => @compileError("disabled due to miscompilations"),
+ },
else => VaListX86_64,
},
.xtensa => VaListXtensa,
diff --git a/lib/std/start.zig b/lib/std/start.zig
index 43355d34f4f400b8c577d7682386f153cef36d8f..f889885c846e1214bdcbfc84692ee4fe642c02ea 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -626,7 +626,6 @@ pub inline fn callMain() u8 {
const result = root.main() catch |err| {
switch (builtin.zig_backend) {
- .stage2_aarch64,
.stage2_powerpc,
.stage2_riscv64,
=> {
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index d030eab47182b80e2af6c8f8c5ba8dd457afb2eb..0ebe451ebb689f843ec59a11e1fcd61291ae3a1e 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -3261,7 +3261,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
assert(dst_int_info.bits == src_child_int_info.bits * src_len);
const src_child_size = src_ty.childType(zcu).abiSize(zcu);
if (8 * src_child_size == src_child_int_info.bits) {
- try dst_vi.value.defAddr(isel, dst_ty, dst_int_info, comptime &.initFill(.free)) orelse break :unused;
+ try dst_vi.value.defAddr(isel, dst_ty, .{ .wrap = dst_int_info }) orelse break :unused;
try call.prepareReturn(isel);
try call.finishReturn(isel);
@@ -3288,7 +3288,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
assert(dst_child_int_info.bits * dst_len == src_int_info.bits);
const dst_child_size = dst_ty.childType(zcu).abiSize(zcu);
if (8 * dst_child_size == dst_child_int_info.bits) {
- try dst_vi.value.defAddr(isel, dst_ty, null, comptime &.initFill(.free)) orelse break :unused;
+ try dst_vi.value.defAddr(isel, dst_ty, .{}) orelse break :unused;
try call.prepareReturn(isel);
try call.finishReturn(isel);
@@ -3438,12 +3438,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
.value, .constant => unreachable,
.address => |address_vi| {
maybe_ret_addr_vi = address_vi;
- _ = try def_ret_vi.value.defAddr(
- isel,
- isel.air.typeOfIndex(air.inst_index, ip),
- null,
- &call.caller_saved_regs,
- );
+ _ = try def_ret_vi.value.defAddr(isel, isel.air.typeOfIndex(air.inst_index, ip), .{
+ .expected_live_registers = &call.caller_saved_regs,
+ });
},
}
}
@@ -4953,37 +4950,34 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (ptr_info.flags.is_volatile) _ = try isel.use(air.inst_index.toRef());
if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
defer dst_vi.value.deref(isel);
- switch (dst_vi.value.size(isel)) {
- 0 => unreachable,
- 1...Value.max_parts => {
- const ptr_vi = try isel.use(ty_op.operand);
- const ptr_mat = try ptr_vi.matReg(isel);
- _ = try dst_vi.value.load(isel, ty_op.ty.toType(), ptr_mat.ra, .{
- .@"volatile" = ptr_info.flags.is_volatile,
- });
- try ptr_mat.finish(isel);
- },
- else => |size| {
- try dst_vi.value.defAddr(isel, .fromInterned(ptr_info.child), null, comptime &.initFill(.free)) orelse break :unused;
+ const size = dst_vi.value.size(isel);
+ if (size <= Value.max_parts and ip.zigTypeTag(ptr_info.child) != .@"union") {
+ const ptr_vi = try isel.use(ty_op.operand);
+ const ptr_mat = try ptr_vi.matReg(isel);
+ _ = try dst_vi.value.load(isel, ty_op.ty.toType(), ptr_mat.ra, .{
+ .@"volatile" = ptr_info.flags.is_volatile,
+ });
+ try ptr_mat.finish(isel);
+ } else {
+ try dst_vi.value.defAddr(isel, .fromInterned(ptr_info.child), .{}) orelse break :unused;
- try call.prepareReturn(isel);
- try call.finishReturn(isel);
+ try call.prepareReturn(isel);
+ try call.finishReturn(isel);
- try call.prepareCallee(isel);
- try isel.global_relocs.append(gpa, .{
- .name = "memcpy",
- .reloc = .{ .label = @intCast(isel.instructions.items.len) },
- });
- try isel.emit(.bl(0));
- try call.finishCallee(isel);
+ try call.prepareCallee(isel);
+ try isel.global_relocs.append(gpa, .{
+ .name = "memcpy",
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+ try call.finishCallee(isel);
- try call.prepareParams(isel);
- const ptr_vi = try isel.use(ty_op.operand);
- try isel.movImmediate(.x2, size);
- try call.paramLiveOut(isel, ptr_vi, .r1);
- try call.paramAddress(isel, dst_vi.value, .r0);
- try call.finishParams(isel);
- },
+ try call.prepareParams(isel);
+ const ptr_vi = try isel.use(ty_op.operand);
+ try isel.movImmediate(.x2, size);
+ try call.paramLiveOut(isel, ptr_vi, .r1);
+ try call.paramAddress(isel, dst_vi.value, .r0);
+ try call.finishParams(isel);
}
}
@@ -5727,26 +5721,14 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const error_set_size = error_set_ty.abiSize(zcu);
const payload_size = payload_ty.abiSize(zcu);
+ var error_set_part_it = error_union_vi.value.field(error_union_ty, error_set_offset, error_set_size);
+ const error_set_part_vi = try error_set_part_it.only(isel);
+ try error_set_part_vi.?.move(isel, ty_op.operand);
if (payload_size > 0) {
var payload_part_it = error_union_vi.value.field(error_union_ty, payload_offset, payload_size);
const payload_part_vi = try payload_part_it.only(isel);
- if (try payload_part_vi.?.defReg(isel)) |payload_part_ra| try isel.emit(switch (payload_size) {
- else => unreachable,
- 1...4 => .orr(payload_part_ra.w(), .wzr, .{ .immediate = .{
- .N = .word,
- .immr = 0b000001,
- .imms = 0b111100,
- } }),
- 5...8 => .orr(payload_part_ra.x(), .xzr, .{ .immediate = .{
- .N = .word,
- .immr = 0b000001,
- .imms = 0b111100,
- } }),
- });
+ try payload_part_vi.?.defUndef(isel, payload_ty, .{});
}
- var error_set_part_it = error_union_vi.value.field(error_union_ty, error_set_offset, error_set_size);
- const error_set_part_vi = try error_set_part_it.only(isel);
- try error_set_part_vi.?.move(isel, ty_op.operand);
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@@ -5820,7 +5802,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
.struct_field_val => {
- if (isel.live_values.fetchRemove(air.inst_index)) |field_vi| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |field_vi| unused: {
defer field_vi.value.deref(isel);
const ty_pl = air.data(air.inst_index).ty_pl;
@@ -5847,27 +5829,55 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
});
const agg_vi = try isel.use(extra.struct_operand);
- var agg_part_it = agg_vi.field(agg_ty, @divExact(field_bit_offset, 8), @divExact(field_bit_size, 8));
- while (try agg_part_it.next(isel)) |agg_part| {
- var field_part_it = field_vi.value.field(ty_pl.ty.toType(), agg_part.offset, agg_part.vi.size(isel));
- const field_part_vi = try field_part_it.only(isel);
- if (field_part_vi.? == agg_part.vi) continue;
- var field_subpart_it = field_part_vi.?.parts(isel);
- const field_part_offset = if (field_subpart_it.only()) |field_subpart_vi|
- field_subpart_vi.get(isel).offset_from_parent
- else
- 0;
- while (field_subpart_it.next()) |field_subpart_vi| {
- const field_subpart_ra = try field_subpart_vi.defReg(isel) orelse continue;
- const field_subpart_offset, const field_subpart_size = field_subpart_vi.position(isel);
- var agg_subpart_it = agg_part.vi.field(
- field_ty,
- agg_part.offset + field_subpart_offset - field_part_offset,
- field_subpart_size,
- );
- const agg_subpart_vi = try agg_subpart_it.only(isel);
- try agg_subpart_vi.?.liveOut(isel, field_subpart_ra);
- }
+ switch (agg_ty.zigTypeTag(zcu)) {
+ else => unreachable,
+ .@"struct" => {
+ var agg_part_it = agg_vi.field(agg_ty, @divExact(field_bit_offset, 8), @divExact(field_bit_size, 8));
+ while (try agg_part_it.next(isel)) |agg_part| {
+ var field_part_it = field_vi.value.field(ty_pl.ty.toType(), agg_part.offset, agg_part.vi.size(isel));
+ const field_part_vi = try field_part_it.only(isel);
+ if (field_part_vi.? == agg_part.vi) continue;
+ var field_subpart_it = field_part_vi.?.parts(isel);
+ const field_part_offset = if (field_subpart_it.only()) |field_subpart_vi|
+ field_subpart_vi.get(isel).offset_from_parent
+ else
+ 0;
+ while (field_subpart_it.next()) |field_subpart_vi| {
+ const field_subpart_ra = try field_subpart_vi.defReg(isel) orelse continue;
+ const field_subpart_offset, const field_subpart_size = field_subpart_vi.position(isel);
+ var agg_subpart_it = agg_part.vi.field(
+ field_ty,
+ agg_part.offset + field_subpart_offset - field_part_offset,
+ field_subpart_size,
+ );
+ const agg_subpart_vi = try agg_subpart_it.only(isel);
+ try agg_subpart_vi.?.liveOut(isel, field_subpart_ra);
+ }
+ }
+ },
+ .@"union" => {
+ try field_vi.value.defAddr(isel, field_ty, .{}) orelse break :unused;
+
+ try call.prepareReturn(isel);
+ try call.finishReturn(isel);
+
+ try call.prepareCallee(isel);
+ try isel.global_relocs.append(gpa, .{
+ .name = "memcpy",
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+ try call.finishCallee(isel);
+
+ try call.prepareParams(isel);
+ const union_layout = agg_ty.unionGetLayout(zcu);
+ var payload_it = agg_vi.field(agg_ty, union_layout.payloadOffset(), union_layout.payload_size);
+ const payload_vi = try payload_it.only(isel);
+ try isel.movImmediate(.x2, field_vi.value.size(isel));
+ try call.paramAddress(isel, payload_vi.?, .r1);
+ try call.paramAddress(isel, field_vi.value, .r0);
+ try call.finishParams(isel);
+ },
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@@ -6899,16 +6909,45 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .union_init => {
- if (isel.live_values.fetchRemove(air.inst_index)) |un_vi| unused: {
- defer un_vi.value.deref(isel);
+ .union_init => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |union_vi| unused: {
+ defer union_vi.value.deref(isel);
const ty_pl = air.data(air.inst_index).ty_pl;
const extra = isel.air.extraData(Air.UnionInit, ty_pl.payload).data;
- const un_ty = ty_pl.ty.toType();
- if (un_ty.containerLayout(zcu) != .@"extern") return isel.fail("bad union init {f}", .{isel.fmtType(un_ty)});
+ const union_ty = ty_pl.ty.toType();
+ const loaded_union = ip.loadUnionType(union_ty.toIntern());
+ const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
- try un_vi.value.defAddr(isel, un_ty, null, comptime &.initFill(.free)) orelse break :unused;
+ if (union_layout.tag_size > 0) unused_tag: {
+ const loaded_tag = loaded_union.loadTagType(ip);
+ var tag_it = union_vi.value.field(union_ty, union_layout.tagOffset(), union_layout.tag_size);
+ const tag_vi = try tag_it.only(isel);
+ const tag_ra = try tag_vi.?.defReg(isel) orelse break :unused_tag;
+ switch (union_layout.tag_size) {
+ 0 => unreachable,
+ 1...4 => try isel.movImmediate(tag_ra.w(), @as(u32, switch (loaded_tag.values.len) {
+ 0 => extra.field_index,
+ else => switch (ip.indexToKey(loaded_tag.values.get(ip)[extra.field_index]).int.storage) {
+ .u64 => |imm| @intCast(imm),
+ .i64 => |imm| @bitCast(@as(i32, @intCast(imm))),
+ else => unreachable,
+ },
+ })),
+ 5...8 => try isel.movImmediate(tag_ra.x(), switch (loaded_tag.values.len) {
+ 0 => extra.field_index,
+ else => switch (ip.indexToKey(loaded_tag.values.get(ip)[extra.field_index]).int.storage) {
+ .u64 => |imm| imm,
+ .i64 => |imm| @bitCast(imm),
+ else => unreachable,
+ },
+ }),
+ else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(union_ty) }),
+ }
+ }
+ var payload_it = union_vi.value.field(union_ty, union_layout.payloadOffset(), union_layout.payload_size);
+ const payload_vi = try payload_it.only(isel);
+ try payload_vi.?.defAddr(isel, union_ty, .{ .root_vi = union_vi.value }) orelse break :unused;
try call.prepareReturn(isel);
try call.finishReturn(isel);
@@ -6925,7 +6964,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const init_vi = try isel.use(extra.init);
try isel.movImmediate(.x2, init_vi.size(isel));
try call.paramAddress(isel, init_vi, .r1);
- try call.paramAddress(isel, un_vi.value, .r0);
+ try call.paramAddress(isel, payload_vi.?, .r0);
try call.finishParams(isel);
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@@ -8944,9 +8983,18 @@ pub const Value = struct {
var dst_part_it = dst_vi.parts(isel);
if (dst_part_it.only()) |dst_part_vi| {
var src_part_it = src_vi.parts(isel);
- if (src_part_it.only()) |src_part_vi| {
- try src_part_vi.liveOut(isel, try dst_part_vi.defReg(isel) orelse return);
- } else while (src_part_it.next()) |src_part_vi| {
+ if (src_part_it.only()) |src_part_vi| only: {
+ const src_part_size = src_part_vi.size(isel);
+ if (src_part_size > @as(@TypeOf(src_part_size), if (src_part_vi.isVector(isel)) 16 else 8)) {
+ var subpart_it = root.src_vi.field(root.ty, root.src_offset, src_part_size - 1);
+ _ = try subpart_it.next(isel);
+ src_part_it = src_vi.parts(isel);
+ assert(src_part_it.only() == null);
+ break :only;
+ }
+ return src_part_vi.liveOut(isel, try dst_part_vi.defReg(isel) orelse return);
+ }
+ while (src_part_it.next()) |src_part_vi| {
const src_part_offset, const src_part_size = src_part_vi.position(isel);
var dst_field_it = root.dst_vi.field(root.ty, root.dst_offset + src_part_offset, src_part_size);
const dst_field_vi = try dst_field_it.only(isel);
@@ -9420,9 +9468,12 @@ pub const Value = struct {
fn defAddr(
def_vi: Value.Index,
isel: *Select,
- def_ty: ZigType,
- wrap: ?std.builtin.Type.Int,
- expected_live_registers: *const LiveRegisters,
+ root_ty: ZigType,
+ opts: struct {
+ root_vi: Value.Index = .free,
+ wrap: ?std.builtin.Type.Int = null,
+ expected_live_registers: *const LiveRegisters = &.initFill(.free),
+ },
) !?void {
if (!def_vi.isUsed(isel)) return null;
const offset_from_parent: i65, const parent_vi = def_vi.valueParent(isel);
@@ -9431,11 +9482,12 @@ pub const Value = struct {
.stack_slot => |stack_slot| .{ stack_slot, false },
else => unreachable,
};
- _ = try def_vi.load(isel, def_ty, stack_slot.base, .{
+ _ = try def_vi.load(isel, root_ty, stack_slot.base, .{
+ .root_vi = opts.root_vi,
.offset = @intCast(stack_slot.offset + offset_from_parent),
.split = false,
- .wrap = wrap,
- .expected_live_registers = expected_live_registers,
+ .wrap = opts.wrap,
+ .expected_live_registers = opts.expected_live_registers,
});
if (allocated) parent_vi.setParent(isel, .{ .stack_slot = stack_slot });
}
@@ -9514,6 +9566,53 @@ pub const Value = struct {
}
}
+ pub fn defUndef(def_vi: Value.Index, isel: *Select, root_ty: ZigType, opts: struct {
+ root_vi: Value.Index = .free,
+ offset: u64 = 0,
+ split: bool = true,
+ }) !void {
+ const root_vi = switch (opts.root_vi) {
+ _ => |root_vi| root_vi,
+ .allocating => unreachable,
+ .free => def_vi,
+ };
+ var part_it = def_vi.parts(isel);
+ if (part_it.only()) |part_vi| only: {
+ const part_size = part_vi.size(isel);
+ const part_is_vector = part_vi.isVector(isel);
+ if (part_size > @as(@TypeOf(part_size), if (part_is_vector) 16 else 8)) {
+ if (!opts.split) return;
+ var subpart_it = root_vi.field(root_ty, opts.offset, part_size - 1);
+ _ = try subpart_it.next(isel);
+ part_it = def_vi.parts(isel);
+ assert(part_it.only() == null);
+ break :only;
+ }
+ return if (try part_vi.defReg(isel)) |part_ra| try isel.emit(if (part_is_vector)
+ .movi(switch (part_size) {
+ else => unreachable,
+ 1...8 => part_ra.@"8b"(),
+ 9...16 => part_ra.@"16b"(),
+ }, 0xaa, .{ .lsl = 0 })
+ else switch (part_size) {
+ else => unreachable,
+ 1...4 => .orr(part_ra.w(), .wzr, .{ .immediate = .{
+ .N = .word,
+ .immr = 0b000001,
+ .imms = 0b111100,
+ } }),
+ 5...8 => .orr(part_ra.x(), .xzr, .{ .immediate = .{
+ .N = .word,
+ .immr = 0b000001,
+ .imms = 0b111100,
+ } }),
+ });
+ }
+ while (part_it.next()) |part_vi| try part_vi.defUndef(isel, root_ty, .{
+ .root_vi = root_vi,
+ });
+ }
+
pub fn liveIn(
vi: Value.Index,
isel: *Select,
@@ -9846,24 +9945,31 @@ pub const Value = struct {
_ = vi.addPart(isel, 8, 8);
} else unreachable,
},
- .opt_type => |child_type| if (ty.optionalReprIsPayload(zcu))
- continue :type_key ip.indexToKey(child_type)
- else switch (ZigType.fromInterned(child_type).abiSize(zcu)) {
- 0...8, 16 => |child_size| if (offset == 0 and size == ty_size) {
- vi.setParts(isel, 2);
- _ = vi.addPart(isel, 0, child_size);
- _ = vi.addPart(isel, child_size, 1);
- } else unreachable,
- 9...15 => |child_size| if (offset == 0 and size == ty_size) {
- vi.setParts(isel, 2);
- _ = vi.addPart(isel, 0, 8);
- _ = vi.addPart(isel, 8, ty_size - 8);
- } else if (offset == 8 and size == ty_size - 8) {
- vi.setParts(isel, 2);
- _ = vi.addPart(isel, 0, child_size - 8);
- _ = vi.addPart(isel, child_size - 8, 1);
- } else unreachable,
- else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}),
+ .opt_type => |child_type| if (ty.optionalReprIsPayload(zcu)) continue :type_key ip.indexToKey(child_type) else {
+ const child_ty: ZigType = .fromInterned(child_type);
+ const child_size = child_ty.abiSize(zcu);
+ if (offset == 0 and size == child_size) {
+ ty = child_ty;
+ ty_size = child_size;
+ continue :type_key ip.indexToKey(child_type);
+ }
+ switch (child_size) {
+ 0...8, 16 => if (offset == 0 and size == ty_size) {
+ vi.setParts(isel, 2);
+ _ = vi.addPart(isel, 0, child_size);
+ _ = vi.addPart(isel, child_size, 1);
+ } else unreachable,
+ 9...15 => if (offset == 0 and size == ty_size) {
+ vi.setParts(isel, 2);
+ _ = vi.addPart(isel, 0, 8);
+ _ = vi.addPart(isel, 8, ty_size - 8);
+ } else if (offset == 8 and size == ty_size - 8) {
+ vi.setParts(isel, 2);
+ _ = vi.addPart(isel, 0, child_size - 8);
+ _ = vi.addPart(isel, child_size - 8, 1);
+ } else unreachable,
+ else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}),
+ }
},
.array_type => |array_type| {
const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
diff --git a/test/behavior/array.zig b/test/behavior/array.zig
index 76dcc8075d597f0954a6d0e1c03c6a43f19a66f6..20c275382fbb2ffb3234a10292581a3539a35963 100644
--- a/test/behavior/array.zig
+++ b/test/behavior/array.zig
@@ -395,7 +395,6 @@ test "array literal as argument to function" {
}
test "double nested array to const slice cast in array literal" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -650,7 +649,6 @@ test "runtime initialized sentinel-terminated array literal" {
}
test "array of array agregate init" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig
index 72a8badc1d1d81d196e9c8595261a5143ff5597b..05f74eff32afd1536eb344fdf073747b5583a157 100644
--- a/test/behavior/defer.zig
+++ b/test/behavior/defer.zig
@@ -107,7 +107,6 @@ test "mixing normal and error defers" {
}
test "errdefer with payload" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -129,7 +128,6 @@ test "errdefer with payload" {
}
test "reference to errdefer payload" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig
index 11d4ee053701cc01c55ff75b53134fd787293f96..d8d26d6a0fc6657ec43dc66b5f61a7488d28a233 100644
--- a/test/behavior/optional.zig
+++ b/test/behavior/optional.zig
@@ -390,7 +390,6 @@ test "0-bit child type coerced to optional" {
}
test "array of optional unaligned types" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig
index 2a786ecdb6c1ab6b5734e14d777cf296812ba531..fbc74c1718cc7963d790b28092e6f11453bc2ae8 100644
--- a/test/behavior/pointers.zig
+++ b/test/behavior/pointers.zig
@@ -246,7 +246,6 @@ test "implicit casting between C pointer and optional non-C pointer" {
}
test "implicit cast error unions with non-optional to optional pointer" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig
index d58cf79276b1be48a78291358c5bd8b8bd943e10..25b501e8411dab05b51f6c3341d9806a22587c68 100644
--- a/test/behavior/slice.zig
+++ b/test/behavior/slice.zig
@@ -710,7 +710,6 @@ test "slice pointer-to-array zero length" {
}
test "type coercion of pointer to anon struct literal to pointer to slice" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig
index 3c4c4d7f80957904163706faf6ed0b56f1f821ba..2c2432afd9a328c8dcd1787cad99cdf12261b607 100644
--- a/test/behavior/struct.zig
+++ b/test/behavior/struct.zig
@@ -955,7 +955,6 @@ test "tuple element initialized with fn call" {
}
test "struct with union field" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
diff --git a/test/behavior/struct_contains_slice_of_itself.zig b/test/behavior/struct_contains_slice_of_itself.zig
index a54049d123ffb00bc47ade969b71829ee90abffa..5cf8d8134a1cc91abbc392c0acf7c1765f3e50bc 100644
--- a/test/behavior/struct_contains_slice_of_itself.zig
+++ b/test/behavior/struct_contains_slice_of_itself.zig
@@ -12,7 +12,6 @@ const NodeAligned = struct {
};
test "struct contains slice of itself" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -53,7 +52,6 @@ test "struct contains slice of itself" {
}
test "struct contains aligned slice of itself" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig
index 0d0cb0e4f4a31d49ebd240c7271bb8d21fc40356..afc38661c313f59a91d32d66dca4e0ea283b5d84 100644
--- a/test/behavior/switch.zig
+++ b/test/behavior/switch.zig
@@ -466,7 +466,6 @@ test "switch on integer with else capturing expr" {
}
test "else prong of switch on error set excludes other cases" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -502,7 +501,6 @@ test "else prong of switch on error set excludes other cases" {
}
test "switch prongs with error set cases make a new error set type for capture value" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -733,7 +731,6 @@ test "switch on error set with single else" {
}
test "switch capture copies its payload" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -928,7 +925,6 @@ test "nested break ignores switch conditions and breaks instead" {
}
test "peer type resolution on switch captures ignores unused payload bits" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
diff --git a/test/behavior/switch_prong_err_enum.zig b/test/behavior/switch_prong_err_enum.zig
index 6dbc76505ca9407dc4c6fc9027a065cf408663c4..a2eed86c0f6e30db3f16182e31bdf2529cce3078 100644
--- a/test/behavior/switch_prong_err_enum.zig
+++ b/test/behavior/switch_prong_err_enum.zig
@@ -21,7 +21,6 @@ fn doThing(form_id: u64) anyerror!FormValue {
}
test "switch prong returns error enum" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
diff --git a/test/behavior/try.zig b/test/behavior/try.zig
index b3014ef669514e2efdaf84cd77b3cfcc0af14054..fd120fc86f6b56b24a034ad5a61747b1a26ec258 100644
--- a/test/behavior/try.zig
+++ b/test/behavior/try.zig
@@ -47,7 +47,6 @@ test "try then not executed with assignment" {
}
test "`try`ing an if/else expression" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
diff --git a/test/behavior/union.zig b/test/behavior/union.zig
index fb05b9edbb0acee15e9e5622f2f9fdef82ce298f..186ae5659378d7bdc0acc0b071da6ba47f945814 100644
--- a/test/behavior/union.zig
+++ b/test/behavior/union.zig
@@ -12,7 +12,6 @@ const FooWithFloats = union {
};
test "basic unions with floats" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -29,7 +28,6 @@ fn setFloat(foo: *FooWithFloats, x: f64) void {
}
test "init union with runtime value - floats" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -41,7 +39,6 @@ test "init union with runtime value - floats" {
}
test "basic unions" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -60,7 +57,6 @@ const Foo = union {
};
test "init union with runtime value" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -101,7 +97,6 @@ const FooExtern = extern union {
};
test "basic extern unions" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -332,7 +327,6 @@ pub const PackThis = union(enum) {
};
test "constant packed union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -478,7 +472,6 @@ pub const FooUnion = union(enum) {
var glbl_array: [2]FooUnion = undefined;
test "initialize global array of union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -810,7 +803,6 @@ test "return union init with void payload" {
}
test "@unionInit stored to a const" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -917,7 +909,6 @@ test "extern union doesn't trigger field check at comptime" {
}
test "anonymous union literal syntax" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1086,7 +1077,6 @@ test "union enum type gets a separate scope" {
}
test "global variable struct contains union initialized to non-most-aligned field" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1113,7 +1103,6 @@ test "global variable struct contains union initialized to non-most-aligned fiel
}
test "union with no result loc initiated with a runtime value" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1131,7 +1120,6 @@ test "union with no result loc initiated with a runtime value" {
}
test "union with a large struct field" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1286,7 +1274,6 @@ test "noreturn field in union" {
}
test "@unionInit uses tag value instead of field index" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1395,7 +1382,6 @@ test "union int tag type is properly managed" {
}
test "no dependency loop when function pointer in union returns the union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1417,7 +1403,6 @@ test "no dependency loop when function pointer in union returns the union" {
}
test "union reassignment can use previous value" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1619,7 +1604,6 @@ test "union with 128 bit integer" {
}
test "memset extern union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
const U = extern union {
@@ -1941,7 +1925,6 @@ test "packed union initialized via reintepreted struct field initializer" {
}
test "store of comptime reinterpreted memory to extern union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
@@ -2048,7 +2031,6 @@ test "circular dependency through pointer field of a union" {
}
test "pass nested union with rls" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -2070,7 +2052,6 @@ test "pass nested union with rls" {
}
test "runtime union init, most-aligned field != largest" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -2239,7 +2220,6 @@ test "matching captures causes union equivalence" {
}
test "signed enum tag with negative value" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -2296,7 +2276,6 @@ test "extern union @FieldType" {
}
test "assign global tagged union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
const U = union(enum) {
--
2.54.0
From 147a852806a5c20680d8be4ec63bea9f972feee6 Mon Sep 17 00:00:00 2001
From: Silver
Date: Mon, 28 Jul 2025 16:33:23 +0100
Subject: [PATCH 031/110] Update `zig init` help with new `-m` arg
This was forgotten in #24555
---
src/main.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main.zig b/src/main.zig
index a0a40ae093456f54905b895207b4087854505723..68fbd5b0a8f0b8957225663502d8ab143444197c 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -4639,7 +4639,7 @@ const usage_init =
\\ directory.
\\
\\Options:
- \\ -s, --strip Generate files without comments
+ \\ -m, --minimal Use minimal init template
\\ -h, --help Print this help and exit
\\
\\
--
2.54.0
From ecd3ea9bd2f543f44813da21fd6b77d53dd72d7c Mon Sep 17 00:00:00 2001
From: Kendall Condon
Date: Mon, 28 Jul 2025 19:13:22 -0400
Subject: [PATCH 032/110] DeprecatedReader.Adapted: fix EndOfStream handling
---
lib/std/Io.zig | 1 +
lib/std/Io/DeprecatedReader.zig | 1 +
lib/std/Io/test.zig | 8 ++++++++
3 files changed, 10 insertions(+)
diff --git a/lib/std/Io.zig b/lib/std/Io.zig
index 1511f0dcadd06f9748f06151f36954a2ed0dadba..b90276cfabe76017d27e643aaa00b9515f35b963 100644
--- a/lib/std/Io.zig
+++ b/lib/std/Io.zig
@@ -333,6 +333,7 @@ pub fn GenericReader(
a.err = err;
return error.ReadFailed;
};
+ if (n == 0) return error.EndOfStream;
w.advance(n);
return n;
}
diff --git a/lib/std/Io/DeprecatedReader.zig b/lib/std/Io/DeprecatedReader.zig
index af1eda84153e9a5a5131e61bab8a98e2b504d7cf..59f163b39ca9d7cfccf608119f58d2458e689c44 100644
--- a/lib/std/Io/DeprecatedReader.zig
+++ b/lib/std/Io/DeprecatedReader.zig
@@ -397,6 +397,7 @@ pub const Adapter = struct {
a.err = err;
return error.ReadFailed;
};
+ if (n == 0) return error.EndOfStream;
w.advance(n);
return n;
}
diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig
index bf14f0c24ca15447283e531db5f125680a9767d0..c08879316e4bd600e8e2f4706908c7839132bc6d 100644
--- a/lib/std/Io/test.zig
+++ b/lib/std/Io/test.zig
@@ -180,3 +180,11 @@ test "GenericReader methods can return error.EndOfStream" {
fbs.reader().isBytes("foo"),
);
}
+
+test "Adapted DeprecatedReader EndOfStream" {
+ var fbs: io.FixedBufferStream([]const u8) = .{ .buffer = &.{}, .pos = 0 };
+ const reader = fbs.reader();
+ var buf: [1]u8 = undefined;
+ var adapted = reader.adaptToNewApi(&buf);
+ try std.testing.expectError(error.EndOfStream, adapted.new_interface.takeByte());
+}
--
2.54.0
From 3fbdd58a874c6b4dae84bed2ed31c945ff4adb54 Mon Sep 17 00:00:00 2001
From: Jacob Young
Date: Mon, 28 Jul 2025 13:03:16 -0400
Subject: [PATCH 033/110] aarch64: implement scalar `@mod`
---
src/codegen/aarch64/Select.zig | 274 +-
src/codegen/aarch64/encoding.zig | 4360 +++++++++++++++++++++++++++---
test/behavior/math.zig | 3 -
3 files changed, 4188 insertions(+), 449 deletions(-)
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
index 0ebe451ebb689f843ec59a11e1fcd61291ae3a1e..13c001a200fb540f49174ddd0e63f315e7a2b9cf 100644
--- a/src/codegen/aarch64/Select.zig
+++ b/src/codegen/aarch64/Select.zig
@@ -1919,8 +1919,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
switch (bits) {
else => unreachable,
1...32 => {
- try isel.emit(.sub(res_ra.w(), div_ra.w(), .{ .register = rem_ra.w() }));
- try isel.emit(.csinc(rem_ra.w(), .wzr, .wzr, .ge));
+ try isel.emit(.csel(res_ra.w(), div_ra.w(), rem_ra.w(), .pl));
+ try isel.emit(.sub(rem_ra.w(), div_ra.w(), .{ .immediate = 1 }));
try isel.emit(.ccmp(
rem_ra.w(),
.{ .immediate = 0 },
@@ -1932,8 +1932,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try isel.emit(.msub(rem_ra.w(), div_ra.w(), rhs_mat.ra.w(), lhs_mat.ra.w()));
},
33...64 => {
- try isel.emit(.sub(res_ra.x(), div_ra.x(), .{ .register = rem_ra.x() }));
- try isel.emit(.csinc(rem_ra.x(), .xzr, .xzr, .ge));
+ try isel.emit(.csel(res_ra.x(), div_ra.x(), rem_ra.x(), .pl));
+ try isel.emit(.sub(rem_ra.x(), div_ra.x(), .{ .immediate = 1 }));
try isel.emit(.ccmp(
rem_ra.x(),
.{ .immediate = 0 },
@@ -2162,7 +2162,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
- .rem => |air_tag| {
+ .rem, .rem_optimized, .mod, .mod_optimized => |air_tag| {
if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
defer res_vi.value.deref(isel);
@@ -2180,17 +2180,57 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const rhs_mat = try rhs_vi.matReg(isel);
const div_ra = try isel.allocIntReg();
defer isel.freeReg(div_ra);
+ const rem_ra = rem_ra: switch (air_tag) {
+ else => unreachable,
+ .rem => res_ra,
+ .mod => switch (int_info.signedness) {
+ .signed => {
+ const rem_ra = try isel.allocIntReg();
+ errdefer isel.freeReg(rem_ra);
+ switch (int_info.bits) {
+ else => unreachable,
+ 1...32 => {
+ try isel.emit(.csel(res_ra.w(), rem_ra.w(), div_ra.w(), .pl));
+ try isel.emit(.add(div_ra.w(), rem_ra.w(), .{ .register = rhs_mat.ra.w() }));
+ try isel.emit(.ccmp(
+ div_ra.w(),
+ .{ .immediate = 0 },
+ .{ .n = false, .z = false, .c = false, .v = false },
+ .ne,
+ ));
+ try isel.emit(.eor(div_ra.w(), rem_ra.w(), .{ .register = rhs_mat.ra.w() }));
+ try isel.emit(.subs(.wzr, rem_ra.w(), .{ .immediate = 0 }));
+ },
+ 33...64 => {
+ try isel.emit(.csel(res_ra.x(), rem_ra.x(), div_ra.x(), .pl));
+ try isel.emit(.add(div_ra.x(), rem_ra.x(), .{ .register = rhs_mat.ra.x() }));
+ try isel.emit(.ccmp(
+ div_ra.x(),
+ .{ .immediate = 0 },
+ .{ .n = false, .z = false, .c = false, .v = false },
+ .ne,
+ ));
+ try isel.emit(.eor(div_ra.x(), rem_ra.x(), .{ .register = rhs_mat.ra.x() }));
+ try isel.emit(.subs(.xzr, rem_ra.x(), .{ .immediate = 0 }));
+ },
+ }
+ break :rem_ra rem_ra;
+ },
+ .unsigned => res_ra,
+ },
+ };
+ defer if (rem_ra != res_ra) isel.freeReg(rem_ra);
switch (int_info.bits) {
else => unreachable,
1...32 => {
- try isel.emit(.msub(res_ra.w(), div_ra.w(), rhs_mat.ra.w(), lhs_mat.ra.w()));
+ try isel.emit(.msub(rem_ra.w(), div_ra.w(), rhs_mat.ra.w(), lhs_mat.ra.w()));
try isel.emit(switch (int_info.signedness) {
.signed => .sdiv(div_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
.unsigned => .udiv(div_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
});
},
33...64 => {
- try isel.emit(.msub(res_ra.x(), div_ra.x(), rhs_mat.ra.x(), lhs_mat.ra.x()));
+ try isel.emit(.msub(rem_ra.x(), div_ra.x(), rhs_mat.ra.x(), lhs_mat.ra.x()));
try isel.emit(switch (int_info.signedness) {
.signed => .sdiv(div_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
.unsigned => .udiv(div_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
@@ -2201,21 +2241,184 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try lhs_mat.finish(isel);
} else {
const bits = ty.floatBits(isel.target);
-
- try call.prepareReturn(isel);
- switch (bits) {
+ switch (air_tag) {
else => unreachable,
- 16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
- 80 => {
- var res_hi16_it = res_vi.value.field(ty, 8, 8);
- const res_hi16_vi = try res_hi16_it.only(isel);
- try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
- var res_lo64_it = res_vi.value.field(ty, 0, 8);
- const res_lo64_vi = try res_lo64_it.only(isel);
- try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
+ .rem, .rem_optimized => {
+ if (!res_vi.value.isUsed(isel)) break :unused;
+ try call.prepareReturn(isel);
+ switch (bits) {
+ else => unreachable,
+ 16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
+ 80 => {
+ var res_hi16_it = res_vi.value.field(ty, 8, 8);
+ const res_hi16_vi = try res_hi16_it.only(isel);
+ try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
+ var res_lo64_it = res_vi.value.field(ty, 0, 8);
+ const res_lo64_vi = try res_lo64_it.only(isel);
+ try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
+ },
+ }
+ try call.finishReturn(isel);
+ },
+ .mod, .mod_optimized => switch (bits) {
+ else => unreachable,
+ 16, 32, 64 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ try call.prepareReturn(isel);
+ const rem_ra: Register.Alias = .v0;
+ const temp1_ra: Register.Alias = .v1;
+ const temp2_ra: Register.Alias = switch (res_ra) {
+ rem_ra, temp1_ra => .v2,
+ else => res_ra,
+ };
+ const need_fcvt = switch (bits) {
+ else => unreachable,
+ 16 => !isel.target.cpu.has(.aarch64, .fullfp16),
+ 32, 64 => false,
+ };
+ if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
+ try isel.emit(switch (res_ra) {
+ rem_ra => .bif(res_ra.@"8b"(), temp2_ra.@"8b"(), temp1_ra.@"8b"()),
+ temp1_ra => .bsl(res_ra.@"8b"(), rem_ra.@"8b"(), temp2_ra.@"8b"()),
+ else => .bit(res_ra.@"8b"(), rem_ra.@"8b"(), temp1_ra.@"8b"()),
+ });
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(bits: switch (bits) {
+ else => unreachable,
+ 16 => if (need_fcvt)
+ continue :bits 32
+ else
+ .fadd(temp2_ra.h(), rem_ra.h(), rhs_mat.ra.h()),
+ 32 => .fadd(temp2_ra.s(), rem_ra.s(), rhs_mat.ra.s()),
+ 64 => .fadd(temp2_ra.d(), rem_ra.d(), rhs_mat.ra.d()),
+ });
+ if (need_fcvt) {
+ try isel.emit(.fcvt(rhs_mat.ra.s(), rhs_mat.ra.h()));
+ try isel.emit(.fcvt(rem_ra.s(), rem_ra.h()));
+ }
+ try isel.emit(.orr(temp1_ra.@"8b"(), temp1_ra.@"8b"(), .{
+ .register = temp2_ra.@"8b"(),
+ }));
+ try isel.emit(switch (bits) {
+ else => unreachable,
+ 16 => .cmge(temp1_ra.@"4h"(), temp1_ra.@"4h"(), .zero),
+ 32 => .cmge(temp1_ra.@"2s"(), temp1_ra.@"2s"(), .zero),
+ 64 => .cmge(temp1_ra.d(), temp1_ra.d(), .zero),
+ });
+ try isel.emit(switch (bits) {
+ else => unreachable,
+ 16 => .fcmeq(temp2_ra.h(), rem_ra.h(), .zero),
+ 32 => .fcmeq(temp2_ra.s(), rem_ra.s(), .zero),
+ 64 => .fcmeq(temp2_ra.d(), rem_ra.d(), .zero),
+ });
+ try isel.emit(.eor(temp1_ra.@"8b"(), rem_ra.@"8b"(), .{
+ .register = rhs_mat.ra.@"8b"(),
+ }));
+ try rhs_mat.finish(isel);
+ try call.finishReturn(isel);
+ },
+ 80, 128 => {
+ if (!res_vi.value.isUsed(isel)) break :unused;
+ try call.prepareReturn(isel);
+ switch (bits) {
+ else => unreachable,
+ 16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
+ 80 => {
+ var res_hi16_it = res_vi.value.field(ty, 8, 8);
+ const res_hi16_vi = try res_hi16_it.only(isel);
+ try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
+ var res_lo64_it = res_vi.value.field(ty, 0, 8);
+ const res_lo64_vi = try res_lo64_it.only(isel);
+ try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
+ },
+ }
+ const skip_label = isel.instructions.items.len;
+ try isel.global_relocs.append(gpa, .{
+ .name = switch (bits) {
+ else => unreachable,
+ 16 => "__addhf3",
+ 32 => "__addsf3",
+ 64 => "__adddf3",
+ 80 => "__addxf3",
+ 128 => "__addtf3",
+ },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+ const rhs_vi = try isel.use(bin_op.rhs);
+ switch (bits) {
+ else => unreachable,
+ 80 => {
+ const lhs_lo64_ra: Register.Alias = .r0;
+ const lhs_hi16_ra: Register.Alias = .r1;
+ const rhs_lo64_ra: Register.Alias = .r2;
+ const rhs_hi16_ra: Register.Alias = .r3;
+ const temp_ra: Register.Alias = .r4;
+ var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
+ const rhs_hi16_vi = try rhs_hi16_it.only(isel);
+ try call.paramLiveOut(isel, rhs_hi16_vi.?, rhs_hi16_ra);
+ var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
+ const rhs_lo64_vi = try rhs_lo64_it.only(isel);
+ try call.paramLiveOut(isel, rhs_lo64_vi.?, rhs_lo64_ra);
+ try isel.emit(.cbz(
+ temp_ra.x(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.orr(temp_ra.x(), lhs_lo64_ra.x(), .{ .shifted_register = .{
+ .register = lhs_hi16_ra.x(),
+ .shift = .{ .lsl = 64 - 15 },
+ } }));
+ try isel.emit(.tbz(
+ temp_ra.w(),
+ 15,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.eor(temp_ra.w(), lhs_hi16_ra.w(), .{
+ .register = rhs_hi16_ra.w(),
+ }));
+ },
+ 128 => {
+ const lhs_ra: Register.Alias = .v0;
+ const rhs_ra: Register.Alias = .v1;
+ const temp1_ra: Register.Alias = .r0;
+ const temp2_ra: Register.Alias = .r1;
+ try call.paramLiveOut(isel, rhs_vi, rhs_ra);
+ try isel.emit(.@"b."(
+ .pl,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.cbz(
+ temp1_ra.x(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.orr(temp1_ra.x(), temp1_ra.x(), .{ .shifted_register = .{
+ .register = temp2_ra.x(),
+ .shift = .{ .lsl = 1 },
+ } }));
+ try isel.emit(.fmov(temp1_ra.x(), .{
+ .register = rhs_ra.d(),
+ }));
+ try isel.emit(.tbz(
+ temp1_ra.x(),
+ 63,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.eor(temp1_ra.x(), temp1_ra.x(), .{
+ .register = temp2_ra.x(),
+ }));
+ try isel.emit(.fmov(temp2_ra.x(), .{
+ .register = rhs_ra.@"d[]"(1),
+ }));
+ try isel.emit(.fmov(temp1_ra.x(), .{
+ .register = lhs_ra.@"d[]"(1),
+ }));
+ },
+ }
+ try call.finishReturn(isel);
+ },
},
}
- try call.finishReturn(isel);
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
@@ -9517,12 +9720,12 @@ pub const Value = struct {
const part_mat = try part_vi.matReg(isel);
try isel.emit(if (part_vi.isVector(isel)) emit: {
assert(part_offset == 0 and part_size == vi_size);
- break :emit size: switch (vi_size) {
+ break :emit switch (vi_size) {
else => unreachable,
2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
.fmov(ra.h(), .{ .register = part_mat.ra.h() })
else
- continue :size 4,
+ .dup(ra.h(), part_mat.ra.@"h[]"(0)),
4 => .fmov(ra.s(), .{ .register = part_mat.ra.s() }),
8 => .fmov(ra.d(), .{ .register = part_mat.ra.d() }),
16 => .orr(ra.@"16b"(), part_mat.ra.@"16b"(), .{ .register = part_mat.ra.@"16b"() }),
@@ -9642,21 +9845,30 @@ pub const Value = struct {
},
true => switch (vi.size(isel)) {
else => unreachable,
- 2 => .fmov(dst_ra.w(), .{ .register = src_ra.h() }),
+ 2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
+ .fmov(dst_ra.w(), .{ .register = src_ra.h() })
+ else
+ .umov(dst_ra.w(), src_ra.@"h[]"(0)),
4 => .fmov(dst_ra.w(), .{ .register = src_ra.s() }),
8 => .fmov(dst_ra.x(), .{ .register = src_ra.d() }),
},
},
true => switch (src_ra.isVector()) {
- false => switch (vi.size(isel)) {
+ false => size: switch (vi.size(isel)) {
else => unreachable,
- 2 => .fmov(dst_ra.h(), .{ .register = src_ra.w() }),
+ 2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
+ .fmov(dst_ra.h(), .{ .register = src_ra.w() })
+ else
+ continue :size 4,
4 => .fmov(dst_ra.s(), .{ .register = src_ra.w() }),
8 => .fmov(dst_ra.d(), .{ .register = src_ra.x() }),
},
true => switch (vi.size(isel)) {
else => unreachable,
- 2 => .fmov(dst_ra.h(), .{ .register = src_ra.h() }),
+ 2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
+ .fmov(dst_ra.h(), .{ .register = src_ra.h() })
+ else
+ .dup(dst_ra.h(), src_ra.@"h[]"(0)),
4 => .fmov(dst_ra.s(), .{ .register = src_ra.s() }),
8 => .fmov(dst_ra.d(), .{ .register = src_ra.d() }),
16 => .orr(dst_ra.@"16b"(), src_ra.@"16b"(), .{ .register = src_ra.@"16b"() }),
@@ -9713,9 +9925,12 @@ pub const Value = struct {
const part_size = part_vi.size(isel);
const part_ra = if (part_vi.isVector(isel)) try isel.allocIntReg() else dst_ra;
defer if (part_ra != dst_ra) isel.freeReg(part_ra);
- if (part_ra != dst_ra) try isel.emit(switch (part_size) {
+ if (part_ra != dst_ra) try isel.emit(part_size: switch (part_size) {
else => unreachable,
- 2 => .fmov(dst_ra.h(), .{ .register = part_ra.w() }),
+ 2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
+ .fmov(dst_ra.h(), .{ .register = part_ra.w() })
+ else
+ continue :part_size 4,
4 => .fmov(dst_ra.s(), .{ .register = part_ra.w() }),
8 => .fmov(dst_ra.d(), .{ .register = part_ra.x() }),
});
@@ -10360,7 +10575,10 @@ pub const Value = struct {
if (vi.register(isel)) |ra| {
if (ra != mat.ra) break :free try isel.emit(if (vi == mat.vi) if (mat.ra.isVector()) switch (size) {
else => unreachable,
- 2 => .fmov(mat.ra.h(), .{ .register = ra.h() }),
+ 2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
+ .fmov(mat.ra.h(), .{ .register = ra.h() })
+ else
+ .dup(mat.ra.h(), ra.@"h[]"(0)),
4 => .fmov(mat.ra.s(), .{ .register = ra.s() }),
8 => .fmov(mat.ra.d(), .{ .register = ra.d() }),
16 => .orr(mat.ra.@"16b"(), ra.@"16b"(), .{ .register = ra.@"16b"() }),
diff --git a/src/codegen/aarch64/encoding.zig b/src/codegen/aarch64/encoding.zig
index 6ddf46b625ca2fbc51d47b0a5e99d4c7deb1d925..42d38d2ea48301e5a9f226c8b6e7b60ad619802f 100644
--- a/src/codegen/aarch64/encoding.zig
+++ b/src/codegen/aarch64/encoding.zig
@@ -81,6 +81,14 @@ pub const Register = struct {
.@"16b", .@"8b" => .byte,
};
}
+
+ pub fn elemSz(arrangement: Arrangement) Instruction.DataProcessingVector.Sz {
+ return switch (arrangement) {
+ else => unreachable,
+ .@"2d", .@"1d" => .double,
+ .@"4s", .@"2s" => .single,
+ };
+ }
};
pub const x0: Register = .{ .alias = .r0, .format = .{ .integer = .doubleword } };
@@ -6817,7 +6825,7 @@ pub const Instruction = packed union {
o0: AddSubtractOp = .add,
Rm: Register.Encoded,
op21: u2 = 0b01,
- U: bool = false,
+ U: std.builtin.Signedness = .signed,
decoded24: u5 = 0b11011,
op54: u2 = 0b00,
sf: Register.IntegerSize = .doubleword,
@@ -6831,7 +6839,7 @@ pub const Instruction = packed union {
o0: AddSubtractOp = .sub,
Rm: Register.Encoded,
op21: u2 = 0b01,
- U: bool = false,
+ U: std.builtin.Signedness = .signed,
decoded24: u5 = 0b11011,
op54: u2 = 0b00,
sf: Register.IntegerSize = .doubleword,
@@ -6845,7 +6853,7 @@ pub const Instruction = packed union {
o0: AddSubtractOp = .add,
Rm: Register.Encoded,
op21: u2 = 0b10,
- U: bool = false,
+ U: std.builtin.Signedness = .signed,
decoded24: u5 = 0b11011,
op54: u2 = 0b00,
sf: Register.IntegerSize = .doubleword,
@@ -6859,7 +6867,7 @@ pub const Instruction = packed union {
o0: AddSubtractOp = .add,
Rm: Register.Encoded,
op21: u2 = 0b01,
- U: bool = true,
+ U: std.builtin.Signedness = .unsigned,
decoded24: u5 = 0b11011,
op54: u2 = 0b00,
sf: Register.IntegerSize = .doubleword,
@@ -6873,7 +6881,7 @@ pub const Instruction = packed union {
o0: AddSubtractOp = .sub,
Rm: Register.Encoded,
op21: u2 = 0b01,
- U: bool = true,
+ U: std.builtin.Signedness = .unsigned,
decoded24: u5 = 0b11011,
op54: u2 = 0b00,
sf: Register.IntegerSize = .doubleword,
@@ -6887,7 +6895,7 @@ pub const Instruction = packed union {
o0: AddSubtractOp = .add,
Rm: Register.Encoded,
op21: u2 = 0b10,
- U: bool = true,
+ U: std.builtin.Signedness = .unsigned,
decoded24: u5 = 0b11011,
op54: u2 = 0b00,
sf: Register.IntegerSize = .doubleword,
@@ -7009,8 +7017,12 @@ pub const Instruction = packed union {
/// C4.1.90 Data Processing -- Scalar Floating-Point and Advanced SIMD
pub const DataProcessingVector = packed union {
group: @This().Group,
+ simd_scalar_copy: SimdScalarCopy,
+ simd_scalar_two_register_miscellaneous_fp16: SimdScalarTwoRegisterMiscellaneousFp16,
+ simd_scalar_two_register_miscellaneous: SimdScalarTwoRegisterMiscellaneous,
simd_scalar_pairwise: SimdScalarPairwise,
simd_copy: SimdCopy,
+ simd_two_register_miscellaneous_fp16: SimdTwoRegisterMiscellaneousFp16,
simd_two_register_miscellaneous: SimdTwoRegisterMiscellaneous,
simd_across_lanes: SimdAcrossLanes,
simd_three_same: SimdThreeSame,
@@ -7020,6 +7032,7 @@ pub const Instruction = packed union {
float_compare: FloatCompare,
float_immediate: FloatImmediate,
float_data_processing_two_source: FloatDataProcessingTwoSource,
+ float_conditional_select: FloatConditionalSelect,
float_data_processing_three_source: FloatDataProcessingThreeSource,
/// Table C4-91 Encoding table for the Data Processing -- Scalar Floating-Point and Advanced SIMD group
@@ -7032,6 +7045,876 @@ pub const Instruction = packed union {
op0: u4,
};
+ /// Advanced SIMD scalar copy
+ pub const SimdScalarCopy = packed union {
+ group: @This().Group,
+ dup: Dup,
+
+ pub const Group = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ imm4: u4,
+ decoded15: u1 = 0b0,
+ imm5: u5,
+ decoded21: u8 = 0b11110000,
+ op: u1,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.39 DUP (element)
+ pub const Dup = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ imm4: u4 = 0b0000,
+ decoded15: u1 = 0b0,
+ imm5: u5,
+ decoded21: u8 = 0b11110000,
+ op: u1 = 0b0,
+ decoded30: u2 = 0b01,
+ };
+
+ pub const Decoded = union(enum) {
+ unallocated,
+ dup: Dup,
+ };
+ pub fn decode(inst: @This()) @This().Decoded {
+ return switch (inst.group.op) {
+ 0b0 => switch (inst.group.imm4) {
+ else => .unallocated,
+ 0b0000 => .{ .dup = inst.dup },
+ },
+ 0b1 => .unallocated,
+ };
+ }
+ };
+
+ /// Advanced SIMD scalar two-register miscellaneous FP16
+ pub const SimdScalarTwoRegisterMiscellaneousFp16 = packed union {
+ group: @This().Group,
+ fcvtns: Fcvtns,
+ fcvtms: Fcvtms,
+ fcvtas: Fcvtas,
+ scvtf: Scvtf,
+ fcmgt: Fcmgt,
+ fcmeq: Fcmeq,
+ fcmlt: Fcmlt,
+ fcvtps: Fcvtps,
+ fcvtzs: Fcvtzs,
+ frecpe: Frecpe,
+ frecpx: Frecpx,
+ fcvtnu: Fcvtnu,
+ fcvtmu: Fcvtmu,
+ fcvtau: Fcvtau,
+ ucvtf: Ucvtf,
+ fcmge: Fcmge,
+ fcmle: Fcmle,
+ fcvtpu: Fcvtpu,
+ fcvtzu: Fcvtzu,
+ frsqrte: Frsqrte,
+
+ pub const Group = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5,
+ decoded17: u6 = 0b111100,
+ a: u1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.80 FCVTNS (vector)
+ pub const Fcvtns = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.75 FCVTMS (vector)
+ pub const Fcvtms = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.70 FCVTAS (vector)
+ pub const Fcvtas = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.234 SCVTF (vector, integer)
+ pub const Scvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.61 FCMGT (zero)
+ pub const Fcmgt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.57 FCMEQ (zero)
+ pub const Fcmeq = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.65 FCMLT (zero)
+ pub const Fcmlt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01110,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.84 FCVTPS (vector)
+ pub const Fcvtps = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.90 FCVTZS (vector, integer)
+ pub const Fcvtzs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.144 FRECPE
+ pub const Frecpe = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.146 FRECPX
+ pub const Frecpx = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11111,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.82 FCVTNU (vector)
+ pub const Fcvtnu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.77 FCVTMU (vector)
+ pub const Fcvtmu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.72 FCVTAU (vector)
+ pub const Fcvtau = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.353 UCVTF (vector, integer)
+ pub const Ucvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.59 FCMGE (zero)
+ pub const Fcmge = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.64 FCMLE (zero)
+ pub const Fcmle = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.86 FCVTPU (vector)
+ pub const Fcvtpu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.94 FCVTZU (vector, integer)
+ pub const Fcvtzu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.169 FRSQRTE
+ pub const Frsqrte = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+ };
+
+ /// Advanced SIMD scalar two-register miscellaneous
+ pub const SimdScalarTwoRegisterMiscellaneous = packed union {
+ group: @This().Group,
+ suqadd: Suqadd,
+ sqabs: Sqabs,
+ cmgt: Cmgt,
+ cmeq: Cmeq,
+ cmlt: Cmlt,
+ abs: Abs,
+ sqxtn: Sqxtn,
+ fcvtns: Fcvtns,
+ fcvtms: Fcvtms,
+ fcvtas: Fcvtas,
+ scvtf: Scvtf,
+ fcmgt: Fcmgt,
+ fcmeq: Fcmeq,
+ fcmlt: Fcmlt,
+ fcvtps: Fcvtps,
+ fcvtzs: Fcvtzs,
+ frecpe: Frecpe,
+ frecpx: Frecpx,
+ usqadd: Usqadd,
+ sqneg: Sqneg,
+ cmge: Cmge,
+ cmle: Cmle,
+ neg: Neg,
+ sqxtun: Sqxtun,
+ uqxtn: Uqxtn,
+ fcvtxn: Fcvtxn,
+ fcvtnu: Fcvtnu,
+ fcvtmu: Fcvtmu,
+ fcvtau: Fcvtau,
+ ucvtf: Ucvtf,
+ fcmge: Fcmge,
+ fcmle: Fcmle,
+ fcvtpu: Fcvtpu,
+ fcvtzu: Fcvtzu,
+ frsqrte: Frsqrte,
+
+ pub const Group = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.337 SUQADD
+ pub const Suqadd = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.282 SQABS
+ pub const Sqabs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00111,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.32 CMGT (zero)
+ pub const Cmgt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01000,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.28 CMEQ (zero)
+ pub const Cmeq = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01001,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.36 CMLT (zero)
+ pub const Cmlt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01010,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.1 ABS
+ pub const Abs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.308 SQXTN
+ pub const Sqxtn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10100,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.80 FCVTNS (vector)
+ pub const Fcvtns = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.75 FCVTMS (vector)
+ pub const Fcvtms = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.70 FCVTAS (vector)
+ pub const Fcvtas = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.234 SCVTF (vector, integer)
+ pub const Scvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.61 FCMGT (zero)
+ pub const Fcmgt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.57 FCMEQ (zero)
+ pub const Fcmeq = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.65 FCMLT (zero)
+ pub const Fcmlt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01110,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.84 FCVTPS (vector)
+ pub const Fcvtps = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.90 FCVTZS (vector, integer)
+ pub const Fcvtzs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.144 FRECPE
+ pub const Frecpe = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.146 FRECPX
+ pub const Frecpx = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11111,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .signed,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.394 USQADD
+ pub const Usqadd = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.292 SQNEG
+ pub const Sqneg = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00111,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.30 CMGE (zero)
+ pub const Cmge = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01000,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.35 CMLE (zero)
+ pub const Cmle = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01001,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.209 NEG (vector)
+ pub const Neg = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.309 SQXTUN
+ pub const Sqxtun = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10010,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.381 UQXTN
+ pub const Uqxtn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10100,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.88 FCVTXN
+ pub const Fcvtxn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10110,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.82 FCVTNU (vector)
+ pub const Fcvtnu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.77 FCVTMU (vector)
+ pub const Fcvtmu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.72 FCVTAU (vector)
+ pub const Fcvtau = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.353 UCVTF (vector, integer)
+ pub const Ucvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.59 FCMGE (zero)
+ pub const Fcmge = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.64 FCMLE (zero)
+ pub const Fcmle = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.86 FCVTPU (vector)
+ pub const Fcvtpu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.94 FCVTZU (vector, integer)
+ pub const Fcvtzu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+
+ /// C7.2.169 FRSQRTE
+ pub const Frsqrte = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b11110,
+ U: std.builtin.Signedness = .unsigned,
+ decoded30: u2 = 0b01,
+ };
+ };
+
/// Advanced SIMD scalar pairwise
pub const SimdScalarPairwise = packed union {
group: @This().Group,
@@ -7045,7 +7928,7 @@ pub const Instruction = packed union {
decoded17: u5 = 0b11000,
size: Size,
decoded24: u5 = 0b11110,
- U: u1,
+ U: std.builtin.Signedness,
decoded30: u2 = 0b01,
};
@@ -7058,7 +7941,7 @@ pub const Instruction = packed union {
decoded17: u5 = 0b11000,
size: Size,
decoded24: u5 = 0b11110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
decoded30: u2 = 0b01,
};
};
@@ -7066,8 +7949,10 @@ pub const Instruction = packed union {
/// Advanced SIMD copy
pub const SimdCopy = packed union {
group: @This().Group,
+ dup: Dup,
smov: Smov,
umov: Umov,
+ ins: Ins,
pub const Group = packed struct {
Rd: Register.Encoded,
@@ -7078,22 +7963,41 @@ pub const Instruction = packed union {
imm5: u5,
decoded21: u8 = 0b01110000,
op: u1,
- Q: Register.IntegerSize,
+ Q: u1,
decoded31: u1 = 0b0,
};
+ /// C7.2.39 DUP (element)
+ /// C7.2.40 DUP (general)
+ pub const Dup = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ imm4: Imm4,
+ decoded15: u1 = 0b0,
+ imm5: u5,
+ decoded21: u8 = 0b01110000,
+ op: u1 = 0b0,
+ Q: Q,
+ decoded31: u1 = 0b0,
+
+ pub const Imm4 = enum(u4) {
+ element = 0b0000,
+ general = 0b0001,
+ _,
+ };
+ };
+
/// C7.2.279 SMOV
pub const Smov = packed struct {
Rd: Register.Encoded,
Rn: Register.Encoded,
decoded10: u1 = 0b1,
- decoded11: u1 = 0b1,
- decoded12: u1 = 0b0,
- decoded13: u2 = 0b01,
+ imm4: u4 = 0b0101,
decoded15: u1 = 0b0,
imm5: u5,
decoded21: u8 = 0b01110000,
- decoded29: u1 = 0b0,
+ op: u1 = 0b0,
Q: Register.IntegerSize,
decoded31: u1 = 0b0,
};
@@ -7103,22 +8007,587 @@ pub const Instruction = packed union {
Rd: Register.Encoded,
Rn: Register.Encoded,
decoded10: u1 = 0b1,
- decoded11: u1 = 0b1,
- decoded12: u1 = 0b1,
- decoded13: u2 = 0b01,
+ imm4: u4 = 0b0111,
decoded15: u1 = 0b0,
imm5: u5,
decoded21: u8 = 0b01110000,
- decoded29: u1 = 0b0,
+ op: u1 = 0b0,
Q: Register.IntegerSize,
decoded31: u1 = 0b0,
};
+
+ /// C7.2.175 INS (element)
+ /// C7.2.176 INS (general)
+ pub const Ins = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ imm4: Imm4,
+ decoded15: u1 = 0b0,
+ imm5: u5,
+ decoded21: u8 = 0b01110000,
+ op: Op,
+ Q: u1 = 0b1,
+ decoded31: u1 = 0b0,
+
+ pub const Imm4 = packed union {
+ general: General,
+ element: u4,
+
+ pub const General = enum(u4) {
+ general = 0b0011,
+ _,
+ };
+ };
+
+ pub const Op = enum(u1) {
+ general = 0b0,
+ element = 0b1,
+ };
+ };
+
+ pub const Decoded = union(enum) {
+ unallocated,
+ dup: Dup,
+ smov: Smov,
+ umov: Umov,
+ ins: Ins,
+ };
+ pub fn decode(inst: @This()) @This().Decoded {
+ return switch (inst.group.op) {
+ 0b0 => switch (inst.group.imm4) {
+ 0b0000, 0b0001 => .{ .dup = inst.dup },
+ else => .unallocated,
+ 0b0101 => switch (@ctz(inst.group.imm5)) {
+ 0, 1 => .{ .smov = inst.smov },
+ 2 => switch (inst.group.Q) {
+ 0b1 => .{ .smov = inst.smov },
+ 0b0 => .unallocated,
+ },
+ else => .unallocated,
+ },
+ 0b0111 => switch (@ctz(inst.group.imm5)) {
+ 0, 1, 2 => switch (inst.group.Q) {
+ 0b0 => .{ .umov = inst.umov },
+ 0b1 => .unallocated,
+ },
+ 3 => switch (inst.group.Q) {
+ 0b1 => .{ .umov = inst.umov },
+ 0b0 => .unallocated,
+ },
+ else => .unallocated,
+ },
+ },
+ 0b1 => switch (inst.group.Q) {
+ 0b0 => .unallocated,
+ 0b1 => .{ .ins = inst.ins },
+ },
+ };
+ }
+ };
+
+ /// Advanced SIMD two-register miscellaneous (FP16)
+ pub const SimdTwoRegisterMiscellaneousFp16 = packed union {
+ group: @This().Group,
+ frintn: Frintn,
+ frintm: Frintm,
+ fcvtns: Fcvtns,
+ fcvtms: Fcvtms,
+ fcvtas: Fcvtas,
+ scvtf: Scvtf,
+ fcmgt: Fcmgt,
+ fcmeq: Fcmeq,
+ fcmlt: Fcmlt,
+ fabs: Fabs,
+ frintp: Frintp,
+ frintz: Frintz,
+ fcvtps: Fcvtps,
+ fcvtzs: Fcvtzs,
+ frecpe: Frecpe,
+ frinta: Frinta,
+ frintx: Frintx,
+ fcvtnu: Fcvtnu,
+ fcvtmu: Fcvtmu,
+ fcvtau: Fcvtau,
+ ucvtf: Ucvtf,
+ fcmge: Fcmge,
+ fcmle: Fcmle,
+ fneg: Fneg,
+ frinti: Frinti,
+ fcvtpu: Fcvtpu,
+ fcvtzu: Fcvtzu,
+ frsqrte: Frsqrte,
+ fsqrt: Fsqrt,
+
+ pub const Group = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5,
+ decoded17: u6 = 0b111100,
+ a: u1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.161 FRINTN (vector)
+ pub const Frintn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11000,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.159 FRINTM (vector)
+ pub const Frintm = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.80 FCVTNS (vector)
+ pub const Fcvtns = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.75 FCVTMS (vector)
+ pub const Fcvtms = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.70 FCVTAS (vector)
+ pub const Fcvtas = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.234 SCVTF (vector, integer)
+ pub const Scvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.61 FCMGT (zero)
+ pub const Fcmgt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.57 FCMEQ (zero)
+ pub const Fcmeq = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.65 FCMLT (zero)
+ pub const Fcmlt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01110,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.45 FABS (vector)
+ pub const Fabs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01111,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.163 FRINTP (vector)
+ pub const Frintp = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11000,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.167 FRINTZ (vector)
+ pub const Frintz = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.84 FCVTPS (vector)
+ pub const Fcvtps = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.90 FCVTZS (vector, integer)
+ pub const Fcvtzs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.144 FRECPE
+ pub const Frecpe = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.155 FRINTA (vector)
+ pub const Frinta = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11000,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.159 FRINTX (vector)
+ pub const Frintx = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.82 FCVTNU (vector)
+ pub const Fcvtnu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.77 FCVTMU (vector)
+ pub const Fcvtmu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.72 FCVTAU (vector)
+ pub const Fcvtau = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.353 UCVTF (vector, integer)
+ pub const Ucvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.59 FCMGE (zero)
+ pub const Fcmge = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.64 FCMLE (zero)
+ pub const Fcmle = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.139 FNEG (vector)
+ pub const Fneg = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01111,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.157 FRINTI (vector)
+ pub const Frinti = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.86 FCVTPU (vector)
+ pub const Fcvtpu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.94 FCVTZU (vector, integer)
+ pub const Fcvtzu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.169 FRSQRTE
+ pub const Frsqrte = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.171 FSQRT
+ pub const Fsqrt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11111,
+ decoded17: u6 = 0b111100,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
};
/// Advanced SIMD two-register miscellaneous
pub const SimdTwoRegisterMiscellaneous = packed union {
group: @This().Group,
+ suqadd: Suqadd,
cnt: Cnt,
+ sqabs: Sqabs,
+ cmgt: Cmgt,
+ cmeq: Cmeq,
+ cmlt: Cmlt,
+ abs: Abs,
+ sqxtn: Sqxtn,
+ frintn: Frintn,
+ frintm: Frintm,
+ fcvtns: Fcvtns,
+ fcvtms: Fcvtms,
+ fcvtas: Fcvtas,
+ scvtf: Scvtf,
+ fcmgt: Fcmgt,
+ fcmeq: Fcmeq,
+ fcmlt: Fcmlt,
+ fabs: Fabs,
+ frintp: Frintp,
+ frintz: Frintz,
+ fcvtps: Fcvtps,
+ fcvtzs: Fcvtzs,
+ frecpe: Frecpe,
+ usqadd: Usqadd,
+ sqneg: Sqneg,
+ cmge: Cmge,
+ cmle: Cmle,
+ neg: Neg,
+ sqxtun: Sqxtun,
+ uqxtn: Uqxtn,
+ fcvtxn: Fcvtxn,
+ frinta: Frinta,
+ frintx: Frintx,
+ fcvtnu: Fcvtnu,
+ fcvtmu: Fcvtmu,
+ fcvtau: Fcvtau,
+ ucvtf: Ucvtf,
+ not: Not,
+ fcmge: Fcmge,
+ fcmle: Fcmle,
+ fneg: Fneg,
+ frinti: Frinti,
+ fcvtpu: Fcvtpu,
+ fcvtzu: Fcvtzu,
+ frsqrte: Frsqrte,
+ fsqrt: Fsqrt,
pub const Group = packed struct {
Rd: Register.Encoded,
@@ -7128,7 +8597,21 @@ pub const Instruction = packed union {
decoded17: u5 = 0b10000,
size: Size,
decoded24: u5 = 0b01110,
- U: u1,
+ U: std.builtin.Signedness,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.337 SUQADD
+ pub const Suqadd = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7142,7 +8625,653 @@ pub const Instruction = packed union {
decoded17: u5 = 0b10000,
size: Size,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.282 SQABS
+ pub const Sqabs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00111,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.32 CMGT (zero)
+ pub const Cmgt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01000,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.28 CMEQ (zero)
+ pub const Cmeq = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01001,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.36 CMLT (zero)
+ pub const Cmlt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01010,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.1 ABS
+ pub const Abs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.308 SQXTN
+ pub const Sqxtn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10100,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.161 FRINTN (vector)
+ pub const Frintn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11000,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.159 FRINTM (vector)
+ pub const Frintm = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.80 FCVTNS (vector)
+ pub const Fcvtns = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.75 FCVTMS (vector)
+ pub const Fcvtms = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.70 FCVTAS (vector)
+ pub const Fcvtas = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.234 SCVTF (vector, integer)
+ pub const Scvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.61 FCMGT (zero)
+ pub const Fcmgt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.57 FCMEQ (zero)
+ pub const Fcmeq = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.65 FCMLT (zero)
+ pub const Fcmlt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01110,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.45 FABS (vector)
+ pub const Fabs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01111,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.163 FRINTP (vector)
+ pub const Frintp = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11000,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.167 FRINTZ (vector)
+ pub const Frintz = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.84 FCVTPS (vector)
+ pub const Fcvtps = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.90 FCVTZS (vector, integer)
+ pub const Fcvtzs = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.144 FRECPE
+ pub const Frecpe = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .signed,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.394 USQADD
+ pub const Usqadd = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.292 SQNEG
+ pub const Sqneg = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00111,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.30 CMGE (zero)
+ pub const Cmge = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01000,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.35 CMLE (zero)
+ pub const Cmle = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01001,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.209 NEG (vector)
+ pub const Neg = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01011,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.309 SQXTUN
+ pub const Sqxtun = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10010,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.381 UQXTN
+ pub const Uqxtn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10100,
+ decoded17: u5 = 0b10000,
+ size: Size,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.88 FCVTXN
+ pub const Fcvtxn = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b10110,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.155 FRINTA (vector)
+ pub const Frinta = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11000,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.165 FRINTX (vector)
+ pub const Frintx = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.82 FCVTNU (vector)
+ pub const Fcvtnu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.77 FCVTMU (vector)
+ pub const Fcvtmu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.72 FCVTAU (vector)
+ pub const Fcvtau = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.353 UCVTF (vector, integer)
+ pub const Ucvtf = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b0,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.210 NOT
+ pub const Not = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b00101,
+ decoded17: u5 = 0b10000,
+ size: Size = .byte,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.59 FCMGE (zero)
+ pub const Fcmge = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01100,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.64 FCMLE (zero)
+ pub const Fcmle = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.139 FNEG (vector)
+ pub const Fneg = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b01111,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.157 FRINTI (vector)
+ pub const Frinti = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11001,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.86 FCVTPU (vector)
+ pub const Fcvtpu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11010,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.94 FCVTZU (vector, integer)
+ pub const Fcvtzu = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11011,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.169 FRSQRTE
+ pub const Frsqrte = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11101,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.171 FSQRT (vector)
+ pub const Fsqrt = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b10,
+ opcode: u5 = 0b11111,
+ decoded17: u5 = 0b10000,
+ sz: Sz,
+ o2: u1 = 0b1,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7161,7 +9290,7 @@ pub const Instruction = packed union {
decoded17: u5 = 0b11000,
size: Size,
decoded24: u5 = 0b01110,
- U: u1,
+ U: std.builtin.Signedness,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7175,7 +9304,7 @@ pub const Instruction = packed union {
decoded17: u5 = 0b11000,
size: Size,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7190,6 +9319,9 @@ pub const Instruction = packed union {
orr: Orr,
orn: Orn,
eor: Eor,
+ bsl: Bsl,
+ bit: Bit,
+ bif: Bif,
pub const Group = packed struct {
Rd: Register.Encoded,
@@ -7200,7 +9332,7 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size,
decoded24: u5 = 0b01110,
- U: u1,
+ U: std.builtin.Signedness,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7215,7 +9347,7 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7230,7 +9362,7 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size = .byte,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7245,7 +9377,7 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size = .half,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7260,7 +9392,7 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size = .single,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7275,7 +9407,7 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size = .double,
decoded24: u5 = 0b01110,
- U: u1 = 0b0,
+ U: std.builtin.Signedness = .signed,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -7290,7 +9422,52 @@ pub const Instruction = packed union {
decoded21: u1 = 0b1,
size: Size = .byte,
decoded24: u5 = 0b01110,
- U: u1 = 0b1,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.24 BSL
+ pub const Bsl = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ opcode: u5 = 0b00011,
+ Rm: Register.Encoded,
+ decoded21: u1 = 0b1,
+ size: Size = .half,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.23 BIT
+ pub const Bit = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ opcode: u5 = 0b00011,
+ Rm: Register.Encoded,
+ decoded21: u1 = 0b1,
+ size: Size = .single,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
+ Q: Q,
+ decoded31: u1 = 0b0,
+ };
+
+ /// C7.2.22 BIF
+ pub const Bif = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u1 = 0b1,
+ opcode: u5 = 0b00011,
+ Rm: Register.Encoded,
+ decoded21: u1 = 0b1,
+ size: Size = .double,
+ decoded24: u5 = 0b01110,
+ U: std.builtin.Signedness = .unsigned,
Q: Q,
decoded31: u1 = 0b0,
};
@@ -8154,6 +10331,58 @@ pub const Instruction = packed union {
};
};
+ /// Floating-point conditional select
+ pub const FloatConditionalSelect = packed union {
+ group: @This().Group,
+ fcsel: Fcsel,
+
+ pub const Group = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b11,
+ cond: ConditionCode,
+ Rm: Register.Encoded,
+ decoded21: u1 = 0b1,
+ ptype: Ftype,
+ decoded24: u5 = 0b11110,
+ S: bool,
+ decoded30: u1 = 0b0,
+ M: u1,
+ };
+
+ /// C7.2.68 FCSEL
+ pub const Fcsel = packed struct {
+ Rd: Register.Encoded,
+ Rn: Register.Encoded,
+ decoded10: u2 = 0b11,
+ cond: ConditionCode,
+ Rm: Register.Encoded,
+ decoded21: u1 = 0b1,
+ ftype: Ftype,
+ decoded24: u5 = 0b11110,
+ S: bool = false,
+ decoded30: u1 = 0b0,
+ M: u1 = 0b0,
+ };
+
+ pub const Decoded = union(enum) {
+ unallocated,
+ fcsel: Fcsel,
+ };
+ pub fn decode(inst: @This()) @This().Decoded {
+ return switch (inst.group.ptype) {
+ .quad => .unallocated,
+ .single, .double, .half => switch (inst.group.S) {
+ true => .unallocated,
+ false => switch (inst.group.M) {
+ 0b0 => .{ .fcsel = inst.fcsel },
+ 0b1 => .unallocated,
+ },
+ },
+ };
+ }
+ };
+
/// Floating-point data-processing (3 source)
pub const FloatDataProcessingThreeSource = packed union {
group: @This().Group,
@@ -8242,11 +10471,6 @@ pub const Instruction = packed union {
};
};
- pub const Q = enum(u1) {
- double = 0b0,
- quad = 0b1,
- };
-
pub const Size = enum(u2) {
byte = 0b00,
half = 0b01,
@@ -8264,6 +10488,7 @@ pub const Instruction = packed union {
pub fn fromVectorSize(vs: Register.VectorSize) Size {
return switch (vs) {
+ else => unreachable,
.byte => .byte,
.half => .half,
.single => .single,
@@ -8272,11 +10497,54 @@ pub const Instruction = packed union {
}
};
+ pub const Sz = enum(u1) {
+ single = 0b0,
+ double = 0b1,
+
+ pub fn toVectorSize(sz: Sz) Register.VectorSize {
+ return switch (sz) {
+ .single => .single,
+ .double => .double,
+ };
+ }
+
+ pub fn fromVectorSize(vs: Register.VectorSize) Sz {
+ return switch (vs) {
+ else => unreachable,
+ .single => .single,
+ .double => .double,
+ };
+ }
+ };
+
+ pub const Q = enum(u1) {
+ double = 0b0,
+ quad = 0b1,
+ };
+
pub const Ftype = enum(u2) {
single = 0b00,
double = 0b01,
quad = 0b10,
half = 0b11,
+
+ pub fn toVectorSize(ftype: Ftype) Register.VectorSize {
+ return switch (ftype) {
+ _ => unreachable,
+ .single => .single,
+ .double => .double,
+ .half => .half,
+ };
+ }
+
+ pub fn fromVectorSize(vs: Register.VectorSize) Ftype {
+ return switch (vs) {
+ else => unreachable,
+ .single => .single,
+ .double => .double,
+ .half => .half,
+ };
+ }
};
};
@@ -8320,6 +10588,33 @@ pub const Instruction = packed union {
};
}
+ /// C7.2.1 ABS (zero)
+ pub fn abs(d: Register, n: Register) Instruction {
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .abs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .abs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ }
+ }
/// C6.2.1 ADC
pub fn adc(d: Register, n: Register, m: Register) Instruction {
const sf = d.format.integer;
@@ -8867,6 +11162,45 @@ pub const Instruction = packed union {
} },
}
}
+ /// C7.2.22 BIF
+ pub fn bif(d: Register, n: Register, m: Register) Instruction {
+ const arrangement = d.format.vector;
+ assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_three_same = .{
+ .bif = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Rm = m.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } };
+ }
+ /// C7.2.23 BIT
+ pub fn bit(d: Register, n: Register, m: Register) Instruction {
+ const arrangement = d.format.vector;
+ assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_three_same = .{
+ .bit = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Rm = m.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } };
+ }
+ /// C7.2.24 BSL
+ pub fn bsl(d: Register, n: Register, m: Register) Instruction {
+ const arrangement = d.format.vector;
+ assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_three_same = .{
+ .bsl = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Rm = m.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } };
+ }
/// C6.2.34 BL
pub fn bl(label: i28) Instruction {
return .{ .branch_exception_generating_system = .{ .unconditional_branch_immediate = .{
@@ -8999,6 +11333,151 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.28 CMEQ (zero)
+ pub fn cmeq(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .cmeq = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .cmeq = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ },
+ }
+ }
+ /// C7.2.30 CMGE (zero)
+ pub fn cmge(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .cmge = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .cmge = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ },
+ }
+ }
+ /// C7.2.32 CMGT (zero)
+ pub fn cmgt(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .cmgt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .cmgt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ },
+ }
+ }
+ /// C7.2.35 CMLE (zero)
+ pub fn cmle(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .cmle = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .cmle = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ },
+ }
+ }
+ /// C7.2.36 CMLT (zero)
+ pub fn cmlt(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .cmlt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .cmlt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ },
+ }
+ }
/// C7.2.38 CNT
pub fn cnt(d: Register, n: Register) Instruction {
const arrangement = d.format.vector;
@@ -9094,6 +11573,57 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.39 DUP (element)
+ /// C7.2.40 DUP (general)
+ pub fn dup(d: Register, n: Register) Instruction {
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(@intFromEnum(elem_size) <= @intFromEnum(Register.VectorSize.double) and elem_size == n.format.element.size);
+ return .{ .data_processing_vector = .{ .simd_scalar_copy = .{
+ .dup = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .imm5 = @shlExact(@as(u5, n.format.element.index) << 1 | @as(u5, 0b1), @intFromEnum(elem_size)),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d");
+ const elem_size = arrangement.elemSize();
+ switch (n.format) {
+ else => unreachable,
+ .element => |element| {
+ assert(elem_size.toVectorSize() == element.size);
+ return .{ .data_processing_vector = .{ .simd_copy = .{
+ .dup = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .imm4 = .element,
+ .imm5 = @shlExact(@as(u5, element.index) << 1 | @as(u5, 0b1), @intFromEnum(elem_size)),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ .integer => |sf| {
+ assert(sf == @as(Register.IntegerSize, switch (elem_size) {
+ .byte, .half, .single => .word,
+ .double => .doubleword,
+ }));
+ return .{ .data_processing_vector = .{ .simd_copy = .{
+ .dup = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{}),
+ .imm4 = .general,
+ .imm5 = @shlExact(@as(u5, 0b1), @intFromEnum(elem_size)),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ }
+ },
+ }
+ }
/// C6.2.118 EON (shifted register)
pub fn eon(d: Register, n: Register, form: union(enum) {
register: Register,
@@ -9212,22 +11742,43 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.45 FABS (vector)
/// C7.2.46 FABS (scalar)
pub fn fabs(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .fabs = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fabs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fabs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .fabs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
/// C7.2.50 FADD (scalar)
pub fn fadd(d: Register, n: Register, m: Register) Instruction {
@@ -9238,15 +11789,265 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
+ /// C7.2.57 FCMEQ (zero)
+ pub fn fcmeq(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |n_scalar| {
+ assert(n_scalar == ftype);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcmeq = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcmeq = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcmeq = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcmeq = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ },
+ }
+ }
+ /// C7.2.59 FCMGE (zero)
+ pub fn fcmge(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |n_scalar| {
+ assert(n_scalar == ftype);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcmge = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcmge = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcmge = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcmge = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ },
+ }
+ }
+ /// C7.2.61 FCMGT (zero)
+ pub fn fcmgt(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |n_scalar| {
+ assert(n_scalar == ftype);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcmgt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcmgt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcmgt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcmgt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ },
+ }
+ }
+ /// C7.2.64 FCMLE (zero)
+ pub fn fcmle(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |n_scalar| {
+ assert(n_scalar == ftype);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcmle = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcmle = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcmle = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcmle = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ },
+ }
+ }
+ /// C7.2.65 FCMLT (zero)
+ pub fn fcmlt(d: Register, n: Register, form: union(enum) { zero }) Instruction {
+ switch (form) {
+ .zero => switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |n_scalar| {
+ assert(n_scalar == ftype);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcmlt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcmlt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcmlt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcmlt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ },
+ }
+ }
/// C7.2.66 FCMP
pub fn fcmp(n: Register, form: union(enum) { register: Register, zero }) Instruction {
const ftype = n.format.scalar;
@@ -9258,12 +12059,7 @@ pub const Instruction = packed union {
.opc0 = .register,
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
},
@@ -9272,12 +12068,7 @@ pub const Instruction = packed union {
.opc0 = .register,
.Rn = n.alias.encode(.{ .V = true }),
.Rm = @enumFromInt(0b00000),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } },
}
@@ -9293,12 +12084,7 @@ pub const Instruction = packed union {
.opc0 = .zero,
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
},
@@ -9307,16 +12093,25 @@ pub const Instruction = packed union {
.opc0 = .zero,
.Rn = n.alias.encode(.{ .V = true }),
.Rm = @enumFromInt(0b00000),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } },
}
}
+ /// C7.2.68 FCSEL
+ pub fn fcsel(d: Register, n: Register, m: Register, cond: ConditionCode) Instruction {
+ const ftype = d.format.scalar;
+ assert(n.format.scalar == ftype and m.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_conditional_select = .{
+ .fcsel = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .cond = cond,
+ .Rm = m.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ }
/// C7.2.69 FCVT
pub fn fcvt(d: Register, n: Register) Instruction {
assert(d.format.scalar != n.format.scalar);
@@ -9330,174 +12125,589 @@ pub const Instruction = packed union {
.double => .double,
.half => .half,
},
- .ftype = switch (n.format.scalar) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(n.format.scalar),
},
} } };
}
+ /// C7.2.70 FCVTAS (vector)
/// C7.2.71 FCVTAS (scalar)
pub fn fcvtas(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtas = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtas = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtas = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtas = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtas = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtas = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.72 FCVTAU (vector)
/// C7.2.73 FCVTAU (scalar)
pub fn fcvtau(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtau = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtau = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtau = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtau = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtau = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtau = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.75 FCVTMS (vector)
/// C7.2.76 FCVTMS (scalar)
pub fn fcvtms(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtms = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtms = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtms = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtms = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtms = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtms = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.77 FCVTMU (vector)
/// C7.2.78 FCVTMU (scalar)
pub fn fcvtmu(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtmu = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtmu = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtmu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtmu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtmu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtmu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.80 FCVTNS (vector)
/// C7.2.81 FCVTNS (scalar)
pub fn fcvtns(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtns = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtns = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtns = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtns = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtns = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtns = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.82 FCVTNU (vector)
/// C7.2.83 FCVTNU (scalar)
pub fn fcvtnu(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtnu = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtnu = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtnu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtnu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtnu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtnu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.84 FCVTPS (vector)
/// C7.2.85 FCVTPS (scalar)
pub fn fcvtps(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtps = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtps = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtps = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtps = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtps = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtps = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.86 FCVTPU (vector)
/// C7.2.87 FCVTPU (scalar)
pub fn fcvtpu(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtpu = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtpu = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtpu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtpu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtpu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtpu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.90 FCVTZS (vector, integer)
/// C7.2.92 FCVTZS (scalar, integer)
pub fn fcvtzs(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtzs = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtzs = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtzs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtzs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtzs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtzs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
+ /// C7.2.94 FCVTZU (vector, integer)
/// C7.2.96 FCVTZU (scalar, integer)
pub fn fcvtzu(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .fcvtzu = .{
- .Rd = d.alias.encode(.{}),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (n.format.scalar) {
+ switch (d.format) {
+ else => unreachable,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .fcvtzu = .{
+ .Rd = d.alias.encode(.{}),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(n.format.scalar),
+ .sf = sf,
+ },
+ } } },
+ .scalar => |elem_size| switch (n.format) {
+ else => unreachable,
+ .scalar => |ftype| {
+ assert(ftype == elem_size);
+ switch (elem_size) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .fcvtzu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .fcvtzu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(elem_size),
+ },
+ } } },
+ }
+ },
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
- .sf = d.format.integer,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fcvtzu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fcvtzu = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ }
}
/// C7.2.98 FDIV (scalar)
pub fn fdiv(d: Register, n: Register, m: Register) Instruction {
@@ -9508,12 +12718,7 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9538,12 +12743,7 @@ pub const Instruction = packed union {
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
.Ra = a.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9556,12 +12756,7 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9574,12 +12769,7 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9592,12 +12782,7 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9610,12 +12795,7 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9642,12 +12822,7 @@ pub const Instruction = packed union {
.fmov = .{
.Rd = d.alias.encode(.{ .V = true }),
.imm8 = imm,
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } },
.vector => |arrangement| {
@@ -9680,12 +12855,7 @@ pub const Instruction = packed union {
.Rn = n.alias.encode(.{ .V = true }),
.opcode = .float_to_integer,
.rmode = .@"0",
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
.sf = sf,
},
} } };
@@ -9723,12 +12893,7 @@ pub const Instruction = packed union {
.Rn = n.alias.encode(.{}),
.opcode = .integer_to_float,
.rmode = .@"0",
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
.sf = sf,
},
} } };
@@ -9739,12 +12904,7 @@ pub const Instruction = packed union {
.fmov = .{
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
},
@@ -9781,12 +12941,7 @@ pub const Instruction = packed union {
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
.Ra = a.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9799,31 +12954,47 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
+ /// C7.2.139 FNEG (vector)
/// C7.2.140 FNEG (scalar)
pub fn fneg(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .fneg = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fneg = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fneg = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .fneg = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
/// C7.2.141 FNMADD
pub fn fnmadd(d: Register, n: Register, m: Register, a: Register) Instruction {
@@ -9835,12 +13006,7 @@ pub const Instruction = packed union {
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
.Ra = a.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9854,12 +13020,7 @@ pub const Instruction = packed union {
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
.Ra = a.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -9872,150 +13033,313 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
+ /// C7.2.155 FRINTA (vector)
/// C7.2.156 FRINTA (scalar)
pub fn frinta(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frinta = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frinta = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frinta = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frinta = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.157 FRINTI (vector)
/// C7.2.158 FRINTI (scalar)
pub fn frinti(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frinti = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frinti = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frinti = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frinti = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.159 FRINTM (vector)
/// C7.2.160 FRINTM (scalar)
pub fn frintm(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frintm = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frintm = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frintm = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frintm = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.161 FRINTN (vector)
/// C7.2.162 FRINTN (scalar)
pub fn frintn(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frintn = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frintn = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frintn = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frintn = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.163 FRINTP (vector)
/// C7.2.164 FRINTP (scalar)
pub fn frintp(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frintp = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frintp = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frintp = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frintp = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.165 FRINTX (vector)
/// C7.2.166 FRINTX (scalar)
pub fn frintx(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frintx = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frintx = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frintx = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frintx = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.167 FRINTZ (vector)
/// C7.2.168 FRINTZ (scalar)
pub fn frintz(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .frintz = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .frintz = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .frintz = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .frintz = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
+ /// C7.2.171 FSQRT (vector)
/// C7.2.172 FSQRT (scalar)
pub fn fsqrt(d: Register, n: Register) Instruction {
- const ftype = d.format.scalar;
- assert(n.format.scalar == ftype);
- return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
- .fsqrt = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
+ switch (d.format) {
+ else => unreachable,
+ .vector => |arrangement| {
+ assert(n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .fsqrt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .fsqrt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
},
- } } };
+ .scalar => |ftype| {
+ assert(n.format.scalar == ftype);
+ return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{
+ .fsqrt = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .ftype = .fromVectorSize(ftype),
+ },
+ } } };
+ },
+ }
}
/// C7.2.174 FSUB (scalar)
pub fn fsub(d: Register, n: Register, m: Register) Instruction {
@@ -10026,12 +13350,7 @@ pub const Instruction = packed union {
.Rd = d.alias.encode(.{ .V = true }),
.Rn = n.alias.encode(.{ .V = true }),
.Rm = m.alias.encode(.{ .V = true }),
- .ftype = switch (ftype) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
- },
+ .ftype = .fromVectorSize(ftype),
},
} } };
}
@@ -11021,12 +14340,52 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.209 NEG (vector)
+ pub fn neg(d: Register, n: Register) Instruction {
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == .double and elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .neg = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .neg = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ }
+ }
/// C6.2.238 NOP
pub fn nop() Instruction {
return .{ .branch_exception_generating_system = .{ .hints = .{
.nop = .{},
} } };
}
+ /// C7.2.210 NOT
+ pub fn not(d: Register, n: Register) Instruction {
+ const arrangement = d.format.vector;
+ assert(arrangement.elemSize() == .byte and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .not = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ }
/// C6.2.239 ORN (shifted register)
/// C7.2.211 ORN (vector)
pub fn orn(d: Register, n: Register, form: union(enum) {
@@ -11343,21 +14702,63 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.234 SCVTF (vector, integer)
/// C7.2.236 SCVTF (scalar, integer)
pub fn scvtf(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .scvtf = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{}),
- .ftype = switch (d.format.scalar) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(ftype == elem_size);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .scvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .scvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
},
- .sf = n.format.integer,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .scvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{}),
+ .ftype = .fromVectorSize(ftype),
+ .sf = sf,
+ },
+ } } },
},
- } } };
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .scvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .scvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ }
}
/// C6.2.270 SDIV
pub fn sdiv(d: Register, n: Register, m: Register) Instruction {
@@ -11448,6 +14849,60 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.282 SQABS
+ pub fn sqabs(d: Register, n: Register) Instruction {
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .sqabs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .sqabs = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ }
+ }
+ /// C7.2.308 SQXTN
+ pub fn sqxtn(d: Register, n: Register) Instruction {
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .sqxtn = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .sqxtn = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ }
+ }
/// C6.2.321 STP
/// C7.2.330 STP (SIMD&FP)
pub fn stp(t1: Register, t2: Register, form: union(enum) {
@@ -11942,6 +15397,33 @@ pub const Instruction = packed union {
} },
}
}
+ /// C7.2.337 SUQADD
+ pub fn suqadd(d: Register, n: Register) Instruction {
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(elem_size == n.format.scalar);
+ return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .suqadd = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = .fromVectorSize(elem_size),
+ },
+ } } };
+ },
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .suqadd = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .size = arrangement.elemSize(),
+ .Q = arrangement.size(),
+ },
+ } } };
+ },
+ }
+ }
/// C6.2.365 SVC
pub fn svc(imm: u16) Instruction {
return .{ .branch_exception_generating_system = .{ .exception_generating = .{
@@ -12021,21 +15503,63 @@ pub const Instruction = packed union {
},
} } };
}
+ /// C7.2.353 UCVTF (vector, integer)
/// C7.2.355 UCVTF (scalar, integer)
pub fn ucvtf(d: Register, n: Register) Instruction {
- return .{ .data_processing_vector = .{ .convert_float_integer = .{
- .ucvtf = .{
- .Rd = d.alias.encode(.{ .V = true }),
- .Rn = n.alias.encode(.{}),
- .ftype = switch (d.format.scalar) {
- else => unreachable,
- .single => .single,
- .double => .double,
- .half => .half,
+ switch (d.format) {
+ else => unreachable,
+ .scalar => |ftype| switch (n.format) {
+ else => unreachable,
+ .scalar => |elem_size| {
+ assert(ftype == elem_size);
+ switch (ftype) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous_fp16 = .{
+ .ucvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_scalar_two_register_miscellaneous = .{
+ .ucvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = .fromVectorSize(ftype),
+ },
+ } } },
+ }
},
- .sf = n.format.integer,
+ .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{
+ .ucvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{}),
+ .ftype = .fromVectorSize(ftype),
+ .sf = sf,
+ },
+ } } },
},
- } } };
+ .vector => |arrangement| {
+ assert(arrangement != .@"1d" and n.format.vector == arrangement);
+ switch (arrangement.elemSize()) {
+ else => unreachable,
+ .half => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous_fp16 = .{
+ .ucvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .Q = arrangement.size(),
+ },
+ } } },
+ .single, .double => return .{ .data_processing_vector = .{ .simd_two_register_miscellaneous = .{
+ .ucvtf = .{
+ .Rd = d.alias.encode(.{ .V = true }),
+ .Rn = n.alias.encode(.{ .V = true }),
+ .sz = arrangement.elemSz(),
+ .Q = arrangement.size(),
+ },
+ } } },
+ }
+ },
+ }
}
/// C6.2.387 UDF
pub fn udf(imm: u16) Instruction {
@@ -12149,7 +15673,7 @@ pub const Instruction = packed union {
}
comptime {
- @setEvalBranchQuota(68_000);
+ @setEvalBranchQuota(110_000);
verify(@typeName(Instruction), Instruction);
}
fn verify(name: []const u8, Type: type) void {
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
index 0f6dc8459dfe7ea46a011cb10200bb0ffce53c98..59f1a15795451decc2d55186c812596d4129c3cb 100644
--- a/test/behavior/math.zig
+++ b/test/behavior/math.zig
@@ -168,7 +168,6 @@ fn testOneCtz(comptime T: type, x: T) u32 {
}
test "@ctz 128-bit integers" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -463,7 +462,6 @@ test "binary not big int <= 128 bits" {
}
test "division" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -1819,7 +1817,6 @@ test "runtime int comparison to inf is comptime-known" {
}
test "float divide by zero" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
--
2.54.0
From a8888afcc07479bf779105f977597b29aea3c28f Mon Sep 17 00:00:00 2001
From: mlugg
Date: Tue, 29 Jul 2025 10:04:15 +0100
Subject: [PATCH 034/110] Sema: remove redundant comptime-known initializer
tracking
This logic predates certain Sema enhancements whose behavior it
essentially tries to emulate in one specific case in a problematic way.
In particular, this logic handled initializing comptime-known `const`s
through RLS, which was reworked a few years back in 644041b to not rely
on this logic, and catching runtime fields in comptime-only
initializers, which has since been *correctly* fixed with better checks
in `Sema.storePtr2`. That made the highly complex logic in
`validateStructInit`, `validateUnionInit`, and `zirValidatePtrArrayInit`
entirely redundant. Worse, it was also causing some tracked bugs, as
well as a bug which I have identified and fixed in this PR (a
corresponding behavior test is added).
This commit simplifies union initialization by bringing the runtime
logic more in line with the comptime logic: the tag is now always
populated by `Sema.unionFieldPtr` based on `initializing`, where this
previously happened only in the comptime case (with `validateUnionInit`
instead handling it in the runtime case). Notably, this means that
backends are now able to consider getting a pointer to an inactive union
field as Illegal Behavior, because the `set_union_tag` instruction now
appears *before* the `struct_field_ptr` instruction as you would
probably expect it to.
Resolves: #24520
Resolves: #24595
---
src/Sema.zig | 645 +++++------------------------
test/behavior/array.zig | 14 +-
test/behavior/field_parent_ptr.zig | 6 +-
3 files changed, 113 insertions(+), 552 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index 93740589bcb007accc6023f17435f298fd8ec60e..c44e2eb5e537b4b4cb8fe2f7e134901b54b33665 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -4829,14 +4829,14 @@ fn zirValidatePtrStructInit(
agg_ty,
init_src,
instrs,
- ),
- .@"union" => return sema.validateUnionInit(
- block,
- agg_ty,
- init_src,
- instrs,
object_ptr,
),
+ .@"union" => return sema.validateUnionInit(
+ block,
+ agg_ty,
+ init_src,
+ instrs,
+ ),
else => unreachable,
}
}
@@ -4847,164 +4847,28 @@ fn validateUnionInit(
union_ty: Type,
init_src: LazySrcLoc,
instrs: []const Zir.Inst.Index,
- union_ptr: Air.Inst.Ref,
) CompileError!void {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const gpa = sema.gpa;
-
- if (instrs.len != 1) {
- const msg = msg: {
- const msg = try sema.errMsg(
- init_src,
- "cannot initialize multiple union fields at once; unions can only have one active field",
- .{},
- );
- errdefer msg.destroy(gpa);
-
- for (instrs[1..]) |inst| {
- const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
- const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
- try sema.errNote(inst_src, msg, "additional initializer here", .{});
- }
- try sema.addDeclaredHereNote(msg, union_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
-
- if (block.isComptime() and
- (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null)
- {
- // In this case, comptime machinery already did everything. No work to do here.
+ if (instrs.len == 1) {
+ // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`.
return;
}
-
- const field_ptr = instrs[0];
- const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
- const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
- const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
- const field_name = try zcu.intern_pool.getOrPutString(
- gpa,
- pt.tid,
- sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
- .no_embedded_nulls,
- );
- const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
- const air_tags = sema.air_instructions.items(.tag);
- const air_datas = sema.air_instructions.items(.data);
- const field_ptr_ref = sema.inst_map.get(field_ptr).?;
-
- // Our task here is to determine if the union is comptime-known. In such case,
- // we erase the runtime AIR instructions for initializing the union, and replace
- // the mapping with the comptime value. Either way, we will need to populate the tag.
-
- // We expect to see something like this in the current block AIR:
- // %a = alloc(*const U)
- // %b = bitcast(*U, %a)
- // %c = field_ptr(..., %b)
- // %e!= store(%c!, %d!)
- // If %d is a comptime operand, the union is comptime.
- // If the union is comptime, we want `first_block_index`
- // to point at %c so that the bitcast becomes the last instruction in the block.
- //
- // Store instruction may be missing; if field type has only one possible value, this case is handled below.
- //
- // In the case of a comptime-known pointer to a union, the
- // the field_ptr instruction is missing, so we have to pattern-match
- // based only on the store instructions.
- // `first_block_index` needs to point to the `field_ptr` if it exists;
- // the `store` otherwise.
- var first_block_index = block.instructions.items.len;
- var block_index = block.instructions.items.len - 1;
- var init_val: ?Value = null;
- var init_ref: ?Air.Inst.Ref = null;
- while (block_index > 0) : (block_index -= 1) {
- const store_inst = block.instructions.items[block_index];
- if (store_inst.toRef() == field_ptr_ref) {
- first_block_index = block_index;
- break;
- }
- switch (air_tags[@intFromEnum(store_inst)]) {
- .store, .store_safe => {},
- else => continue,
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ init_src,
+ "cannot initialize multiple union fields at once; unions can only have one active field",
+ .{},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ for (instrs[1..]) |inst| {
+ const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
+ const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
+ try sema.errNote(inst_src, msg, "additional initializer here", .{});
}
- const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
- var ptr_ref = bin_op.lhs;
- if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
- ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
- };
- if (ptr_ref != field_ptr_ref) continue;
- first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst|
- std.mem.lastIndexOfScalar(
- Air.Inst.Index,
- block.instructions.items[0..block_index],
- field_ptr_inst,
- ).?
- else
- block_index, first_block_index);
- init_ref = bin_op.rhs;
- init_val = try sema.resolveValue(bin_op.rhs);
- break;
- }
-
- const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
- const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
- const field_type = union_ty.unionFieldType(tag_val, zcu).?;
-
- if (try sema.typeHasOnePossibleValue(field_type)) |field_only_value| {
- init_val = field_only_value;
- }
-
- if (init_val) |val| {
- // Our task is to delete all the `field_ptr` and `store` instructions, and insert
- // instead a single `store` to the result ptr with a comptime union value.
- block_index = first_block_index;
- for (block.instructions.items[first_block_index..]) |cur_inst| {
- switch (air_tags[@intFromEnum(cur_inst)]) {
- .struct_field_ptr,
- .struct_field_ptr_index_0,
- .struct_field_ptr_index_1,
- .struct_field_ptr_index_2,
- .struct_field_ptr_index_3,
- => if (cur_inst.toRef() == field_ptr_ref) continue,
- .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue,
- .store, .store_safe => {
- var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
- if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
- ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
- };
- if (ptr_ref == field_ptr_ref) continue;
- },
- else => {},
- }
- block.instructions.items[block_index] = cur_inst;
- block_index += 1;
- }
- block.instructions.shrinkRetainingCapacity(block_index);
-
- const union_val = try pt.internUnion(.{
- .ty = union_ty.toIntern(),
- .tag = tag_val.toIntern(),
- .val = val.toIntern(),
- });
- const union_init = Air.internedToRef(union_val);
- try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store);
- return;
- } else if (try union_ty.comptimeOnlySema(pt)) {
- const src = block.nodeOffset(field_ptr_data.src_node);
- return sema.failWithNeededComptime(block, src, .{ .comptime_only = .{
- .ty = union_ty,
- .msg = .union_init,
- } });
- }
- if (init_ref) |v| try sema.validateRuntimeValue(block, block.nodeOffset(field_ptr_data.src_node), v);
-
- if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) {
- const new_tag = Air.internedToRef(tag_val.toIntern());
- const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
- try sema.checkComptimeKnownStore(block, set_tag_inst, LazySrcLoc.unneeded); // `unneeded` since this isn't a "proper" store
- }
+ try sema.addDeclaredHereNote(msg, union_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
}
fn validateStructInit(
@@ -5013,187 +4877,62 @@ fn validateStructInit(
struct_ty: Type,
init_src: LazySrcLoc,
instrs: []const Zir.Inst.Index,
+ struct_ptr: Air.Inst.Ref,
) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const gpa = sema.gpa;
const ip = &zcu.intern_pool;
- const field_indices = try gpa.alloc(u32, instrs.len);
- defer gpa.free(field_indices);
-
- // Maps field index to field_ptr index of where it was already initialized.
- const found_fields = try gpa.alloc(Zir.Inst.OptionalIndex, struct_ty.structFieldCount(zcu));
+ // Tracks whether each field was explicitly initialized.
+ const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu));
defer gpa.free(found_fields);
- @memset(found_fields, .none);
+ @memset(found_fields, false);
- var struct_ptr_zir_ref: Zir.Inst.Ref = undefined;
-
- for (instrs, field_indices) |field_ptr, *field_index| {
+ for (instrs) |field_ptr| {
const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
- struct_ptr_zir_ref = field_ptr_extra.lhs;
const field_name = try ip.getOrPutString(
gpa,
pt.tid,
sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
.no_embedded_nulls,
);
- field_index.* = if (struct_ty.isTuple(zcu))
+ const field_index = if (struct_ty.isTuple(zcu))
try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
else
try sema.structFieldIndex(block, struct_ty, field_name, field_src);
- assert(found_fields[field_index.*] == .none);
- found_fields[field_index.*] = field_ptr.toOptional();
+ assert(found_fields[field_index] == false);
+ found_fields[field_index] = true;
}
+ // Our job is simply to deal with default field values. Specifically, any field which was not
+ // explicitly initialized must have its default value stored to the field pointer, or, if the
+ // field has no default value, a compile error must be emitted instead.
+
+ // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However,
+ // that work was actually all redundant:
+ //
+ // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing
+ // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known
+ // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends.
+ //
+ // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known.
+ // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the
+ // field stores were to comptime-mutable pointers, so have already errored if not comptime-known.
+ //
+ // * If the value is runtime-known, then comptime-known fields must be validated as runtime values.
+ // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`.
+
var root_msg: ?*Zcu.ErrorMsg = null;
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
- const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
- if (block.isComptime() and
- (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
- {
- try struct_ty.resolveLayout(pt);
- // In this case the only thing we need to do is evaluate the implicit
- // store instructions for default field values, and report any missing fields.
- // Avoid the cost of the extra machinery for detecting a comptime struct init value.
- for (found_fields, 0..) |field_ptr, i_usize| {
- const i: u32 = @intCast(i_usize);
- if (field_ptr != .none) continue;
-
- try struct_ty.resolveStructFieldInits(pt);
- const default_val = struct_ty.structFieldDefaultValue(i, zcu);
- if (default_val.toIntern() == .unreachable_value) {
- const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
- const template = "missing tuple field with index {d}";
- if (root_msg) |msg| {
- try sema.errNote(init_src, msg, template, .{i});
- } else {
- root_msg = try sema.errMsg(init_src, template, .{i});
- }
- continue;
- };
- const template = "missing struct field: {f}";
- const args = .{field_name.fmt(ip)};
- if (root_msg) |msg| {
- try sema.errNote(init_src, msg, template, args);
- } else {
- root_msg = try sema.errMsg(init_src, template, args);
- }
- continue;
- }
-
- const field_src = init_src; // TODO better source location
- const default_field_ptr = if (struct_ty.isTuple(zcu))
- try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
- else
- try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
- const init = Air.internedToRef(default_val.toIntern());
- try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
- }
-
- if (root_msg) |msg| {
- try sema.addDeclaredHereNote(msg, struct_ty);
- root_msg = null;
- return sema.failWithOwnedErrorMsg(block, msg);
- }
-
- return;
- }
-
- var fields_allow_runtime = true;
-
- var struct_is_comptime = true;
- var first_block_index = block.instructions.items.len;
-
- const require_comptime = try struct_ty.comptimeOnlySema(pt);
- const air_tags = sema.air_instructions.items(.tag);
- const air_datas = sema.air_instructions.items(.data);
-
- try struct_ty.resolveStructFieldInits(pt);
-
- // We collect the comptime field values in case the struct initialization
- // ends up being comptime-known.
- const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(zcu));
-
- field: for (found_fields, 0..) |opt_field_ptr, i_usize| {
+ for (found_fields, 0..) |explicit, i_usize| {
+ if (explicit) continue;
const i: u32 = @intCast(i_usize);
- if (opt_field_ptr.unwrap()) |field_ptr| {
- // Determine whether the value stored to this pointer is comptime-known.
- const field_ty = struct_ty.fieldType(i, zcu);
- if (try sema.typeHasOnePossibleValue(field_ty)) |opv| {
- field_values[i] = opv.toIntern();
- continue;
- }
-
- const field_ptr_ref = sema.inst_map.get(field_ptr).?;
-
- //std.debug.print("validateStructInit (field_ptr_ref=%{d}):\n", .{field_ptr_ref});
- //for (block.instructions.items) |item| {
- // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[@intFromEnum(item)])});
- //}
-
- // We expect to see something like this in the current block AIR:
- // %a = field_ptr(...)
- // store(%a, %b)
- // With an optional bitcast between the store and the field_ptr.
- // If %b is a comptime operand, this field is comptime.
- //
- // However, in the case of a comptime-known pointer to a struct, the
- // the field_ptr instruction is missing, so we have to pattern-match
- // based only on the store instructions.
- // `first_block_index` needs to point to the `field_ptr` if it exists;
- // the `store` otherwise.
-
- // Possible performance enhancement: save the `block_index` between iterations
- // of the for loop.
- var block_index = block.instructions.items.len;
- while (block_index > 0) {
- block_index -= 1;
- const store_inst = block.instructions.items[block_index];
- if (store_inst.toRef() == field_ptr_ref) {
- struct_is_comptime = false;
- continue :field;
- }
- switch (air_tags[@intFromEnum(store_inst)]) {
- .store, .store_safe => {},
- else => continue,
- }
- const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
- var ptr_ref = bin_op.lhs;
- if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
- ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
- };
- if (ptr_ref != field_ptr_ref) continue;
- first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst|
- std.mem.lastIndexOfScalar(
- Air.Inst.Index,
- block.instructions.items[0..block_index],
- field_ptr_inst,
- ).?
- else
- block_index, first_block_index);
- if (!sema.checkRuntimeValue(bin_op.rhs)) fields_allow_runtime = false;
- if (try sema.resolveValue(bin_op.rhs)) |val| {
- field_values[i] = val.toIntern();
- } else if (require_comptime) {
- const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
- const src = block.nodeOffset(field_ptr_data.src_node);
- return sema.failWithNeededComptime(block, src, .{ .comptime_only = .{
- .ty = struct_ty,
- .msg = .struct_init,
- } });
- } else {
- struct_is_comptime = false;
- }
- continue :field;
- }
- struct_is_comptime = false;
- continue :field;
- }
+ try struct_ty.resolveStructFieldInits(pt);
const default_val = struct_ty.structFieldDefaultValue(i, zcu);
if (default_val.toIntern() == .unreachable_value) {
const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
@@ -5214,70 +4953,6 @@ fn validateStructInit(
}
continue;
}
- field_values[i] = default_val.toIntern();
- }
-
- if (!struct_is_comptime and !fields_allow_runtime and root_msg == null) {
- root_msg = try sema.errMsg(init_src, "runtime value contains reference to comptime var", .{});
- try sema.errNote(init_src, root_msg.?, "comptime var pointers are not available at runtime", .{});
- }
-
- if (root_msg) |msg| {
- try sema.addDeclaredHereNote(msg, struct_ty);
- root_msg = null;
- return sema.failWithOwnedErrorMsg(block, msg);
- }
-
- if (struct_is_comptime) {
- // Our task is to delete all the `field_ptr` and `store` instructions, and insert
- // instead a single `store` to the struct_ptr with a comptime struct value.
- var init_index: usize = 0;
- var field_ptr_ref = Air.Inst.Ref.none;
- var block_index = first_block_index;
- for (block.instructions.items[first_block_index..]) |cur_inst| {
- while (field_ptr_ref == .none and init_index < instrs.len) : (init_index += 1) {
- const field_ty = struct_ty.fieldType(field_indices[init_index], zcu);
- if (try field_ty.onePossibleValue(pt)) |_| continue;
- field_ptr_ref = sema.inst_map.get(instrs[init_index]).?;
- }
- switch (air_tags[@intFromEnum(cur_inst)]) {
- .struct_field_ptr,
- .struct_field_ptr_index_0,
- .struct_field_ptr_index_1,
- .struct_field_ptr_index_2,
- .struct_field_ptr_index_3,
- => if (cur_inst.toRef() == field_ptr_ref) continue,
- .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue,
- .store, .store_safe => {
- var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
- if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
- ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
- };
- if (ptr_ref == field_ptr_ref) {
- field_ptr_ref = .none;
- continue;
- }
- },
- else => {},
- }
- block.instructions.items[block_index] = cur_inst;
- block_index += 1;
- }
- block.instructions.shrinkRetainingCapacity(block_index);
-
- const struct_val = try pt.intern(.{ .aggregate = .{
- .ty = struct_ty.toIntern(),
- .storage = .{ .elems = field_values },
- } });
- const struct_init = Air.internedToRef(struct_val);
- try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
- return;
- }
- try struct_ty.resolveLayout(pt);
-
- // Our task is to insert `store` instructions for all the default field values.
- for (found_fields, 0..) |field_ptr, i| {
- if (field_ptr != .none) continue;
const field_src = init_src; // TODO better source location
const default_field_ptr = if (struct_ty.isTuple(zcu))
@@ -5285,8 +4960,13 @@ fn validateStructInit(
else
try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
- const init = Air.internedToRef(field_values[i]);
- try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
+ try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
+ }
+
+ if (root_msg) |msg| {
+ try sema.addDeclaredHereNote(msg, struct_ty);
+ root_msg = null;
+ return sema.failWithOwnedErrorMsg(block, msg);
}
}
@@ -5307,15 +4987,14 @@ fn zirValidatePtrArrayInit(
const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);
const array_len = array_ty.arrayLen(zcu);
- // Collect the comptime element values in case the array literal ends up
- // being comptime-known.
- const element_vals = try sema.arena.alloc(
- InternPool.Index,
- try sema.usizeCast(block, init_src, array_len),
- );
+ // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR
+ // to initialize them, or emitting a compile error if an unspecified field has no default. For
+ // tuples, there are literally default field values, although they're guaranteed to be comptime
+ // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never
+ // specified so we always need to initialize here. For vectors, there's no such thing.
- if (instrs.len != array_len) switch (array_ty.zigTypeTag(zcu)) {
- .@"struct" => {
+ switch (array_ty.zigTypeTag(zcu)) {
+ .@"struct" => if (instrs.len != array_len) {
var root_msg: ?*Zcu.ErrorMsg = null;
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
@@ -5332,8 +5011,6 @@ fn zirValidatePtrArrayInit(
}
continue;
}
-
- element_vals[i] = default_val;
}
if (root_msg) |msg| {
@@ -5341,162 +5018,25 @@ fn zirValidatePtrArrayInit(
return sema.failWithOwnedErrorMsg(block, msg);
}
},
- .array => {
+
+ .array => if (instrs.len != array_len) {
return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
array_len, instrs.len,
});
- },
- .vector => {
- return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
- array_len, instrs.len,
- });
- },
- else => unreachable,
- };
-
- if (block.isComptime() and
- (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null)
- {
- // In this case the comptime machinery will have evaluated the store instructions
- // at comptime so we have almost nothing to do here. However, in case of a
- // sentinel-terminated array, the sentinel will not have been populated by
- // any ZIR instructions at comptime; we need to do that here.
- if (array_ty.sentinel(zcu)) |sentinel_val| {
+ } else if (array_ty.sentinel(zcu)) |sentinel| {
const array_len_ref = try pt.intRef(.usize, array_len);
const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
- const sentinel = Air.internedToRef(sentinel_val.toIntern());
- try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
- }
- return;
- }
+ try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr);
+ try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store);
+ },
- // If the array has one possible value, the value is always comptime-known.
- if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| {
- const array_init = Air.internedToRef(array_opv.toIntern());
- try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
- return;
- }
+ .vector => if (instrs.len != array_len) {
+ return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
+ array_len, instrs.len,
+ });
+ },
- var array_is_comptime = true;
- var first_block_index = block.instructions.items.len;
-
- const air_tags = sema.air_instructions.items(.tag);
- const air_datas = sema.air_instructions.items(.data);
-
- outer: for (instrs, 0..) |elem_ptr, i| {
- // Determine whether the value stored to this pointer is comptime-known.
-
- if (array_ty.isTuple(zcu)) {
- if (array_ty.structFieldIsComptime(i, zcu))
- try array_ty.resolveStructFieldInits(pt);
- if (try array_ty.structFieldValueComptime(pt, i)) |opv| {
- element_vals[i] = opv.toIntern();
- continue;
- }
- }
-
- const elem_ptr_ref = sema.inst_map.get(elem_ptr).?;
-
- // We expect to see something like this in the current block AIR:
- // %a = elem_ptr(...)
- // store(%a, %b)
- // With an optional bitcast between the store and the elem_ptr.
- // If %b is a comptime operand, this element is comptime.
- //
- // However, in the case of a comptime-known pointer to an array, the
- // the elem_ptr instruction is missing, so we have to pattern-match
- // based only on the store instructions.
- // `first_block_index` needs to point to the `elem_ptr` if it exists;
- // the `store` otherwise.
- //
- // This is nearly identical to similar logic in `validateStructInit`.
-
- // Possible performance enhancement: save the `block_index` between iterations
- // of the for loop.
- var block_index = block.instructions.items.len;
- while (block_index > 0) {
- block_index -= 1;
- const store_inst = block.instructions.items[block_index];
- if (store_inst.toRef() == elem_ptr_ref) {
- array_is_comptime = false;
- continue :outer;
- }
- switch (air_tags[@intFromEnum(store_inst)]) {
- .store, .store_safe => {},
- else => continue,
- }
- const bin_op = air_datas[@intFromEnum(store_inst)].bin_op;
- var ptr_ref = bin_op.lhs;
- if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
- ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
- };
- if (ptr_ref != elem_ptr_ref) continue;
- first_block_index = @min(if (elem_ptr_ref.toIndex()) |elem_ptr_inst|
- std.mem.lastIndexOfScalar(
- Air.Inst.Index,
- block.instructions.items[0..block_index],
- elem_ptr_inst,
- ).?
- else
- block_index, first_block_index);
- if (try sema.resolveValue(bin_op.rhs)) |val| {
- element_vals[i] = val.toIntern();
- } else {
- array_is_comptime = false;
- }
- continue :outer;
- }
- array_is_comptime = false;
- continue :outer;
- }
-
- if (array_is_comptime) {
- if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| {
- switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
- .ptr => |ptr| switch (ptr.base_addr) {
- .comptime_field => return, // This store was validated by the individual elem ptrs.
- else => {},
- },
- else => {},
- }
- }
-
- // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
- // instead a single `store` to the array_ptr with a comptime struct value.
- var elem_index: usize = 0;
- var elem_ptr_ref = Air.Inst.Ref.none;
- var block_index = first_block_index;
- for (block.instructions.items[first_block_index..]) |cur_inst| {
- while (elem_ptr_ref == .none and elem_index < instrs.len) : (elem_index += 1) {
- if (array_ty.isTuple(zcu) and array_ty.structFieldIsComptime(elem_index, zcu)) continue;
- elem_ptr_ref = sema.inst_map.get(instrs[elem_index]).?;
- }
- switch (air_tags[@intFromEnum(cur_inst)]) {
- .ptr_elem_ptr => if (cur_inst.toRef() == elem_ptr_ref) continue,
- .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == elem_ptr_ref) continue,
- .store, .store_safe => {
- var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs;
- if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
- ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
- };
- if (ptr_ref == elem_ptr_ref) {
- elem_ptr_ref = .none;
- continue;
- }
- },
- else => {},
- }
- block.instructions.items[block_index] = cur_inst;
- block_index += 1;
- }
- block.instructions.shrinkRetainingCapacity(block_index);
-
- const array_val = try pt.intern(.{ .aggregate = .{
- .ty = array_ty.toIntern(),
- .storage = .{ .elems = element_vals },
- } });
- const array_init = Air.internedToRef(array_val);
- try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
+ else => unreachable,
}
}
@@ -28015,15 +27555,24 @@ fn unionFieldPtr(
return Air.internedToRef(field_ptr_val.toIntern());
}
- if (!initializing and union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and
- union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1)
- {
- const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
- const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
- // TODO would it be better if get_union_tag supported pointers to unions?
- const union_val = try block.addTyOp(.load, union_ty, union_ptr);
- const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_val);
- try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag);
+ // If the union has a tag, we must either set or or safety check it depending on `initializing`.
+ tag: {
+ if (union_ty.containerLayout(zcu) != .auto) break :tag;
+ const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty);
+ if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag;
+ // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
+ // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
+ const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
+ if (initializing) {
+ const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
+ try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
+ } else if (block.wantSafety() and union_obj.hasTag(ip)) {
+ // The tag exists at runtime (safety tag), so emit a safety check.
+ // TODO would it be better if get_union_tag supported pointers to unions?
+ const union_val = try block.addTyOp(.load, union_ty, union_ptr);
+ const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val);
+ try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag));
+ }
}
if (field_ty.zigTypeTag(zcu) == .noreturn) {
_ = try block.addNoOp(.unreach);
diff --git a/test/behavior/array.zig b/test/behavior/array.zig
index 20c275382fbb2ffb3234a10292581a3539a35963..07eb632b1ef640b8b87f68aa2cf21029e6193a36 100644
--- a/test/behavior/array.zig
+++ b/test/behavior/array.zig
@@ -540,7 +540,6 @@ test "sentinel element count towards the ABI size calculation" {
}
test "zero-sized array with recursive type definition" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1098,3 +1097,16 @@ test "initialize pointer to anyopaque with reference to empty array initializer"
// We can't check the value, but it's zero-bit, so the type matching is good enough.
comptime assert(@TypeOf(loaded) == @TypeOf(.{}));
}
+
+test "sentinel of runtime-known array initialization is populated" {
+ if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
+
+ var rt: u32 = undefined;
+ rt = 42;
+
+ const arr: [1:123]u32 = .{rt};
+ const elems: [*]const u32 = &arr;
+
+ try expect(elems[0] == 42);
+ try expect(elems[1] == 123);
+}
diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig
index 742b3060595cc7515316a4945d779ab551cfe10d..04021c28f7deb3308b6b4f70888f77431dd62775 100644
--- a/test/behavior/field_parent_ptr.zig
+++ b/test/behavior/field_parent_ptr.zig
@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
const builtin = @import("builtin");
test "@fieldParentPtr struct" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@@ -591,6 +590,7 @@ test "@fieldParentPtr unaligned packed struct" {
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
const C = packed struct {
a: bool = true,
@@ -729,6 +729,7 @@ test "@fieldParentPtr aligned packed struct" {
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
const C = packed struct {
a: f32 = 3.14,
@@ -866,6 +867,7 @@ test "@fieldParentPtr nested packed struct" {
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
{
const C = packed struct {
@@ -1340,7 +1342,6 @@ test "@fieldParentPtr packed struct last zero-bit field" {
}
test "@fieldParentPtr tagged union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1477,7 +1478,6 @@ test "@fieldParentPtr tagged union" {
}
test "@fieldParentPtr untagged union" {
- if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
--
2.54.0
From b1dcf2b149c55cf8bc53cd9b9bdb707a0003e93f Mon Sep 17 00:00:00 2001
From: mlugg
Date: Tue, 29 Jul 2025 10:36:31 +0100
Subject: [PATCH 035/110] Sema: fix comptime-known union initialization with
OPV field
The previous commit uncovered this existing OPV bug by triggering this
logic more frequently.
---
src/Sema.zig | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index c44e2eb5e537b4b4cb8fe2f7e134901b54b33665..8e0237bb9eefa6ec2abae5d1577b2867071b0f59 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3922,7 +3922,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
const ptr_to_map = switch (store_inst.tag) {
.store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.
- .set_union_tag => continue, // We can completely ignore these: we'll do it implicitly when we get the field pointer.
+ .set_union_tag => continue, // Ignore for now; handled after we map pointers
.optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
else => unreachable,
};
@@ -4055,19 +4055,33 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
}
// We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.
- // Any implicit stores performed by `optional_payload_ptr_set`, `errunion_payload_ptr_set`, or
- // `set_union_tag` instructions were already done above.
+ // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set`
+ // instructions were already done above.
for (stores) |store_inst_idx| {
const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
switch (store_inst.tag) {
- .set_union_tag => {}, // Handled implicitly by field pointers above
.optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above
+ .set_union_tag => {
+ // Usually, we can ignore these, because the creation of the field pointer above
+ // already did it for us. However, if the field is OPV, this is relevant, because
+ // there is not going to be a store to the field. So we must initialize the union
+ // tag if the field is OPV.
+ const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
+ const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?);
+ const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?);
+ const union_ty = union_ptr_val.typeOf(zcu).childType(zcu);
+ const field_ty = union_ty.unionFieldType(tag_val, zcu).?;
+ if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| {
+ const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val);
+ try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty);
+ }
+ },
.store, .store_safe => {
const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?;
const new_ptr = ptr_mapping.get(air_ptr_inst).?;
- try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(new_ptr), store_val, .fromInterned(zcu.intern_pool.typeOf(store_val.toIntern())));
+ try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu));
},
else => unreachable,
}
--
2.54.0
From d0bc5efba4979471f092a533c20ad04f9e730dec Mon Sep 17 00:00:00 2001
From: mlugg
Date: Tue, 29 Jul 2025 11:13:13 +0100
Subject: [PATCH 036/110] Sema: remove dead logic
This is redundant because `storePtr2` will coerce to the return type
which (in `Sema.coerceInMemoryAllowedErrorSets`) will add errors to the
current function's IES if necessary.
---
src/Sema.zig | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index 8e0237bb9eefa6ec2abae5d1577b2867071b0f59..8200b2b23429a7eeb421cb5f7514acf1cbf52e92 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -5328,8 +5328,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
const tracy = trace(@src());
defer tracy.end();
- const pt = sema.pt;
- const zcu = pt.zcu;
const zir_tags = sema.code.instructions.items(.tag);
const zir_datas = sema.code.instructions.items(.data);
const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
@@ -5343,16 +5341,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
else
false;
- // Check for the possibility of this pattern:
- // %a = ret_ptr
- // %b = store(%a, %c)
- // Where %c is an error union or error set. In such case we need to add
- // to the current function's inferred error set, if any.
- if (is_ret and sema.fn_ret_ty_ies != null) switch (sema.typeOf(operand).zigTypeTag(zcu)) {
- .error_union, .error_set => try sema.addToInferredErrorSet(operand),
- else => {},
- };
-
const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });
const air_tag: Air.Inst.Tag = if (is_ret)
--
2.54.0
From 0d482775cc58a086009e63b2dc288ed29904529e Mon Sep 17 00:00:00 2001
From: mlugg
Date: Tue, 29 Jul 2025 11:18:48 +0100
Subject: [PATCH 037/110] Sema: don't rely on Liveness
We're currently experimenting with backends which effectively do their
own liveness analysis, so this old trick of mine isn't necessarily valid
anymore. However, we can fix that trivially: just make the "nop"
instruction we jam into here have the right type. That way, the leftover
field/element pointer instructions are perfectly valid, but still
unused.
---
src/Sema.zig | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index 8200b2b23429a7eeb421cb5f7514acf1cbf52e92..ae7a50af3c60411ca02704e8fe09ddd64e3b4ec3 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -4113,14 +4113,13 @@ fn finishResolveComptimeKnownAllocPtr(
// We're almost done - we have the resolved comptime value. We just need to
// eliminate the now-dead runtime instructions.
- // We will rewrite the AIR to eliminate the alloc and all stores to it.
- // This will cause instructions deriving field pointers etc of the alloc to
- // become invalid, however, since we are removing all stores to those pointers,
- // they will be eliminated by Liveness before they reach codegen.
-
- // The specifics of this instruction aren't really important: we just want
- // Liveness to elide it.
- const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = .u8_type, .operand = .zero_u8 } } };
+ // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to
+ // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this
+ // too because it doesn't really matter what they become.
+ const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{
+ .ty = .fromIntern(alloc_ty.toIntern()),
+ .operand = .zero_usize,
+ } } };
sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);
for (comptime_info.stores.items(.inst)) |store_inst| {
--
2.54.0
From 08f1d63be1baf18ec00514204d49cb77b35115ba Mon Sep 17 00:00:00 2001
From: mlugg
Date: Tue, 29 Jul 2025 22:44:01 +0100
Subject: [PATCH 038/110] disable more failing tests
Wow, *lots* of backends were reliant on Sema doing the heavy lifting for
them. CBE, Wasm, and SPIR-V have all regressed in places now that they
actually need to, like, initialize unions and such.
---
lib/std/fmt.zig | 2 ++
test/behavior/cast_int.zig | 1 +
test/behavior/field_parent_ptr.zig | 3 +++
test/behavior/packed-struct.zig | 1 +
4 files changed, 7 insertions(+)
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index 0c51d56e30b42c2105ff2058fc50bf378cee8efb..b6730e1cf1a65a21b366bb855f114bb0e75c42a1 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -1101,6 +1101,8 @@ test "float.libc.sanity" {
}
test "union" {
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+
const TU = union(enum) {
float: f32,
int: u32,
diff --git a/test/behavior/cast_int.zig b/test/behavior/cast_int.zig
index 30cad924fe101fd2d5cf3dedf3cbbb90667146a7..0c4d01f5010dad80009375591ba497a5e9378996 100644
--- a/test/behavior/cast_int.zig
+++ b/test/behavior/cast_int.zig
@@ -217,6 +217,7 @@ test "load non byte-sized value in struct" {
test "load non byte-sized value in union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig
index 04021c28f7deb3308b6b4f70888f77431dd62775..65050e3df08a868f6a0520a4fa080ab4e481323d 100644
--- a/test/behavior/field_parent_ptr.zig
+++ b/test/behavior/field_parent_ptr.zig
@@ -586,6 +586,7 @@ test "@fieldParentPtr extern struct last zero-bit field" {
}
test "@fieldParentPtr unaligned packed struct" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -725,6 +726,7 @@ test "@fieldParentPtr unaligned packed struct" {
}
test "@fieldParentPtr aligned packed struct" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@@ -1614,6 +1616,7 @@ test "@fieldParentPtr untagged union" {
}
test "@fieldParentPtr extern union" {
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig
index 2d057e21df43ab542abf37790faacb3aac74dcf5..90b5eedb9da0b1cba0c06b69c34eedabf11cb65b 100644
--- a/test/behavior/packed-struct.zig
+++ b/test/behavior/packed-struct.zig
@@ -1319,6 +1319,7 @@ test "packed struct equality ignores padding bits" {
}
test "packed struct with signed field" {
+ if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
var s: packed struct {
--
2.54.0
From 1fcaf90dd3c99d452fcab13698a63faf17e8f3c1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 29 Jul 2025 18:18:49 -0700
Subject: [PATCH 039/110] std.Io.Reader: make fillUnbuffered respect prexisting
buffer
addresses only one usage pattern in #24608
---
lib/std/Io/Reader.zig | 9 ---------
lib/std/compress/zstd/Decompress.zig | 5 ++---
2 files changed, 2 insertions(+), 12 deletions(-)
diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig
index da9e01dd2c43e65d7522f4afa8d10e506cf4a2f6..db188c87bcb3b4046608667c64f96db815c25055 100644
--- a/lib/std/Io/Reader.zig
+++ b/lib/std/Io/Reader.zig
@@ -1055,15 +1055,6 @@ pub fn fill(r: *Reader, n: usize) Error!void {
/// Missing this optimization can result in wall-clock time for the most affected benchmarks
/// increasing by a factor of 5 or more.
fn fillUnbuffered(r: *Reader, n: usize) Error!void {
- 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;
- };
try rebase(r, n);
var writer: Writer = .{
.buffer = r.buffer,
diff --git a/lib/std/compress/zstd/Decompress.zig b/lib/std/compress/zstd/Decompress.zig
index b13a2dcf7a70b3b0aedf81978f43ccee0c20ca5f..eb431e644cc7cce94bce3dd5fc0f259cd1d84ff3 100644
--- a/lib/std/compress/zstd/Decompress.zig
+++ b/lib/std/compress/zstd/Decompress.zig
@@ -100,9 +100,8 @@ fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
assert(capacity <= r.buffer.len - d.window_len);
assert(r.end + capacity > r.buffer.len);
- const buffered = r.buffer[0..r.end];
- const discard = buffered.len - d.window_len;
- const keep = buffered[discard..];
+ const discard = r.end - d.window_len;
+ const keep = r.buffer[discard..r.end];
@memmove(r.buffer[0..keep.len], keep);
r.end = keep.len;
r.seek -= discard;
--
2.54.0
From 4a1594fbdebfeae989ff7c74be737b4879e1916e Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 29 May 2025 13:27:51 +0200
Subject: [PATCH 040/110] update `zig env` to respect `ZIG_LIB_DIR` and support
wasi
---
src/main.zig | 7 ++++++-
src/print_env.zig | 42 ++++++++++++++++++++++++++++++++----------
2 files changed, 38 insertions(+), 11 deletions(-)
diff --git a/src/main.zig b/src/main.zig
index 68fbd5b0a8f0b8957225663502d8ab143444197c..9349899a561a1f9b024e7b9afcf61393acc86367 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -361,7 +361,12 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
dev.check(.env_command);
verifyLibcxxCorrectlyLinked();
var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
- try @import("print_env.zig").cmdEnv(arena, &stdout_writer.interface);
+ try @import("print_env.zig").cmdEnv(
+ arena,
+ &stdout_writer.interface,
+ args,
+ if (native_os == .wasi) wasi_preopens,
+ );
return stdout_writer.interface.flush();
} else if (mem.eql(u8, cmd, "reduce")) {
return jitCmd(gpa, arena, cmd_args, .{
diff --git a/src/print_env.zig b/src/print_env.zig
index d1251c0d62c89f4aaa950f98d520bbae2983b980..e1b2b1eb83b812e0cfda2d7861e80b7611686af8 100644
--- a/src/print_env.zig
+++ b/src/print_env.zig
@@ -1,21 +1,43 @@
const std = @import("std");
+const builtin = @import("builtin");
const build_options = @import("build_options");
-const introspect = @import("introspect.zig");
+const Compilation = @import("Compilation.zig");
const Allocator = std.mem.Allocator;
+const EnvVar = std.zig.EnvVar;
const fatal = std.process.fatal;
-pub fn cmdEnv(arena: Allocator, out: *std.Io.Writer) !void {
- const cwd_path = try introspect.getResolvedCwd(arena);
- const self_exe_path = try std.fs.selfExePathAlloc(arena);
+pub fn cmdEnv(
+ arena: Allocator,
+ out: *std.Io.Writer,
+ args: []const []const u8,
+ wasi_preopens: switch (builtin.target.os.tag) {
+ .wasi => std.fs.wasi.Preopens,
+ else => void,
+ },
+) !void {
+ const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
+ const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
- var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, cwd_path, self_exe_path) catch |err| {
- fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
+ const self_exe_path = switch (builtin.target.os.tag) {
+ .wasi => args[0],
+ else => std.fs.selfExePathAlloc(arena) catch |err| {
+ fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
+ },
};
- defer zig_lib_directory.handle.close();
- const zig_std_dir = try std.fs.path.join(arena, &[_][]const u8{ zig_lib_directory.path.?, "std" });
+ var dirs: Compilation.Directories = .init(
+ arena,
+ override_lib_dir,
+ override_global_cache_dir,
+ .global,
+ if (builtin.target.os.tag == .wasi) wasi_preopens,
+ if (builtin.target.os.tag != .wasi) self_exe_path,
+ );
+ defer dirs.deinit();
- const global_cache_dir = try introspect.resolveGlobalCacheDir(arena);
+ const zig_lib_dir = dirs.zig_lib.path orelse "";
+ const zig_std_dir = try dirs.zig_lib.join(arena, &.{"std"});
+ const global_cache_dir = dirs.global_cache.path orelse "";
const host = try std.zig.system.resolveTargetQuery(.{});
const triple = try host.zigTriple(arena);
@@ -24,7 +46,7 @@ pub fn cmdEnv(arena: Allocator, out: *std.Io.Writer) !void {
var root = try serializer.beginStruct(.{});
try root.field("zig_exe", self_exe_path, .{});
- try root.field("lib_dir", zig_lib_directory.path.?, .{});
+ try root.field("lib_dir", zig_lib_dir, .{});
try root.field("std_dir", zig_std_dir, .{});
try root.field("global_cache_dir", global_cache_dir, .{});
try root.field("version", build_options.version, .{});
--
2.54.0
From f7dc9b50ab1ebc9714b1fa1b9929ae5f778fed69 Mon Sep 17 00:00:00 2001
From: Kendall Condon
Date: Fri, 27 Jun 2025 13:38:54 -0400
Subject: [PATCH 041/110] llvm: fix atomic widening of packed structs
Additionally, disable failing big-endian atomic test
also improve test paramaters to catch this when condition is removed
also some other cleanups
---
src/codegen/llvm.zig | 4 +++-
test/behavior/atomics.zig | 16 +++++++++++-----
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 111fc6ec14bda2fda509af46f030bc8c2e8954c1..5e522c3d73d0ca9e0469995306ec7c4b2043c98a 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -4339,9 +4339,11 @@ pub const Object = struct {
/// types to work around a LLVM deficiency when targeting ARM/AArch64.
fn getAtomicAbiType(o: *Object, pt: Zcu.PerThread, ty: Type, is_rmw_xchg: bool) Allocator.Error!Builder.Type {
const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
const int_ty = switch (ty.zigTypeTag(zcu)) {
.int => ty,
.@"enum" => ty.intTagType(zcu),
+ .@"struct" => Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntTypeUnordered(ip)),
.float => {
if (!is_rmw_xchg) return .none;
return o.builder.intType(@intCast(ty.abiSize(zcu) * 8));
@@ -11424,7 +11426,7 @@ pub const FuncGen = struct {
if (workaround_disable_truncate) {
// see https://github.com/llvm/llvm-project/issues/64222
- // disable the truncation codepath for larger that 32bits value - with this heuristic, the backend passes the test suite.
+ // disable the truncation codepath for larger than 32bits value - with this heuristic, the backend passes the test suite.
return try fg.wip.load(access_kind, payload_llvm_ty, payload_ptr, payload_alignment, "");
}
diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig
index cda8b5f03317ca0cfacc114c04caee8b9c0d8d61..15d4b99ba49a5bd7aa5bcc6f54f5cc56df83294c 100644
--- a/test/behavior/atomics.zig
+++ b/test/behavior/atomics.zig
@@ -1,7 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
-const expectEqual = std.testing.expectEqual;
const supports_128_bit_atomics = switch (builtin.cpu.arch) {
// TODO: Ideally this could be sync'd with the logic in Sema.
@@ -364,25 +363,32 @@ test "atomics with different types" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+ if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // #24282
try testAtomicsWithType(bool, true, false);
try testAtomicsWithType(u1, 0, 1);
- try testAtomicsWithType(i4, 0, 1);
- try testAtomicsWithType(u5, 0, 1);
- try testAtomicsWithType(i15, 0, 1);
- try testAtomicsWithType(u24, 0, 1);
+ try testAtomicsWithType(i4, 2, 1);
+ try testAtomicsWithType(u5, 2, 1);
+ try testAtomicsWithType(i15, 2, 1);
+ try testAtomicsWithType(u24, 2, 1);
try testAtomicsWithType(u0, 0, 0);
try testAtomicsWithType(i0, 0, 0);
try testAtomicsWithType(enum(u32) { x = 1234, y = 5678 }, .x, .y);
+ try testAtomicsWithType(enum(u19) { x = 1234, y = 5678 }, .x, .y);
try testAtomicsWithPackedStruct(
packed struct { x: u7, y: u24, z: bool },
.{ .x = 1, .y = 2, .z = true },
.{ .x = 3, .y = 4, .z = false },
);
+ try testAtomicsWithPackedStruct(
+ packed struct { x: u19, y: bool },
+ .{ .x = 1, .y = true },
+ .{ .x = 3, .y = false },
+ );
}
fn testAtomicsWithType(comptime T: type, a: T, b: T) !void {
--
2.54.0
From b0d6c227d3fe972844de5660d7398a721f2ba234 Mon Sep 17 00:00:00 2001
From: Kendall Condon
Date: Fri, 27 Jun 2025 13:42:09 -0400
Subject: [PATCH 042/110] Sema: catch error sets in atomic operations
also fix the struct test
---
src/Zcu.zig | 7 ++++++-
.../atomics_with_invalid_type.zig | 17 +++++++++++++++--
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/Zcu.zig b/src/Zcu.zig
index df35777231e5c08c0043e6d9f4962edc3f2d8aad..c13f7aaac9be01da55c2bbff74bdc4c9d54a9416 100644
--- a/src/Zcu.zig
+++ b/src/Zcu.zig
@@ -3859,7 +3859,12 @@ pub fn atomicPtrAlignment(
}
return .none;
}
- if (ty.isAbiInt(zcu)) {
+ if (switch (ty.zigTypeTag(zcu)) {
+ .int, .@"enum" => true,
+ .@"struct" => ty.containerLayout(zcu) == .@"packed",
+ else => false,
+ }) {
+ assert(ty.isAbiInt(zcu));
const bit_count = ty.intInfo(zcu).bits;
if (bit_count > max_atomic_bits) {
diags.* = .{
diff --git a/test/cases/compile_errors/atomics_with_invalid_type.zig b/test/cases/compile_errors/atomics_with_invalid_type.zig
index 321cda3655664a4009ab3614368cb15d6e67b020..4643dc7543bf25ec98330b91fce7d7453569ae7e 100644
--- a/test/cases/compile_errors/atomics_with_invalid_type.zig
+++ b/test/cases/compile_errors/atomics_with_invalid_type.zig
@@ -5,14 +5,27 @@ export fn float() void {
const NormalStruct = struct { x: u32 };
export fn normalStruct() void {
- var x: NormalStruct = 0;
+ var x: NormalStruct = .{ .x = 0 };
_ = @cmpxchgWeak(NormalStruct, &x, .{ .x = 1 }, .{ .x = 2 }, .seq_cst, .seq_cst);
}
+export fn anyError() void {
+ var x: anyerror = error.A;
+ _ = @cmpxchgWeak(anyerror, &x, error.A, error.B, .seq_cst, .seq_cst);
+}
+
+const ErrorSet = error{ A, B };
+export fn errorSet() void {
+ var x: ErrorSet = error.A;
+ _ = @cmpxchgWeak(ErrorSet, &x, error.A, error.B, .seq_cst, .seq_cst);
+}
+
// error
// backend=stage2
// target=native
//
// :3:22: error: expected bool, integer, enum, packed struct, or pointer type; found 'f32'
-// :8:27: error: expected type 'tmp.NormalStruct', found 'comptime_int'
+// :9:22: error: expected bool, integer, float, enum, packed struct, or pointer type; found 'tmp.NormalStruct'
// :6:22: note: struct declared here
+// :14:22: error: expected bool, integer, float, enum, packed struct, or pointer type; found 'anyerror'
+// :20:22: error: expected bool, integer, float, enum, packed struct, or pointer type; found 'error{A,B}'
--
2.54.0
From cbe6e5b7fece38623d5eab43b71a83d1b6c7f323 Mon Sep 17 00:00:00 2001
From: Kendall Condon
Date: Fri, 27 Jun 2025 13:42:50 -0400
Subject: [PATCH 043/110] langref: clarify allowed atomic types
Floats are not allowed in @cmpxchg
Packed structs are allowed for all atomic builtins
---
doc/langref.html.in | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e8189e5c420ce42559b313b9b16090a5c8b94455..e3aa8c584f08de2f902becdb5ecc5189b8d0eb60 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4319,7 +4319,7 @@ comptime {
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
- an integer or an enum.
+ an integer, an enum, or a packed struct.
{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
{#see_also|@atomicStore|@atomicRmw||@cmpxchgWeak|@cmpxchgStrong#}
@@ -4333,7 +4333,7 @@ comptime {
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
- an integer or an enum.
+ an integer, an enum, or a packed struct.
{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
{#syntax#}AtomicRmwOp{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicRmwOp{#endsyntax#}.
@@ -4347,7 +4347,7 @@ comptime {
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
- an integer or an enum.
+ an integer, an enum, or a packed struct.
{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
{#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
@@ -4576,8 +4576,8 @@ comptime {
more efficiently in machine instructions.
- {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
- an integer or an enum.
+ {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#},
+ an integer, an enum, or a packed struct.
{#syntax#}@typeInfo(@TypeOf(ptr)).pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}
{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
@@ -4608,8 +4608,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
- {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
- an integer or an enum.
+ {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#},
+ an integer, an enum, or a packed struct.
{#syntax#}@typeInfo(@TypeOf(ptr)).pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}
{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
--
2.54.0
From 135a34c9631254d43b37be6c7b41d56669960669 Mon Sep 17 00:00:00 2001
From: Kurt Wagner <1269283+KurtWagner@users.noreply.github.com>
Date: Wed, 2 Jul 2025 18:25:57 +1000
Subject: [PATCH 044/110] Update doc comment for `ptr_type` and
`ptr_type_bit_range` to `data` of `.extra_and_node`
The other pointer types are `.opt_node_and_node` but `ptr_type` and `ptr_type_bit_range` contain `.extra_and_node` in their `data` field
---
lib/std/zig/Ast.zig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig
index c15693fd625ee4cd521326cfcc92a031f7dab312..0405e410ee71ca5468ea95e2361ebbda9095a865 100644
--- a/lib/std/zig/Ast.zig
+++ b/lib/std/zig/Ast.zig
@@ -3361,7 +3361,7 @@ pub const Node = struct {
/// The `main_token` might be a ** token, which is shared with a
/// parent/child pointer type and may require special handling.
ptr_type_sentinel,
- /// The `data` field is a `.opt_node_and_node`:
+ /// The `data` field is a `.extra_and_node`:
/// 1. a `ExtraIndex` to `PtrType`.
/// 2. a `Node.Index` to the element type expression.
///
@@ -3370,7 +3370,7 @@ pub const Node = struct {
/// The `main_token` might be a ** token, which is shared with a
/// parent/child pointer type and may require special handling.
ptr_type,
- /// The `data` field is a `.opt_node_and_node`:
+ /// The `data` field is a `.extra_and_node`:
/// 1. a `ExtraIndex` to `PtrTypeBitRange`.
/// 2. a `Node.Index` to the element type expression.
///
--
2.54.0
From 6ec275ebd8fce2e816f3f66e0fec5e53669b96c1 Mon Sep 17 00:00:00 2001
From: Justus Klausecker
Date: Fri, 11 Jul 2025 03:40:30 +0200
Subject: [PATCH 045/110] Sema: remove incorrect safety check for saturating
left shift
---
src/Sema.zig | 2 +-
test/behavior/bit_shifting.zig | 2 --
test/behavior/x86_64/binary.zig | 2 --
3 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index ae7a50af3c60411ca02704e8fe09ddd64e3b4ec3..2483f313a8992c9ed2295f40cef9868c6e6400dc 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -13677,7 +13677,7 @@ fn zirShl(
try sema.requireRuntimeBlock(block, src, runtime_src);
if (block.wantSafety()) {
const bit_count = scalar_ty.intInfo(zcu).bits;
- if (!std.math.isPowerOfTwo(bit_count)) {
+ if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) {
const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count);
const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
diff --git a/test/behavior/bit_shifting.zig b/test/behavior/bit_shifting.zig
index 05b44447086b866588d4bd3767dca0b7b70001a3..8c426dc05e6570813b613d4c6cd7640723cfc3b3 100644
--- a/test/behavior/bit_shifting.zig
+++ b/test/behavior/bit_shifting.zig
@@ -170,8 +170,6 @@ test "Saturating Shift Left" {
const S = struct {
fn shlSat(x: anytype, y: std.math.Log2Int(@TypeOf(x))) @TypeOf(x) {
- // workaround https://github.com/ziglang/zig/issues/23033
- @setRuntimeSafety(false);
return x <<| y;
}
diff --git a/test/behavior/x86_64/binary.zig b/test/behavior/x86_64/binary.zig
index 99dd47155bbfa2225927645ae8d3d154459cc5f3..e90c4f1eb05a45a689a327c3bf46021c0d4ac77e 100644
--- a/test/behavior/x86_64/binary.zig
+++ b/test/behavior/x86_64/binary.zig
@@ -5473,8 +5473,6 @@ inline fn shlSaturate(comptime Type: type, lhs: Type, rhs: Type) Type {
// workaround https://github.com/ziglang/zig/issues/23139
return lhs <<| @min(@abs(rhs), splat(ChangeScalar(Type, u64), imax(u64)));
}
- // workaround https://github.com/ziglang/zig/issues/23033
- @setRuntimeSafety(false);
return lhs <<| @abs(rhs);
}
test shlSaturate {
--
2.54.0
From a9773944dc2b930facd66350dcbe87178aacf487 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?=
Date: Wed, 30 Jul 2025 09:56:21 +0200
Subject: [PATCH 046/110] compiler: disable self-hosted x86_64 backend on
OpenBSD
Same as 97ecb6c551eb628e5a37d18d5a9720d3714a04ef for NetBSD.
---
src/target.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/target.zig b/src/target.zig
index ad83414c23c079ddf23f802efa1a0e116ab9263f..ba7cca639129483610c0ebae103175e40410e26a 100644
--- a/src/target.zig
+++ b/src/target.zig
@@ -236,7 +236,7 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
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) {
- if (target.os.tag == .netbsd) {
+ if (target.os.tag == .netbsd or target.os.tag == .openbsd) {
// Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341
return false;
}
--
2.54.0
From cf7a28febbbe877003d8d4f9a13ceb94698c1e3e Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 29 Jul 2025 23:11:10 -0700
Subject: [PATCH 047/110] std.Io.Reader: introduce readVec back into the VTable
simplifies and fixes things
addresses a subset of #24608
---
lib/std/Io/Reader.zig | 347 ++++++++++++++++-----------
lib/std/Io/Writer.zig | 98 --------
lib/std/compress/zstd/Decompress.zig | 20 +-
lib/std/fs/File.zig | 88 ++++---
lib/std/net.zig | 52 ++--
5 files changed, 313 insertions(+), 292 deletions(-)
diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig
index db188c87bcb3b4046608667c64f96db815c25055..2b3f4d9cd8364b03bc50053507b40f963767716c 100644
--- a/lib/std/Io/Reader.zig
+++ b/lib/std/Io/Reader.zig
@@ -43,8 +43,8 @@ pub const VTable = struct {
///
/// 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.
+ /// accordingly. Implementations are encouraged to take advantage of
+ /// this if it simplifies the logic.
stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
/// Consumes bytes from the internally tracked stream position without
@@ -68,6 +68,21 @@ pub const VTable = struct {
/// This function is only called when `buffer` is empty.
discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
+ /// Returns number of bytes written to `data`.
+ ///
+ /// `data` may not have nonzero length.
+ ///
+ /// `data` may not contain an alias to `Reader.buffer`.
+ ///
+ /// Implementations may ignore `data`, writing directly to `Reader.buffer`,
+ /// modifying `seek` and `end` accordingly, and returning 0 from this
+ /// function. Implementations are encouraged to take advantage of this if
+ /// it simplifies the logic.
+ ///
+ /// The default implementation calls `stream` with either `data[0]` or
+ /// `Reader.buffer`, whichever is bigger.
+ readVec: *const fn (r: *Reader, data: []const []u8) Error!usize = defaultReadVec,
+
/// Ensures `capacity` more data can be buffered without rebasing.
///
/// Asserts `capacity` is within buffer capacity, or that the stream ends
@@ -138,6 +153,7 @@ pub fn fixed(buffer: []const u8) Reader {
.vtable = &.{
.stream = endingStream,
.discard = endingDiscard,
+ .readVec = endingReadVec,
.rebase = endingRebase,
},
// This cast is safe because all potential writes to it will instead
@@ -170,18 +186,18 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {
}
break :l .limited(n - buffered_len);
} else .unlimited;
- r.seek = 0;
- r.end = 0;
+ r.seek = r.end;
const n = try r.vtable.discard(r, remaining);
assert(n <= @intFromEnum(remaining));
return buffered_len + n;
}
pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
- assert(r.seek == 0);
- assert(r.end == 0);
- var dw: Writer.Discarding = .init(r.buffer);
- const n = r.stream(&dw.writer, limit) catch |err| switch (err) {
+ assert(r.seek == r.end);
+ r.seek = 0;
+ r.end = 0;
+ var d: Writer.Discarding = .init(r.buffer);
+ const n = r.stream(&d.writer, limit) catch |err| switch (err) {
error.WriteFailed => unreachable,
error.ReadFailed => return error.ReadFailed,
error.EndOfStream => return error.EndOfStream,
@@ -294,7 +310,8 @@ pub fn appendRemaining(
list: *std.ArrayListAlignedUnmanaged(u8, alignment),
limit: Limit,
) LimitedAllocError!void {
- if (limit != .unlimited) assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
+ if (limit == .unlimited) return appendRemainingUnlimited(r, gpa, alignment, list, 1);
+ assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
const buffer_contents = r.buffer[r.seek..r.end];
const copy_len = limit.minInt(buffer_contents.len);
try list.appendSlice(gpa, r.buffer[0..copy_len]);
@@ -303,32 +320,67 @@ pub fn appendRemaining(
r.seek = 0;
r.end = 0;
var remaining = @intFromEnum(limit) - copy_len;
+ // From here, we leave `buffer` empty, appending directly to `list`.
+ var writer: Writer = .{
+ .buffer = undefined,
+ .end = undefined,
+ .vtable = &.{ .drain = Writer.fixedDrain },
+ };
while (true) {
- try list.ensureUnusedCapacity(gpa, 1);
+ try list.ensureUnusedCapacity(gpa, 2);
const cap = list.unusedCapacitySlice();
- const dest = cap[0..@min(cap.len, remaining)];
- if (remaining - dest.len == 0) {
- // Additionally provides `buffer` to detect end.
- const new_remaining = readVecInner(r, &.{}, dest, remaining) catch |err| switch (err) {
- error.EndOfStream => {
- if (r.bufferedLen() != 0) return error.StreamTooLong;
- return;
- },
- error.ReadFailed => return error.ReadFailed,
- };
- list.items.len += remaining - new_remaining;
- remaining = new_remaining;
- } else {
- // Leave `buffer` empty, appending directly to `list`.
- var dest_w: Writer = .fixed(dest);
- const n = r.vtable.stream(r, &dest_w, .limited(dest.len)) catch |err| switch (err) {
- error.WriteFailed => unreachable, // Prevented by the limit.
- error.EndOfStream => return,
- error.ReadFailed => return error.ReadFailed,
- };
- list.items.len += n;
- remaining -= n;
+ const dest = cap[0..@min(cap.len, remaining + 1)];
+ writer.buffer = list.allocatedSlice();
+ writer.end = list.items.len;
+ const n = r.vtable.stream(r, &writer, .limited(dest.len)) catch |err| switch (err) {
+ error.WriteFailed => unreachable, // Prevented by the limit.
+ error.EndOfStream => return,
+ error.ReadFailed => return error.ReadFailed,
+ };
+ list.items.len += n;
+ if (n > remaining) {
+ // Move the byte to `Reader.buffer` so it is not lost.
+ assert(n - remaining == 1);
+ assert(r.end == 0);
+ r.buffer[0] = list.items[list.items.len - 1];
+ list.items.len -= 1;
+ r.end = 1;
+ return;
}
+ remaining -= n;
+ }
+}
+
+pub const UnlimitedAllocError = Allocator.Error || ShortError;
+
+pub fn appendRemainingUnlimited(
+ r: *Reader,
+ gpa: Allocator,
+ comptime alignment: ?std.mem.Alignment,
+ list: *std.ArrayListAlignedUnmanaged(u8, alignment),
+ bump: usize,
+) UnlimitedAllocError!void {
+ const buffer_contents = r.buffer[r.seek..r.end];
+ try list.ensureUnusedCapacity(gpa, buffer_contents.len + bump);
+ list.appendSliceAssumeCapacity(buffer_contents);
+ r.seek = 0;
+ r.end = 0;
+ // From here, we leave `buffer` empty, appending directly to `list`.
+ var writer: Writer = .{
+ .buffer = undefined,
+ .end = undefined,
+ .vtable = &.{ .drain = Writer.fixedDrain },
+ };
+ while (true) {
+ try list.ensureUnusedCapacity(gpa, bump);
+ writer.buffer = list.allocatedSlice();
+ writer.end = list.items.len;
+ const n = r.vtable.stream(r, &writer, .limited(list.unusedCapacitySlice().len)) catch |err| switch (err) {
+ error.WriteFailed => unreachable, // Prevented by the limit.
+ error.EndOfStream => return,
+ error.ReadFailed => return error.ReadFailed,
+ };
+ list.items.len += n;
}
}
@@ -340,95 +392,64 @@ pub fn appendRemaining(
///
/// The reader's internal logical seek position moves forward in accordance
/// with the number of bytes returned from this function.
-pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
- return readVecLimit(r, data, .unlimited);
-}
-
-/// Equivalent to `readVec` but reads at most `limit` bytes.
-///
-/// This ultimately will lower to a call to `stream`, but it must ensure
-/// that the buffer used has at least as much capacity, in case that function
-/// depends on a minimum buffer capacity. It also ensures that if the `stream`
-/// implementation calls `Writer.writableVector`, it will get this data slice
-/// along with the buffer at the end.
-pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
- comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));
- var remaining = @intFromEnum(limit);
+pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
+ var seek = r.seek;
for (data, 0..) |buf, i| {
- const buffer_contents = r.buffer[r.seek..r.end];
- const copy_len = @min(buffer_contents.len, buf.len, remaining);
- @memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);
- r.seek += copy_len;
- remaining -= copy_len;
- if (remaining == 0) break;
+ const contents = r.buffer[seek..r.end];
+ const copy_len = @min(contents.len, buf.len);
+ @memcpy(buf[0..copy_len], contents[0..copy_len]);
+ seek += copy_len;
if (buf.len - copy_len == 0) continue;
- // All of `buffer` has been copied to `data`. We now set up a structure
- // that enables the `Writer.writableVector` API, while also ensuring
- // API that directly operates on the `Writable.buffer` has its minimum
- // buffer capacity requirements met.
- r.seek = 0;
- r.end = 0;
- remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
- break;
+ // All of `buffer` has been copied to `data`.
+ const n = seek - r.seek;
+ r.seek = seek;
+ data[i] = buf[copy_len..];
+ defer data[i] = buf;
+ return n + try r.vtable.readVec(r, data[i..]);
}
- return @intFromEnum(limit) - remaining;
+ const n = seek - r.seek;
+ r.seek = seek;
+ return n;
}
-fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {
- var wrapper: Writer.VectorWrapper = .{
- .it = .{
- .first = first,
- .middle = middle,
- .last = r.buffer,
- },
- .writer = .{
- .buffer = if (first.len >= r.buffer.len) first else r.buffer,
- .vtable = Writer.VectorWrapper.vtable,
- },
+/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.
+pub fn defaultReadVec(r: *Reader, data: []const []u8) Error!usize {
+ assert(r.seek == r.end);
+ r.seek = 0;
+ r.end = 0;
+ const first = data[0];
+ const direct = first.len >= r.buffer.len;
+ var writer: Writer = .{
+ .buffer = if (direct) first else r.buffer,
+ .end = 0,
+ .vtable = &.{ .drain = Writer.fixedDrain },
};
- // If the limit may pass beyond user buffer into Reader buffer, use
- // unlimited, allowing the Reader buffer to fill.
- const limit: Limit = l: {
- var n: usize = first.len;
- for (middle) |m| n += m.len;
- break :l if (remaining >= n) .unlimited else .limited(remaining);
+ const limit: Limit = .limited(writer.buffer.len - writer.end);
+ const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
+ error.WriteFailed => unreachable,
+ else => |e| return e,
+ };
+ if (direct) return n;
+ r.end += n;
+ return 0;
+}
+
+/// Always writes to `Reader.buffer` and returns 0.
+pub fn indirectReadVec(r: *Reader, data: []const []u8) Error!usize {
+ _ = data;
+ assert(r.seek == r.end);
+ var writer: Writer = .{
+ .buffer = r.buffer,
+ .end = r.end,
+ .vtable = &.{ .drain = Writer.fixedDrain },
};
- var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {
- error.WriteFailed => {
- assert(!wrapper.used);
- if (wrapper.writer.buffer.ptr == first.ptr) {
- return remaining - wrapper.writer.end;
- } else {
- assert(wrapper.writer.end <= r.buffer.len);
- r.end = wrapper.writer.end;
- return remaining;
- }
- },
+ const limit: Limit = .limited(writer.buffer.len - writer.end);
+ r.end += r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
+ error.WriteFailed => unreachable,
else => |e| return e,
};
- if (!wrapper.used) {
- if (wrapper.writer.buffer.ptr == first.ptr) {
- return remaining - n;
- } else {
- assert(n <= r.buffer.len);
- r.end = n;
- return remaining;
- }
- }
- if (n < first.len) return remaining - n;
- var result = remaining - first.len;
- n -= first.len;
- for (middle) |mid| {
- if (n < mid.len) {
- return result - n;
- }
- result -= mid.len;
- n -= mid.len;
- }
- assert(n <= r.buffer.len);
- r.end = n;
- return result;
+ return 0;
}
pub fn buffered(r: *Reader) []u8 {
@@ -642,29 +663,24 @@ pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
/// See also:
/// * `readSliceAll`
pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
- var i: usize = 0;
+ const contents = r.buffer[r.seek..r.end];
+ const copy_len = @min(buffer.len, contents.len);
+ @memcpy(buffer[0..copy_len], contents[0..copy_len]);
+ r.seek += copy_len;
+ if (buffer.len - copy_len == 0) {
+ @branchHint(.likely);
+ return buffer.len;
+ }
+ var i: usize = copy_len;
+ var data: [1][]u8 = undefined;
while (true) {
- const buffer_contents = r.buffer[r.seek..r.end];
- const dest = buffer[i..];
- const copy_len = @min(dest.len, buffer_contents.len);
- @memcpy(dest[0..copy_len], buffer_contents[0..copy_len]);
- if (dest.len - copy_len == 0) {
- @branchHint(.likely);
- r.seek += copy_len;
- return buffer.len;
- }
- i += copy_len;
- r.end = 0;
- r.seek = 0;
- const remaining = buffer[i..];
- const new_remaining_len = readVecInner(r, &.{}, remaining, remaining.len) catch |err| switch (err) {
+ data[0] = buffer[i..];
+ i += readVec(r, &data) catch |err| switch (err) {
error.EndOfStream => return i,
error.ReadFailed => return error.ReadFailed,
};
- if (new_remaining_len == 0) return buffer.len;
- i += remaining.len - new_remaining_len;
+ if (buffer.len - i == 0) return buffer.len;
}
- return buffer.len;
}
/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
@@ -1632,19 +1648,6 @@ test readVec {
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 buffer: [3]u8 = undefined;
@@ -1661,6 +1664,12 @@ fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
return error.EndOfStream;
}
+fn endingReadVec(r: *Reader, data: []const []u8) Error!usize {
+ _ = r;
+ _ = data;
+ return error.EndOfStream;
+}
+
fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
_ = r;
_ = limit;
@@ -1797,3 +1806,57 @@ pub fn Hashed(comptime Hasher: type) type {
}
};
}
+
+pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } {
+ var i: usize = 0;
+ var n: usize = 0;
+ for (data) |buf| {
+ if (buffer.len - i == 0) return .{ i, n };
+ if (buf.len != 0) {
+ buffer[i] = .{ .base = buf.ptr, .len = buf.len };
+ i += 1;
+ n += buf.len;
+ }
+ }
+ assert(r.seek == r.end);
+ const buf = r.buffer;
+ if (buf.len != 0) {
+ buffer[i] = .{ .base = buf.ptr, .len = buf.len };
+ i += 1;
+ }
+ return .{ i, n };
+}
+
+pub fn writableVectorWsa(
+ r: *Reader,
+ buffer: []std.os.windows.ws2_32.WSABUF,
+ data: []const []u8,
+) Error!struct { usize, usize } {
+ var i: usize = 0;
+ var n: usize = 0;
+ for (data) |buf| {
+ if (buffer.len - i == 0) return .{ i, n };
+ if (buf.len == 0) continue;
+ if (std.math.cast(u32, buf.len)) |len| {
+ buffer[i] = .{ .buf = buf.ptr, .len = len };
+ i += 1;
+ n += len;
+ continue;
+ }
+ buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
+ i += 1;
+ n += std.math.maxInt(u32);
+ return .{ i, n };
+ }
+ assert(r.seek == r.end);
+ const buf = r.buffer;
+ if (buf.len != 0) {
+ if (std.math.cast(u32, buf.len)) |len| {
+ buffer[i] = .{ .buf = buf.ptr, .len = len };
+ } else {
+ buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
+ }
+ i += 1;
+ }
+ return .{ i, n };
+}
diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig
index 06a65340713a17e602ffc9a3c8714740e3bc1f97..a177bda8ffa21ea1135e49dfac5ae37b4cd4a173 100644
--- a/lib/std/Io/Writer.zig
+++ b/lib/std/Io/Writer.zig
@@ -342,97 +342,6 @@ pub fn writableSlicePreserve(w: *Writer, preserve_len: usize, len: usize) Error!
return big_slice[0..len];
}
-pub const WritableVectorIterator = struct {
- first: []u8,
- middle: []const []u8 = &.{},
- last: []u8 = &.{},
- index: usize = 0,
-
- pub fn next(it: *WritableVectorIterator) ?[]u8 {
- while (true) {
- const i = it.index;
- it.index += 1;
- if (i == 0) {
- if (it.first.len == 0) continue;
- return it.first;
- }
- const middle_index = i - 1;
- if (middle_index < it.middle.len) {
- const middle = it.middle[middle_index];
- if (middle.len == 0) continue;
- return middle;
- }
- if (middle_index == it.middle.len) {
- if (it.last.len == 0) continue;
- return it.last;
- }
- return null;
- }
- }
-};
-
-pub const VectorWrapper = struct {
- writer: Writer,
- it: WritableVectorIterator,
- /// 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) {
- const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
- wrapper.used = true;
- return wrapper.it;
- }
- return .{ .first = try writableSliceGreedy(w, 1) };
-}
-
-pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit) Error![]std.posix.iovec {
- var it = try writableVectorIterator(w);
- var i: usize = 0;
- var remaining = limit;
- while (it.next()) |full_buffer| {
- if (!remaining.nonzero()) break;
- if (buffer.len - i == 0) break;
- const buf = remaining.slice(full_buffer);
- if (buf.len == 0) continue;
- buffer[i] = .{ .base = buf.ptr, .len = buf.len };
- i += 1;
- remaining = remaining.subtract(buf.len).?;
- }
- return buffer[0..i];
-}
-
-pub fn writableVectorWsa(
- w: *Writer,
- buffer: []std.os.windows.ws2_32.WSABUF,
- limit: Limit,
-) Error![]std.os.windows.ws2_32.WSABUF {
- var it = try writableVectorIterator(w);
- var i: usize = 0;
- var remaining = limit;
- while (it.next()) |full_buffer| {
- if (!remaining.nonzero()) break;
- if (buffer.len - i == 0) break;
- const buf = remaining.slice(full_buffer);
- if (buf.len == 0) continue;
- if (std.math.cast(u32, buf.len)) |len| {
- buffer[i] = .{ .buf = buf.ptr, .len = len };
- i += 1;
- remaining = remaining.subtract(len).?;
- continue;
- }
- buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
- i += 1;
- break;
- }
- return buffer[0..i];
-}
-
pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
_ = try writableSliceGreedy(w, n);
}
@@ -451,13 +360,6 @@ pub fn advance(w: *Writer, n: usize) void {
w.end = new_end;
}
-/// After calling `writableVector`, this function tracks how many bytes were
-/// written to it.
-pub fn advanceVector(w: *Writer, n: usize) usize {
- if (w.vtable != VectorWrapper.vtable) advance(w, n);
- return n;
-}
-
/// The `data` parameter is mutable because this function needs to mutate the
/// fields in order to handle partial writes from `VTable.writeSplat`.
pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {
diff --git a/lib/std/compress/zstd/Decompress.zig b/lib/std/compress/zstd/Decompress.zig
index eb431e644cc7cce94bce3dd5fc0f259cd1d84ff3..db85474e00d8cfa67a0efb7f5974ab380d293d40 100644
--- a/lib/std/compress/zstd/Decompress.zig
+++ b/lib/std/compress/zstd/Decompress.zig
@@ -88,6 +88,8 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
.vtable = &.{
.stream = stream,
.rebase = rebase,
+ .discard = discard,
+ .readVec = Reader.indirectReadVec,
},
.buffer = buffer,
.seek = 0,
@@ -100,11 +102,23 @@ fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
assert(capacity <= r.buffer.len - d.window_len);
assert(r.end + capacity > r.buffer.len);
- const discard = r.end - d.window_len;
- const keep = r.buffer[discard..r.end];
+ const discard_n = r.end - d.window_len;
+ const keep = r.buffer[discard_n..r.end];
@memmove(r.buffer[0..keep.len], keep);
r.end = keep.len;
- r.seek -= discard;
+ r.seek -= discard_n;
+}
+
+fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
+ r.rebase(zstd.block_size_max) catch unreachable;
+ var d: Writer.Discarding = .init(r.buffer);
+ const n = r.stream(&d.writer, limit) catch |err| switch (err) {
+ error.WriteFailed => unreachable,
+ error.ReadFailed => return error.ReadFailed,
+ error.EndOfStream => return error.EndOfStream,
+ };
+ assert(n <= @intFromEnum(limit));
+ return n;
}
fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig
index fd965babfc6e0de1c01e56ae945fa23a39ec1870..eca2d6667f3e95989203b11c4bde5218e7e3efe2 100644
--- a/lib/std/fs/File.zig
+++ b/lib/std/fs/File.zig
@@ -1129,7 +1129,7 @@ pub fn seekableStream(file: File) SeekableStream {
/// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)
/// versus plain variants (e.g. `read`).
///
-/// Fulfills the `std.io.Reader` interface.
+/// Fulfills the `std.Io.Reader` interface.
pub const Reader = struct {
file: File,
err: ?ReadError = null,
@@ -1140,7 +1140,7 @@ pub const Reader = struct {
size: ?u64 = null,
size_err: ?GetEndPosError = null,
seek_err: ?Reader.SeekError = null,
- interface: std.io.Reader,
+ interface: std.Io.Reader,
pub const SeekError = File.SeekError || error{
/// Seeking fell back to reading, and reached the end before the requested seek position.
@@ -1177,11 +1177,12 @@ pub const Reader = struct {
}
};
- pub fn initInterface(buffer: []u8) std.io.Reader {
+ pub fn initInterface(buffer: []u8) std.Io.Reader {
return .{
.vtable = &.{
.stream = Reader.stream,
.discard = Reader.discard,
+ .readVec = Reader.readVec,
},
.buffer = buffer,
.seek = 0,
@@ -1294,7 +1295,7 @@ pub const Reader = struct {
/// vectors through the underlying read calls as possible.
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 {
+ fn stream(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
switch (r.mode) {
.positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
@@ -1305,16 +1306,33 @@ pub const Reader = struct {
else => |e| return e,
},
.positional_reading => {
+ const dest = limit.slice(try w.writableSliceGreedy(1));
+ const n = try readPositional(r, dest);
+ w.advance(n);
+ return n;
+ },
+ .streaming_reading => {
+ const dest = limit.slice(try w.writableSliceGreedy(1));
+ const n = try readStreaming(r, dest);
+ w.advance(n);
+ return n;
+ },
+ .failure => return error.ReadFailed,
+ }
+ }
+
+ fn readVec(io_reader: *std.Io.Reader, data: []const []u8) std.Io.Reader.Error!usize {
+ const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
+ switch (r.mode) {
+ .positional, .positional_reading => {
if (is_windows) {
// Unfortunately, `ReadFileScatter` cannot be used since it
// requires page alignment.
- const dest = limit.slice(try w.writableSliceGreedy(1));
- const n = try readPositional(r, dest);
- w.advance(n);
- return n;
+ return readPositional(r, data[0]);
}
var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
- const dest = try w.writableVectorPosix(&iovecs_buffer, limit);
+ const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
+ const dest = iovecs_buffer[0..dest_n];
assert(dest[0].len > 0);
const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
error.Unseekable => {
@@ -1339,19 +1357,22 @@ pub const Reader = struct {
return error.EndOfStream;
}
r.pos += n;
- return w.advanceVector(n);
+ if (n > data_size) {
+ io_reader.seek = 0;
+ io_reader.end = n - data_size;
+ return data_size;
+ }
+ return n;
},
- .streaming_reading => {
+ .streaming, .streaming_reading => {
if (is_windows) {
// Unfortunately, `ReadFileScatter` cannot be used since it
// requires page alignment.
- const dest = limit.slice(try w.writableSliceGreedy(1));
- const n = try readStreaming(r, dest);
- w.advance(n);
- return n;
+ return readStreaming(r, data[0]);
}
var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
- const dest = try w.writableVectorPosix(&iovecs_buffer, limit);
+ const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
+ const dest = iovecs_buffer[0..dest_n];
assert(dest[0].len > 0);
const n = posix.readv(r.file.handle, dest) catch |err| {
r.err = err;
@@ -1362,13 +1383,18 @@ pub const Reader = struct {
return error.EndOfStream;
}
r.pos += n;
- return w.advanceVector(n);
+ if (n > data_size) {
+ io_reader.seek = 0;
+ io_reader.end = n - data_size;
+ return data_size;
+ }
+ return n;
},
.failure => return error.ReadFailed,
}
}
- fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize {
+ fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
const file = r.file;
const pos = r.pos;
@@ -1447,7 +1473,7 @@ pub const Reader = struct {
}
}
- pub fn readPositional(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
+ pub fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
error.Unseekable => {
r.mode = r.mode.toStreaming();
@@ -1474,7 +1500,7 @@ pub const Reader = struct {
return n;
}
- pub fn readStreaming(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
+ pub fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
const n = r.file.read(dest) catch |err| {
r.err = err;
return error.ReadFailed;
@@ -1487,7 +1513,7 @@ pub const Reader = struct {
return n;
}
- pub fn read(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
+ pub fn read(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
switch (r.mode) {
.positional, .positional_reading => return readPositional(r, dest),
.streaming, .streaming_reading => return readStreaming(r, dest),
@@ -1513,7 +1539,7 @@ pub const Writer = struct {
copy_file_range_err: ?CopyFileRangeError = null,
fcopyfile_err: ?FcopyfileError = null,
seek_err: ?SeekError = null,
- interface: std.io.Writer,
+ interface: std.Io.Writer,
pub const Mode = Reader.Mode;
@@ -1550,13 +1576,13 @@ pub const Writer = struct {
};
}
- pub fn initInterface(buffer: []u8) std.io.Writer {
+ pub fn initInterface(buffer: []u8) std.Io.Writer {
return .{
.vtable = &.{
.drain = drain,
.sendFile = switch (builtin.zig_backend) {
else => sendFile,
- .stage2_aarch64 => std.io.Writer.unimplementedSendFile,
+ .stage2_aarch64 => std.Io.Writer.unimplementedSendFile,
},
},
.buffer = buffer,
@@ -1574,7 +1600,7 @@ pub const Writer = struct {
};
}
- pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
+ 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_w.buffered();
@@ -1724,10 +1750,10 @@ pub const Writer = struct {
}
pub fn sendFile(
- io_w: *std.io.Writer,
+ io_w: *std.Io.Writer,
file_reader: *Reader,
- limit: std.io.Limit,
- ) std.io.Writer.FileError!usize {
+ limit: std.Io.Limit,
+ ) std.Io.Writer.FileError!usize {
const reader_buffered = file_reader.interface.buffered();
if (reader_buffered.len >= @intFromEnum(limit))
return sendFileBuffered(io_w, file_reader, reader_buffered);
@@ -1989,10 +2015,10 @@ pub const Writer = struct {
}
fn sendFileBuffered(
- io_w: *std.io.Writer,
+ io_w: *std.Io.Writer,
file_reader: *Reader,
reader_buffered: []const u8,
- ) std.io.Writer.FileError!usize {
+ ) std.Io.Writer.FileError!usize {
const n = try drain(io_w, &.{reader_buffered}, 1);
file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
return n;
@@ -2015,7 +2041,7 @@ pub const Writer = struct {
}
}
- pub const EndError = SetEndPosError || std.io.Writer.Error;
+ pub const EndError = SetEndPosError || std.Io.Writer.Error;
/// Flushes any buffered data and sets the end position of the file.
///
diff --git a/lib/std/net.zig b/lib/std/net.zig
index f43c2f9b53fe55bd57b460e9fe810f08225c8a96..d7387662c004311223de6466eab77dd9eb462404 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -7,7 +7,7 @@ const net = @This();
const mem = std.mem;
const posix = std.posix;
const fs = std.fs;
-const io = std.io;
+const Io = std.Io;
const native_endian = builtin.target.cpu.arch.endian();
const native_os = builtin.os.tag;
const windows = std.os.windows;
@@ -165,7 +165,7 @@ pub const Address = extern union {
}
}
- pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void {
+ pub fn format(self: Address, w: *Io.Writer) Io.Writer.Error!void {
switch (self.any.family) {
posix.AF.INET => try self.in.format(w),
posix.AF.INET6 => try self.in6.format(w),
@@ -342,7 +342,7 @@ pub const Ip4Address = extern struct {
self.sa.port = mem.nativeToBig(u16, port);
}
- pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void {
+ pub fn format(self: Ip4Address, w: *Io.Writer) 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() });
}
@@ -633,7 +633,7 @@ pub const Ip6Address = extern struct {
self.sa.port = mem.nativeToBig(u16, port);
}
- pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void {
+ pub fn format(self: Ip6Address, w: *Io.Writer) 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 w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
@@ -1348,7 +1348,7 @@ fn parseHosts(
name: []const u8,
family: posix.sa_family_t,
port: u16,
- br: *io.Reader,
+ br: *Io.Reader,
) error{ OutOfMemory, ReadFailed }!void {
while (true) {
const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {
@@ -1402,7 +1402,7 @@ test parseHosts {
// TODO parsing addresses should not have OS dependencies
return error.SkipZigTest;
}
- var reader: std.io.Reader = .fixed(
+ var reader: Io.Reader = .fixed(
\\127.0.0.1 localhost
\\::1 localhost
\\127.0.0.2 abcd
@@ -1583,7 +1583,7 @@ const ResolvConf = struct {
const Directive = enum { options, nameserver, domain, search };
const Option = enum { ndots, attempts, timeout };
- fn parse(rc: *ResolvConf, reader: *io.Reader) !void {
+ fn parse(rc: *ResolvConf, reader: *Io.Reader) !void {
const gpa = rc.gpa;
while (reader.takeSentinel('\n')) |line_with_comment| {
const line = line: {
@@ -1894,7 +1894,7 @@ pub const Stream = struct {
pub const Reader = switch (native_os) {
.windows => struct {
/// Use `interface` for portable code.
- interface_state: io.Reader,
+ interface_state: Io.Reader,
/// Use `getStream` for portable code.
net_stream: Stream,
/// Use `getError` for portable code.
@@ -1910,14 +1910,17 @@ pub const Stream = struct {
return r.error_state;
}
- pub fn interface(r: *Reader) *io.Reader {
+ pub fn interface(r: *Reader) *Io.Reader {
return &r.interface_state;
}
pub fn init(net_stream: Stream, buffer: []u8) Reader {
return .{
.interface_state = .{
- .vtable = &.{ .stream = stream },
+ .vtable = &.{
+ .stream = stream,
+ .readVec = readVec,
+ },
.buffer = buffer,
.seek = 0,
.end = 0,
@@ -1927,16 +1930,29 @@ pub const Stream = struct {
};
}
- fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {
+ fn stream(io_r: *Io.Reader, io_w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
+ const dest = limit.slice(try io_w.writableSliceGreedy(1));
+ const n = try readVec(io_r, &.{dest});
+ io_w.advance(n);
+ return n;
+ }
+
+ fn readVec(io_r: *std.Io.Reader, data: []const []u8) Io.Reader.Error!usize {
const r: *Reader = @alignCast(@fieldParentPtr("interface_state", io_r));
var iovecs: [max_buffers_len]windows.ws2_32.WSABUF = undefined;
- const bufs = try io_w.writableVectorWsa(&iovecs, limit);
+ const bufs_n, const data_size = try io_r.writableVectorWsa(&iovecs, data);
+ const bufs = iovecs[0..bufs_n];
assert(bufs[0].len != 0);
const n = streamBufs(r, bufs) catch |err| {
r.error_state = err;
return error.ReadFailed;
};
if (n == 0) return error.EndOfStream;
+ if (n > data_size) {
+ io_r.seek = 0;
+ io_r.end = n - data_size;
+ return data_size;
+ }
return n;
}
@@ -1968,7 +1984,7 @@ pub const Stream = struct {
pub const Error = ReadError;
- pub fn interface(r: *Reader) *io.Reader {
+ pub fn interface(r: *Reader) *Io.Reader {
return &r.file_reader.interface;
}
@@ -1996,7 +2012,7 @@ pub const Stream = struct {
pub const Writer = switch (native_os) {
.windows => struct {
/// This field is present on all systems.
- interface: io.Writer,
+ interface: Io.Writer,
/// Use `getStream` for cross-platform support.
stream: Stream,
/// This field is present on all systems.
@@ -2034,7 +2050,7 @@ pub const Stream = struct {
}
}
- fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {
+ fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
const buffered = io_w.buffered();
comptime assert(native_os == .windows);
@@ -2106,7 +2122,7 @@ pub const Stream = struct {
},
else => struct {
/// This field is present on all systems.
- interface: io.Writer,
+ interface: Io.Writer,
err: ?Error = null,
file_writer: File.Writer,
@@ -2138,7 +2154,7 @@ pub const Stream = struct {
i.* += 1;
}
- fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {
+ fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
const buffered = io_w.buffered();
var iovecs: [max_buffers_len]posix.iovec_const = undefined;
@@ -2190,7 +2206,7 @@ pub const Stream = struct {
});
}
- fn sendFile(io_w: *io.Writer, file_reader: *File.Reader, limit: io.Limit) io.Writer.FileError!usize {
+ fn sendFile(io_w: *Io.Writer, file_reader: *File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
const n = try w.file_writer.interface.sendFileHeader(io_w.buffered(), file_reader, limit);
return io_w.consume(n);
--
2.54.0
From 3d639481d9001df391744720b756b7f97dce2c01 Mon Sep 17 00:00:00 2001
From: Krzysztof Wolicki
Date: Wed, 30 Jul 2025 20:18:49 +0200
Subject: [PATCH 048/110] Sema: disallow tags on non-auto unions when reifying
(#23488)
---
src/Sema.zig | 10 ++++++
.../reify_type_for_tagged_extern_union.zig | 34 +++++++++++++++++++
.../reify_type_for_tagged_packed_union.zig | 34 +++++++++++++++++++
3 files changed, 78 insertions(+)
create mode 100644 test/cases/compile_errors/reify_type_for_tagged_extern_union.zig
create mode 100644 test/cases/compile_errors/reify_type_for_tagged_packed_union.zig
diff --git a/src/Sema.zig b/src/Sema.zig
index 2483f313a8992c9ed2295f40cef9868c6e6400dc..63c39b3bb66675cfd7a3a2068869e20a74d0bfa5 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -20630,6 +20630,16 @@ fn zirReify(
}
const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout);
+ const has_tag = tag_type_val.optionalValue(zcu) != null;
+
+ if (has_tag) {
+ switch (layout) {
+ .@"extern" => return sema.fail(block, src, "extern union does not support enum tag type", .{}),
+ .@"packed" => return sema.fail(block, src, "packed union does not support enum tag type", .{}),
+ .auto => {},
+ }
+ }
+
const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .union_fields });
return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy);
diff --git a/test/cases/compile_errors/reify_type_for_tagged_extern_union.zig b/test/cases/compile_errors/reify_type_for_tagged_extern_union.zig
new file mode 100644
index 0000000000000000000000000000000000000000..24169e2ff3ac19e8d8f214ea4888c5e97e11eb99
--- /dev/null
+++ b/test/cases/compile_errors/reify_type_for_tagged_extern_union.zig
@@ -0,0 +1,34 @@
+const Tag = @Type(.{
+ .@"enum" = .{
+ .tag_type = u2,
+ .fields = &.{
+ .{ .name = "signed", .value = 0 },
+ .{ .name = "unsigned", .value = 1 },
+ },
+ .decls = &.{},
+ .is_exhaustive = true,
+ },
+});
+
+const Extern = @Type(.{
+ .@"union" = .{
+ .layout = .@"extern",
+ .tag_type = Tag,
+ .fields = &.{
+ .{ .name = "signed", .type = i32, .alignment = @alignOf(i32) },
+ .{ .name = "unsigned", .type = u32, .alignment = @alignOf(u32) },
+ },
+ .decls = &.{},
+ },
+});
+
+export fn entry() void {
+ const tagged: Extern = .{ .signed = -1 };
+ _ = tagged;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :13:16: error: extern union does not support enum tag type
diff --git a/test/cases/compile_errors/reify_type_for_tagged_packed_union.zig b/test/cases/compile_errors/reify_type_for_tagged_packed_union.zig
new file mode 100644
index 0000000000000000000000000000000000000000..ee085b07b3b6ac0c7943447b5eaff4ae14113f92
--- /dev/null
+++ b/test/cases/compile_errors/reify_type_for_tagged_packed_union.zig
@@ -0,0 +1,34 @@
+const Tag = @Type(.{
+ .@"enum" = .{
+ .tag_type = u2,
+ .fields = &.{
+ .{ .name = "signed", .value = 0 },
+ .{ .name = "unsigned", .value = 1 },
+ },
+ .decls = &.{},
+ .is_exhaustive = true,
+ },
+});
+
+const Packed = @Type(.{
+ .@"union" = .{
+ .layout = .@"packed",
+ .tag_type = Tag,
+ .fields = &.{
+ .{ .name = "signed", .type = i32, .alignment = @alignOf(i32) },
+ .{ .name = "unsigned", .type = u32, .alignment = @alignOf(u32) },
+ },
+ .decls = &.{},
+ },
+});
+
+export fn entry() void {
+ const tagged: Packed = .{ .signed = -1 };
+ _ = tagged;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :13:16: error: packed union does not support enum tag type
--
2.54.0
From eb1a4970dae76b49fe8cf1fa792a571cfebed86d Mon Sep 17 00:00:00 2001
From: Jackson Wambolt
Date: Wed, 30 Jul 2025 15:48:38 -0500
Subject: [PATCH 049/110] Sema: check min/max operand types
---
src/Sema.zig | 1 +
.../minmax_nonnumeric_operand.zig | 41 +++++++++++++++++++
2 files changed, 42 insertions(+)
create mode 100644 test/cases/compile_errors/minmax_nonnumeric_operand.zig
diff --git a/src/Sema.zig b/src/Sema.zig
index 63c39b3bb66675cfd7a3a2068869e20a74d0bfa5..94bf21e03b5c0aa364e0c4e183e53e097843cc30 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -24609,6 +24609,7 @@ fn analyzeMinMax(
} else {
for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
const operand_ty = sema.typeOf(operand);
+ 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_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)});
diff --git a/test/cases/compile_errors/minmax_nonnumeric_operand.zig b/test/cases/compile_errors/minmax_nonnumeric_operand.zig
new file mode 100644
index 0000000000000000000000000000000000000000..7100879b5b90a3cff36ec70d1f740decb5727c83
--- /dev/null
+++ b/test/cases/compile_errors/minmax_nonnumeric_operand.zig
@@ -0,0 +1,41 @@
+// zig fmt: off
+comptime { _ = @min(0, u32); } // type
+comptime { _ = @max(0, {}); } // void
+comptime { _ = @min(0, false); } // boolean
+comptime { _ = @min(0, &@as(u8, 0)); } // pointer
+comptime { _ = @max(0, [0]u8{}); } // array
+comptime { _ = @min(0, Struct{}); } // struct
+comptime { _ = @max(0, null); } // null
+comptime { _ = @min(0, @as(?u8, 0)); } // nullable
+comptime { _ = @max(0, @as(error{}!u8, 0)); } // error union
+comptime { _ = @min(0, error.Foo); } // error set
+comptime { _ = @max(0, Enum.foo); } // enum
+comptime { _ = @min(0, Union{ .foo = {} }); } // union
+comptime { _ = @max(0, struct { fn func() u8 { return 42; }}.func); }
+comptime { _ = @max(0, .foo); } // enum literal
+
+const Struct = struct {};
+const Enum = enum { foo };
+const Union = union { foo: void };
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:24: error: expected number, found 'type'
+// :3:24: error: expected number, found 'void'
+// :4:24: error: expected number, found 'bool'
+// :5:24: error: expected number, found '*const u8'
+// :6:29: error: expected number, found '[0]u8'
+// :7:30: error: expected number, found 'tmp.Struct'
+// :17:16: note: struct declared here
+// :8:24: error: expected number, found '@TypeOf(null)'
+// :9:24: error: expected number, found '?u8'
+// :10:24: error: expected number, found 'error{}!u8'
+// :11:24: error: expected number, found 'error{Foo}'
+// :12:28: error: expected number, found 'tmp.Enum'
+// :18:14: note: enum declared here
+// :13:29: error: expected number, found 'tmp.Union'
+// :19:15: note: union declared here
+// :14:61: error: expected number, found 'fn () u8'
+// :15:25: error: expected number, found '@Type(.enum_literal)'
--
2.54.0
From de23ccfad1630e30d5b5ea1278ab2f375f987568 Mon Sep 17 00:00:00 2001
From: Loris Cro
Date: Fri, 25 Jul 2025 17:38:36 +0200
Subject: [PATCH 050/110] build system: print captured stderr on Run step
failure
when a Run step that captures stderr fails, no output from it is visible
by the user and, since the step failed, any downstream step that would
process the captured stream will not run, making it impossible for the
user to see the stderr output from the failed process invocation, which
makes for a frustrating puzzle when this happens in CI.
---
lib/std/Build/Step/Run.zig | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig
index 57f5d73f0c3496cffa935eac08c6d774b70f1974..819fc6745d4e10f94f7aca0b5e1cd1350d5ff1b0 100644
--- a/lib/std/Build/Step/Run.zig
+++ b/lib/std/Build/Step/Run.zig
@@ -1391,6 +1391,16 @@ fn runCommand(
}
},
else => {
+ // On failure, print stderr if captured.
+ const bad_exit = switch (result.term) {
+ .Exited => |code| code != 0,
+ .Signal, .Stopped, .Unknown => true,
+ };
+
+ if (bad_exit) if (result.stdio.stderr) |err| {
+ try step.addError("stderr:\n{s}", .{err});
+ };
+
try step.handleChildProcessTerm(result.term, cwd, final_argv);
},
}
--
2.54.0
From 467a1f4a1c58bc17b80e8af88ac8c7f6ba3d2035 Mon Sep 17 00:00:00 2001
From: Linus Groh
Date: Wed, 30 Jul 2025 23:19:29 +0100
Subject: [PATCH 051/110] std.c: Fix msghdr_const for serenity
---
lib/std/c.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index e2f55dd6fb08b4ce1722bb6799bd493a90b165bc..a12312ba39a5c47f01d7c8e1296b9f183b2bf6f8 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -4121,7 +4121,7 @@ pub const msghdr_const = switch (native_os) {
.serenity => extern struct {
name: ?*const anyopaque,
namelen: socklen_t,
- iov: [*]const iovec,
+ iov: [*]const iovec_const,
iovlen: c_uint,
control: ?*const anyopaque,
controllen: socklen_t,
--
2.54.0
From f5e938433555fb8cb0719d63e842ea26d012bb1d Mon Sep 17 00:00:00 2001
From: Linus Groh
Date: Wed, 30 Jul 2025 23:22:06 +0100
Subject: [PATCH 052/110] std.c: Fix MAP for serenity
I accidentally translated MAP_ constants representing the type as
individual fields. MAP_FILE is for compatibility only and not needed
here.
---
lib/std/c.zig | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index a12312ba39a5c47f01d7c8e1296b9f183b2bf6f8..b9a659cae65895798bfa568c928615251f231a03 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -8760,10 +8760,10 @@ pub const MAP = switch (native_os) {
},
// https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L16-L26
.serenity => packed struct(c_int) {
- FILE: bool = false,
- SHARED: bool = false,
- PRIVATE: bool = false,
- _3: u2 = 0,
+ TYPE: enum(u4) {
+ SHARED = 0x01,
+ PRIVATE = 0x02,
+ },
FIXED: bool = false,
ANONYMOUS: bool = false,
STACK: bool = false,
@@ -8771,7 +8771,7 @@ pub const MAP = switch (native_os) {
RANDOMIZED: bool = false,
PURGEABLE: bool = false,
FIXED_NOREPLACE: bool = false,
- _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 12) = 0,
+ _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 11) = 0,
},
else => void,
};
--
2.54.0
From 813a0f125e2619f0a056d675339cab6ce34a2cbf Mon Sep 17 00:00:00 2001
From: Linus Groh
Date: Wed, 30 Jul 2025 23:27:32 +0100
Subject: [PATCH 053/110] std.posix: Default ACCMODE to NONE for serenity
Unlike all other platforms where RDONLY is 0 it does not work as a
default for the O flags on serenity - various syscalls other than
'open', e.g. 'pipe', return EINVAL if unexpected bits are set in the
flags.
---
lib/std/c.zig | 2 +-
lib/std/posix.zig | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index b9a659cae65895798bfa568c928615251f231a03..2339ede07683ff2a931f43d29289437086081ff9 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -8604,7 +8604,7 @@ pub const O = switch (native_os) {
},
// https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L28-L43
.serenity => packed struct(c_int) {
- ACCMODE: std.posix.ACCMODE = .RDONLY,
+ ACCMODE: std.posix.ACCMODE = .NONE,
EXEC: bool = false,
CREAT: bool = false,
EXCL: bool = false,
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
index 54c6470d2c11f1eda31d16c56fe9269b2c6f96e1..e10023f6b1901a3a1bc8ab24118790aa009adcdd 100644
--- a/lib/std/posix.zig
+++ b/lib/std/posix.zig
@@ -204,6 +204,7 @@ pub const ACCMODE = switch (native_os) {
// implements this suggestion.
// https://github.com/SerenityOS/serenity/blob/4adc51fdf6af7d50679c48b39362e062f5a3b2cb/Kernel/API/POSIX/fcntl.h#L28-L30
.serenity => enum(u2) {
+ NONE = 0,
RDONLY = 1,
WRONLY = 2,
RDWR = 3,
--
2.54.0
From ce776d32455f5ac43f91cc8904765836c00605cf Mon Sep 17 00:00:00 2001
From: Linus Groh
Date: Wed, 30 Jul 2025 23:28:58 +0100
Subject: [PATCH 054/110] std: Add serenity to more OS checks
---
lib/std/Progress.zig | 1 +
lib/std/heap.zig | 2 +-
lib/std/posix.zig | 3 ++-
lib/std/start.zig | 1 +
4 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index 2806c1a09c2f29f21e485f2b6f74279752348ff4..8b741187e75248625193ebd66dd56fe0b621556a 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -1548,6 +1548,7 @@ const have_sigwinch = switch (builtin.os.tag) {
.visionos,
.dragonfly,
.freebsd,
+ .serenity,
=> true,
else => false,
diff --git a/lib/std/heap.zig b/lib/std/heap.zig
index 51e4fe44a2ca4583081a06209c8776d7546c51ac..a39cf5e1cb92f8ea81a5af1315b4116f0083f25c 100644
--- a/lib/std/heap.zig
+++ b/lib/std/heap.zig
@@ -146,7 +146,7 @@ const CAllocator = struct {
else {};
pub const supports_posix_memalign = switch (builtin.os.tag) {
- .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos => true,
+ .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos, .serenity => true,
else => false,
};
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
index e10023f6b1901a3a1bc8ab24118790aa009adcdd..fefb57de1c86212a0ca6cdb6727fd01656492193 100644
--- a/lib/std/posix.zig
+++ b/lib/std/posix.zig
@@ -1129,8 +1129,9 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
/// * Windows
/// On these systems, the read races with concurrent writes to the same file descriptor.
pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
+ // NOTE: serenity does not have preadv but it *does* have pwritev.
const have_pread_but_not_preadv = switch (native_os) {
- .windows, .macos, .ios, .watchos, .tvos, .visionos, .haiku => true,
+ .windows, .macos, .ios, .watchos, .tvos, .visionos, .haiku, .serenity => true,
else => false,
};
if (have_pread_but_not_preadv) {
diff --git a/lib/std/start.zig b/lib/std/start.zig
index f889885c846e1214bdcbfc84692ee4fe642c02ea..30543ead8a65ab0049fb74fa6be85d923a9af14b 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -699,6 +699,7 @@ fn maybeIgnoreSigpipe() void {
.visionos,
.dragonfly,
.freebsd,
+ .serenity,
=> true,
else => false,
--
2.54.0
From e941ce3e689991081d0ae726bfa1ccccf4fc196f Mon Sep 17 00:00:00 2001
From: Chinmay Dalal
Date: Sun, 27 Jul 2025 12:34:40 +0530
Subject: [PATCH 055/110] add grp.h functions to c.zig
---
lib/std/c.zig | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index e2f55dd6fb08b4ce1722bb6799bd493a90b165bc..32f5fc1c3bd73971f31bceab163e62f4334e701e 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -207,6 +207,16 @@ pub const passwd = switch (native_os) {
else => void,
};
+pub const group = switch (native_os) {
+ .linux, .freebsd, .openbsd, .dragonfly, .netbsd, .macos => extern struct {
+ name: ?[*:0]const u8,
+ passwd: ?[*:0]const u8,
+ gid: gid_t,
+ mem: [*:null]?[*:0]const u8,
+ },
+ else => void,
+};
+
pub const blkcnt_t = switch (native_os) {
.linux => linux.blkcnt_t,
.emscripten => emscripten.blkcnt_t,
@@ -3291,8 +3301,8 @@ pub const T = switch (native_os) {
.macos, .ios, .tvos, .watchos, .visionos => struct {
pub const IOCGWINSZ = ior(0x40000000, 't', 104, @sizeOf(winsize));
- fn ior(inout: u32, group: usize, num: usize, len: usize) usize {
- return (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num));
+ fn ior(inout: u32, group_arg: usize, num: usize, len: usize) usize {
+ return (inout | ((len & IOCPARM_MASK) << 16) | ((group_arg) << 8) | (num));
}
},
.freebsd => struct {
@@ -10264,6 +10274,13 @@ pub const fstatat = switch (native_os) {
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
pub extern "c" fn getpwuid(uid: uid_t) ?*passwd;
+pub extern "c" fn getgrent() ?*group;
+pub extern "c" fn setgrent() void;
+pub extern "c" fn endgrent() void;
+pub extern "c" fn getgrnam(name: [*:0]const u8) ?*passwd;
+pub extern "c" fn getgrnam_r(name: [*:0]const u8, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int;
+pub extern "c" fn getgrgid(gid: gid_t) ?*group;
+pub extern "c" fn getgrgid_r(gid: gid_t, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int;
pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;
pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;
pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
--
2.54.0
From bce6a7c21529e9b294a641b5d7472c000204aaef Mon Sep 17 00:00:00 2001
From: mlugg
Date: Wed, 30 Jul 2025 11:17:45 +0100
Subject: [PATCH 056/110] langref: improve `@import` documentation
Rewrite to be more clear and correct. Also, explain ZON imports.
Resolves: #23314
---
doc/langref.html.in | 43 +++++++++++++++++--------------------------
1 file changed, 17 insertions(+), 26 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index efa671aa857a30b3fe4005823161de3dcb3c6624..de26c7d6432c6651180662183df0bde8a6377217 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4952,34 +4952,25 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_close#}
{#header_open|@import#}
- {#syntax#}@import(comptime path: []const u8) type{#endsyntax#}
-
- This function finds a zig file corresponding to {#syntax#}path{#endsyntax#} and adds it to the build,
- if it is not already added.
-
-
- Zig source files are implicitly structs, with a name equal to the file's basename with the extension
- truncated. {#syntax#}@import{#endsyntax#} returns the struct type corresponding to the file.
-
-
- Declarations which have the {#syntax#}pub{#endsyntax#} keyword may be referenced from a different
- source file than the one they are declared in.
-
-
- {#syntax#}path{#endsyntax#} can be a relative path or it can be the name of a package.
- If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#}
- function call.
-
-
- The following packages are always available:
-
+ {#syntax#}@import(comptime target: []const u8) anytype{#endsyntax#}
+ Imports the file at {#syntax#}target{#endsyntax#}, adding it to the compilation if it is not already
+ added. {#syntax#}target{#endsyntax#} is either a relative path to another file from the file containing
+ the {#syntax#}@import{#endsyntax#} call, or it is the name of a {#link|module|Compilation Model#}, with
+ the import referring to the root source file of that module. Either way, the file path must end in
+ either .zig (for a Zig source file) or .zon (for a ZON data file).
+ If {#syntax#}target{#endsyntax#} refers to a Zig source file, then {#syntax#}@import{#endsyntax#} returns
+ that file's {#link|corresponding struct type|Source File Structs#}, essentially as if the builtin call was
+ replaced by {#syntax#}struct { FILE_CONTENTS }{#endsyntax#}. The return type is {#syntax#}type{#endsyntax#}.
+ If {#syntax#}target{#endsyntax#} refers to a ZON file, then {#syntax#}@import{#endsyntax#} returns the value
+ of the literal in the file. If there is an inferred {#link|result type|Result Types#}, then the return type
+ is that type, and the ZON literal is interpreted as that type ({#link|Result Types#} are propagated through
+ the ZON expression). Otherwise, the return type is the type of the equivalent Zig expression, essentially as
+ if the builtin call was replaced by the ZON file contents.
+ The following modules are always available for import:
- {#syntax#}@import("std"){#endsyntax#} - Zig Standard Library
- - {#syntax#}@import("builtin"){#endsyntax#} - Target-specific information
- The command
zig build-exe --show-builtin outputs the source to stdout for reference.
-
- - {#syntax#}@import("root"){#endsyntax#} - Root source file
- This is usually
src/main.zig but depends on what file is built.
+ - {#syntax#}@import("builtin"){#endsyntax#} - Target-specific information. The command
zig build-exe --show-builtin outputs the source to stdout for reference.
+ - {#syntax#}@import("root"){#endsyntax#} - Alias for the root module. In typical project structures, this means it refers back to
src/main.zig.
{#see_also|Compile Variables|@embedFile#}
--
2.54.0
From e664bf4d81e9266ee4749b5da88cab4554499bf6 Mon Sep 17 00:00:00 2001
From: mlugg
Date: Wed, 30 Jul 2025 23:22:32 +0100
Subject: [PATCH 057/110] Sema: compile error on lossy int to float coercion
Resolves: #21586
---
src/Sema.zig | 41 ++++++++++++++-----
.../int_to_float_coercion_loses_precision.zig | 9 ++++
2 files changed, 39 insertions(+), 11 deletions(-)
create mode 100644 test/cases/compile_errors/int_to_float_coercion_loses_precision.zig
diff --git a/src/Sema.zig b/src/Sema.zig
index 94bf21e03b5c0aa364e0c4e183e53e097843cc30..edba4e687407341de0abff35e2661363c66d27c5 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -28733,17 +28733,36 @@ fn coerceExtra(
break :int;
};
const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
- // TODO implement this compile error
- //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty);
- //if (!int_again_val.eql(val, inst_ty, zcu)) {
- // return sema.fail(
- // block,
- // inst_src,
- // "type '{f}' cannot represent integer value '{f}'",
- // .{ dest_ty.fmt(pt), val },
- // );
- //}
- return Air.internedToRef(result_val.toIntern());
+ const fits: bool = switch (ip.indexToKey(result_val.toIntern())) {
+ else => unreachable,
+ .undef => true,
+ .float => |float| fits: {
+ var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
+ const operand_big_int = val.toBigInt(&buffer, zcu);
+ switch (float.storage) {
+ inline else => |x| {
+ if (!std.math.isFinite(x)) break :fits false;
+ var result_big_int: std.math.big.int.Mutable = .{
+ .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)),
+ .len = undefined,
+ .positive = undefined,
+ };
+ switch (result_big_int.setFloat(x, .nearest_even)) {
+ .inexact => break :fits false,
+ .exact => {},
+ }
+ break :fits result_big_int.toConst().eql(operand_big_int);
+ },
+ }
+ },
+ };
+ if (!fits) return sema.fail(
+ block,
+ inst_src,
+ "type '{f}' cannot represent integer value '{f}'",
+ .{ dest_ty.fmt(pt), val.fmtValue(pt) },
+ );
+ return .fromValue(result_val);
},
else => {},
},
diff --git a/test/cases/compile_errors/int_to_float_coercion_loses_precision.zig b/test/cases/compile_errors/int_to_float_coercion_loses_precision.zig
new file mode 100644
index 0000000000000000000000000000000000000000..bc1d8e7dee48eb9fc45afd0a82cbd33921f7e037
--- /dev/null
+++ b/test/cases/compile_errors/int_to_float_coercion_loses_precision.zig
@@ -0,0 +1,9 @@
+export fn foo() void {
+ const int: u16 = 65535;
+ const float: f16 = int;
+ _ = float;
+}
+
+// error
+//
+// :3:24: error: type 'f16' cannot represent integer value '65535'
--
2.54.0
From 64bf8bb146099b51d74635a1f116a913e442bcf4 Mon Sep 17 00:00:00 2001
From: mlugg
Date: Thu, 31 Jul 2025 10:56:49 +0100
Subject: [PATCH 058/110] std: stop relying on precision-losing coercions
---
lib/std/math.zig | 10 +++++++---
lib/std/math/gamma.zig | 32 ++++++++++++++++----------------
lib/std/math/modf.zig | 2 +-
lib/std/math/pow.zig | 4 ++--
lib/std/zon/parse.zig | 6 +++---
5 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/lib/std/math.zig b/lib/std/math.zig
index 9f2d12a65e1a6e190a9154c9ae97daa0d6127e54..c36f19ec855310cd8ba04cee681e912636041659 100644
--- a/lib/std/math.zig
+++ b/lib/std/math.zig
@@ -1345,11 +1345,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T {
}
},
.float, .comptime_float => {
+ // In extreme cases, we probably need a language enhancement to be able to
+ // specify a rounding mode here to prevent `@intFromFloat` panics.
+ const max: @TypeOf(value) = @floatFromInt(maxInt(T));
+ const min: @TypeOf(value) = @floatFromInt(minInt(T));
if (isNan(value)) {
return 0;
- } else if (value >= maxInt(T)) {
+ } else if (value >= max) {
return maxInt(T);
- } else if (value <= minInt(T)) {
+ } else if (value <= min) {
return minInt(T);
} else {
return @intFromFloat(value);
@@ -1366,7 +1370,7 @@ test lossyCast {
try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
- try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));
+ try testing.expect(lossyCast(u32, @as(f32, @floatFromInt(maxInt(u32)))) == maxInt(u32));
try testing.expect(lossyCast(u32, nan(f32)) == 0);
}
diff --git a/lib/std/math/gamma.zig b/lib/std/math/gamma.zig
index 5577f71461715198631c76ad0f6c4113e1bff15c..ce9a2b07f91b2cc5e5909069813d28bd4b7e9ce6 100644
--- a/lib/std/math/gamma.zig
+++ b/lib/std/math/gamma.zig
@@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T {
2.5066282746310002701649081771338373386264310793408,
};
const denominator = [_]T{
- 0,
- 39916800,
- 120543840,
- 150917976,
- 105258076,
- 45995730,
- 13339535,
- 2637558,
- 357423,
- 32670,
- 1925,
- 66,
- 1,
+ 0.0,
+ 39916800.0,
+ 120543840.0,
+ 150917976.0,
+ 105258076.0,
+ 45995730.0,
+ 13339535.0,
+ 2637558.0,
+ 357423.0,
+ 32670.0,
+ 1925.0,
+ 66.0,
+ 1.0,
};
var num: T = 0;
var den: T = 0;
@@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel;
test gamma {
inline for (&.{ f32, f64 }) |T| {
const eps = @sqrt(std.math.floatEps(T));
- try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps);
- try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps);
- try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps);
+ try expectApproxEqRel(@as(T, 120.0), gamma(T, 6), eps);
+ try expectApproxEqRel(@as(T, 362880.0), gamma(T, 10), eps);
+ try expectApproxEqRel(@as(T, 6402373705728000.0), gamma(T, 19), eps);
try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);
try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);
diff --git a/lib/std/math/modf.zig b/lib/std/math/modf.zig
index 77d58bd34e7e4c5093669d15453af936e4d8da43..dda34454e35f6262facd3d8b557a0454e0e4d58b 100644
--- a/lib/std/math/modf.zig
+++ b/lib/std/math/modf.zig
@@ -74,7 +74,7 @@ fn ModfTests(comptime T: type) type {
r = modf(@as(T, 43874.3));
try expectEqual(43874.0, r.ipart);
// account for precision error
- const expected_b: T = 43874.3 - @as(T, 43874);
+ const expected_b: T = 43874.3 - @as(T, 43874.0);
try expectApproxEqAbs(expected_b, r.fpart, epsilon);
r = modf(@as(T, 1234.340780));
diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig
index acaafe76093a0ec4563c81bb5f7703264096c17a..42f28ce4657650cc19015c49b5600c0f73fe0c8f 100644
--- a/lib/std/math/pow.zig
+++ b/lib/std/math/pow.zig
@@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool {
}
test isOddInteger {
- try expect(isOddInteger(math.maxInt(i64) * 2) == false);
- try expect(isOddInteger(math.maxInt(i64) * 2 + 1) == false);
+ try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false);
+ try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
try expect(isOddInteger(1 << 53) == false);
try expect(isOddInteger(12.0) == false);
try expect(isOddInteger(15.0) == true);
diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig
index 96a7fa65953320b04310d647c6806fec49971ca5..5f74400c29cf10415a6059c05558a382587f95e4 100644
--- a/lib/std/zon/parse.zig
+++ b/lib/std/zon/parse.zig
@@ -2774,11 +2774,11 @@ test "std.zon parse float" {
// Test big integers
try std.testing.expectEqual(
- @as(f32, 36893488147419103231),
+ @as(f32, 36893488147419103231.0),
try fromSlice(f32, gpa, "36893488147419103231", null, .{}),
);
try std.testing.expectEqual(
- @as(f32, -36893488147419103231),
+ @as(f32, -36893488147419103231.0),
try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),
);
try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(
@@ -2788,7 +2788,7 @@ test "std.zon parse float" {
null,
.{},
));
- try std.testing.expectEqual(@as(f32, 0x1ffffffffffffffff), try fromSlice(
+ try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(
f32,
gpa,
"0x1ffffffffffffffff",
--
2.54.0
From 19fc5f4fb29a525252b2eaf3f6388d07f97bd32f Mon Sep 17 00:00:00 2001
From: dweiller <4678790+dweiller@users.noreply.github.com>
Date: Tue, 31 Dec 2024 16:07:08 +1100
Subject: [PATCH 059/110] Sema: disallow slicing many-item pointer with
different sentinel
This change prevents adding or changing the sentinel in the type of a
many-item pointer via the slicing syntax `ptr[a.. :S]`.
---
src/Sema.zig | 32 +++++++++++++++++++
test/behavior/slice.zig | 3 --
...f_many-item_pointer_preserves_sentinel.zig | 18 +++++++++++
3 files changed, 50 insertions(+), 3 deletions(-)
create mode 100644 test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig
diff --git a/src/Sema.zig b/src/Sema.zig
index 94bf21e03b5c0aa364e0c4e183e53e097843cc30..9345c4bcaccb708a7eb2a53ba92406b81fa8b3f6 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -31876,6 +31876,38 @@ fn analyzeSlice(
break :e try sema.coerce(block, .usize, uncasted_end, end_src);
} else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src);
}
+
+ // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it
+ // must match the sentinel of `@TypeOf(ptr)`.
+ sentinel_check: {
+ if (sentinel_opt == .none) break :sentinel_check;
+ const provided = provided: {
+ const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
+ try checkSentinelType(sema, block, sentinel_src, elem_ty);
+ break :provided try sema.resolveConstDefinedValue(
+ block,
+ sentinel_src,
+ casted,
+ .{ .simple = .slice_sentinel },
+ );
+ };
+
+ if (ptr_sentinel) |current| {
+ if (provided.toIntern() == current.toIntern()) break :sentinel_check;
+ }
+
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{});
+ errdefer msg.destroy(sema.gpa);
+ if (ptr_sentinel) |current| {
+ try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) });
+ } else {
+ try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)});
+ }
+ try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
+ break :msg msg;
+ });
+ }
return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
};
diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig
index 25b501e8411dab05b51f6c3341d9806a22587c68..22e4e06dfe8d6dd8d8b7e1a7479a3184cae34a98 100644
--- a/test/behavior/slice.zig
+++ b/test/behavior/slice.zig
@@ -628,9 +628,6 @@ test "slice syntax resulting in pointer-to-array" {
comptime assert(@TypeOf(ptr[1..][0..2]) == *[2]u8);
comptime assert(@TypeOf(ptr[1..][0..4]) == *[4]u8);
comptime assert(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
- comptime assert(@TypeOf(ptr[1.. :0][0..2]) == *[2]u8);
- comptime assert(@TypeOf(ptr[1.. :0][0..4]) == *[4]u8);
- comptime assert(@TypeOf(ptr[1.. :0][0..2 :4]) == *[2:4]u8);
var ptr_z: [*:0]u8 = &array;
comptime assert(@TypeOf(ptr_z[1..][0..2]) == *[2]u8);
diff --git a/test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig b/test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig
new file mode 100644
index 0000000000000000000000000000000000000000..02fbb5289d3cfb780762f9fa1c6ca5f6328f9705
--- /dev/null
+++ b/test/cases/compile_errors/slice_of_many-item_pointer_preserves_sentinel.zig
@@ -0,0 +1,18 @@
+comptime {
+ var ptr: [*]const u8 = undefined;
+ _ = ptr[0.. :0];
+}
+
+comptime {
+ var ptrz: [*:0]const u8 = undefined;
+ _ = ptrz[0.. :1];
+}
+
+// error
+//
+// :3:18: error: sentinel-terminated slicing of many-item pointer must match existing sentinel
+// :3:9: note: type '[*]const u8' does not have a sentinel
+// :3:12: note: use @ptrCast to cast pointer sentinel
+// :8:19: error: sentinel-terminated slicing of many-item pointer must match existing sentinel
+// :8:19: note: expected sentinel '0', found '1'
+// :8:13: note: use @ptrCast to cast pointer sentinel
--
2.54.0
From 1a15fbe9607c74096c875f6d871213c7d4db1483 Mon Sep 17 00:00:00 2001
From: mikastiv
Date: Sun, 3 Nov 2024 22:48:41 -0500
Subject: [PATCH 060/110] Sema: add note suggesting dropping try on non
error-unions
---
src/Sema.zig | 24 ++++++++++++++-----
.../compile_errors/comptime_try_non_error.zig | 1 +
test/cases/compile_errors/redundant_try.zig | 6 +++++
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index 9345c4bcaccb708a7eb2a53ba92406b81fa8b3f6..5f22e48530ce3bf082ee9cc1d3613e3c73c0f23e 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1905,8 +1905,12 @@ fn analyzeBodyInner(
const err_union = try sema.resolveInst(extra.data.operand);
const err_union_ty = sema.typeOf(err_union);
if (err_union_ty.zigTypeTag(zcu) != .error_union) {
- return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
- err_union_ty.fmt(pt),
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, err_union_ty);
+ try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
+ break :msg msg;
});
}
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
@@ -18175,8 +18179,12 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
const pt = sema.pt;
const zcu = pt.zcu;
if (err_union_ty.zigTypeTag(zcu) != .error_union) {
- return sema.fail(parent_block, operand_src, "expected error union type, found '{f}'", .{
- err_union_ty.fmt(pt),
+ return sema.failWithOwnedErrorMsg(parent_block, msg: {
+ const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, err_union_ty);
+ try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
+ break :msg msg;
});
}
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
@@ -18235,8 +18243,12 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
const pt = sema.pt;
const zcu = pt.zcu;
if (err_union_ty.zigTypeTag(zcu) != .error_union) {
- return sema.fail(parent_block, operand_src, "expected error union type, found '{f}'", .{
- err_union_ty.fmt(pt),
+ return sema.failWithOwnedErrorMsg(parent_block, msg: {
+ const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, err_union_ty);
+ try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
+ break :msg msg;
});
}
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
diff --git a/test/cases/compile_errors/comptime_try_non_error.zig b/test/cases/compile_errors/comptime_try_non_error.zig
index 8d61df6e9ad451f3f541e9fbf9d509e44ba9abfb..44f7dbe6149f462b81822f72c01e0fddc879851e 100644
--- a/test/cases/compile_errors/comptime_try_non_error.zig
+++ b/test/cases/compile_errors/comptime_try_non_error.zig
@@ -13,4 +13,5 @@ pub fn bar() u8 {
// error
//
// :6:12: error: expected error union type, found 'u8'
+// :6:12: note: consider omitting 'try'
// :2:8: note: called at comptime here
diff --git a/test/cases/compile_errors/redundant_try.zig b/test/cases/compile_errors/redundant_try.zig
index 5472701ce0461581d80bb4dc47a11f78318da71e..73beca2c2df3034aa1618b5001f7a03204d8d171 100644
--- a/test/cases/compile_errors/redundant_try.zig
+++ b/test/cases/compile_errors/redundant_try.zig
@@ -43,10 +43,16 @@ comptime {
// error
//
// :5:23: error: expected error union type, found 'comptime_int'
+// :5:23: note: consider omitting 'try'
// :10:23: error: expected error union type, found '@TypeOf(.{})'
+// :10:23: note: consider omitting 'try'
// :15:23: error: expected error union type, found 'tmp.S'
// :1:11: note: struct declared here
+// :15:23: note: consider omitting 'try'
// :20:27: error: expected error union type, found 'tmp.S'
// :1:11: note: struct declared here
+// :20:27: note: consider omitting 'try'
// :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }'
+// :25:23: note: consider omitting 'try'
// :31:13: error: expected error union type, found 'u32'
+// :31:13: note: consider omitting 'try'
--
2.54.0
From 627a292a1139c96f5de80084ab5ce8b852db5132 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 30 Jul 2025 10:00:42 -0700
Subject: [PATCH 061/110] fetch: remove calls to fsync
fsync blocks until the contents have been actually written to disk,
which would be useful if we didn't want to report success until having
achieved durability. But the OS will ensure coherency; i.e. if one
process writes stuff without calling fsync, then another process reads
that stuff, the writes will be seen even if they didn't get flushed to
disk yet.
Since this code deals with ephemeral cache data, it's not worth trying
to achieve this kind of durability guarantee. This is consistent with
all the other tooling on the system.
Certainly, if we wanted to change our stance on this, it would not be
something that affects only the git fetching logic.
---
src/Package/Fetch.zig | 2 --
src/Package/Fetch/git.zig | 2 --
2 files changed, 4 deletions(-)
diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig
index 6ad0030c17a1317f2858ca955e503960622faaed..f47086bf58630b174ee171ecb2747cd6875904bf 100644
--- a/src/Package/Fetch.zig
+++ b/src/Package/Fetch.zig
@@ -1386,7 +1386,6 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U
defer pack_file.close();
var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
try fifo.pump(resource.fetch_stream.reader(), pack_file.deprecatedWriter());
- try pack_file.sync();
var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });
defer index_file.close();
@@ -1396,7 +1395,6 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U
var index_buffered_writer = std.io.bufferedWriter(index_file.deprecatedWriter());
try git.indexPack(gpa, object_format, pack_file, index_buffered_writer.writer());
try index_buffered_writer.flush();
- try index_file.sync();
}
{
diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig
index a8446d48a83c016f5ab1e3ff3edf01b50e36bc22..34d373f5533754e4b3b1fd764b41513499de9903 100644
--- a/src/Package/Fetch/git.zig
+++ b/src/Package/Fetch/git.zig
@@ -238,7 +238,6 @@ pub const Repository = struct {
};
defer file.close();
try file.writeAll(file_object.data);
- try file.sync();
},
.symlink => {
try repository.odb.seekOid(entry.oid);
@@ -1690,7 +1689,6 @@ pub fn main() !void {
var index_buffered_writer = std.io.bufferedWriter(index_file.deprecatedWriter());
try indexPack(allocator, format, pack_file, index_buffered_writer.writer());
try index_buffered_writer.flush();
- try index_file.sync();
std.debug.print("Starting checkout...\n", .{});
var repository = try Repository.init(allocator, format, pack_file, index_file);
--
2.54.0
From ac1e73e249f8ce06bc7c89d2bdc4359b0399236c Mon Sep 17 00:00:00 2001
From: mlugg
Date: Thu, 31 Jul 2025 10:10:40 +0100
Subject: [PATCH 062/110] std.enums: fix `EnumIndexer` branch quota
It's quite silly to have this override which nonetheless makes
assumptions about the input type. Encode the actual complexity of the
sort.
Also, simplify the sorting logic, and fix a bug (grab min and max
*after* the sort, not *before*!)
---
lib/std/enums.zig | 58 +++++++++++++++++++++++++++++------------------
1 file changed, 36 insertions(+), 22 deletions(-)
diff --git a/lib/std/enums.zig b/lib/std/enums.zig
index e34cf17e07203b2beaeeebb10f25e527776b762a..e47af4aca31b51e98ecb76ae0ec32c16c20d6d4e 100644
--- a/lib/std/enums.zig
+++ b/lib/std/enums.zig
@@ -1317,9 +1317,9 @@ test "EnumSet non-exhaustive" {
}
pub fn EnumIndexer(comptime E: type) type {
- // Assumes that the enum fields are sorted in ascending order (optimistic).
- // Unsorted enums may require the user to manually increase the quota.
- @setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);
+ // n log n for `std.mem.sortUnstable` call below.
+ const fields_len = @typeInfo(E).@"enum".fields.len;
+ @setEvalBranchQuota(3 * fields_len * std.math.log2(@max(fields_len, 1)) + eval_branch_quota_cushion);
if (!@typeInfo(E).@"enum".is_exhaustive) {
const BackingInt = @typeInfo(E).@"enum".tag_type;
@@ -1354,10 +1354,6 @@ pub fn EnumIndexer(comptime E: type) type {
};
}
- const const_fields = @typeInfo(E).@"enum".fields;
- var fields = const_fields[0..const_fields.len].*;
- const fields_len = fields.len;
-
if (fields_len == 0) {
return struct {
pub const Key = E;
@@ -1373,22 +1369,17 @@ pub fn EnumIndexer(comptime E: type) type {
};
}
+ var fields: [fields_len]EnumField = @typeInfo(E).@"enum".fields[0..].*;
+
+ std.mem.sortUnstable(EnumField, &fields, {}, struct {
+ fn lessThan(ctx: void, lhs: EnumField, rhs: EnumField) bool {
+ ctx;
+ return lhs.value < rhs.value;
+ }
+ }.lessThan);
+
const min = fields[0].value;
- const max = fields[fields.len - 1].value;
-
- const SortContext = struct {
- fields: []EnumField,
-
- pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
- return ctx.fields[a].value < ctx.fields[b].value;
- }
-
- pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
- return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
- }
- };
- std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields });
-
+ const max = fields[fields_len - 1].value;
if (max - min == fields.len - 1) {
return struct {
pub const Key = E;
@@ -1538,6 +1529,29 @@ test "EnumIndexer empty" {
try testing.expectEqual(0, Indexer.count);
}
+test "EnumIndexer large dense unsorted" {
+ @setEvalBranchQuota(500_000); // many `comptimePrint`s
+ // Make an enum with 500 fields with values in *descending* order.
+ const E = @Type(.{ .@"enum" = .{
+ .tag_type = u32,
+ .fields = comptime fields: {
+ var fields: [500]EnumField = undefined;
+ for (&fields, 0..) |*f, i| f.* = .{
+ .name = std.fmt.comptimePrint("f{d}", .{i}),
+ .value = 500 - i,
+ };
+ break :fields &fields;
+ },
+ .decls = &.{},
+ .is_exhaustive = true,
+ } });
+ const Indexer = EnumIndexer(E);
+ try testing.expectEqual(E.f0, Indexer.keyForIndex(499));
+ try testing.expectEqual(E.f499, Indexer.keyForIndex(0));
+ try testing.expectEqual(499, Indexer.indexOf(.f0));
+ try testing.expectEqual(0, Indexer.indexOf(.f499));
+}
+
test values {
const E = enum {
X,
--
2.54.0
From 264bd7053edb948d0f96525b54b859c0422e12a2 Mon Sep 17 00:00:00 2001
From: Jackson Wambolt
Date: Sat, 28 Jun 2025 19:33:38 -0500
Subject: [PATCH 063/110] Sema: remove incorrect `requireRuntimeBlock` calls
Part of #22353
Resolves: #24273
Co-Authored-By: Matthew Lugg
---
src/Sema.zig | 14 ++------------
.../runtime_value_in_comptime_array.zig | 14 ++++++++++++++
.../runtime_value_in_comptime_struct.zig | 14 ++++++++++++++
3 files changed, 30 insertions(+), 12 deletions(-)
create mode 100644 test/cases/compile_errors/runtime_value_in_comptime_array.zig
create mode 100644 test/cases/compile_errors/runtime_value_in_comptime_struct.zig
diff --git a/src/Sema.zig b/src/Sema.zig
index f81bb15f875c163cc2d7e0838bd23717e9e71581..d7add0724d7e6462040703f8450a20241ab376f8 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -19572,7 +19572,7 @@ fn structInitAnon(
try sema.declareDependency(.{ .interned = struct_ty });
try sema.addTypeReferenceEntry(src, struct_ty);
- const runtime_index = opt_runtime_index orelse {
+ _ = opt_runtime_index orelse {
const struct_val = try pt.intern(.{ .aggregate = .{
.ty = struct_ty,
.storage = .{ .elems = values },
@@ -19580,11 +19580,6 @@ fn structInitAnon(
return sema.addConstantMaybeRef(struct_val, is_ref);
};
- try sema.requireRuntimeBlock(block, LazySrcLoc.unneeded, block.src(.{ .init_elem = .{
- .init_node_offset = src.offset.node_offset.x,
- .elem_index = @intCast(runtime_index),
- } }));
-
if (is_ref) {
const target = zcu.getTarget();
const alloc_ty = try pt.ptrTypeSema(.{
@@ -19713,7 +19708,7 @@ fn zirArrayInit(
if (!comptime_known) break @intCast(i);
} else null;
- const runtime_index = opt_runtime_index orelse {
+ _ = opt_runtime_index orelse {
const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
for (elem_vals, resolved_args) |*val, arg| {
// We checked that all args are comptime above.
@@ -19728,11 +19723,6 @@ fn zirArrayInit(
return sema.addConstantMaybeRef(result_val.toIntern(), is_ref);
};
- try sema.requireRuntimeBlock(block, LazySrcLoc.unneeded, block.src(.{ .init_elem = .{
- .init_node_offset = src.offset.node_offset.x,
- .elem_index = runtime_index,
- } }));
-
if (is_ref) {
const target = zcu.getTarget();
const alloc_ty = try pt.ptrTypeSema(.{
diff --git a/test/cases/compile_errors/runtime_value_in_comptime_array.zig b/test/cases/compile_errors/runtime_value_in_comptime_array.zig
new file mode 100644
index 0000000000000000000000000000000000000000..804cd258fbe6de253ee419a2dec82b518a71d12c
--- /dev/null
+++ b/test/cases/compile_errors/runtime_value_in_comptime_array.zig
@@ -0,0 +1,14 @@
+fn comptimeArray(comptime _: []const u8) void {}
+fn bar() u8 {
+ return 123;
+}
+export fn entry() void {
+ const y = bar();
+ comptimeArray(&.{y});
+}
+
+// error
+//
+// :7:19: error: unable to resolve comptime value
+// :7:19: note: argument to comptime parameter must be comptime-known
+// :1:18: note: parameter declared comptime here
diff --git a/test/cases/compile_errors/runtime_value_in_comptime_struct.zig b/test/cases/compile_errors/runtime_value_in_comptime_struct.zig
new file mode 100644
index 0000000000000000000000000000000000000000..acaef3c543274869524a3d96361a7c39f8c045f0
--- /dev/null
+++ b/test/cases/compile_errors/runtime_value_in_comptime_struct.zig
@@ -0,0 +1,14 @@
+fn comptimeStruct(comptime _: anytype) void {}
+fn bar() u8 {
+ return 123;
+}
+export fn entry() void {
+ const y = bar();
+ comptimeStruct(.{ .foo = y });
+}
+
+// error
+//
+// :7:21: error: unable to resolve comptime value
+// :7:21: note: argument to comptime parameter must be comptime-known
+// :1:19: note: parameter declared comptime here
--
2.54.0
From 0294e91451ba8fb06a40cbd0d3da80e2792f5923 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 31 Jul 2025 14:34:21 -0700
Subject: [PATCH 064/110] std.Io.Reader: fix readVec at end
---
lib/std/Io/Reader.zig | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig
index 2b3f4d9cd8364b03bc50053507b40f963767716c..7ba58bfbf4597f6b86f555568339d23253ecbf0c 100644
--- a/lib/std/Io/Reader.zig
+++ b/lib/std/Io/Reader.zig
@@ -406,7 +406,10 @@ pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
r.seek = seek;
data[i] = buf[copy_len..];
defer data[i] = buf;
- return n + try r.vtable.readVec(r, data[i..]);
+ return n + (r.vtable.readVec(r, data[i..]) catch |err| switch (err) {
+ error.EndOfStream => if (n == 0) return error.EndOfStream else 0,
+ error.ReadFailed => return error.ReadFailed,
+ });
}
const n = seek - r.seek;
r.seek = seek;
@@ -1657,6 +1660,17 @@ test "expected error.EndOfStream" {
try std.testing.expectError(error.EndOfStream, r.take(3));
}
+test "readVec at end" {
+ var reader_buffer: [8]u8 = "abcd1234".*;
+ var reader: testing.Reader = .init(&reader_buffer, &.{});
+ reader.interface.end = reader_buffer.len;
+
+ var out: [16]u8 = undefined;
+ var vecs: [1][]u8 = .{&out};
+ try testing.expectEqual(8, try reader.interface.readVec(&vecs));
+ try testing.expectEqualStrings("abcd1234", vecs[0][0..8]);
+}
+
fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
_ = r;
_ = w;
--
2.54.0
From a2d21d63270ebb5eec0d437f7726c261455da66b Mon Sep 17 00:00:00 2001
From: Chinmay Dalal
Date: Thu, 31 Jul 2025 20:52:56 +0530
Subject: [PATCH 065/110] enable pwd.h functions for other OSes
also add the layout of `struct passwd` for DragonflyBSD
and FreeBSD:
- https://github.com/DragonFlyBSD/DragonFlyBSD/blob/c267aac0072dae6cf4ae874605f3f0659a2fc820/include/pwd.h#L112
- https://cgit.freebsd.org/src/tree/include/pwd.h?id=d66f9c86fa3fd8d8f0a56ea96b03ca11f2fac1fb#n114
---
lib/std/c.zig | 24 ++++++++++++++++++------
lib/std/c/openbsd.zig | 5 -----
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index 818a4ae0cc2d000c2fe5e95641a149ec798f9a33..6a0289f5f0c234ee4601d84a469f726751f445aa 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -204,6 +204,19 @@ pub const passwd = switch (native_os) {
shell: ?[*:0]const u8, // default shell
expire: time_t, // account expiration
},
+ .dragonfly, .freebsd => extern struct {
+ name: ?[*:0]const u8, // user name
+ passwd: ?[*:0]const u8, // encrypted password
+ uid: uid_t, // user uid
+ gid: gid_t, // user gid
+ change: time_t, // password change time
+ class: ?[*:0]const u8, // user access class
+ gecos: ?[*:0]const u8, // Honeywell login info
+ dir: ?[*:0]const u8, // home directory
+ shell: ?[*:0]const u8, // default shell
+ expire: time_t, // account expiration
+ fields: c_int, // internal
+ },
else => void,
};
@@ -10271,9 +10284,13 @@ pub const fstatat = switch (native_os) {
},
else => private.fstatat,
};
-
+pub extern "c" fn getpwent() ?*passwd;
+pub extern "c" fn endpwent() void;
+pub extern "c" fn setpwent() void;
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
+pub extern "c" fn getpwnam_r(name: [*:0]const u8, pwd: *passwd, buf: [*]u8, buflen: usize, result: *?*passwd) c_int;
pub extern "c" fn getpwuid(uid: uid_t) ?*passwd;
+pub extern "c" fn getpwuid_r(uid: uid_t, pwd: *passwd, buf: [*]u8, buflen: usize, result: *?*passwd) c_int;
pub extern "c" fn getgrent() ?*group;
pub extern "c" fn setgrent() void;
pub extern "c" fn endgrent() void;
@@ -11009,11 +11026,7 @@ pub const bcrypt = openbsd.bcrypt;
pub const bcrypt_checkpass = openbsd.bcrypt_checkpass;
pub const bcrypt_gensalt = openbsd.bcrypt_gensalt;
pub const bcrypt_newhash = openbsd.bcrypt_newhash;
-pub const endpwent = openbsd.endpwent;
-pub const getpwent = openbsd.getpwent;
-pub const getpwnam_r = openbsd.getpwnam_r;
pub const getpwnam_shadow = openbsd.getpwnam_shadow;
-pub const getpwuid_r = openbsd.getpwuid_r;
pub const getpwuid_shadow = openbsd.getpwuid_shadow;
pub const getthrid = openbsd.getthrid;
pub const login_cap_t = openbsd.login_cap_t;
@@ -11030,7 +11043,6 @@ pub const pthread_spinlock_t = openbsd.pthread_spinlock_t;
pub const pw_dup = openbsd.pw_dup;
pub const setclasscontext = openbsd.setclasscontext;
pub const setpassent = openbsd.setpassent;
-pub const setpwent = openbsd.setpwent;
pub const setusercontext = openbsd.setusercontext;
pub const uid_from_user = openbsd.uid_from_user;
pub const unveil = openbsd.unveil;
diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig
index af86d383975f84f2e52b972776660ff527f10513..242d988de3d3f8052ed68be0af7ed6aa729f316b 100644
--- a/lib/std/c/openbsd.zig
+++ b/lib/std/c/openbsd.zig
@@ -81,11 +81,6 @@ pub extern "c" fn auth_checknologin(lc: *login_cap_t) void;
pub extern "c" fn getpwuid_shadow(uid: uid_t) ?*passwd;
pub extern "c" fn getpwnam_shadow(name: [*:0]const u8) ?*passwd;
-pub extern "c" fn getpwnam_r(name: [*:0]const u8, pw: *passwd, buf: [*]u8, buflen: usize, pwretp: *?*passwd) c_int;
-pub extern "c" fn getpwuid_r(uid: uid_t, pw: *passwd, buf: [*]u8, buflen: usize, pwretp: *?*passwd) c_int;
-pub extern "c" fn getpwent() ?*passwd;
-pub extern "c" fn setpwent() void;
-pub extern "c" fn endpwent() void;
pub extern "c" fn setpassent(stayopen: c_int) c_int;
pub extern "c" fn uid_from_user(name: [*:0]const u8, uid: *uid_t) c_int;
pub extern "c" fn user_from_uid(uid: uid_t, noname: c_int) ?[*:0]const u8;
--
2.54.0
From 83513ade3591de673e9ac4824fe974cd8f90c847 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 25 Jul 2025 22:10:29 -0700
Subject: [PATCH 066/110] std.compress: rework flate to new I/O API
---
lib/std/compress.zig | 5 +-
lib/std/compress/flate.zig | 511 +++++---
lib/std/compress/flate/BlockWriter.zig | 696 +++++++++++
lib/std/compress/flate/CircularBuffer.zig | 240 ----
lib/std/compress/flate/Compress.zig | 1264 ++++++++++++++++++++
lib/std/compress/flate/Decompress.zig | 894 ++++++++++++++
lib/std/compress/flate/Lookup.zig | 30 +-
lib/std/compress/flate/SlidingWindow.zig | 160 ---
lib/std/compress/flate/Token.zig | 14 +-
lib/std/compress/flate/bit_reader.zig | 422 -------
lib/std/compress/flate/bit_writer.zig | 99 --
lib/std/compress/flate/block_writer.zig | 706 -----------
lib/std/compress/flate/consts.zig | 49 -
lib/std/compress/flate/container.zig | 208 ----
lib/std/compress/flate/deflate.zig | 744 ------------
lib/std/compress/flate/huffman_decoder.zig | 302 -----
lib/std/compress/flate/huffman_encoder.zig | 536 ---------
lib/std/compress/flate/inflate.zig | 570 ---------
lib/std/compress/gzip.zig | 66 -
lib/std/compress/zlib.zig | 101 --
lib/std/debug/Dwarf.zig | 14 +-
lib/std/http/Client.zig | 9 +-
lib/std/http/Server.zig | 4 +-
lib/std/zip.zig | 933 ++++++---------
24 files changed, 3617 insertions(+), 4960 deletions(-)
create mode 100644 lib/std/compress/flate/BlockWriter.zig
delete mode 100644 lib/std/compress/flate/CircularBuffer.zig
create mode 100644 lib/std/compress/flate/Compress.zig
create mode 100644 lib/std/compress/flate/Decompress.zig
delete mode 100644 lib/std/compress/flate/SlidingWindow.zig
delete mode 100644 lib/std/compress/flate/bit_reader.zig
delete mode 100644 lib/std/compress/flate/bit_writer.zig
delete mode 100644 lib/std/compress/flate/block_writer.zig
delete mode 100644 lib/std/compress/flate/consts.zig
delete mode 100644 lib/std/compress/flate/container.zig
delete mode 100644 lib/std/compress/flate/deflate.zig
delete mode 100644 lib/std/compress/flate/huffman_decoder.zig
delete mode 100644 lib/std/compress/flate/huffman_encoder.zig
delete mode 100644 lib/std/compress/flate/inflate.zig
delete mode 100644 lib/std/compress/gzip.zig
delete mode 100644 lib/std/compress/zlib.zig
diff --git a/lib/std/compress.zig b/lib/std/compress.zig
index 018de51001219e63376a7a4b41e687d75ab5c0ae..199d046af66cc2c1bb777784b5a8cd7ee9df911a 100644
--- a/lib/std/compress.zig
+++ b/lib/std/compress.zig
@@ -1,8 +1,7 @@
//! Compression algorithms.
+/// gzip and zlib are here.
pub const flate = @import("compress/flate.zig");
-pub const gzip = @import("compress/gzip.zig");
-pub const zlib = @import("compress/zlib.zig");
pub const lzma = @import("compress/lzma.zig");
pub const lzma2 = @import("compress/lzma2.zig");
pub const xz = @import("compress/xz.zig");
@@ -14,6 +13,4 @@ test {
_ = lzma2;
_ = xz;
_ = zstd;
- _ = gzip;
- _ = zlib;
}
diff --git a/lib/std/compress/flate.zig b/lib/std/compress/flate.zig
index 6a111ac0fcfb097717008a6bde0cb916a97fc5a2..5a54643f45f2d79e6b7d49e88f9a6269a727be1c 100644
--- a/lib/std/compress/flate.zig
+++ b/lib/std/compress/flate.zig
@@ -1,94 +1,189 @@
+const builtin = @import("builtin");
+const std = @import("../std.zig");
+const testing = std.testing;
+const Writer = std.io.Writer;
+
+/// Container of the deflate bit stream body. Container adds header before
+/// deflate bit stream and footer after. It can bi gzip, zlib or raw (no header,
+/// no footer, raw bit stream).
+///
+/// Zlib format is defined in rfc 1950. Header has 2 bytes and footer 4 bytes
+/// addler 32 checksum.
+///
+/// Gzip format is defined in rfc 1952. Header has 10+ bytes and footer 4 bytes
+/// crc32 checksum and 4 bytes of uncompressed data length.
+///
+///
+/// rfc 1950: https://datatracker.ietf.org/doc/html/rfc1950#page-4
+/// rfc 1952: https://datatracker.ietf.org/doc/html/rfc1952#page-5
+pub const Container = enum {
+ raw, // no header or footer
+ gzip, // gzip header and footer
+ zlib, // zlib header and footer
+
+ pub fn size(w: Container) usize {
+ return headerSize(w) + footerSize(w);
+ }
+
+ pub fn headerSize(w: Container) usize {
+ return header(w).len;
+ }
+
+ pub fn footerSize(w: Container) usize {
+ return switch (w) {
+ .gzip => 8,
+ .zlib => 4,
+ .raw => 0,
+ };
+ }
+
+ pub const list = [_]Container{ .raw, .gzip, .zlib };
+
+ pub const Error = error{
+ BadGzipHeader,
+ BadZlibHeader,
+ WrongGzipChecksum,
+ WrongGzipSize,
+ WrongZlibChecksum,
+ };
+
+ pub fn header(container: Container) []const u8 {
+ return switch (container) {
+ // GZIP 10 byte header (https://datatracker.ietf.org/doc/html/rfc1952#page-5):
+ // - ID1 (IDentification 1), always 0x1f
+ // - ID2 (IDentification 2), always 0x8b
+ // - CM (Compression Method), always 8 = deflate
+ // - FLG (Flags), all set to 0
+ // - 4 bytes, MTIME (Modification time), not used, all set to zero
+ // - XFL (eXtra FLags), all set to zero
+ // - OS (Operating System), 03 = Unix
+ .gzip => &[_]u8{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 },
+ // ZLIB has a two-byte header (https://datatracker.ietf.org/doc/html/rfc1950#page-4):
+ // 1st byte:
+ // - First four bits is the CINFO (compression info), which is 7 for the default deflate window size.
+ // - The next four bits is the CM (compression method), which is 8 for deflate.
+ // 2nd byte:
+ // - Two bits is the FLEVEL (compression level). Values are: 0=fastest, 1=fast, 2=default, 3=best.
+ // - The next bit, FDICT, is set if a dictionary is given.
+ // - The final five FCHECK bits form a mod-31 checksum.
+ //
+ // CINFO = 7, CM = 8, FLEVEL = 0b10, FDICT = 0, FCHECK = 0b11100
+ .zlib => &[_]u8{ 0x78, 0b10_0_11100 },
+ .raw => &.{},
+ };
+ }
+
+ pub const Hasher = union(Container) {
+ raw: void,
+ gzip: struct {
+ crc: std.hash.Crc32 = .init(),
+ count: usize = 0,
+ },
+ zlib: std.hash.Adler32,
+
+ pub fn init(containter: Container) Hasher {
+ return switch (containter) {
+ .gzip => .{ .gzip = .{} },
+ .zlib => .{ .zlib = .init() },
+ .raw => .raw,
+ };
+ }
+
+ pub fn container(h: Hasher) Container {
+ return h;
+ }
+
+ pub fn update(h: *Hasher, buf: []const u8) void {
+ switch (h.*) {
+ .raw => {},
+ .gzip => |*gzip| {
+ gzip.update(buf);
+ gzip.count += buf.len;
+ },
+ .zlib => |*zlib| {
+ zlib.update(buf);
+ },
+ inline .gzip, .zlib => |*x| x.update(buf),
+ }
+ }
+
+ pub fn writeFooter(hasher: *Hasher, writer: *Writer) Writer.Error!void {
+ var bits: [4]u8 = undefined;
+ switch (hasher.*) {
+ .gzip => |*gzip| {
+ // GZIP 8 bytes footer
+ // - 4 bytes, CRC32 (CRC-32)
+ // - 4 bytes, ISIZE (Input SIZE) - size of the original (uncompressed) input data modulo 2^32
+ std.mem.writeInt(u32, &bits, gzip.final(), .little);
+ try writer.writeAll(&bits);
+
+ std.mem.writeInt(u32, &bits, gzip.bytes_read, .little);
+ try writer.writeAll(&bits);
+ },
+ .zlib => |*zlib| {
+ // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
+ // 4 bytes of ADLER32 (Adler-32 checksum)
+ // Checksum value of the uncompressed data (excluding any
+ // dictionary data) computed according to Adler-32
+ // algorithm.
+ std.mem.writeInt(u32, &bits, zlib.final, .big);
+ try writer.writeAll(&bits);
+ },
+ .raw => {},
+ }
+ }
+ };
+};
+
+/// When decompressing, the output buffer is used as the history window, so
+/// less than this may result in failure to decompress streams that were
+/// compressed with a larger window.
+pub const max_window_len = 1 << 16;
+
/// Deflate is a lossless data compression file format that uses a combination
/// of LZ77 and Huffman coding.
-pub const deflate = @import("flate/deflate.zig");
+pub const Compress = @import("flate/Compress.zig");
/// Inflate is the decoding process that takes a Deflate bitstream for
/// decompression and correctly produces the original full-size data or file.
-pub const inflate = @import("flate/inflate.zig");
-
-/// Decompress compressed data from reader and write plain data to the writer.
-pub fn decompress(reader: anytype, writer: anytype) !void {
- try inflate.decompress(.raw, reader, writer);
-}
-
-/// Decompressor type
-pub fn Decompressor(comptime ReaderType: type) type {
- return inflate.Decompressor(.raw, ReaderType);
-}
-
-/// Create Decompressor which will read compressed data from reader.
-pub fn decompressor(reader: anytype) Decompressor(@TypeOf(reader)) {
- return inflate.decompressor(.raw, reader);
-}
-
-/// Compression level, trades between speed and compression size.
-pub const Options = deflate.Options;
-
-/// Compress plain data from reader and write compressed data to the writer.
-pub fn compress(reader: anytype, writer: anytype, options: Options) !void {
- try deflate.compress(.raw, reader, writer, options);
-}
-
-/// Compressor type
-pub fn Compressor(comptime WriterType: type) type {
- return deflate.Compressor(.raw, WriterType);
-}
-
-/// Create Compressor which outputs compressed data to the writer.
-pub fn compressor(writer: anytype, options: Options) !Compressor(@TypeOf(writer)) {
- return try deflate.compressor(.raw, writer, options);
-}
+pub const Decompress = @import("flate/Decompress.zig");
/// Huffman only compression. Without Lempel-Ziv match searching. Faster
/// compression, less memory requirements but bigger compressed sizes.
pub const huffman = struct {
- pub fn compress(reader: anytype, writer: anytype) !void {
- try deflate.huffman.compress(.raw, reader, writer);
- }
+ // The odd order in which the codegen code sizes are written.
+ pub const codegen_order = [_]u32{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+ // The number of codegen codes.
+ pub const codegen_code_count = 19;
- pub fn Compressor(comptime WriterType: type) type {
- return deflate.huffman.Compressor(.raw, WriterType);
- }
+ // The largest distance code.
+ pub const distance_code_count = 30;
- pub fn compressor(writer: anytype) !huffman.Compressor(@TypeOf(writer)) {
- return deflate.huffman.compressor(.raw, writer);
- }
-};
-
-// No compression store only. Compressed size is slightly bigger than plain.
-pub const store = struct {
- pub fn compress(reader: anytype, writer: anytype) !void {
- try deflate.store.compress(.raw, reader, writer);
- }
+ // Maximum number of literals.
+ pub const max_num_lit = 286;
- pub fn Compressor(comptime WriterType: type) type {
- return deflate.store.Compressor(.raw, WriterType);
- }
+ // Max number of frequencies used for a Huffman Code
+ // Possible lengths are codegen_code_count (19), distance_code_count (30) and max_num_lit (286).
+ // The largest of these is max_num_lit.
+ pub const max_num_frequencies = max_num_lit;
- pub fn compressor(writer: anytype) !store.Compressor(@TypeOf(writer)) {
- return deflate.store.compressor(.raw, writer);
- }
+ // Biggest block size for uncompressed block.
+ pub const max_store_block_size = 65535;
+ // The special code used to mark the end of a block.
+ pub const end_block_marker = 256;
};
-/// Container defines header/footer around deflate bit stream. Gzip and zlib
-/// compression algorithms are containers around deflate bit stream body.
-const Container = @import("flate/container.zig").Container;
-const std = @import("std");
-const testing = std.testing;
-const fixedBufferStream = std.io.fixedBufferStream;
-const print = std.debug.print;
-const builtin = @import("builtin");
-
test {
- _ = deflate;
- _ = inflate;
+ _ = Compress;
+ _ = Decompress;
}
test "compress/decompress" {
+ const print = std.debug.print;
var cmp_buf: [64 * 1024]u8 = undefined; // compressed data buffer
var dcm_buf: [64 * 1024]u8 = undefined; // decompressed data buffer
- const levels = [_]deflate.Level{ .level_4, .level_5, .level_6, .level_7, .level_8, .level_9 };
+ const levels = [_]Compress.Level{ .level_4, .level_5, .level_6, .level_7, .level_8, .level_9 };
const cases = [_]struct {
data: []const u8, // uncompressed content
// compressed data sizes per level 4-9
@@ -135,28 +230,34 @@ test "compress/decompress" {
// compress original stream to compressed stream
{
- var original = fixedBufferStream(data);
- var compressed = fixedBufferStream(&cmp_buf);
- try deflate.compress(container, original.reader(), compressed.writer(), .{ .level = level });
+ var original: std.io.Reader = .fixed(data);
+ var compressed: Writer = .fixed(&cmp_buf);
+ var compress: Compress = .init(&original, &.{}, .{ .container = .raw, .level = level });
+ const n = try compress.reader.streamRemaining(&compressed);
if (compressed_size == 0) {
if (container == .gzip)
print("case {d} gzip level {} compressed size: {d}\n", .{ case_no, level, compressed.pos });
- compressed_size = compressed.pos;
+ compressed_size = compressed.end;
}
- try testing.expectEqual(compressed_size, compressed.pos);
+ try testing.expectEqual(compressed_size, n);
+ try testing.expectEqual(compressed_size, compressed.end);
}
// decompress compressed stream to decompressed stream
{
- var compressed = fixedBufferStream(cmp_buf[0..compressed_size]);
- var decompressed = fixedBufferStream(&dcm_buf);
- try inflate.decompress(container, compressed.reader(), decompressed.writer());
- try testing.expectEqualSlices(u8, data, decompressed.getWritten());
+ var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var decompressed: Writer = .fixed(&dcm_buf);
+ var decompress: Decompress = .init(&compressed, container, &.{});
+ _ = try decompress.reader.streamRemaining(&decompressed);
+ try testing.expectEqualSlices(u8, data, decompressed.buffered());
}
// compressor writer interface
{
- var compressed = fixedBufferStream(&cmp_buf);
- var cmp = try deflate.compressor(container, compressed.writer(), .{ .level = level });
+ var compressed: Writer = .fixed(&cmp_buf);
+ var cmp = try Compress.init(&compressed, &.{}, .{
+ .level = level,
+ .container = container,
+ });
var cmp_wrt = cmp.writer();
try cmp_wrt.writeAll(data);
try cmp.finish();
@@ -165,10 +266,9 @@ test "compress/decompress" {
}
// decompressor reader interface
{
- var compressed = fixedBufferStream(cmp_buf[0..compressed_size]);
- var dcm = inflate.decompressor(container, compressed.reader());
- var dcm_rdr = dcm.reader();
- const n = try dcm_rdr.readAll(&dcm_buf);
+ var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var decompress: Decompress = .init(&compressed, container, &.{});
+ const n = try decompress.reader.readSliceShort(&dcm_buf);
try testing.expectEqual(data.len, n);
try testing.expectEqualSlices(u8, data, dcm_buf[0..n]);
}
@@ -184,9 +284,9 @@ test "compress/decompress" {
// compress original stream to compressed stream
{
- var original = fixedBufferStream(data);
- var compressed = fixedBufferStream(&cmp_buf);
- var cmp = try deflate.huffman.compressor(container, compressed.writer());
+ var original: std.io.Reader = .fixed(data);
+ var compressed: Writer = .fixed(&cmp_buf);
+ var cmp = try Compress.Huffman.init(container, &compressed);
try cmp.compress(original.reader());
try cmp.finish();
if (compressed_size == 0) {
@@ -198,10 +298,11 @@ test "compress/decompress" {
}
// decompress compressed stream to decompressed stream
{
- var compressed = fixedBufferStream(cmp_buf[0..compressed_size]);
- var decompressed = fixedBufferStream(&dcm_buf);
- try inflate.decompress(container, compressed.reader(), decompressed.writer());
- try testing.expectEqualSlices(u8, data, decompressed.getWritten());
+ var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var decompress: Decompress = .init(&compressed, container, &.{});
+ var decompressed: Writer = .fixed(&dcm_buf);
+ _ = try decompress.reader.streamRemaining(&decompressed);
+ try testing.expectEqualSlices(u8, data, decompressed.buffered());
}
}
}
@@ -216,9 +317,9 @@ test "compress/decompress" {
// compress original stream to compressed stream
{
- var original = fixedBufferStream(data);
- var compressed = fixedBufferStream(&cmp_buf);
- var cmp = try deflate.store.compressor(container, compressed.writer());
+ var original: std.io.Reader = .fixed(data);
+ var compressed: Writer = .fixed(&cmp_buf);
+ var cmp = try Compress.SimpleCompressor(.store, container).init(&compressed);
try cmp.compress(original.reader());
try cmp.finish();
if (compressed_size == 0) {
@@ -231,23 +332,25 @@ test "compress/decompress" {
}
// decompress compressed stream to decompressed stream
{
- var compressed = fixedBufferStream(cmp_buf[0..compressed_size]);
- var decompressed = fixedBufferStream(&dcm_buf);
- try inflate.decompress(container, compressed.reader(), decompressed.writer());
- try testing.expectEqualSlices(u8, data, decompressed.getWritten());
+ var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var decompress: Decompress = .init(&compressed, container, &.{});
+ var decompressed: Writer = .fixed(&dcm_buf);
+ _ = try decompress.reader.streamRemaining(&decompressed);
+ try testing.expectEqualSlices(u8, data, decompressed.buffered());
}
}
}
}
}
-fn testDecompress(comptime container: Container, compressed: []const u8, expected_plain: []const u8) !void {
- var in = fixedBufferStream(compressed);
- var out = std.ArrayList(u8).init(testing.allocator);
- defer out.deinit();
+fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {
+ var in: std.io.Reader = .fixed(compressed);
+ var aw: std.io.Writer.Allocating = .init(testing.allocator);
+ defer aw.deinit();
- try inflate.decompress(container, in.reader(), out.writer());
- try testing.expectEqualSlices(u8, expected_plain, out.items);
+ var decompress: Decompress = .init(&in, container, &.{});
+ _ = try decompress.reader.streamRemaining(&aw.writer);
+ try testing.expectEqualSlices(u8, expected_plain, aw.items);
}
test "don't read past deflate stream's end" {
@@ -352,126 +455,186 @@ test "gzip header" {
}
test "public interface" {
- const plain_data = [_]u8{ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a };
+ const plain_data_buf = [_]u8{ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a };
// deflate final stored block, header + plain (stored) data
const deflate_block = [_]u8{
0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
- } ++ plain_data;
+ } ++ plain_data_buf;
- // gzip header/footer + deflate block
- const gzip_data =
- [_]u8{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 } ++ // gzip header (10 bytes)
- deflate_block ++
- [_]u8{ 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00 }; // gzip footer checksum (4 byte), size (4 bytes)
+ const plain_data: []const u8 = &plain_data_buf;
+ const gzip_data: []const u8 = &deflate_block;
- // zlib header/footer + deflate block
- const zlib_data = [_]u8{ 0x78, 0b10_0_11100 } ++ // zlib header (2 bytes)}
- deflate_block ++
- [_]u8{ 0x1c, 0xf2, 0x04, 0x47 }; // zlib footer: checksum
+ //// gzip header/footer + deflate block
+ //const gzip_data =
+ // [_]u8{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 } ++ // gzip header (10 bytes)
+ // deflate_block ++
+ // [_]u8{ 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00 }; // gzip footer checksum (4 byte), size (4 bytes)
- const gzip = @import("gzip.zig");
- const zlib = @import("zlib.zig");
- const flate = @This();
+ //// zlib header/footer + deflate block
+ //const zlib_data = [_]u8{ 0x78, 0b10_0_11100 } ++ // zlib header (2 bytes)}
+ // deflate_block ++
+ // [_]u8{ 0x1c, 0xf2, 0x04, 0x47 }; // zlib footer: checksum
- try testInterface(gzip, &gzip_data, &plain_data);
- try testInterface(zlib, &zlib_data, &plain_data);
- try testInterface(flate, &deflate_block, &plain_data);
-}
+ // TODO
+ //const gzip = @import("gzip.zig");
+ //const zlib = @import("zlib.zig");
-fn testInterface(comptime pkg: type, gzip_data: []const u8, plain_data: []const u8) !void {
var buffer1: [64]u8 = undefined;
var buffer2: [64]u8 = undefined;
- var compressed = fixedBufferStream(&buffer1);
- var plain = fixedBufferStream(&buffer2);
+ // TODO These used to be functions, need to migrate the tests
+ const decompress = void;
+ const compress = void;
+ const store = void;
// decompress
{
- var in = fixedBufferStream(gzip_data);
- try pkg.decompress(in.reader(), plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var plain: Writer = .fixed(&buffer2);
+
+ var in: std.io.Reader = .fixed(gzip_data);
+ try decompress(&in, &plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
- plain.reset();
- compressed.reset();
// compress/decompress
{
- var in = fixedBufferStream(plain_data);
- try pkg.compress(in.reader(), compressed.writer(), .{});
- compressed.reset();
- try pkg.decompress(compressed.reader(), plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var plain: Writer = .fixed(&buffer2);
+ var compressed: Writer = .fixed(&buffer1);
+
+ var in: std.io.Reader = .fixed(plain_data);
+ try compress(&in, &compressed, .{});
+
+ var r: std.io.Reader = .fixed(&buffer1);
+ try decompress(&r, &plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
- plain.reset();
- compressed.reset();
// compressor/decompressor
{
- var in = fixedBufferStream(plain_data);
- var cmp = try pkg.compressor(compressed.writer(), .{});
- try cmp.compress(in.reader());
+ var plain: Writer = .fixed(&buffer2);
+ var compressed: Writer = .fixed(&buffer1);
+
+ var in: std.io.Reader = .fixed(plain_data);
+ var cmp = try Compress(&compressed, .{});
+ try cmp.compress(&in);
try cmp.finish();
- compressed.reset();
- var dcp = pkg.decompressor(compressed.reader());
- try dcp.decompress(plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var r: std.io.Reader = .fixed(&buffer1);
+ var dcp = Decompress(&r);
+ try dcp.decompress(&plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
- plain.reset();
- compressed.reset();
// huffman
{
// huffman compress/decompress
{
- var in = fixedBufferStream(plain_data);
- try pkg.huffman.compress(in.reader(), compressed.writer());
- compressed.reset();
- try pkg.decompress(compressed.reader(), plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var plain: Writer = .fixed(&buffer2);
+ var compressed: Writer = .fixed(&buffer1);
+
+ var in: std.io.Reader = .fixed(plain_data);
+ try huffman.compress(&in, &compressed);
+
+ var r: std.io.Reader = .fixed(&buffer1);
+ try decompress(&r, &plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
- plain.reset();
- compressed.reset();
// huffman compressor/decompressor
{
- var in = fixedBufferStream(plain_data);
- var cmp = try pkg.huffman.compressor(compressed.writer());
- try cmp.compress(in.reader());
+ var plain: Writer = .fixed(&buffer2);
+ var compressed: Writer = .fixed(&buffer1);
+
+ var in: std.io.Reader = .fixed(plain_data);
+ var cmp = try huffman.Compressor(&compressed);
+ try cmp.compress(&in);
try cmp.finish();
- compressed.reset();
- try pkg.decompress(compressed.reader(), plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var r: std.io.Reader = .fixed(&buffer1);
+ try decompress(&r, &plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
}
- plain.reset();
- compressed.reset();
// store
{
// store compress/decompress
{
- var in = fixedBufferStream(plain_data);
- try pkg.store.compress(in.reader(), compressed.writer());
- compressed.reset();
- try pkg.decompress(compressed.reader(), plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var plain: Writer = .fixed(&buffer2);
+ var compressed: Writer = .fixed(&buffer1);
+
+ var in: std.io.Reader = .fixed(plain_data);
+ try store.compress(&in, &compressed);
+
+ var r: std.io.Reader = .fixed(&buffer1);
+ try decompress(&r, &plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
- plain.reset();
- compressed.reset();
// store compressor/decompressor
{
- var in = fixedBufferStream(plain_data);
- var cmp = try pkg.store.compressor(compressed.writer());
- try cmp.compress(in.reader());
+ var plain: Writer = .fixed(&buffer2);
+ var compressed: Writer = .fixed(&buffer1);
+
+ var in: std.io.Reader = .fixed(plain_data);
+ var cmp = try store.compressor(&compressed);
+ try cmp.compress(&in);
try cmp.finish();
- compressed.reset();
- try pkg.decompress(compressed.reader(), plain.writer());
- try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
+ var r: std.io.Reader = .fixed(&buffer1);
+ try decompress(&r, &plain);
+ try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
}
}
+
+pub const match = struct {
+ pub const base_length = 3; // smallest match length per the RFC section 3.2.5
+ pub const min_length = 4; // min length used in this algorithm
+ pub const max_length = 258;
+
+ pub const min_distance = 1;
+ pub const max_distance = 32768;
+};
+
+pub const history_len = match.max_distance;
+
+pub const lookup = struct {
+ pub const bits = 15;
+ pub const len = 1 << bits;
+ pub const shift = 32 - bits;
+};
+
+test "zlib should not overshoot" {
+ // Compressed zlib data with extra 4 bytes at the end.
+ const data = [_]u8{
+ 0x78, 0x9c, 0x73, 0xce, 0x2f, 0xa8, 0x2c, 0xca, 0x4c, 0xcf, 0x28, 0x51, 0x08, 0xcf, 0xcc, 0xc9,
+ 0x49, 0xcd, 0x55, 0x28, 0x4b, 0xcc, 0x53, 0x08, 0x4e, 0xce, 0x48, 0xcc, 0xcc, 0xd6, 0x51, 0x08,
+ 0xce, 0xcc, 0x4b, 0x4f, 0x2c, 0xc8, 0x2f, 0x4a, 0x55, 0x30, 0xb4, 0xb4, 0x34, 0xd5, 0xb5, 0x34,
+ 0x03, 0x00, 0x8b, 0x61, 0x0f, 0xa4, 0x52, 0x5a, 0x94, 0x12,
+ };
+
+ var stream: std.io.Reader = .fixed(&data);
+ const reader = stream.reader();
+
+ var dcp = Decompress.init(reader);
+ var out: [128]u8 = undefined;
+
+ // Decompress
+ var n = try dcp.reader().readAll(out[0..]);
+
+ // Expected decompressed data
+ try std.testing.expectEqual(46, n);
+ try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
+
+ // Decompressor don't overshoot underlying reader.
+ // It is leaving it at the end of compressed data chunk.
+ try std.testing.expectEqual(data.len - 4, stream.getPos());
+ try std.testing.expectEqual(0, dcp.unreadBytes());
+
+ // 4 bytes after compressed chunk are available in reader.
+ n = try reader.readAll(out[0..]);
+ try std.testing.expectEqual(n, 4);
+ try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
+}
diff --git a/lib/std/compress/flate/BlockWriter.zig b/lib/std/compress/flate/BlockWriter.zig
new file mode 100644
index 0000000000000000000000000000000000000000..d1eb3a068ee434cd609a99b4a3981f0d06bcae99
--- /dev/null
+++ b/lib/std/compress/flate/BlockWriter.zig
@@ -0,0 +1,696 @@
+//! Accepts list of tokens, decides what is best block type to write. What block
+//! type will provide best compression. Writes header and body of the block.
+const std = @import("std");
+const io = std.io;
+const assert = std.debug.assert;
+const Writer = std.io.Writer;
+
+const BlockWriter = @This();
+const flate = @import("../flate.zig");
+const Compress = flate.Compress;
+const huffman = flate.huffman;
+const Token = @import("Token.zig");
+
+const codegen_order = huffman.codegen_order;
+const end_code_mark = 255;
+
+output: *Writer,
+
+codegen_freq: [huffman.codegen_code_count]u16 = undefined,
+literal_freq: [huffman.max_num_lit]u16 = undefined,
+distance_freq: [huffman.distance_code_count]u16 = undefined,
+codegen: [huffman.max_num_lit + huffman.distance_code_count + 1]u8 = undefined,
+literal_encoding: Compress.LiteralEncoder = .{},
+distance_encoding: Compress.DistanceEncoder = .{},
+codegen_encoding: Compress.CodegenEncoder = .{},
+fixed_literal_encoding: Compress.LiteralEncoder,
+fixed_distance_encoding: Compress.DistanceEncoder,
+huff_distance: Compress.DistanceEncoder,
+
+pub fn init(output: *Writer) BlockWriter {
+ return .{
+ .output = output,
+ .fixed_literal_encoding = Compress.fixedLiteralEncoder(),
+ .fixed_distance_encoding = Compress.fixedDistanceEncoder(),
+ .huff_distance = Compress.huffmanDistanceEncoder(),
+ };
+}
+
+/// Flush intrenal bit buffer to the writer.
+/// Should be called only when bit stream is at byte boundary.
+///
+/// That is after final block; when last byte could be incomplete or
+/// after stored block; which is aligned to the byte boundary (it has x
+/// padding bits after first 3 bits).
+pub fn flush(self: *BlockWriter) Writer.Error!void {
+ try self.bit_writer.flush();
+}
+
+pub fn setWriter(self: *BlockWriter, new_writer: *Writer) void {
+ self.bit_writer.setWriter(new_writer);
+}
+
+fn writeCode(self: *BlockWriter, c: Compress.HuffCode) Writer.Error!void {
+ try self.bit_writer.writeBits(c.code, c.len);
+}
+
+// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
+// the literal and distance lengths arrays (which are concatenated into a single
+// array). This method generates that run-length encoding.
+//
+// The result is written into the codegen array, and the frequencies
+// of each code is written into the codegen_freq array.
+// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
+// information. Code bad_code is an end marker
+//
+// num_literals: The number of literals in literal_encoding
+// num_distances: The number of distances in distance_encoding
+// lit_enc: The literal encoder to use
+// dist_enc: The distance encoder to use
+fn generateCodegen(
+ self: *BlockWriter,
+ num_literals: u32,
+ num_distances: u32,
+ lit_enc: *Compress.LiteralEncoder,
+ dist_enc: *Compress.DistanceEncoder,
+) void {
+ for (self.codegen_freq, 0..) |_, i| {
+ self.codegen_freq[i] = 0;
+ }
+
+ // Note that we are using codegen both as a temporary variable for holding
+ // a copy of the frequencies, and as the place where we put the result.
+ // This is fine because the output is always shorter than the input used
+ // so far.
+ var codegen = &self.codegen; // cache
+ // Copy the concatenated code sizes to codegen. Put a marker at the end.
+ var cgnl = codegen[0..num_literals];
+ for (cgnl, 0..) |_, i| {
+ cgnl[i] = @as(u8, @intCast(lit_enc.codes[i].len));
+ }
+
+ cgnl = codegen[num_literals .. num_literals + num_distances];
+ for (cgnl, 0..) |_, i| {
+ cgnl[i] = @as(u8, @intCast(dist_enc.codes[i].len));
+ }
+ codegen[num_literals + num_distances] = end_code_mark;
+
+ var size = codegen[0];
+ var count: i32 = 1;
+ var out_index: u32 = 0;
+ var in_index: u32 = 1;
+ while (size != end_code_mark) : (in_index += 1) {
+ // INVARIANT: We have seen "count" copies of size that have not yet
+ // had output generated for them.
+ const next_size = codegen[in_index];
+ if (next_size == size) {
+ count += 1;
+ continue;
+ }
+ // We need to generate codegen indicating "count" of size.
+ if (size != 0) {
+ codegen[out_index] = size;
+ out_index += 1;
+ self.codegen_freq[size] += 1;
+ count -= 1;
+ while (count >= 3) {
+ var n: i32 = 6;
+ if (n > count) {
+ n = count;
+ }
+ codegen[out_index] = 16;
+ out_index += 1;
+ codegen[out_index] = @as(u8, @intCast(n - 3));
+ out_index += 1;
+ self.codegen_freq[16] += 1;
+ count -= n;
+ }
+ } else {
+ while (count >= 11) {
+ var n: i32 = 138;
+ if (n > count) {
+ n = count;
+ }
+ codegen[out_index] = 18;
+ out_index += 1;
+ codegen[out_index] = @as(u8, @intCast(n - 11));
+ out_index += 1;
+ self.codegen_freq[18] += 1;
+ count -= n;
+ }
+ if (count >= 3) {
+ // 3 <= count <= 10
+ codegen[out_index] = 17;
+ out_index += 1;
+ codegen[out_index] = @as(u8, @intCast(count - 3));
+ out_index += 1;
+ self.codegen_freq[17] += 1;
+ count = 0;
+ }
+ }
+ count -= 1;
+ while (count >= 0) : (count -= 1) {
+ codegen[out_index] = size;
+ out_index += 1;
+ self.codegen_freq[size] += 1;
+ }
+ // Set up invariant for next time through the loop.
+ size = next_size;
+ count = 1;
+ }
+ // Marker indicating the end of the codegen.
+ codegen[out_index] = end_code_mark;
+}
+
+const DynamicSize = struct {
+ size: u32,
+ num_codegens: u32,
+};
+
+// dynamicSize returns the size of dynamically encoded data in bits.
+fn dynamicSize(
+ self: *BlockWriter,
+ lit_enc: *Compress.LiteralEncoder, // literal encoder
+ dist_enc: *Compress.DistanceEncoder, // distance encoder
+ extra_bits: u32,
+) DynamicSize {
+ var num_codegens = self.codegen_freq.len;
+ while (num_codegens > 4 and self.codegen_freq[codegen_order[num_codegens - 1]] == 0) {
+ num_codegens -= 1;
+ }
+ const header = 3 + 5 + 5 + 4 + (3 * num_codegens) +
+ self.codegen_encoding.bitLength(self.codegen_freq[0..]) +
+ self.codegen_freq[16] * 2 +
+ self.codegen_freq[17] * 3 +
+ self.codegen_freq[18] * 7;
+ const size = header +
+ lit_enc.bitLength(&self.literal_freq) +
+ dist_enc.bitLength(&self.distance_freq) +
+ extra_bits;
+
+ return DynamicSize{
+ .size = @as(u32, @intCast(size)),
+ .num_codegens = @as(u32, @intCast(num_codegens)),
+ };
+}
+
+// fixedSize returns the size of dynamically encoded data in bits.
+fn fixedSize(self: *BlockWriter, extra_bits: u32) u32 {
+ return 3 +
+ self.fixed_literal_encoding.bitLength(&self.literal_freq) +
+ self.fixed_distance_encoding.bitLength(&self.distance_freq) +
+ extra_bits;
+}
+
+const StoredSize = struct {
+ size: u32,
+ storable: bool,
+};
+
+// storedSizeFits calculates the stored size, including header.
+// The function returns the size in bits and whether the block
+// fits inside a single block.
+fn storedSizeFits(in: ?[]const u8) StoredSize {
+ if (in == null) {
+ return .{ .size = 0, .storable = false };
+ }
+ if (in.?.len <= huffman.max_store_block_size) {
+ return .{ .size = @as(u32, @intCast((in.?.len + 5) * 8)), .storable = true };
+ }
+ return .{ .size = 0, .storable = false };
+}
+
+// Write the header of a dynamic Huffman block to the output stream.
+//
+// num_literals: The number of literals specified in codegen
+// num_distances: The number of distances specified in codegen
+// num_codegens: The number of codegens used in codegen
+// eof: Is it the end-of-file? (end of stream)
+fn dynamicHeader(
+ self: *BlockWriter,
+ num_literals: u32,
+ num_distances: u32,
+ num_codegens: u32,
+ eof: bool,
+) Writer.Error!void {
+ const first_bits: u32 = if (eof) 5 else 4;
+ try self.bit_writer.writeBits(first_bits, 3);
+ try self.bit_writer.writeBits(num_literals - 257, 5);
+ try self.bit_writer.writeBits(num_distances - 1, 5);
+ try self.bit_writer.writeBits(num_codegens - 4, 4);
+
+ var i: u32 = 0;
+ while (i < num_codegens) : (i += 1) {
+ const value = self.codegen_encoding.codes[codegen_order[i]].len;
+ try self.bit_writer.writeBits(value, 3);
+ }
+
+ i = 0;
+ while (true) {
+ const code_word: u32 = @as(u32, @intCast(self.codegen[i]));
+ i += 1;
+ if (code_word == end_code_mark) {
+ break;
+ }
+ try self.writeCode(self.codegen_encoding.codes[@as(u32, @intCast(code_word))]);
+
+ switch (code_word) {
+ 16 => {
+ try self.bit_writer.writeBits(self.codegen[i], 2);
+ i += 1;
+ },
+ 17 => {
+ try self.bit_writer.writeBits(self.codegen[i], 3);
+ i += 1;
+ },
+ 18 => {
+ try self.bit_writer.writeBits(self.codegen[i], 7);
+ i += 1;
+ },
+ else => {},
+ }
+ }
+}
+
+fn storedHeader(self: *BlockWriter, length: usize, eof: bool) Writer.Error!void {
+ assert(length <= 65535);
+ const flag: u32 = if (eof) 1 else 0;
+ try self.bit_writer.writeBits(flag, 3);
+ try self.flush();
+ const l: u16 = @intCast(length);
+ try self.bit_writer.writeBits(l, 16);
+ try self.bit_writer.writeBits(~l, 16);
+}
+
+fn fixedHeader(self: *BlockWriter, eof: bool) Writer.Error!void {
+ // Indicate that we are a fixed Huffman block
+ var value: u32 = 2;
+ if (eof) {
+ value = 3;
+ }
+ try self.bit_writer.writeBits(value, 3);
+}
+
+// Write a block of tokens with the smallest encoding. Will choose block type.
+// The original input can be supplied, and if the huffman encoded data
+// is larger than the original bytes, the data will be written as a
+// stored block.
+// If the input is null, the tokens will always be Huffman encoded.
+pub fn write(self: *BlockWriter, tokens: []const Token, eof: bool, input: ?[]const u8) Writer.Error!void {
+ const lit_and_dist = self.indexTokens(tokens);
+ const num_literals = lit_and_dist.num_literals;
+ const num_distances = lit_and_dist.num_distances;
+
+ var extra_bits: u32 = 0;
+ const ret = storedSizeFits(input);
+ const stored_size = ret.size;
+ const storable = ret.storable;
+
+ if (storable) {
+ // We only bother calculating the costs of the extra bits required by
+ // the length of distance fields (which will be the same for both fixed
+ // and dynamic encoding), if we need to compare those two encodings
+ // against stored encoding.
+ var length_code: u16 = Token.length_codes_start + 8;
+ while (length_code < num_literals) : (length_code += 1) {
+ // First eight length codes have extra size = 0.
+ extra_bits += @as(u32, @intCast(self.literal_freq[length_code])) *
+ @as(u32, @intCast(Token.lengthExtraBits(length_code)));
+ }
+ var distance_code: u16 = 4;
+ while (distance_code < num_distances) : (distance_code += 1) {
+ // First four distance codes have extra size = 0.
+ extra_bits += @as(u32, @intCast(self.distance_freq[distance_code])) *
+ @as(u32, @intCast(Token.distanceExtraBits(distance_code)));
+ }
+ }
+
+ // Figure out smallest code.
+ // Fixed Huffman baseline.
+ var literal_encoding = &self.fixed_literal_encoding;
+ var distance_encoding = &self.fixed_distance_encoding;
+ var size = self.fixedSize(extra_bits);
+
+ // Dynamic Huffman?
+ var num_codegens: u32 = 0;
+
+ // Generate codegen and codegenFrequencies, which indicates how to encode
+ // the literal_encoding and the distance_encoding.
+ self.generateCodegen(
+ num_literals,
+ num_distances,
+ &self.literal_encoding,
+ &self.distance_encoding,
+ );
+ self.codegen_encoding.generate(self.codegen_freq[0..], 7);
+ const dynamic_size = self.dynamicSize(
+ &self.literal_encoding,
+ &self.distance_encoding,
+ extra_bits,
+ );
+ const dyn_size = dynamic_size.size;
+ num_codegens = dynamic_size.num_codegens;
+
+ if (dyn_size < size) {
+ size = dyn_size;
+ literal_encoding = &self.literal_encoding;
+ distance_encoding = &self.distance_encoding;
+ }
+
+ // Stored bytes?
+ if (storable and stored_size < size) {
+ try self.storedBlock(input.?, eof);
+ return;
+ }
+
+ // Huffman.
+ if (@intFromPtr(literal_encoding) == @intFromPtr(&self.fixed_literal_encoding)) {
+ try self.fixedHeader(eof);
+ } else {
+ try self.dynamicHeader(num_literals, num_distances, num_codegens, eof);
+ }
+
+ // Write the tokens.
+ try self.writeTokens(tokens, &literal_encoding.codes, &distance_encoding.codes);
+}
+
+pub fn storedBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Error!void {
+ try self.storedHeader(input.len, eof);
+ try self.bit_writer.writeBytes(input);
+}
+
+// writeBlockDynamic encodes a block using a dynamic Huffman table.
+// This should be used if the symbols used have a disproportionate
+// histogram distribution.
+// If input is supplied and the compression savings are below 1/16th of the
+// input size the block is stored.
+fn dynamicBlock(
+ self: *BlockWriter,
+ tokens: []const Token,
+ eof: bool,
+ input: ?[]const u8,
+) Writer.Error!void {
+ const total_tokens = self.indexTokens(tokens);
+ const num_literals = total_tokens.num_literals;
+ const num_distances = total_tokens.num_distances;
+
+ // Generate codegen and codegenFrequencies, which indicates how to encode
+ // the literal_encoding and the distance_encoding.
+ self.generateCodegen(
+ num_literals,
+ num_distances,
+ &self.literal_encoding,
+ &self.distance_encoding,
+ );
+ self.codegen_encoding.generate(self.codegen_freq[0..], 7);
+ const dynamic_size = self.dynamicSize(&self.literal_encoding, &self.distance_encoding, 0);
+ const size = dynamic_size.size;
+ const num_codegens = dynamic_size.num_codegens;
+
+ // Store bytes, if we don't get a reasonable improvement.
+
+ const stored_size = storedSizeFits(input);
+ const ssize = stored_size.size;
+ const storable = stored_size.storable;
+ if (storable and ssize < (size + (size >> 4))) {
+ try self.storedBlock(input.?, eof);
+ return;
+ }
+
+ // Write Huffman table.
+ try self.dynamicHeader(num_literals, num_distances, num_codegens, eof);
+
+ // Write the tokens.
+ try self.writeTokens(tokens, &self.literal_encoding.codes, &self.distance_encoding.codes);
+}
+
+const TotalIndexedTokens = struct {
+ num_literals: u32,
+ num_distances: u32,
+};
+
+// Indexes a slice of tokens followed by an end_block_marker, and updates
+// literal_freq and distance_freq, and generates literal_encoding
+// and distance_encoding.
+// The number of literal and distance tokens is returned.
+fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
+ var num_literals: u32 = 0;
+ var num_distances: u32 = 0;
+
+ for (self.literal_freq, 0..) |_, i| {
+ self.literal_freq[i] = 0;
+ }
+ for (self.distance_freq, 0..) |_, i| {
+ self.distance_freq[i] = 0;
+ }
+
+ for (tokens) |t| {
+ if (t.kind == Token.Kind.literal) {
+ self.literal_freq[t.literal()] += 1;
+ continue;
+ }
+ self.literal_freq[t.lengthCode()] += 1;
+ self.distance_freq[t.distanceCode()] += 1;
+ }
+ // add end_block_marker token at the end
+ self.literal_freq[huffman.end_block_marker] += 1;
+
+ // get the number of literals
+ num_literals = @as(u32, @intCast(self.literal_freq.len));
+ while (self.literal_freq[num_literals - 1] == 0) {
+ num_literals -= 1;
+ }
+ // get the number of distances
+ num_distances = @as(u32, @intCast(self.distance_freq.len));
+ while (num_distances > 0 and self.distance_freq[num_distances - 1] == 0) {
+ num_distances -= 1;
+ }
+ if (num_distances == 0) {
+ // We haven't found a single match. If we want to go with the dynamic encoding,
+ // we should count at least one distance to be sure that the distance huffman tree could be encoded.
+ self.distance_freq[0] = 1;
+ num_distances = 1;
+ }
+ self.literal_encoding.generate(&self.literal_freq, 15);
+ self.distance_encoding.generate(&self.distance_freq, 15);
+ return TotalIndexedTokens{
+ .num_literals = num_literals,
+ .num_distances = num_distances,
+ };
+}
+
+// Writes a slice of tokens to the output followed by and end_block_marker.
+// codes for literal and distance encoding must be supplied.
+fn writeTokens(
+ self: *BlockWriter,
+ tokens: []const Token,
+ le_codes: []Compress.HuffCode,
+ oe_codes: []Compress.HuffCode,
+) Writer.Error!void {
+ for (tokens) |t| {
+ if (t.kind == Token.Kind.literal) {
+ try self.writeCode(le_codes[t.literal()]);
+ continue;
+ }
+
+ // Write the length
+ const le = t.lengthEncoding();
+ try self.writeCode(le_codes[le.code]);
+ if (le.extra_bits > 0) {
+ try self.bit_writer.writeBits(le.extra_length, le.extra_bits);
+ }
+
+ // Write the distance
+ const oe = t.distanceEncoding();
+ try self.writeCode(oe_codes[oe.code]);
+ if (oe.extra_bits > 0) {
+ try self.bit_writer.writeBits(oe.extra_distance, oe.extra_bits);
+ }
+ }
+ // add end_block_marker at the end
+ try self.writeCode(le_codes[huffman.end_block_marker]);
+}
+
+// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
+// if the results only gains very little from compression.
+pub fn huffmanBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Error!void {
+ // Add everything as literals
+ histogram(input, &self.literal_freq);
+
+ self.literal_freq[huffman.end_block_marker] = 1;
+
+ const num_literals = huffman.end_block_marker + 1;
+ self.distance_freq[0] = 1;
+ const num_distances = 1;
+
+ self.literal_encoding.generate(&self.literal_freq, 15);
+
+ // Figure out smallest code.
+ // Always use dynamic Huffman or Store
+ var num_codegens: u32 = 0;
+
+ // Generate codegen and codegenFrequencies, which indicates how to encode
+ // the literal_encoding and the distance_encoding.
+ self.generateCodegen(
+ num_literals,
+ num_distances,
+ &self.literal_encoding,
+ &self.huff_distance,
+ );
+ self.codegen_encoding.generate(self.codegen_freq[0..], 7);
+ const dynamic_size = self.dynamicSize(&self.literal_encoding, &self.huff_distance, 0);
+ const size = dynamic_size.size;
+ num_codegens = dynamic_size.num_codegens;
+
+ // Store bytes, if we don't get a reasonable improvement.
+ const stored_size_ret = storedSizeFits(input);
+ const ssize = stored_size_ret.size;
+ const storable = stored_size_ret.storable;
+
+ if (storable and ssize < (size + (size >> 4))) {
+ try self.storedBlock(input, eof);
+ return;
+ }
+
+ // Huffman.
+ try self.dynamicHeader(num_literals, num_distances, num_codegens, eof);
+ const encoding = self.literal_encoding.codes[0..257];
+
+ for (input) |t| {
+ const c = encoding[t];
+ try self.bit_writer.writeBits(c.code, c.len);
+ }
+ try self.writeCode(encoding[huffman.end_block_marker]);
+}
+
+// histogram accumulates a histogram of b in h.
+fn histogram(b: []const u8, h: *[286]u16) void {
+ // Clear histogram
+ for (h, 0..) |_, i| {
+ h[i] = 0;
+ }
+
+ var lh = h.*[0..256];
+ for (b) |t| {
+ lh[t] += 1;
+ }
+}
+
+// tests
+const expect = std.testing.expect;
+const fmt = std.fmt;
+const testing = std.testing;
+const ArrayList = std.ArrayList;
+
+const TestCase = @import("testdata/block_writer.zig").TestCase;
+const testCases = @import("testdata/block_writer.zig").testCases;
+
+// tests if the writeBlock encoding has changed.
+test "write" {
+ inline for (0..testCases.len) |i| {
+ try testBlock(testCases[i], .write_block);
+ }
+}
+
+// tests if the writeBlockDynamic encoding has changed.
+test "dynamicBlock" {
+ inline for (0..testCases.len) |i| {
+ try testBlock(testCases[i], .write_dyn_block);
+ }
+}
+
+test "huffmanBlock" {
+ inline for (0..testCases.len) |i| {
+ try testBlock(testCases[i], .write_huffman_block);
+ }
+ try testBlock(.{
+ .tokens = &[_]Token{},
+ .input = "huffman-rand-max.input",
+ .want = "huffman-rand-max.{s}.expect",
+ }, .write_huffman_block);
+}
+
+const TestFn = enum {
+ write_block,
+ write_dyn_block, // write dynamic block
+ write_huffman_block,
+
+ fn to_s(self: TestFn) []const u8 {
+ return switch (self) {
+ .write_block => "wb",
+ .write_dyn_block => "dyn",
+ .write_huffman_block => "huff",
+ };
+ }
+
+ fn write(
+ comptime self: TestFn,
+ bw: anytype,
+ tok: []const Token,
+ input: ?[]const u8,
+ final: bool,
+ ) !void {
+ switch (self) {
+ .write_block => try bw.write(tok, final, input),
+ .write_dyn_block => try bw.dynamicBlock(tok, final, input),
+ .write_huffman_block => try bw.huffmanBlock(input.?, final),
+ }
+ try bw.flush();
+ }
+};
+
+// testBlock tests a block against its references
+//
+// size
+// 64K [file-name].input - input non compressed file
+// 8.1K [file-name].golden -
+// 78 [file-name].dyn.expect - output with writeBlockDynamic
+// 78 [file-name].wb.expect - output with writeBlock
+// 8.1K [file-name].huff.expect - output with writeBlockHuff
+// 78 [file-name].dyn.expect-noinput - output with writeBlockDynamic when input is null
+// 78 [file-name].wb.expect-noinput - output with writeBlock when input is null
+//
+// wb - writeBlock
+// dyn - writeBlockDynamic
+// huff - writeBlockHuff
+//
+fn testBlock(comptime tc: TestCase, comptime tfn: TestFn) !void {
+ if (tc.input.len != 0 and tc.want.len != 0) {
+ const want_name = comptime fmt.comptimePrint(tc.want, .{tfn.to_s()});
+ const input = @embedFile("testdata/block_writer/" ++ tc.input);
+ const want = @embedFile("testdata/block_writer/" ++ want_name);
+ try testWriteBlock(tfn, input, want, tc.tokens);
+ }
+
+ if (tfn == .write_huffman_block) {
+ return;
+ }
+
+ const want_name_no_input = comptime fmt.comptimePrint(tc.want_no_input, .{tfn.to_s()});
+ const want = @embedFile("testdata/block_writer/" ++ want_name_no_input);
+ try testWriteBlock(tfn, null, want, tc.tokens);
+}
+
+// Uses writer function `tfn` to write `tokens`, tests that we got `want` as output.
+fn testWriteBlock(comptime tfn: TestFn, input: ?[]const u8, want: []const u8, tokens: []const Token) !void {
+ var buf = ArrayList(u8).init(testing.allocator);
+ var bw: BlockWriter = .init(buf.writer());
+ try tfn.write(&bw, tokens, input, false);
+ var got = buf.items;
+ try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
+ try expect(got[0] & 0b0000_0001 == 0); // bfinal is not set
+ //
+ // Test if the writer produces the same output after reset.
+ buf.deinit();
+ buf = ArrayList(u8).init(testing.allocator);
+ defer buf.deinit();
+ bw.setWriter(buf.writer());
+
+ try tfn.write(&bw, tokens, input, true);
+ try bw.flush();
+ got = buf.items;
+
+ try expect(got[0] & 1 == 1); // bfinal is set
+ buf.items[0] &= 0b1111_1110; // remove bfinal bit, so we can run test slices
+ try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
+}
diff --git a/lib/std/compress/flate/CircularBuffer.zig b/lib/std/compress/flate/CircularBuffer.zig
deleted file mode 100644
index 552d364894c7f17abea84c105b92708a29652f7a..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/CircularBuffer.zig
+++ /dev/null
@@ -1,240 +0,0 @@
-//! 64K buffer of uncompressed data created in inflate (decompression). Has enough
-//! history to support writing match; copying length of bytes
-//! from the position distance backward from current.
-//!
-//! Reads can return less than available bytes if they are spread across
-//! different circles. So reads should repeat until get required number of bytes
-//! or until returned slice is zero length.
-//!
-//! Note on deflate limits:
-//! * non-compressible block is limited to 65,535 bytes.
-//! * backward pointer is limited in distance to 32K bytes and in length to 258 bytes.
-//!
-//! Whole non-compressed block can be written without overlap. We always have
-//! history of up to 64K, more then 32K needed.
-//!
-const std = @import("std");
-const assert = std.debug.assert;
-const testing = std.testing;
-
-const consts = @import("consts.zig").match;
-
-const mask = 0xffff; // 64K - 1
-const buffer_len = mask + 1; // 64K buffer
-
-const Self = @This();
-
-buffer: [buffer_len]u8 = undefined,
-wp: usize = 0, // write position
-rp: usize = 0, // read position
-
-fn writeAll(self: *Self, buf: []const u8) void {
- for (buf) |c| self.write(c);
-}
-
-/// Write literal.
-pub fn write(self: *Self, b: u8) void {
- assert(self.wp - self.rp < mask);
- self.buffer[self.wp & mask] = b;
- self.wp += 1;
-}
-
-/// Write match (back-reference to the same data slice) starting at `distance`
-/// back from current write position, and `length` of bytes.
-pub fn writeMatch(self: *Self, length: u16, distance: u16) !void {
- if (self.wp < distance or
- length < consts.base_length or length > consts.max_length or
- distance < consts.min_distance or distance > consts.max_distance)
- {
- return error.InvalidMatch;
- }
- assert(self.wp - self.rp < mask);
-
- var from: usize = self.wp - distance & mask;
- const from_end: usize = from + length;
- var to: usize = self.wp & mask;
- const to_end: usize = to + length;
-
- self.wp += length;
-
- // Fast path using memcpy
- if (from_end < buffer_len and to_end < buffer_len) // start and end at the same circle
- {
- var cur_len = distance;
- var remaining_len = length;
- while (cur_len < remaining_len) {
- @memcpy(self.buffer[to..][0..cur_len], self.buffer[from..][0..cur_len]);
- to += cur_len;
- remaining_len -= cur_len;
- cur_len = cur_len * 2;
- }
- @memcpy(self.buffer[to..][0..remaining_len], self.buffer[from..][0..remaining_len]);
- return;
- }
-
- // Slow byte by byte
- while (to < to_end) {
- self.buffer[to & mask] = self.buffer[from & mask];
- to += 1;
- from += 1;
- }
-}
-
-/// Returns writable part of the internal buffer of size `n` at most. Advances
-/// write pointer, assumes that returned buffer will be filled with data.
-pub fn getWritable(self: *Self, n: usize) []u8 {
- const wp = self.wp & mask;
- const len = @min(n, buffer_len - wp);
- self.wp += len;
- return self.buffer[wp .. wp + len];
-}
-
-/// Read available data. Can return part of the available data if it is
-/// spread across two circles. So read until this returns zero length.
-pub fn read(self: *Self) []const u8 {
- return self.readAtMost(buffer_len);
-}
-
-/// Read part of available data. Can return less than max even if there are
-/// more than max decoded data.
-pub fn readAtMost(self: *Self, limit: usize) []const u8 {
- const rb = self.readBlock(if (limit == 0) buffer_len else limit);
- defer self.rp += rb.len;
- return self.buffer[rb.head..rb.tail];
-}
-
-const ReadBlock = struct {
- head: usize,
- tail: usize,
- len: usize,
-};
-
-/// Returns position of continuous read block data.
-fn readBlock(self: *Self, max: usize) ReadBlock {
- const r = self.rp & mask;
- const w = self.wp & mask;
- const n = @min(
- max,
- if (w >= r) w - r else buffer_len - r,
- );
- return .{
- .head = r,
- .tail = r + n,
- .len = n,
- };
-}
-
-/// Number of free bytes for write.
-pub fn free(self: *Self) usize {
- return buffer_len - (self.wp - self.rp);
-}
-
-/// Full if largest match can't fit. 258 is largest match length. That much
-/// bytes can be produced in single decode step.
-pub fn full(self: *Self) bool {
- return self.free() < 258 + 1;
-}
-
-// example from: https://youtu.be/SJPvNi4HrWQ?t=3558
-test writeMatch {
- var cb: Self = .{};
-
- cb.writeAll("a salad; ");
- try cb.writeMatch(5, 9);
- try cb.writeMatch(3, 3);
-
- try testing.expectEqualStrings("a salad; a salsal", cb.read());
-}
-
-test "writeMatch overlap" {
- var cb: Self = .{};
-
- cb.writeAll("a b c ");
- try cb.writeMatch(8, 4);
- cb.write('d');
-
- try testing.expectEqualStrings("a b c b c b c d", cb.read());
-}
-
-test readAtMost {
- var cb: Self = .{};
-
- cb.writeAll("0123456789");
- try cb.writeMatch(50, 10);
-
- try testing.expectEqualStrings("0123456789" ** 6, cb.buffer[cb.rp..cb.wp]);
- for (0..6) |i| {
- try testing.expectEqual(i * 10, cb.rp);
- try testing.expectEqualStrings("0123456789", cb.readAtMost(10));
- }
- try testing.expectEqualStrings("", cb.readAtMost(10));
- try testing.expectEqualStrings("", cb.read());
-}
-
-test Self {
- var cb: Self = .{};
-
- const data = "0123456789abcdef" ** (1024 / 16);
- cb.writeAll(data);
- try testing.expectEqual(@as(usize, 0), cb.rp);
- try testing.expectEqual(@as(usize, 1024), cb.wp);
- try testing.expectEqual(@as(usize, 1024 * 63), cb.free());
-
- for (0..62 * 4) |_|
- try cb.writeMatch(256, 1024); // write 62K
-
- try testing.expectEqual(@as(usize, 0), cb.rp);
- try testing.expectEqual(@as(usize, 63 * 1024), cb.wp);
- try testing.expectEqual(@as(usize, 1024), cb.free());
-
- cb.writeAll(data[0..200]);
- _ = cb.readAtMost(1024); // make some space
- cb.writeAll(data); // overflows write position
- try testing.expectEqual(@as(usize, 200 + 65536), cb.wp);
- try testing.expectEqual(@as(usize, 1024), cb.rp);
- try testing.expectEqual(@as(usize, 1024 - 200), cb.free());
-
- const rb = cb.readBlock(Self.buffer_len);
- try testing.expectEqual(@as(usize, 65536 - 1024), rb.len);
- try testing.expectEqual(@as(usize, 1024), rb.head);
- try testing.expectEqual(@as(usize, 65536), rb.tail);
-
- try testing.expectEqual(@as(usize, 65536 - 1024), cb.read().len); // read to the end of the buffer
- try testing.expectEqual(@as(usize, 200 + 65536), cb.wp);
- try testing.expectEqual(@as(usize, 65536), cb.rp);
- try testing.expectEqual(@as(usize, 65536 - 200), cb.free());
-
- try testing.expectEqual(@as(usize, 200), cb.read().len); // read the rest
-}
-
-test "write overlap" {
- var cb: Self = .{};
- cb.wp = cb.buffer.len - 15;
- cb.rp = cb.wp;
-
- cb.writeAll("0123456789");
- cb.writeAll("abcdefghij");
-
- try testing.expectEqual(cb.buffer.len + 5, cb.wp);
- try testing.expectEqual(cb.buffer.len - 15, cb.rp);
-
- try testing.expectEqualStrings("0123456789abcde", cb.read());
- try testing.expectEqualStrings("fghij", cb.read());
-
- try testing.expect(cb.wp == cb.rp);
-}
-
-test "writeMatch/read overlap" {
- var cb: Self = .{};
- cb.wp = cb.buffer.len - 15;
- cb.rp = cb.wp;
-
- cb.writeAll("0123456789");
- try cb.writeMatch(15, 5);
-
- try testing.expectEqualStrings("012345678956789", cb.read());
- try testing.expectEqualStrings("5678956789", cb.read());
-
- try cb.writeMatch(20, 25);
- try testing.expectEqualStrings("01234567895678956789", cb.read());
-}
diff --git a/lib/std/compress/flate/Compress.zig b/lib/std/compress/flate/Compress.zig
new file mode 100644
index 0000000000000000000000000000000000000000..4d827fd590e87deb51e5682d1b63396d8a51830b
--- /dev/null
+++ b/lib/std/compress/flate/Compress.zig
@@ -0,0 +1,1264 @@
+//! Default compression algorithm. Has two steps: tokenization and token
+//! encoding.
+//!
+//! Tokenization takes uncompressed input stream and produces list of tokens.
+//! Each token can be literal (byte of data) or match (backrefernce to previous
+//! data with length and distance). Tokenization accumulators 32K tokens, when
+//! full or `flush` is called tokens are passed to the `block_writer`. Level
+//! defines how hard (how slow) it tries to find match.
+//!
+//! Block writer will decide which type of deflate block to write (stored, fixed,
+//! dynamic) and encode tokens to the output byte stream. Client has to call
+//! `finish` to write block with the final bit set.
+//!
+//! Container defines type of header and footer which can be gzip, zlib or raw.
+//! They all share same deflate body. Raw has no header or footer just deflate
+//! body.
+//!
+//! Compression algorithm explained in rfc-1951 (slightly edited for this case):
+//!
+//! The compressor uses a chained hash table `lookup` to find duplicated
+//! strings, using a hash function that operates on 4-byte sequences. At any
+//! given point during compression, let XYZW be the next 4 input bytes
+//! (lookahead) to be examined (not necessarily all different, of course).
+//! First, the compressor examines the hash chain for XYZW. If the chain is
+//! empty, the compressor simply writes out X as a literal byte and advances
+//! one byte in the input. If the hash chain is not empty, indicating that the
+//! sequence XYZW (or, if we are unlucky, some other 4 bytes with the same
+//! hash function value) has occurred recently, the compressor compares all
+//! strings on the XYZW hash chain with the actual input data sequence
+//! starting at the current point, and selects the longest match.
+//!
+//! To improve overall compression, the compressor defers the selection of
+//! matches ("lazy matching"): after a match of length N has been found, the
+//! compressor searches for a longer match starting at the next input byte. If
+//! it finds a longer match, it truncates the previous match to a length of
+//! one (thus producing a single literal byte) and then emits the longer
+//! match. Otherwise, it emits the original match, and, as described above,
+//! advances N bytes before continuing.
+//!
+//!
+//! Allocates statically ~400K (192K lookup, 128K tokens, 64K window).
+const builtin = @import("builtin");
+const std = @import("std");
+const assert = std.debug.assert;
+const testing = std.testing;
+const expect = testing.expect;
+const mem = std.mem;
+const math = std.math;
+const Writer = std.Io.Writer;
+const Reader = std.Io.Reader;
+
+const Compress = @This();
+const Token = @import("Token.zig");
+const BlockWriter = @import("BlockWriter.zig");
+const flate = @import("../flate.zig");
+const Container = flate.Container;
+const Lookup = @import("Lookup.zig");
+const huffman = flate.huffman;
+
+lookup: Lookup = .{},
+tokens: Tokens = .{},
+/// Asserted to have a buffer capacity of at least `flate.max_window_len`.
+input: *Reader,
+block_writer: BlockWriter,
+level: LevelArgs,
+hasher: Container.Hasher,
+reader: Reader,
+
+// Match and literal at the previous position.
+// Used for lazy match finding in processWindow.
+prev_match: ?Token = null,
+prev_literal: ?u8 = null,
+
+/// Trades between speed and compression size.
+/// Starts with level 4: in [zlib](https://github.com/madler/zlib/blob/abd3d1a28930f89375d4b41408b39f6c1be157b2/deflate.c#L115C1-L117C43)
+/// levels 1-3 are using different algorithm to perform faster but with less
+/// compression. That is not implemented here.
+pub const Level = enum(u4) {
+ level_4 = 4,
+ level_5 = 5,
+ level_6 = 6,
+ level_7 = 7,
+ level_8 = 8,
+ level_9 = 9,
+
+ fast = 0xb,
+ default = 0xc,
+ best = 0xd,
+};
+
+/// Number of tokens to accumulate in deflate before starting block encoding.
+///
+/// In zlib this depends on memlevel: 6 + memlevel, where default memlevel is
+/// 8 and max 9 that gives 14 or 15 bits.
+pub const n_tokens = 1 << 15;
+
+/// Algorithm knobs for each level.
+const LevelArgs = struct {
+ good: u16, // Do less lookups if we already have match of this length.
+ nice: u16, // Stop looking for better match if we found match with at least this length.
+ lazy: u16, // Don't do lazy match find if got match with at least this length.
+ chain: u16, // How many lookups for previous match to perform.
+
+ pub fn get(level: Level) LevelArgs {
+ return switch (level) {
+ .fast, .level_4 => .{ .good = 4, .lazy = 4, .nice = 16, .chain = 16 },
+ .level_5 => .{ .good = 8, .lazy = 16, .nice = 32, .chain = 32 },
+ .default, .level_6 => .{ .good = 8, .lazy = 16, .nice = 128, .chain = 128 },
+ .level_7 => .{ .good = 8, .lazy = 32, .nice = 128, .chain = 256 },
+ .level_8 => .{ .good = 32, .lazy = 128, .nice = 258, .chain = 1024 },
+ .best, .level_9 => .{ .good = 32, .lazy = 258, .nice = 258, .chain = 4096 },
+ };
+ }
+};
+
+pub const Options = struct {
+ level: Level = .default,
+ container: Container = .raw,
+};
+
+pub fn init(input: *Reader, buffer: []u8, options: Options) Compress {
+ return .{
+ .input = input,
+ .block_writer = undefined,
+ .level = .get(options.level),
+ .hasher = .init(options.container),
+ .state = .header,
+ .reader = .{
+ .buffer = buffer,
+ .stream = stream,
+ },
+ };
+}
+
+const FlushOption = enum { none, flush, final };
+
+/// Process data in window and create tokens. If token buffer is full
+/// flush tokens to the token writer.
+///
+/// Returns number of bytes consumed from `lh`.
+fn tokenizeSlice(c: *Compress, bw: *Writer, limit: std.Io.Limit, lh: []const u8) !usize {
+ _ = bw;
+ _ = limit;
+ if (true) @panic("TODO");
+ var step: u16 = 1; // 1 in the case of literal, match length otherwise
+ const pos: u16 = c.win.pos();
+ const literal = lh[0]; // literal at current position
+ const min_len: u16 = if (c.prev_match) |m| m.length() else 0;
+
+ // Try to find match at least min_len long.
+ if (c.findMatch(pos, lh, min_len)) |match| {
+ // Found better match than previous.
+ try c.addPrevLiteral();
+
+ // Is found match length good enough?
+ if (match.length() >= c.level.lazy) {
+ // Don't try to lazy find better match, use this.
+ step = try c.addMatch(match);
+ } else {
+ // Store this match.
+ c.prev_literal = literal;
+ c.prev_match = match;
+ }
+ } else {
+ // There is no better match at current pos then it was previous.
+ // Write previous match or literal.
+ if (c.prev_match) |m| {
+ // Write match from previous position.
+ step = try c.addMatch(m) - 1; // we already advanced 1 from previous position
+ } else {
+ // No match at previous position.
+ // Write previous literal if any, and remember this literal.
+ try c.addPrevLiteral();
+ c.prev_literal = literal;
+ }
+ }
+ // Advance window and add hashes.
+ c.windowAdvance(step, lh, pos);
+}
+
+fn windowAdvance(self: *Compress, step: u16, lh: []const u8, pos: u16) void {
+ // current position is already added in findMatch
+ self.lookup.bulkAdd(lh[1..], step - 1, pos + 1);
+ self.win.advance(step);
+}
+
+// Add previous literal (if any) to the tokens list.
+fn addPrevLiteral(self: *Compress) !void {
+ if (self.prev_literal) |l| try self.addToken(Token.initLiteral(l));
+}
+
+// Add match to the tokens list, reset prev pointers.
+// Returns length of the added match.
+fn addMatch(self: *Compress, m: Token) !u16 {
+ try self.addToken(m);
+ self.prev_literal = null;
+ self.prev_match = null;
+ return m.length();
+}
+
+fn addToken(self: *Compress, token: Token) !void {
+ self.tokens.add(token);
+ if (self.tokens.full()) try self.flushTokens(.none);
+}
+
+// Finds largest match in the history window with the data at current pos.
+fn findMatch(self: *Compress, pos: u16, lh: []const u8, min_len: u16) ?Token {
+ var len: u16 = min_len;
+ // Previous location with the same hash (same 4 bytes).
+ var prev_pos = self.lookup.add(lh, pos);
+ // Last found match.
+ var match: ?Token = null;
+
+ // How much back-references to try, performance knob.
+ var chain: usize = self.level.chain;
+ if (len >= self.level.good) {
+ // If we've got a match that's good enough, only look in 1/4 the chain.
+ chain >>= 2;
+ }
+
+ // Hot path loop!
+ while (prev_pos > 0 and chain > 0) : (chain -= 1) {
+ const distance = pos - prev_pos;
+ if (distance > flate.match.max_distance)
+ break;
+
+ const new_len = self.win.match(prev_pos, pos, len);
+ if (new_len > len) {
+ match = Token.initMatch(@intCast(distance), new_len);
+ if (new_len >= self.level.nice) {
+ // The match is good enough that we don't try to find a better one.
+ return match;
+ }
+ len = new_len;
+ }
+ prev_pos = self.lookup.prev(prev_pos);
+ }
+
+ return match;
+}
+
+fn flushTokens(self: *Compress, flush_opt: FlushOption) !void {
+ // Pass tokens to the token writer
+ try self.block_writer.write(self.tokens.tokens(), flush_opt == .final, self.win.tokensBuffer());
+ // Stored block ensures byte alignment.
+ // It has 3 bits (final, block_type) and then padding until byte boundary.
+ // After that everything is aligned to the boundary in the stored block.
+ // Empty stored block is Ob000 + (0-7) bits of padding + 0x00 0x00 0xFF 0xFF.
+ // Last 4 bytes are byte aligned.
+ if (flush_opt == .flush) {
+ try self.block_writer.storedBlock("", false);
+ }
+ if (flush_opt != .none) {
+ // Safe to call only when byte aligned or it is OK to add
+ // padding bits (on last byte of the final block).
+ try self.block_writer.flush();
+ }
+ // Reset internal tokens store.
+ self.tokens.reset();
+ // Notify win that tokens are flushed.
+ self.win.flush();
+}
+
+// Slide win and if needed lookup tables.
+fn slide(self: *Compress) void {
+ const n = self.win.slide();
+ self.lookup.slide(n);
+}
+
+/// Flushes internal buffers to the output writer. Outputs empty stored
+/// block to sync bit stream to the byte boundary, so that the
+/// decompressor can get all input data available so far.
+///
+/// It is useful mainly in compressed network protocols, to ensure that
+/// deflate bit stream can be used as byte stream. May degrade
+/// compression so it should be used only when necessary.
+///
+/// Completes the current deflate block and follows it with an empty
+/// stored block that is three zero bits plus filler bits to the next
+/// byte, followed by four bytes (00 00 ff ff).
+///
+pub fn flush(c: *Compress) !void {
+ try c.tokenize(.flush);
+}
+
+/// Completes deflate bit stream by writing any pending data as deflate
+/// final deflate block. HAS to be called once all data are written to
+/// the compressor as a signal that next block has to have final bit
+/// set.
+///
+pub fn finish(c: *Compress) !void {
+ _ = c;
+ @panic("TODO");
+}
+
+/// Use another writer while preserving history. Most probably flush
+/// should be called on old writer before setting new.
+pub fn setWriter(self: *Compress, new_writer: *Writer) void {
+ self.block_writer.setWriter(new_writer);
+ self.output = new_writer;
+}
+
+// Tokens store
+const Tokens = struct {
+ list: [n_tokens]Token = undefined,
+ pos: usize = 0,
+
+ fn add(self: *Tokens, t: Token) void {
+ self.list[self.pos] = t;
+ self.pos += 1;
+ }
+
+ fn full(self: *Tokens) bool {
+ return self.pos == self.list.len;
+ }
+
+ fn reset(self: *Tokens) void {
+ self.pos = 0;
+ }
+
+ fn tokens(self: *Tokens) []const Token {
+ return self.list[0..self.pos];
+ }
+};
+
+/// Creates huffman only deflate blocks. Disables Lempel-Ziv match searching and
+/// only performs Huffman entropy encoding. Results in faster compression, much
+/// less memory requirements during compression but bigger compressed sizes.
+pub const Huffman = SimpleCompressor(.huffman, .raw);
+
+/// Creates store blocks only. Data are not compressed only packed into deflate
+/// store blocks. That adds 9 bytes of header for each block. Max stored block
+/// size is 64K. Block is emitted when flush is called on on finish.
+pub const store = struct {
+ pub fn Compressor(comptime container: Container, comptime WriterType: type) type {
+ return SimpleCompressor(.store, container, WriterType);
+ }
+
+ pub fn compressor(comptime container: Container, writer: anytype) !store.Compressor(container, @TypeOf(writer)) {
+ return try store.Compressor(container, @TypeOf(writer)).init(writer);
+ }
+};
+
+const SimpleCompressorKind = enum {
+ huffman,
+ store,
+};
+
+fn simpleCompressor(
+ comptime kind: SimpleCompressorKind,
+ comptime container: Container,
+ writer: anytype,
+) !SimpleCompressor(kind, container, @TypeOf(writer)) {
+ return try SimpleCompressor(kind, container, @TypeOf(writer)).init(writer);
+}
+
+fn SimpleCompressor(
+ comptime kind: SimpleCompressorKind,
+ comptime container: Container,
+ comptime WriterType: type,
+) type {
+ const BlockWriterType = BlockWriter(WriterType);
+ return struct {
+ buffer: [65535]u8 = undefined, // because store blocks are limited to 65535 bytes
+ wp: usize = 0,
+
+ output: WriterType,
+ block_writer: BlockWriterType,
+ hasher: container.Hasher() = .{},
+
+ const Self = @This();
+
+ pub fn init(output: WriterType) !Self {
+ const self = Self{
+ .output = output,
+ .block_writer = BlockWriterType.init(output),
+ };
+ try container.writeHeader(self.output);
+ return self;
+ }
+
+ pub fn flush(self: *Self) !void {
+ try self.flushBuffer(false);
+ try self.block_writer.storedBlock("", false);
+ try self.block_writer.flush();
+ }
+
+ pub fn finish(self: *Self) !void {
+ try self.flushBuffer(true);
+ try self.block_writer.flush();
+ try container.writeFooter(&self.hasher, self.output);
+ }
+
+ fn flushBuffer(self: *Self, final: bool) !void {
+ const buf = self.buffer[0..self.wp];
+ switch (kind) {
+ .huffman => try self.block_writer.huffmanBlock(buf, final),
+ .store => try self.block_writer.storedBlock(buf, final),
+ }
+ self.wp = 0;
+ }
+ };
+}
+
+const LiteralNode = struct {
+ literal: u16,
+ freq: u16,
+};
+
+// Describes the state of the constructed tree for a given depth.
+const LevelInfo = struct {
+ // Our level. for better printing
+ level: u32,
+
+ // The frequency of the last node at this level
+ last_freq: u32,
+
+ // The frequency of the next character to add to this level
+ next_char_freq: u32,
+
+ // The frequency of the next pair (from level below) to add to this level.
+ // Only valid if the "needed" value of the next lower level is 0.
+ next_pair_freq: u32,
+
+ // The number of chains remaining to generate for this level before moving
+ // up to the next level
+ needed: u32,
+};
+
+// hcode is a huffman code with a bit code and bit length.
+pub const HuffCode = struct {
+ code: u16 = 0,
+ len: u16 = 0,
+
+ // set sets the code and length of an hcode.
+ fn set(self: *HuffCode, code: u16, length: u16) void {
+ self.len = length;
+ self.code = code;
+ }
+};
+
+pub fn HuffmanEncoder(comptime size: usize) type {
+ return struct {
+ codes: [size]HuffCode = undefined,
+ // Reusable buffer with the longest possible frequency table.
+ freq_cache: [huffman.max_num_frequencies + 1]LiteralNode = undefined,
+ bit_count: [17]u32 = undefined,
+ lns: []LiteralNode = undefined, // sorted by literal, stored to avoid repeated allocation in generate
+ lfs: []LiteralNode = undefined, // sorted by frequency, stored to avoid repeated allocation in generate
+
+ const Self = @This();
+
+ // Update this Huffman Code object to be the minimum code for the specified frequency count.
+ //
+ // freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
+ // max_bits The maximum number of bits to use for any literal.
+ pub fn generate(self: *Self, freq: []u16, max_bits: u32) void {
+ var list = self.freq_cache[0 .. freq.len + 1];
+ // Number of non-zero literals
+ var count: u32 = 0;
+ // Set list to be the set of all non-zero literals and their frequencies
+ for (freq, 0..) |f, i| {
+ if (f != 0) {
+ list[count] = LiteralNode{ .literal = @as(u16, @intCast(i)), .freq = f };
+ count += 1;
+ } else {
+ list[count] = LiteralNode{ .literal = 0x00, .freq = 0 };
+ self.codes[i].len = 0;
+ }
+ }
+ list[freq.len] = LiteralNode{ .literal = 0x00, .freq = 0 };
+
+ list = list[0..count];
+ if (count <= 2) {
+ // Handle the small cases here, because they are awkward for the general case code. With
+ // two or fewer literals, everything has bit length 1.
+ for (list, 0..) |node, i| {
+ // "list" is in order of increasing literal value.
+ self.codes[node.literal].set(@as(u16, @intCast(i)), 1);
+ }
+ return;
+ }
+ self.lfs = list;
+ mem.sort(LiteralNode, self.lfs, {}, byFreq);
+
+ // Get the number of literals for each bit count
+ const bit_count = self.bitCounts(list, max_bits);
+ // And do the assignment
+ self.assignEncodingAndSize(bit_count, list);
+ }
+
+ pub fn bitLength(self: *Self, freq: []u16) u32 {
+ var total: u32 = 0;
+ for (freq, 0..) |f, i| {
+ if (f != 0) {
+ total += @as(u32, @intCast(f)) * @as(u32, @intCast(self.codes[i].len));
+ }
+ }
+ return total;
+ }
+
+ // Return the number of literals assigned to each bit size in the Huffman encoding
+ //
+ // This method is only called when list.len >= 3
+ // The cases of 0, 1, and 2 literals are handled by special case code.
+ //
+ // list: An array of the literals with non-zero frequencies
+ // and their associated frequencies. The array is in order of increasing
+ // frequency, and has as its last element a special element with frequency
+ // `math.maxInt(i32)`
+ //
+ // max_bits: The maximum number of bits that should be used to encode any literal.
+ // Must be less than 16.
+ //
+ // Returns an integer array in which array[i] indicates the number of literals
+ // that should be encoded in i bits.
+ fn bitCounts(self: *Self, list: []LiteralNode, max_bits_to_use: usize) []u32 {
+ var max_bits = max_bits_to_use;
+ const n = list.len;
+ const max_bits_limit = 16;
+
+ assert(max_bits < max_bits_limit);
+
+ // The tree can't have greater depth than n - 1, no matter what. This
+ // saves a little bit of work in some small cases
+ max_bits = @min(max_bits, n - 1);
+
+ // Create information about each of the levels.
+ // A bogus "Level 0" whose sole purpose is so that
+ // level1.prev.needed == 0. This makes level1.next_pair_freq
+ // be a legitimate value that never gets chosen.
+ var levels: [max_bits_limit]LevelInfo = mem.zeroes([max_bits_limit]LevelInfo);
+ // leaf_counts[i] counts the number of literals at the left
+ // of ancestors of the rightmost node at level i.
+ // leaf_counts[i][j] is the number of literals at the left
+ // of the level j ancestor.
+ var leaf_counts: [max_bits_limit][max_bits_limit]u32 = mem.zeroes([max_bits_limit][max_bits_limit]u32);
+
+ {
+ var level = @as(u32, 1);
+ while (level <= max_bits) : (level += 1) {
+ // For every level, the first two items are the first two characters.
+ // We initialize the levels as if we had already figured this out.
+ levels[level] = LevelInfo{
+ .level = level,
+ .last_freq = list[1].freq,
+ .next_char_freq = list[2].freq,
+ .next_pair_freq = list[0].freq + list[1].freq,
+ .needed = 0,
+ };
+ leaf_counts[level][level] = 2;
+ if (level == 1) {
+ levels[level].next_pair_freq = math.maxInt(i32);
+ }
+ }
+ }
+
+ // We need a total of 2*n - 2 items at top level and have already generated 2.
+ levels[max_bits].needed = 2 * @as(u32, @intCast(n)) - 4;
+
+ {
+ var level = max_bits;
+ while (true) {
+ var l = &levels[level];
+ if (l.next_pair_freq == math.maxInt(i32) and l.next_char_freq == math.maxInt(i32)) {
+ // We've run out of both leaves and pairs.
+ // End all calculations for this level.
+ // To make sure we never come back to this level or any lower level,
+ // set next_pair_freq impossibly large.
+ l.needed = 0;
+ levels[level + 1].next_pair_freq = math.maxInt(i32);
+ level += 1;
+ continue;
+ }
+
+ const prev_freq = l.last_freq;
+ if (l.next_char_freq < l.next_pair_freq) {
+ // The next item on this row is a leaf node.
+ const next = leaf_counts[level][level] + 1;
+ l.last_freq = l.next_char_freq;
+ // Lower leaf_counts are the same of the previous node.
+ leaf_counts[level][level] = next;
+ if (next >= list.len) {
+ l.next_char_freq = maxNode().freq;
+ } else {
+ l.next_char_freq = list[next].freq;
+ }
+ } else {
+ // The next item on this row is a pair from the previous row.
+ // next_pair_freq isn't valid until we generate two
+ // more values in the level below
+ l.last_freq = l.next_pair_freq;
+ // Take leaf counts from the lower level, except counts[level] remains the same.
+ @memcpy(leaf_counts[level][0..level], leaf_counts[level - 1][0..level]);
+ levels[l.level - 1].needed = 2;
+ }
+
+ l.needed -= 1;
+ if (l.needed == 0) {
+ // We've done everything we need to do for this level.
+ // Continue calculating one level up. Fill in next_pair_freq
+ // of that level with the sum of the two nodes we've just calculated on
+ // this level.
+ if (l.level == max_bits) {
+ // All done!
+ break;
+ }
+ levels[l.level + 1].next_pair_freq = prev_freq + l.last_freq;
+ level += 1;
+ } else {
+ // If we stole from below, move down temporarily to replenish it.
+ while (levels[level - 1].needed > 0) {
+ level -= 1;
+ if (level == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Somethings is wrong if at the end, the top level is null or hasn't used
+ // all of the leaves.
+ assert(leaf_counts[max_bits][max_bits] == n);
+
+ var bit_count = self.bit_count[0 .. max_bits + 1];
+ var bits: u32 = 1;
+ const counts = &leaf_counts[max_bits];
+ {
+ var level = max_bits;
+ while (level > 0) : (level -= 1) {
+ // counts[level] gives the number of literals requiring at least "bits"
+ // bits to encode.
+ bit_count[bits] = counts[level] - counts[level - 1];
+ bits += 1;
+ if (level == 0) {
+ break;
+ }
+ }
+ }
+ return bit_count;
+ }
+
+ // Look at the leaves and assign them a bit count and an encoding as specified
+ // in RFC 1951 3.2.2
+ fn assignEncodingAndSize(self: *Self, bit_count: []u32, list_arg: []LiteralNode) void {
+ var code = @as(u16, 0);
+ var list = list_arg;
+
+ for (bit_count, 0..) |bits, n| {
+ code <<= 1;
+ if (n == 0 or bits == 0) {
+ continue;
+ }
+ // The literals list[list.len-bits] .. list[list.len-bits]
+ // are encoded using "bits" bits, and get the values
+ // code, code + 1, .... The code values are
+ // assigned in literal order (not frequency order).
+ const chunk = list[list.len - @as(u32, @intCast(bits)) ..];
+
+ self.lns = chunk;
+ mem.sort(LiteralNode, self.lns, {}, byLiteral);
+
+ for (chunk) |node| {
+ self.codes[node.literal] = HuffCode{
+ .code = bitReverse(u16, code, @as(u5, @intCast(n))),
+ .len = @as(u16, @intCast(n)),
+ };
+ code += 1;
+ }
+ list = list[0 .. list.len - @as(u32, @intCast(bits))];
+ }
+ }
+ };
+}
+
+fn maxNode() LiteralNode {
+ return LiteralNode{
+ .literal = math.maxInt(u16),
+ .freq = math.maxInt(u16),
+ };
+}
+
+pub fn huffmanEncoder(comptime size: u32) HuffmanEncoder(size) {
+ return .{};
+}
+
+pub const LiteralEncoder = HuffmanEncoder(huffman.max_num_frequencies);
+pub const DistanceEncoder = HuffmanEncoder(huffman.distance_code_count);
+pub const CodegenEncoder = HuffmanEncoder(19);
+
+// Generates a HuffmanCode corresponding to the fixed literal table
+pub fn fixedLiteralEncoder() LiteralEncoder {
+ var h: LiteralEncoder = undefined;
+ var ch: u16 = 0;
+
+ while (ch < huffman.max_num_frequencies) : (ch += 1) {
+ var bits: u16 = undefined;
+ var size: u16 = undefined;
+ switch (ch) {
+ 0...143 => {
+ // size 8, 000110000 .. 10111111
+ bits = ch + 48;
+ size = 8;
+ },
+ 144...255 => {
+ // size 9, 110010000 .. 111111111
+ bits = ch + 400 - 144;
+ size = 9;
+ },
+ 256...279 => {
+ // size 7, 0000000 .. 0010111
+ bits = ch - 256;
+ size = 7;
+ },
+ else => {
+ // size 8, 11000000 .. 11000111
+ bits = ch + 192 - 280;
+ size = 8;
+ },
+ }
+ h.codes[ch] = HuffCode{ .code = bitReverse(u16, bits, @as(u5, @intCast(size))), .len = size };
+ }
+ return h;
+}
+
+pub fn fixedDistanceEncoder() DistanceEncoder {
+ var h: DistanceEncoder = undefined;
+ for (h.codes, 0..) |_, ch| {
+ h.codes[ch] = HuffCode{ .code = bitReverse(u16, @as(u16, @intCast(ch)), 5), .len = 5 };
+ }
+ return h;
+}
+
+pub fn huffmanDistanceEncoder() DistanceEncoder {
+ var distance_freq = [1]u16{0} ** huffman.distance_code_count;
+ distance_freq[0] = 1;
+ // huff_distance is a static distance encoder used for huffman only encoding.
+ // It can be reused since we will not be encoding distance values.
+ var h: DistanceEncoder = .{};
+ h.generate(distance_freq[0..], 15);
+ return h;
+}
+
+fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
+ _ = context;
+ return a.literal < b.literal;
+}
+
+fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
+ _ = context;
+ if (a.freq == b.freq) {
+ return a.literal < b.literal;
+ }
+ return a.freq < b.freq;
+}
+
+fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
+ const c: *Compress = @fieldParentPtr("reader", r);
+ switch (c.state) {
+ .header => |i| {
+ const header = c.hasher.container().header();
+ const n = try w.write(header[i..]);
+ if (header.len - i - n == 0) {
+ c.state = .middle;
+ } else {
+ c.state.header += n;
+ }
+ return n;
+ },
+ .middle => {
+ c.input.fillMore() catch |err| switch (err) {
+ error.EndOfStream => {
+ c.state = .final;
+ return 0;
+ },
+ else => |e| return e,
+ };
+ const buffer_contents = c.input.buffered();
+ const min_lookahead = flate.match.min_length + flate.match.max_length;
+ const history_plus_lookahead_len = flate.history_len + min_lookahead;
+ if (buffer_contents.len < history_plus_lookahead_len) return 0;
+ const lookahead = buffer_contents[flate.history_len..];
+ const start = w.count;
+ const n = try c.tokenizeSlice(w, limit, lookahead) catch |err| switch (err) {
+ error.WriteFailed => return error.WriteFailed,
+ };
+ c.hasher.update(lookahead[0..n]);
+ c.input.toss(n);
+ return w.count - start;
+ },
+ .final => {
+ const buffer_contents = c.input.buffered();
+ const start = w.count;
+ const n = c.tokenizeSlice(w, limit, buffer_contents) catch |err| switch (err) {
+ error.WriteFailed => return error.WriteFailed,
+ };
+ if (buffer_contents.len - n == 0) {
+ c.hasher.update(buffer_contents);
+ c.input.tossAll();
+ {
+ // In the case of flushing, last few lookahead buffers were
+ // smaller than min match len, so only last literal can be
+ // unwritten.
+ assert(c.prev_match == null);
+ try c.addPrevLiteral();
+ c.prev_literal = null;
+
+ try c.flushTokens(.final);
+ }
+ switch (c.hasher) {
+ .gzip => |*gzip| {
+ // GZIP 8 bytes footer
+ // - 4 bytes, CRC32 (CRC-32)
+ // - 4 bytes, ISIZE (Input SIZE) - size of the original (uncompressed) input data modulo 2^32
+ comptime assert(c.footer_buffer.len == 8);
+ std.mem.writeInt(u32, c.footer_buffer[0..4], gzip.final(), .little);
+ std.mem.writeInt(u32, c.footer_buffer[4..8], gzip.bytes_read, .little);
+ c.state = .{ .footer = 0 };
+ },
+ .zlib => |*zlib| {
+ // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
+ // 4 bytes of ADLER32 (Adler-32 checksum)
+ // Checksum value of the uncompressed data (excluding any
+ // dictionary data) computed according to Adler-32
+ // algorithm.
+ comptime assert(c.footer_buffer.len == 8);
+ std.mem.writeInt(u32, c.footer_buffer[4..8], zlib.final, .big);
+ c.state = .{ .footer = 4 };
+ },
+ .raw => {
+ c.state = .ended;
+ },
+ }
+ }
+ return w.count - start;
+ },
+ .ended => return error.EndOfStream,
+ .footer => |i| {
+ const remaining = c.footer_buffer[i..];
+ const n = try w.write(limit.slice(remaining));
+ c.state = if (n == remaining) .ended else .{ .footer = i - n };
+ return n;
+ },
+ }
+}
+
+test "generate a Huffman code from an array of frequencies" {
+ var freqs: [19]u16 = [_]u16{
+ 8, // 0
+ 1, // 1
+ 1, // 2
+ 2, // 3
+ 5, // 4
+ 10, // 5
+ 9, // 6
+ 1, // 7
+ 0, // 8
+ 0, // 9
+ 0, // 10
+ 0, // 11
+ 0, // 12
+ 0, // 13
+ 0, // 14
+ 0, // 15
+ 1, // 16
+ 3, // 17
+ 5, // 18
+ };
+
+ var enc = huffmanEncoder(19);
+ enc.generate(freqs[0..], 7);
+
+ try testing.expectEqual(@as(u32, 141), enc.bitLength(freqs[0..]));
+
+ try testing.expectEqual(@as(usize, 3), enc.codes[0].len);
+ try testing.expectEqual(@as(usize, 6), enc.codes[1].len);
+ try testing.expectEqual(@as(usize, 6), enc.codes[2].len);
+ try testing.expectEqual(@as(usize, 5), enc.codes[3].len);
+ try testing.expectEqual(@as(usize, 3), enc.codes[4].len);
+ try testing.expectEqual(@as(usize, 2), enc.codes[5].len);
+ try testing.expectEqual(@as(usize, 2), enc.codes[6].len);
+ try testing.expectEqual(@as(usize, 6), enc.codes[7].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[8].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[9].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[10].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[11].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[12].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[13].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[14].len);
+ try testing.expectEqual(@as(usize, 0), enc.codes[15].len);
+ try testing.expectEqual(@as(usize, 6), enc.codes[16].len);
+ try testing.expectEqual(@as(usize, 5), enc.codes[17].len);
+ try testing.expectEqual(@as(usize, 3), enc.codes[18].len);
+
+ try testing.expectEqual(@as(u16, 0x0), enc.codes[5].code);
+ try testing.expectEqual(@as(u16, 0x2), enc.codes[6].code);
+ try testing.expectEqual(@as(u16, 0x1), enc.codes[0].code);
+ try testing.expectEqual(@as(u16, 0x5), enc.codes[4].code);
+ try testing.expectEqual(@as(u16, 0x3), enc.codes[18].code);
+ try testing.expectEqual(@as(u16, 0x7), enc.codes[3].code);
+ try testing.expectEqual(@as(u16, 0x17), enc.codes[17].code);
+ try testing.expectEqual(@as(u16, 0x0f), enc.codes[1].code);
+ try testing.expectEqual(@as(u16, 0x2f), enc.codes[2].code);
+ try testing.expectEqual(@as(u16, 0x1f), enc.codes[7].code);
+ try testing.expectEqual(@as(u16, 0x3f), enc.codes[16].code);
+}
+
+test "generate a Huffman code for the fixed literal table specific to Deflate" {
+ const enc = fixedLiteralEncoder();
+ for (enc.codes) |c| {
+ switch (c.len) {
+ 7 => {
+ const v = @bitReverse(@as(u7, @intCast(c.code)));
+ try testing.expect(v <= 0b0010111);
+ },
+ 8 => {
+ const v = @bitReverse(@as(u8, @intCast(c.code)));
+ try testing.expect((v >= 0b000110000 and v <= 0b10111111) or
+ (v >= 0b11000000 and v <= 11000111));
+ },
+ 9 => {
+ const v = @bitReverse(@as(u9, @intCast(c.code)));
+ try testing.expect(v >= 0b110010000 and v <= 0b111111111);
+ },
+ else => unreachable,
+ }
+ }
+}
+
+test "generate a Huffman code for the 30 possible relative distances (LZ77 distances) of Deflate" {
+ const enc = fixedDistanceEncoder();
+ for (enc.codes) |c| {
+ const v = @bitReverse(@as(u5, @intCast(c.code)));
+ try testing.expect(v <= 29);
+ try testing.expect(c.len == 5);
+ }
+}
+
+// Reverse bit-by-bit a N-bit code.
+fn bitReverse(comptime T: type, value: T, n: usize) T {
+ const r = @bitReverse(value);
+ return r >> @as(math.Log2Int(T), @intCast(@typeInfo(T).int.bits - n));
+}
+
+test bitReverse {
+ const ReverseBitsTest = struct {
+ in: u16,
+ bit_count: u5,
+ out: u16,
+ };
+
+ const reverse_bits_tests = [_]ReverseBitsTest{
+ .{ .in = 1, .bit_count = 1, .out = 1 },
+ .{ .in = 1, .bit_count = 2, .out = 2 },
+ .{ .in = 1, .bit_count = 3, .out = 4 },
+ .{ .in = 1, .bit_count = 4, .out = 8 },
+ .{ .in = 1, .bit_count = 5, .out = 16 },
+ .{ .in = 17, .bit_count = 5, .out = 17 },
+ .{ .in = 257, .bit_count = 9, .out = 257 },
+ .{ .in = 29, .bit_count = 5, .out = 23 },
+ };
+
+ for (reverse_bits_tests) |h| {
+ const v = bitReverse(u16, h.in, h.bit_count);
+ try std.testing.expectEqual(h.out, v);
+ }
+}
+
+test "fixedLiteralEncoder codes" {
+ var al = std.ArrayList(u8).init(testing.allocator);
+ defer al.deinit();
+ var bw = std.Io.bitWriter(.little, al.writer());
+
+ const f = fixedLiteralEncoder();
+ for (f.codes) |c| {
+ try bw.writeBits(c.code, c.len);
+ }
+ try testing.expectEqualSlices(u8, &fixed_codes, al.items);
+}
+
+pub const fixed_codes = [_]u8{
+ 0b00001100, 0b10001100, 0b01001100, 0b11001100, 0b00101100, 0b10101100, 0b01101100, 0b11101100,
+ 0b00011100, 0b10011100, 0b01011100, 0b11011100, 0b00111100, 0b10111100, 0b01111100, 0b11111100,
+ 0b00000010, 0b10000010, 0b01000010, 0b11000010, 0b00100010, 0b10100010, 0b01100010, 0b11100010,
+ 0b00010010, 0b10010010, 0b01010010, 0b11010010, 0b00110010, 0b10110010, 0b01110010, 0b11110010,
+ 0b00001010, 0b10001010, 0b01001010, 0b11001010, 0b00101010, 0b10101010, 0b01101010, 0b11101010,
+ 0b00011010, 0b10011010, 0b01011010, 0b11011010, 0b00111010, 0b10111010, 0b01111010, 0b11111010,
+ 0b00000110, 0b10000110, 0b01000110, 0b11000110, 0b00100110, 0b10100110, 0b01100110, 0b11100110,
+ 0b00010110, 0b10010110, 0b01010110, 0b11010110, 0b00110110, 0b10110110, 0b01110110, 0b11110110,
+ 0b00001110, 0b10001110, 0b01001110, 0b11001110, 0b00101110, 0b10101110, 0b01101110, 0b11101110,
+ 0b00011110, 0b10011110, 0b01011110, 0b11011110, 0b00111110, 0b10111110, 0b01111110, 0b11111110,
+ 0b00000001, 0b10000001, 0b01000001, 0b11000001, 0b00100001, 0b10100001, 0b01100001, 0b11100001,
+ 0b00010001, 0b10010001, 0b01010001, 0b11010001, 0b00110001, 0b10110001, 0b01110001, 0b11110001,
+ 0b00001001, 0b10001001, 0b01001001, 0b11001001, 0b00101001, 0b10101001, 0b01101001, 0b11101001,
+ 0b00011001, 0b10011001, 0b01011001, 0b11011001, 0b00111001, 0b10111001, 0b01111001, 0b11111001,
+ 0b00000101, 0b10000101, 0b01000101, 0b11000101, 0b00100101, 0b10100101, 0b01100101, 0b11100101,
+ 0b00010101, 0b10010101, 0b01010101, 0b11010101, 0b00110101, 0b10110101, 0b01110101, 0b11110101,
+ 0b00001101, 0b10001101, 0b01001101, 0b11001101, 0b00101101, 0b10101101, 0b01101101, 0b11101101,
+ 0b00011101, 0b10011101, 0b01011101, 0b11011101, 0b00111101, 0b10111101, 0b01111101, 0b11111101,
+ 0b00010011, 0b00100110, 0b01001110, 0b10011010, 0b00111100, 0b01100101, 0b11101010, 0b10110100,
+ 0b11101001, 0b00110011, 0b01100110, 0b11001110, 0b10011010, 0b00111101, 0b01100111, 0b11101110,
+ 0b10111100, 0b11111001, 0b00001011, 0b00010110, 0b00101110, 0b01011010, 0b10111100, 0b01100100,
+ 0b11101001, 0b10110010, 0b11100101, 0b00101011, 0b01010110, 0b10101110, 0b01011010, 0b10111101,
+ 0b01100110, 0b11101101, 0b10111010, 0b11110101, 0b00011011, 0b00110110, 0b01101110, 0b11011010,
+ 0b10111100, 0b01100101, 0b11101011, 0b10110110, 0b11101101, 0b00111011, 0b01110110, 0b11101110,
+ 0b11011010, 0b10111101, 0b01100111, 0b11101111, 0b10111110, 0b11111101, 0b00000111, 0b00001110,
+ 0b00011110, 0b00111010, 0b01111100, 0b11100100, 0b11101000, 0b10110001, 0b11100011, 0b00100111,
+ 0b01001110, 0b10011110, 0b00111010, 0b01111101, 0b11100110, 0b11101100, 0b10111001, 0b11110011,
+ 0b00010111, 0b00101110, 0b01011110, 0b10111010, 0b01111100, 0b11100101, 0b11101010, 0b10110101,
+ 0b11101011, 0b00110111, 0b01101110, 0b11011110, 0b10111010, 0b01111101, 0b11100111, 0b11101110,
+ 0b10111101, 0b11111011, 0b00001111, 0b00011110, 0b00111110, 0b01111010, 0b11111100, 0b11100100,
+ 0b11101001, 0b10110011, 0b11100111, 0b00101111, 0b01011110, 0b10111110, 0b01111010, 0b11111101,
+ 0b11100110, 0b11101101, 0b10111011, 0b11110111, 0b00011111, 0b00111110, 0b01111110, 0b11111010,
+ 0b11111100, 0b11100101, 0b11101011, 0b10110111, 0b11101111, 0b00111111, 0b01111110, 0b11111110,
+ 0b11111010, 0b11111101, 0b11100111, 0b11101111, 0b10111111, 0b11111111, 0b00000000, 0b00100000,
+ 0b00001000, 0b00001100, 0b10000001, 0b11000010, 0b11100000, 0b00001000, 0b00100100, 0b00001010,
+ 0b10001101, 0b11000001, 0b11100010, 0b11110000, 0b00000100, 0b00100010, 0b10001001, 0b01001100,
+ 0b10100001, 0b11010010, 0b11101000, 0b00000011, 0b10000011, 0b01000011, 0b11000011, 0b00100011,
+ 0b10100011,
+};
+
+test "tokenization" {
+ const L = Token.initLiteral;
+ const M = Token.initMatch;
+
+ const cases = [_]struct {
+ data: []const u8,
+ tokens: []const Token,
+ }{
+ .{
+ .data = "Blah blah blah blah blah!",
+ .tokens = &[_]Token{ L('B'), L('l'), L('a'), L('h'), L(' '), L('b'), M(5, 18), L('!') },
+ },
+ .{
+ .data = "ABCDEABCD ABCDEABCD",
+ .tokens = &[_]Token{
+ L('A'), L('B'), L('C'), L('D'), L('E'), L('A'), L('B'), L('C'), L('D'), L(' '),
+ L('A'), M(10, 8),
+ },
+ },
+ };
+
+ for (cases) |c| {
+ inline for (Container.list) |container| { // for each wrapping
+
+ var cw = std.Io.countingWriter(std.Io.null_writer);
+ const cww = cw.writer();
+ var df = try Compress(container, @TypeOf(cww), TestTokenWriter).init(cww, .{});
+
+ _ = try df.write(c.data);
+ try df.flush();
+
+ // df.token_writer.show();
+ try expect(df.block_writer.pos == c.tokens.len); // number of tokens written
+ try testing.expectEqualSlices(Token, df.block_writer.get(), c.tokens); // tokens match
+
+ try testing.expectEqual(container.headerSize(), cw.bytes_written);
+ try df.finish();
+ try testing.expectEqual(container.size(), cw.bytes_written);
+ }
+ }
+}
+
+// Tests that tokens written are equal to expected token list.
+const TestTokenWriter = struct {
+ const Self = @This();
+
+ pos: usize = 0,
+ actual: [128]Token = undefined,
+
+ pub fn init(_: anytype) Self {
+ return .{};
+ }
+ pub fn write(self: *Self, tokens: []const Token, _: bool, _: ?[]const u8) !void {
+ for (tokens) |t| {
+ self.actual[self.pos] = t;
+ self.pos += 1;
+ }
+ }
+
+ pub fn storedBlock(_: *Self, _: []const u8, _: bool) !void {}
+
+ pub fn get(self: *Self) []Token {
+ return self.actual[0..self.pos];
+ }
+
+ pub fn show(self: *Self) void {
+ std.debug.print("\n", .{});
+ for (self.get()) |t| {
+ t.show();
+ }
+ }
+
+ pub fn flush(_: *Self) !void {}
+};
+
+test "file tokenization" {
+ const levels = [_]Level{ .level_4, .level_5, .level_6, .level_7, .level_8, .level_9 };
+ const cases = [_]struct {
+ data: []const u8, // uncompressed content
+ // expected number of tokens producet in deflate tokenization
+ tokens_count: [levels.len]usize = .{0} ** levels.len,
+ }{
+ .{
+ .data = @embedFile("testdata/rfc1951.txt"),
+ .tokens_count = .{ 7675, 7672, 7599, 7594, 7598, 7599 },
+ },
+
+ .{
+ .data = @embedFile("testdata/block_writer/huffman-null-max.input"),
+ .tokens_count = .{ 257, 257, 257, 257, 257, 257 },
+ },
+ .{
+ .data = @embedFile("testdata/block_writer/huffman-pi.input"),
+ .tokens_count = .{ 2570, 2564, 2564, 2564, 2564, 2564 },
+ },
+ .{
+ .data = @embedFile("testdata/block_writer/huffman-text.input"),
+ .tokens_count = .{ 235, 234, 234, 234, 234, 234 },
+ },
+ .{
+ .data = @embedFile("testdata/fuzz/roundtrip1.input"),
+ .tokens_count = .{ 333, 331, 331, 331, 331, 331 },
+ },
+ .{
+ .data = @embedFile("testdata/fuzz/roundtrip2.input"),
+ .tokens_count = .{ 334, 334, 334, 334, 334, 334 },
+ },
+ };
+
+ for (cases) |case| { // for each case
+ const data = case.data;
+
+ for (levels, 0..) |level, i| { // for each compression level
+ var original: Reader = .fixed(data);
+
+ // buffer for decompressed data
+ var al = std.ArrayList(u8).init(testing.allocator);
+ defer al.deinit();
+ const writer = al.writer();
+
+ // create compressor
+ const WriterType = @TypeOf(writer);
+ const TokenWriter = TokenDecoder(@TypeOf(writer));
+ var cmp = try Compress(.raw, WriterType, TokenWriter).init(writer, .{ .level = level });
+
+ // Stream uncompressed `original` data to the compressor. It will
+ // produce tokens list and pass that list to the TokenDecoder. This
+ // TokenDecoder uses CircularBuffer from inflate to convert list of
+ // tokens back to the uncompressed stream.
+ try cmp.compress(original.reader());
+ try cmp.flush();
+ const expected_count = case.tokens_count[i];
+ const actual = cmp.block_writer.tokens_count;
+ if (expected_count == 0) {
+ std.debug.print("actual token count {d}\n", .{actual});
+ } else {
+ try testing.expectEqual(expected_count, actual);
+ }
+
+ try testing.expectEqual(data.len, al.items.len);
+ try testing.expectEqualSlices(u8, data, al.items);
+ }
+ }
+}
+
+const TokenDecoder = struct {
+ output: *Writer,
+ tokens_count: usize,
+
+ pub fn init(output: *Writer) TokenDecoder {
+ return .{
+ .output = output,
+ .tokens_count = 0,
+ };
+ }
+
+ pub fn write(self: *TokenDecoder, tokens: []const Token, _: bool, _: ?[]const u8) !void {
+ self.tokens_count += tokens.len;
+ for (tokens) |t| {
+ switch (t.kind) {
+ .literal => self.hist.write(t.literal()),
+ .match => try self.hist.writeMatch(t.length(), t.distance()),
+ }
+ if (self.hist.free() < 285) try self.flushWin();
+ }
+ try self.flushWin();
+ }
+
+ fn flushWin(self: *TokenDecoder) !void {
+ while (true) {
+ const buf = self.hist.read();
+ if (buf.len == 0) break;
+ try self.output.writeAll(buf);
+ }
+ }
+};
+
+test "store simple compressor" {
+ const data = "Hello world!";
+ const expected = [_]u8{
+ 0x1, // block type 0, final bit set
+ 0xc, 0x0, // len = 12
+ 0xf3, 0xff, // ~len
+ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', //
+ //0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
+ };
+
+ var fbs: Reader = .fixed(data);
+ var al = std.ArrayList(u8).init(testing.allocator);
+ defer al.deinit();
+
+ var cmp = try store.compressor(.raw, al.writer());
+ try cmp.compress(&fbs);
+ try cmp.finish();
+ try testing.expectEqualSlices(u8, &expected, al.items);
+
+ fbs = .fixed(data);
+ try al.resize(0);
+
+ // huffman only compresoor will also emit store block for this small sample
+ var hc = try huffman.compressor(.raw, al.writer());
+ try hc.compress(&fbs);
+ try hc.finish();
+ try testing.expectEqualSlices(u8, &expected, al.items);
+}
+
+test "sliding window match" {
+ const data = "Blah blah blah blah blah!";
+ var win: Writer = .{};
+ try expect(win.write(data) == data.len);
+ try expect(win.wp == data.len);
+ try expect(win.rp == 0);
+
+ // length between l symbols
+ try expect(win.match(1, 6, 0) == 18);
+ try expect(win.match(1, 11, 0) == 13);
+ try expect(win.match(1, 16, 0) == 8);
+ try expect(win.match(1, 21, 0) == 0);
+
+ // position 15 = "blah blah!"
+ // position 20 = "blah!"
+ try expect(win.match(15, 20, 0) == 4);
+ try expect(win.match(15, 20, 3) == 4);
+ try expect(win.match(15, 20, 4) == 0);
+}
+
+test "sliding window slide" {
+ var win: Writer = .{};
+ win.wp = Writer.buffer_len - 11;
+ win.rp = Writer.buffer_len - 111;
+ win.buffer[win.rp] = 0xab;
+ try expect(win.lookahead().len == 100);
+ try expect(win.tokensBuffer().?.len == win.rp);
+
+ const n = win.slide();
+ try expect(n == 32757);
+ try expect(win.buffer[win.rp] == 0xab);
+ try expect(win.rp == Writer.hist_len - 111);
+ try expect(win.wp == Writer.hist_len - 11);
+ try expect(win.lookahead().len == 100);
+ try expect(win.tokensBuffer() == null);
+}
diff --git a/lib/std/compress/flate/Decompress.zig b/lib/std/compress/flate/Decompress.zig
new file mode 100644
index 0000000000000000000000000000000000000000..6cb595376378d13b2fdd73673a79e4c991fd663e
--- /dev/null
+++ b/lib/std/compress/flate/Decompress.zig
@@ -0,0 +1,894 @@
+const std = @import("../../std.zig");
+const flate = std.compress.flate;
+const Container = flate.Container;
+const Token = @import("Token.zig");
+const testing = std.testing;
+const Decompress = @This();
+const Writer = std.io.Writer;
+const Reader = std.io.Reader;
+
+input: *Reader,
+reader: Reader,
+/// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.
+hasher: Container.Hasher,
+
+lit_dec: LiteralDecoder,
+dst_dec: DistanceDecoder,
+
+final_block: bool,
+state: State,
+
+read_err: ?Error,
+
+const BlockType = enum(u2) {
+ stored = 0,
+ fixed = 1,
+ dynamic = 2,
+};
+
+const State = union(enum) {
+ protocol_header,
+ block_header,
+ stored_block: u16,
+ fixed_block,
+ dynamic_block,
+ protocol_footer,
+ end,
+};
+
+pub const Error = Container.Error || error{
+ InvalidCode,
+ InvalidMatch,
+ InvalidBlockType,
+ WrongStoredBlockNlen,
+ InvalidDynamicBlockHeader,
+ EndOfStream,
+ ReadFailed,
+ OversubscribedHuffmanTree,
+ IncompleteHuffmanTree,
+ MissingEndOfBlockCode,
+};
+
+pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
+ return .{
+ .reader = .{
+ // TODO populate discard so that when an amount is discarded that
+ // includes an entire frame, skip decoding that frame.
+ .vtable = &.{ .stream = stream },
+ .buffer = buffer,
+ .seek = 0,
+ .end = 0,
+ },
+ .input = input,
+ .hasher = .init(container),
+ .lit_dec = .{},
+ .dst_dec = .{},
+ .final_block = false,
+ .state = .protocol_header,
+ .read_err = null,
+ };
+}
+
+fn decodeLength(self: *Decompress, code: u8) !u16 {
+ if (code > 28) return error.InvalidCode;
+ const ml = Token.matchLength(code);
+ return if (ml.extra_bits == 0) // 0 - 5 extra bits
+ ml.base
+ else
+ ml.base + try self.takeNBitsBuffered(ml.extra_bits);
+}
+
+fn decodeDistance(self: *Decompress, code: u8) !u16 {
+ if (code > 29) return error.InvalidCode;
+ const md = Token.matchDistance(code);
+ return if (md.extra_bits == 0) // 0 - 13 extra bits
+ md.base
+ else
+ md.base + try self.takeNBitsBuffered(md.extra_bits);
+}
+
+// Decode code length symbol to code length. Writes decoded length into
+// lens slice starting at position pos. Returns number of positions
+// advanced.
+fn dynamicCodeLength(self: *Decompress, code: u16, lens: []u4, pos: usize) !usize {
+ if (pos >= lens.len)
+ return error.InvalidDynamicBlockHeader;
+
+ switch (code) {
+ 0...15 => {
+ // Represent code lengths of 0 - 15
+ lens[pos] = @intCast(code);
+ return 1;
+ },
+ 16 => {
+ // Copy the previous code length 3 - 6 times.
+ // The next 2 bits indicate repeat length
+ const n: u8 = @as(u8, try self.takeBits(u2)) + 3;
+ if (pos == 0 or pos + n > lens.len)
+ return error.InvalidDynamicBlockHeader;
+ for (0..n) |i| {
+ lens[pos + i] = lens[pos + i - 1];
+ }
+ return n;
+ },
+ // Repeat a code length of 0 for 3 - 10 times. (3 bits of length)
+ 17 => return @as(u8, try self.takeBits(u3)) + 3,
+ // Repeat a code length of 0 for 11 - 138 times (7 bits of length)
+ 18 => return @as(u8, try self.takeBits(u7)) + 11,
+ else => return error.InvalidDynamicBlockHeader,
+ }
+}
+
+// Peek 15 bits from bits reader (maximum code len is 15 bits). Use
+// decoder to find symbol for that code. We then know how many bits is
+// used. Shift bit reader for that much bits, those bits are used. And
+// return symbol.
+fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
+ const sym = try decoder.find(try self.peekBitsReverseBuffered(u15));
+ try self.shiftBits(sym.code_bits);
+ return sym;
+}
+
+pub fn stream(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
+ const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
+ return readInner(d, w, limit) catch |err| switch (err) {
+ error.EndOfStream => return error.EndOfStream,
+ error.WriteFailed => return error.WriteFailed,
+ else => |e| {
+ // In the event of an error, state is unmodified so that it can be
+ // better used to diagnose the failure.
+ d.read_err = e;
+ return error.ReadFailed;
+ },
+ };
+}
+
+fn readInner(d: *Decompress, w: *Writer, limit: std.io.Limit) (Error || Reader.StreamError)!usize {
+ const in = d.input;
+ sw: switch (d.state) {
+ .protocol_header => switch (d.hasher.container()) {
+ .gzip => {
+ const Header = extern struct {
+ magic: u16 align(1),
+ method: u8,
+ flags: packed struct(u8) {
+ text: bool,
+ hcrc: bool,
+ extra: bool,
+ name: bool,
+ comment: bool,
+ reserved: u3,
+ },
+ mtime: u32 align(1),
+ xfl: u8,
+ os: u8,
+ };
+ const header = try in.takeStruct(Header, .little);
+ if (header.magic != 0x8b1f or header.method != 0x08)
+ return error.BadGzipHeader;
+ if (header.flags.extra) {
+ const extra_len = try in.takeInt(u16, .little);
+ try in.discardAll(extra_len);
+ }
+ if (header.flags.name) {
+ _ = try in.discardDelimiterInclusive(0);
+ }
+ if (header.flags.comment) {
+ _ = try in.discardDelimiterInclusive(0);
+ }
+ if (header.flags.hcrc) {
+ try in.discardAll(2);
+ }
+ continue :sw .block_header;
+ },
+ .zlib => {
+ const Header = extern struct {
+ cmf: packed struct(u8) {
+ cm: u4,
+ cinfo: u4,
+ },
+ flg: u8,
+ };
+ const header = try in.takeStruct(Header);
+ if (header.cmf.cm != 8 or header.cmf.cinfo > 7) return error.BadZlibHeader;
+ continue :sw .block_header;
+ },
+ .raw => continue :sw .block_header,
+ },
+ .block_header => {
+ d.final_block = (try d.takeBits(u1)) != 0;
+ const block_type = try d.takeBits(BlockType);
+ switch (block_type) {
+ .stored => {
+ d.alignBitsToByte(); // skip padding until byte boundary
+ // everything after this is byte aligned in stored block
+ const len = try in.takeInt(u16, .little);
+ const nlen = try in.takeInt(u16, .little);
+ if (len != ~nlen) return error.WrongStoredBlockNlen;
+ continue :sw .{ .stored_block = len };
+ },
+ .fixed => continue :sw .fixed_block,
+ .dynamic => {
+ const hlit: u16 = @as(u16, try d.takeBits(u5)) + 257; // number of ll code entries present - 257
+ const hdist: u16 = @as(u16, try d.takeBits(u5)) + 1; // number of distance code entries - 1
+ const hclen: u8 = @as(u8, try d.takeBits(u4)) + 4; // hclen + 4 code lengths are encoded
+
+ if (hlit > 286 or hdist > 30)
+ return error.InvalidDynamicBlockHeader;
+
+ // lengths for code lengths
+ var cl_lens = [_]u4{0} ** 19;
+ for (0..hclen) |i| {
+ cl_lens[flate.huffman.codegen_order[i]] = try d.takeBits(u3);
+ }
+ var cl_dec: CodegenDecoder = .{};
+ try cl_dec.generate(&cl_lens);
+
+ // decoded code lengths
+ var dec_lens = [_]u4{0} ** (286 + 30);
+ var pos: usize = 0;
+ while (pos < hlit + hdist) {
+ const sym = try cl_dec.find(try d.peekBitsReverse(u7));
+ try d.shiftBits(sym.code_bits);
+ pos += try d.dynamicCodeLength(sym.symbol, &dec_lens, pos);
+ }
+ if (pos > hlit + hdist) {
+ return error.InvalidDynamicBlockHeader;
+ }
+
+ // literal code lengths to literal decoder
+ try d.lit_dec.generate(dec_lens[0..hlit]);
+
+ // distance code lengths to distance decoder
+ try d.dst_dec.generate(dec_lens[hlit .. hlit + hdist]);
+
+ continue :sw .dynamic_block;
+ },
+ }
+ },
+ .stored_block => |remaining_len| {
+ const out = try w.writableSliceGreedyPreserve(flate.history_len, 1);
+ const limited_out = limit.min(.limited(remaining_len)).slice(out);
+ const n = try d.input.readVec(&.{limited_out});
+ if (remaining_len - n == 0) {
+ d.state = if (d.final_block) .protocol_footer else .block_header;
+ } else {
+ d.state = .{ .stored_block = @intCast(remaining_len - n) };
+ }
+ w.advance(n);
+ return n;
+ },
+ .fixed_block => {
+ const start = w.count;
+ while (@intFromEnum(limit) > w.count - start) {
+ const code = try d.readFixedCode();
+ switch (code) {
+ 0...255 => try w.writeBytePreserve(flate.history_len, @intCast(code)),
+ 256 => {
+ d.state = if (d.final_block) .protocol_footer else .block_header;
+ return w.count - start;
+ },
+ 257...285 => {
+ // Handles fixed block non literal (length) code.
+ // Length code is followed by 5 bits of distance code.
+ const length = try d.decodeLength(@intCast(code - 257));
+ const distance = try d.decodeDistance(try d.takeBitsReverseBuffered(u5));
+ try writeMatch(w, length, distance);
+ },
+ else => return error.InvalidCode,
+ }
+ }
+ d.state = .fixed_block;
+ return w.count - start;
+ },
+ .dynamic_block => {
+ // In larger archives most blocks are usually dynamic, so decompression
+ // performance depends on this logic.
+ const start = w.count;
+ while (@intFromEnum(limit) > w.count - start) {
+ const sym = try d.decodeSymbol(&d.lit_dec);
+
+ switch (sym.kind) {
+ .literal => try w.writeBytePreserve(flate.history_len, sym.symbol),
+ .match => {
+ // Decode match backreference
+ const length = try d.decodeLength(sym.symbol);
+ const dsm = try d.decodeSymbol(&d.dst_dec);
+ const distance = try d.decodeDistance(dsm.symbol);
+ try writeMatch(w, length, distance);
+ },
+ .end_of_block => {
+ d.state = if (d.final_block) .protocol_footer else .block_header;
+ return w.count - start;
+ },
+ }
+ }
+ d.state = .dynamic_block;
+ return w.count - start;
+ },
+ .protocol_footer => {
+ d.alignBitsToByte();
+ switch (d.hasher) {
+ .gzip => |*gzip| {
+ if (try in.takeInt(u32, .little) != gzip.crc.final()) return error.WrongGzipChecksum;
+ if (try in.takeInt(u32, .little) != gzip.count) return error.WrongGzipSize;
+ },
+ .zlib => |*zlib| {
+ const chksum: u32 = @byteSwap(zlib.final());
+ if (try in.takeInt(u32, .big) != chksum) return error.WrongZlibChecksum;
+ },
+ .raw => {},
+ }
+ d.state = .end;
+ return 0;
+ },
+ .end => return error.EndOfStream,
+ }
+}
+
+/// Write match (back-reference to the same data slice) starting at `distance`
+/// back from current write position, and `length` of bytes.
+fn writeMatch(bw: *Writer, length: u16, distance: u16) !void {
+ _ = bw;
+ _ = length;
+ _ = distance;
+ @panic("TODO");
+}
+
+fn takeBits(d: *Decompress, comptime T: type) !T {
+ _ = d;
+ @panic("TODO");
+}
+
+fn takeBitsReverseBuffered(d: *Decompress, comptime T: type) !T {
+ _ = d;
+ @panic("TODO");
+}
+
+fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 {
+ _ = d;
+ _ = n;
+ @panic("TODO");
+}
+
+fn peekBitsReverse(d: *Decompress, comptime T: type) !T {
+ _ = d;
+ @panic("TODO");
+}
+
+fn peekBitsReverseBuffered(d: *Decompress, comptime T: type) !T {
+ _ = d;
+ @panic("TODO");
+}
+
+fn alignBitsToByte(d: *Decompress) void {
+ _ = d;
+ @panic("TODO");
+}
+
+fn shiftBits(d: *Decompress, n: u6) !void {
+ _ = d;
+ _ = n;
+ @panic("TODO");
+}
+
+fn readFixedCode(d: *Decompress) !u16 {
+ _ = d;
+ @panic("TODO");
+}
+
+pub const Symbol = packed struct {
+ pub const Kind = enum(u2) {
+ literal,
+ end_of_block,
+ match,
+ };
+
+ symbol: u8 = 0, // symbol from alphabet
+ code_bits: u4 = 0, // number of bits in code 0-15
+ kind: Kind = .literal,
+
+ code: u16 = 0, // huffman code of the symbol
+ next: u16 = 0, // pointer to the next symbol in linked list
+ // it is safe to use 0 as null pointer, when sorted 0 has shortest code and fits into lookup
+
+ // Sorting less than function.
+ pub fn asc(_: void, a: Symbol, b: Symbol) bool {
+ if (a.code_bits == b.code_bits) {
+ if (a.kind == b.kind) {
+ return a.symbol < b.symbol;
+ }
+ return @intFromEnum(a.kind) < @intFromEnum(b.kind);
+ }
+ return a.code_bits < b.code_bits;
+ }
+};
+
+pub const LiteralDecoder = HuffmanDecoder(286, 15, 9);
+pub const DistanceDecoder = HuffmanDecoder(30, 15, 9);
+pub const CodegenDecoder = HuffmanDecoder(19, 7, 7);
+
+/// Creates huffman tree codes from list of code lengths (in `build`).
+///
+/// `find` then finds symbol for code bits. Code can be any length between 1 and
+/// 15 bits. When calling `find` we don't know how many bits will be used to
+/// find symbol. When symbol is returned it has code_bits field which defines
+/// how much we should advance in bit stream.
+///
+/// Lookup table is used to map 15 bit int to symbol. Same symbol is written
+/// many times in this table; 32K places for 286 (at most) symbols.
+/// Small lookup table is optimization for faster search.
+/// It is variation of the algorithm explained in [zlib](https://github.com/madler/zlib/blob/643e17b7498d12ab8d15565662880579692f769d/doc/algorithm.txt#L92)
+/// with difference that we here use statically allocated arrays.
+///
+fn HuffmanDecoder(
+ comptime alphabet_size: u16,
+ comptime max_code_bits: u4,
+ comptime lookup_bits: u4,
+) type {
+ const lookup_shift = max_code_bits - lookup_bits;
+
+ return struct {
+ // all symbols in alaphabet, sorted by code_len, symbol
+ symbols: [alphabet_size]Symbol = undefined,
+ // lookup table code -> symbol
+ lookup: [1 << lookup_bits]Symbol = undefined,
+
+ const Self = @This();
+
+ /// Generates symbols and lookup tables from list of code lens for each symbol.
+ pub fn generate(self: *Self, lens: []const u4) !void {
+ try checkCompleteness(lens);
+
+ // init alphabet with code_bits
+ for (self.symbols, 0..) |_, i| {
+ const cb: u4 = if (i < lens.len) lens[i] else 0;
+ self.symbols[i] = if (i < 256)
+ .{ .kind = .literal, .symbol = @intCast(i), .code_bits = cb }
+ else if (i == 256)
+ .{ .kind = .end_of_block, .symbol = 0xff, .code_bits = cb }
+ else
+ .{ .kind = .match, .symbol = @intCast(i - 257), .code_bits = cb };
+ }
+ std.sort.heap(Symbol, &self.symbols, {}, Symbol.asc);
+
+ // reset lookup table
+ for (0..self.lookup.len) |i| {
+ self.lookup[i] = .{};
+ }
+
+ // assign code to symbols
+ // reference: https://youtu.be/9_YEGLe33NA?list=PLU4IQLU9e_OrY8oASHx0u3IXAL9TOdidm&t=2639
+ var code: u16 = 0;
+ var idx: u16 = 0;
+ for (&self.symbols, 0..) |*sym, pos| {
+ if (sym.code_bits == 0) continue; // skip unused
+ sym.code = code;
+
+ const next_code = code + (@as(u16, 1) << (max_code_bits - sym.code_bits));
+ const next_idx = next_code >> lookup_shift;
+
+ if (next_idx > self.lookup.len or idx >= self.lookup.len) break;
+ if (sym.code_bits <= lookup_bits) {
+ // fill small lookup table
+ for (idx..next_idx) |j|
+ self.lookup[j] = sym.*;
+ } else {
+ // insert into linked table starting at root
+ const root = &self.lookup[idx];
+ const root_next = root.next;
+ root.next = @intCast(pos);
+ sym.next = root_next;
+ }
+
+ idx = next_idx;
+ code = next_code;
+ }
+ }
+
+ /// Given the list of code lengths check that it represents a canonical
+ /// Huffman code for n symbols.
+ ///
+ /// Reference: https://github.com/madler/zlib/blob/5c42a230b7b468dff011f444161c0145b5efae59/contrib/puff/puff.c#L340
+ fn checkCompleteness(lens: []const u4) !void {
+ if (alphabet_size == 286)
+ if (lens[256] == 0) return error.MissingEndOfBlockCode;
+
+ var count = [_]u16{0} ** (@as(usize, max_code_bits) + 1);
+ var max: usize = 0;
+ for (lens) |n| {
+ if (n == 0) continue;
+ if (n > max) max = n;
+ count[n] += 1;
+ }
+ if (max == 0) // empty tree
+ return;
+
+ // check for an over-subscribed or incomplete set of lengths
+ var left: usize = 1; // one possible code of zero length
+ for (1..count.len) |len| {
+ left <<= 1; // one more bit, double codes left
+ if (count[len] > left)
+ return error.OversubscribedHuffmanTree;
+ left -= count[len]; // deduct count from possible codes
+ }
+ if (left > 0) { // left > 0 means incomplete
+ // incomplete code ok only for single length 1 code
+ if (max_code_bits > 7 and max == count[0] + count[1]) return;
+ return error.IncompleteHuffmanTree;
+ }
+ }
+
+ /// Finds symbol for lookup table code.
+ pub fn find(self: *Self, code: u16) !Symbol {
+ // try to find in lookup table
+ const idx = code >> lookup_shift;
+ const sym = self.lookup[idx];
+ if (sym.code_bits != 0) return sym;
+ // if not use linked list of symbols with same prefix
+ return self.findLinked(code, sym.next);
+ }
+
+ inline fn findLinked(self: *Self, code: u16, start: u16) !Symbol {
+ var pos = start;
+ while (pos > 0) {
+ const sym = self.symbols[pos];
+ const shift = max_code_bits - sym.code_bits;
+ // compare code_bits number of upper bits
+ if ((code ^ sym.code) >> shift == 0) return sym;
+ pos = sym.next;
+ }
+ return error.InvalidCode;
+ }
+ };
+}
+
+test "init/find" {
+ // example data from: https://youtu.be/SJPvNi4HrWQ?t=8423
+ const code_lens = [_]u4{ 4, 3, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 2 };
+ var h: CodegenDecoder = .{};
+ try h.generate(&code_lens);
+
+ const expected = [_]struct {
+ sym: Symbol,
+ code: u16,
+ }{
+ .{
+ .code = 0b00_00000,
+ .sym = .{ .symbol = 3, .code_bits = 2 },
+ },
+ .{
+ .code = 0b01_00000,
+ .sym = .{ .symbol = 18, .code_bits = 2 },
+ },
+ .{
+ .code = 0b100_0000,
+ .sym = .{ .symbol = 1, .code_bits = 3 },
+ },
+ .{
+ .code = 0b101_0000,
+ .sym = .{ .symbol = 4, .code_bits = 3 },
+ },
+ .{
+ .code = 0b110_0000,
+ .sym = .{ .symbol = 17, .code_bits = 3 },
+ },
+ .{
+ .code = 0b1110_000,
+ .sym = .{ .symbol = 0, .code_bits = 4 },
+ },
+ .{
+ .code = 0b1111_000,
+ .sym = .{ .symbol = 16, .code_bits = 4 },
+ },
+ };
+
+ // unused symbols
+ for (0..12) |i| {
+ try testing.expectEqual(0, h.symbols[i].code_bits);
+ }
+ // used, from index 12
+ for (expected, 12..) |e, i| {
+ try testing.expectEqual(e.sym.symbol, h.symbols[i].symbol);
+ try testing.expectEqual(e.sym.code_bits, h.symbols[i].code_bits);
+ const sym_from_code = try h.find(e.code);
+ try testing.expectEqual(e.sym.symbol, sym_from_code.symbol);
+ }
+
+ // All possible codes for each symbol.
+ // Lookup table has 126 elements, to cover all possible 7 bit codes.
+ for (0b0000_000..0b0100_000) |c| // 0..32 (32)
+ try testing.expectEqual(3, (try h.find(@intCast(c))).symbol);
+
+ for (0b0100_000..0b1000_000) |c| // 32..64 (32)
+ try testing.expectEqual(18, (try h.find(@intCast(c))).symbol);
+
+ for (0b1000_000..0b1010_000) |c| // 64..80 (16)
+ try testing.expectEqual(1, (try h.find(@intCast(c))).symbol);
+
+ for (0b1010_000..0b1100_000) |c| // 80..96 (16)
+ try testing.expectEqual(4, (try h.find(@intCast(c))).symbol);
+
+ for (0b1100_000..0b1110_000) |c| // 96..112 (16)
+ try testing.expectEqual(17, (try h.find(@intCast(c))).symbol);
+
+ for (0b1110_000..0b1111_000) |c| // 112..120 (8)
+ try testing.expectEqual(0, (try h.find(@intCast(c))).symbol);
+
+ for (0b1111_000..0b1_0000_000) |c| // 120...128 (8)
+ try testing.expectEqual(16, (try h.find(@intCast(c))).symbol);
+}
+
+test "encode/decode literals" {
+ const LiteralEncoder = std.compress.flate.Compress.LiteralEncoder;
+
+ for (1..286) |j| { // for all different number of codes
+ var enc: LiteralEncoder = .{};
+ // create frequencies
+ var freq = [_]u16{0} ** 286;
+ freq[256] = 1; // ensure we have end of block code
+ for (&freq, 1..) |*f, i| {
+ if (i % j == 0)
+ f.* = @intCast(i);
+ }
+
+ // encoder from frequencies
+ enc.generate(&freq, 15);
+
+ // get code_lens from encoder
+ var code_lens = [_]u4{0} ** 286;
+ for (code_lens, 0..) |_, i| {
+ code_lens[i] = @intCast(enc.codes[i].len);
+ }
+ // generate decoder from code lens
+ var dec: LiteralDecoder = .{};
+ try dec.generate(&code_lens);
+
+ // expect decoder code to match original encoder code
+ for (dec.symbols) |s| {
+ if (s.code_bits == 0) continue;
+ const c_code: u16 = @bitReverse(@as(u15, @intCast(s.code)));
+ const symbol: u16 = switch (s.kind) {
+ .literal => s.symbol,
+ .end_of_block => 256,
+ .match => @as(u16, s.symbol) + 257,
+ };
+
+ const c = enc.codes[symbol];
+ try testing.expect(c.code == c_code);
+ }
+
+ // find each symbol by code
+ for (enc.codes) |c| {
+ if (c.len == 0) continue;
+
+ const s_code: u15 = @bitReverse(@as(u15, @intCast(c.code)));
+ const s = try dec.find(s_code);
+ try testing.expect(s.code == s_code);
+ try testing.expect(s.code_bits == c.len);
+ }
+ }
+}
+
+test "decompress" {
+ const cases = [_]struct {
+ in: []const u8,
+ out: []const u8,
+ }{
+ // non compressed block (type 0)
+ .{
+ .in = &[_]u8{
+ 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
+ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
+ },
+ .out = "Hello world\n",
+ },
+ // fixed code block (type 1)
+ .{
+ .in = &[_]u8{
+ 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1
+ 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00,
+ },
+ .out = "Hello world\n",
+ },
+ // dynamic block (type 2)
+ .{
+ .in = &[_]u8{
+ 0x3d, 0xc6, 0x39, 0x11, 0x00, 0x00, 0x0c, 0x02, // deflate data block type 2
+ 0x30, 0x2b, 0xb5, 0x52, 0x1e, 0xff, 0x96, 0x38,
+ 0x16, 0x96, 0x5c, 0x1e, 0x94, 0xcb, 0x6d, 0x01,
+ },
+ .out = "ABCDEABCD ABCDEABCD",
+ },
+ };
+ for (cases) |c| {
+ var fb: Reader = .fixed(c.in);
+ var aw: Writer.Allocating = .init(testing.allocator);
+ defer aw.deinit();
+
+ var decompress: Decompress = .init(&fb, .raw, &.{});
+ const r = &decompress.reader;
+ _ = try r.streamRemaining(&aw.writer);
+ try testing.expectEqualStrings(c.out, aw.getWritten());
+ }
+}
+
+test "gzip decompress" {
+ const cases = [_]struct {
+ in: []const u8,
+ out: []const u8,
+ }{
+ // non compressed block (type 0)
+ .{
+ .in = &[_]u8{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // gzip header (10 bytes)
+ 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
+ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
+ 0xd5, 0xe0, 0x39, 0xb7, // gzip footer: checksum
+ 0x0c, 0x00, 0x00, 0x00, // gzip footer: size
+ },
+ .out = "Hello world\n",
+ },
+ // fixed code block (type 1)
+ .{
+ .in = &[_]u8{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, // gzip header (10 bytes)
+ 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1
+ 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00,
+ 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00, // gzip footer (chksum, len)
+ },
+ .out = "Hello world\n",
+ },
+ // dynamic block (type 2)
+ .{
+ .in = &[_]u8{
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // gzip header (10 bytes)
+ 0x3d, 0xc6, 0x39, 0x11, 0x00, 0x00, 0x0c, 0x02, // deflate data block type 2
+ 0x30, 0x2b, 0xb5, 0x52, 0x1e, 0xff, 0x96, 0x38,
+ 0x16, 0x96, 0x5c, 0x1e, 0x94, 0xcb, 0x6d, 0x01,
+ 0x17, 0x1c, 0x39, 0xb4, 0x13, 0x00, 0x00, 0x00, // gzip footer (chksum, len)
+ },
+ .out = "ABCDEABCD ABCDEABCD",
+ },
+ // gzip header with name
+ .{
+ .in = &[_]u8{
+ 0x1f, 0x8b, 0x08, 0x08, 0xe5, 0x70, 0xb1, 0x65, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e,
+ 0x74, 0x78, 0x74, 0x00, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1,
+ 0x02, 0x00, 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00,
+ },
+ .out = "Hello world\n",
+ },
+ };
+ for (cases) |c| {
+ var fb: Reader = .fixed(c.in);
+ var aw: Writer.Allocating = .init(testing.allocator);
+ defer aw.deinit();
+
+ var decompress: Decompress = .init(&fb, .gzip, &.{});
+ const r = &decompress.reader;
+ _ = try r.streamRemaining(&aw.writer);
+ try testing.expectEqualStrings(c.out, aw.getWritten());
+ }
+}
+
+test "zlib decompress" {
+ const cases = [_]struct {
+ in: []const u8,
+ out: []const u8,
+ }{
+ // non compressed block (type 0)
+ .{
+ .in = &[_]u8{
+ 0x78, 0b10_0_11100, // zlib header (2 bytes)
+ 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
+ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
+ 0x1c, 0xf2, 0x04, 0x47, // zlib footer: checksum
+ },
+ .out = "Hello world\n",
+ },
+ };
+ for (cases) |c| {
+ var fb: Reader = .fixed(c.in);
+ var aw: Writer.Allocating = .init(testing.allocator);
+ defer aw.deinit();
+
+ var decompress: Decompress = .init(&fb, .zlib, &.{});
+ const r = &decompress.reader;
+ _ = try r.streamRemaining(&aw.writer);
+ try testing.expectEqualStrings(c.out, aw.getWritten());
+ }
+}
+
+test "fuzzing tests" {
+ const cases = [_]struct {
+ input: []const u8,
+ out: []const u8 = "",
+ err: ?anyerror = null,
+ }{
+ .{ .input = "deflate-stream", .out = @embedFile("testdata/fuzz/deflate-stream.expect") }, // 0
+ .{ .input = "empty-distance-alphabet01" },
+ .{ .input = "empty-distance-alphabet02" },
+ .{ .input = "end-of-stream", .err = error.EndOfStream },
+ .{ .input = "invalid-distance", .err = error.InvalidMatch },
+ .{ .input = "invalid-tree01", .err = error.IncompleteHuffmanTree }, // 5
+ .{ .input = "invalid-tree02", .err = error.IncompleteHuffmanTree },
+ .{ .input = "invalid-tree03", .err = error.IncompleteHuffmanTree },
+ .{ .input = "lengths-overflow", .err = error.InvalidDynamicBlockHeader },
+ .{ .input = "out-of-codes", .err = error.InvalidCode },
+ .{ .input = "puff01", .err = error.WrongStoredBlockNlen }, // 10
+ .{ .input = "puff02", .err = error.EndOfStream },
+ .{ .input = "puff03", .out = &[_]u8{0xa} },
+ .{ .input = "puff04", .err = error.InvalidCode },
+ .{ .input = "puff05", .err = error.EndOfStream },
+ .{ .input = "puff06", .err = error.EndOfStream },
+ .{ .input = "puff08", .err = error.InvalidCode },
+ .{ .input = "puff09", .out = "P" },
+ .{ .input = "puff10", .err = error.InvalidCode },
+ .{ .input = "puff11", .err = error.InvalidMatch },
+ .{ .input = "puff12", .err = error.InvalidDynamicBlockHeader }, // 20
+ .{ .input = "puff13", .err = error.IncompleteHuffmanTree },
+ .{ .input = "puff14", .err = error.EndOfStream },
+ .{ .input = "puff15", .err = error.IncompleteHuffmanTree },
+ .{ .input = "puff16", .err = error.InvalidDynamicBlockHeader },
+ .{ .input = "puff17", .err = error.MissingEndOfBlockCode }, // 25
+ .{ .input = "fuzz1", .err = error.InvalidDynamicBlockHeader },
+ .{ .input = "fuzz2", .err = error.InvalidDynamicBlockHeader },
+ .{ .input = "fuzz3", .err = error.InvalidMatch },
+ .{ .input = "fuzz4", .err = error.OversubscribedHuffmanTree },
+ .{ .input = "puff18", .err = error.OversubscribedHuffmanTree }, // 30
+ .{ .input = "puff19", .err = error.OversubscribedHuffmanTree },
+ .{ .input = "puff20", .err = error.OversubscribedHuffmanTree },
+ .{ .input = "puff21", .err = error.OversubscribedHuffmanTree },
+ .{ .input = "puff22", .err = error.OversubscribedHuffmanTree },
+ .{ .input = "puff23", .err = error.OversubscribedHuffmanTree }, // 35
+ .{ .input = "puff24", .err = error.IncompleteHuffmanTree },
+ .{ .input = "puff25", .err = error.OversubscribedHuffmanTree },
+ .{ .input = "puff26", .err = error.InvalidDynamicBlockHeader },
+ .{ .input = "puff27", .err = error.InvalidDynamicBlockHeader },
+ };
+
+ inline for (cases, 0..) |c, case_no| {
+ var in: Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
+ var aw: Writer.Allocating = .init(testing.allocator);
+ defer aw.deinit();
+ errdefer std.debug.print("test case failed {}\n", .{case_no});
+
+ var decompress: Decompress = .init(&in, .raw, &.{});
+ const r = &decompress.reader;
+ if (c.err) |expected_err| {
+ try testing.expectError(error.ReadFailed, r.streamRemaining(&aw.writer));
+ try testing.expectError(expected_err, decompress.read_err.?);
+ } else {
+ _ = try r.streamRemaining(&aw.writer);
+ try testing.expectEqualStrings(c.out, aw.getWritten());
+ }
+ }
+}
+
+test "bug 18966" {
+ const input = @embedFile("testdata/fuzz/bug_18966.input");
+ const expect = @embedFile("testdata/fuzz/bug_18966.expect");
+
+ var in: Reader = .fixed(input);
+ var aw: Writer.Allocating = .init(testing.allocator);
+ defer aw.deinit();
+
+ var decompress: Decompress = .init(&in, .gzip, &.{});
+ const r = &decompress.reader;
+ _ = try r.streamRemaining(&aw.writer);
+ try testing.expectEqualStrings(expect, aw.getWritten());
+}
+
+test "reading into empty buffer" {
+ // Inspired by https://github.com/ziglang/zig/issues/19895
+ const input = &[_]u8{
+ 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
+ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
+ };
+ var in: Reader = .fixed(input);
+ var decomp: Decompress = .init(&in, .raw, &.{});
+ const r = &decomp.reader;
+ var buf: [0]u8 = undefined;
+ try testing.expectEqual(0, try r.readVec(&.{&buf}));
+}
diff --git a/lib/std/compress/flate/Lookup.zig b/lib/std/compress/flate/Lookup.zig
index 90d0341bca602d3c1d52407b285928ca6850213b..722e175c8ade937ed5b3246f7bf3ac0bc57ac470 100644
--- a/lib/std/compress/flate/Lookup.zig
+++ b/lib/std/compress/flate/Lookup.zig
@@ -5,22 +5,22 @@
const std = @import("std");
const testing = std.testing;
const expect = testing.expect;
-const consts = @import("consts.zig");
+const flate = @import("../flate.zig");
-const Self = @This();
+const Lookup = @This();
const prime4 = 0x9E3779B1; // 4 bytes prime number 2654435761
-const chain_len = 2 * consts.history.len;
+const chain_len = 2 * flate.history_len;
// Maps hash => first position
-head: [consts.lookup.len]u16 = [_]u16{0} ** consts.lookup.len,
+head: [flate.lookup.len]u16 = [_]u16{0} ** flate.lookup.len,
// Maps position => previous positions for the same hash value
chain: [chain_len]u16 = [_]u16{0} ** (chain_len),
// Calculates hash of the 4 bytes from data.
// Inserts `pos` position of that hash in the lookup tables.
// Returns previous location with the same hash value.
-pub fn add(self: *Self, data: []const u8, pos: u16) u16 {
+pub fn add(self: *Lookup, data: []const u8, pos: u16) u16 {
if (data.len < 4) return 0;
const h = hash(data[0..4]);
return self.set(h, pos);
@@ -28,11 +28,11 @@ pub fn add(self: *Self, data: []const u8, pos: u16) u16 {
// Returns previous location with the same hash value given the current
// position.
-pub fn prev(self: *Self, pos: u16) u16 {
+pub fn prev(self: *Lookup, pos: u16) u16 {
return self.chain[pos];
}
-fn set(self: *Self, h: u32, pos: u16) u16 {
+fn set(self: *Lookup, h: u32, pos: u16) u16 {
const p = self.head[h];
self.head[h] = pos;
self.chain[pos] = p;
@@ -40,7 +40,7 @@ fn set(self: *Self, h: u32, pos: u16) u16 {
}
// Slide all positions in head and chain for `n`
-pub fn slide(self: *Self, n: u16) void {
+pub fn slide(self: *Lookup, n: u16) void {
for (&self.head) |*v| {
v.* -|= n;
}
@@ -52,8 +52,8 @@ pub fn slide(self: *Self, n: u16) void {
// Add `len` 4 bytes hashes from `data` into lookup.
// Position of the first byte is `pos`.
-pub fn bulkAdd(self: *Self, data: []const u8, len: u16, pos: u16) void {
- if (len == 0 or data.len < consts.match.min_length) {
+pub fn bulkAdd(self: *Lookup, data: []const u8, len: u16, pos: u16) void {
+ if (len == 0 or data.len < flate.match.min_length) {
return;
}
var hb =
@@ -80,7 +80,7 @@ fn hash(b: *const [4]u8) u32 {
}
fn hashu(v: u32) u32 {
- return @intCast((v *% prime4) >> consts.lookup.shift);
+ return @intCast((v *% prime4) >> flate.lookup.shift);
}
test add {
@@ -91,7 +91,7 @@ test add {
0x01, 0x02, 0x03,
};
- var h: Self = .{};
+ var h: Lookup = .{};
for (data, 0..) |_, i| {
const p = h.add(data[i..], @intCast(i));
if (i >= 8 and i < 24) {
@@ -101,7 +101,7 @@ test add {
}
}
- const v = Self.hash(data[2 .. 2 + 4]);
+ const v = Lookup.hash(data[2 .. 2 + 4]);
try expect(h.head[v] == 2 + 16);
try expect(h.chain[2 + 16] == 2 + 8);
try expect(h.chain[2 + 8] == 2);
@@ -111,13 +111,13 @@ test bulkAdd {
const data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
// one by one
- var h: Self = .{};
+ var h: Lookup = .{};
for (data, 0..) |_, i| {
_ = h.add(data[i..], @intCast(i));
}
// in bulk
- var bh: Self = .{};
+ var bh: Lookup = .{};
bh.bulkAdd(data, data.len, 0);
try testing.expectEqualSlices(u16, &h.head, &bh.head);
diff --git a/lib/std/compress/flate/SlidingWindow.zig b/lib/std/compress/flate/SlidingWindow.zig
deleted file mode 100644
index ece907c32fab409e2df9362ec1dd6bf4c45d7926..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/SlidingWindow.zig
+++ /dev/null
@@ -1,160 +0,0 @@
-//! Used in deflate (compression), holds uncompressed data form which Tokens are
-//! produces. In combination with Lookup it is used to find matches in history data.
-//!
-const std = @import("std");
-const consts = @import("consts.zig");
-
-const expect = testing.expect;
-const assert = std.debug.assert;
-const testing = std.testing;
-
-const hist_len = consts.history.len;
-const buffer_len = 2 * hist_len;
-const min_lookahead = consts.match.min_length + consts.match.max_length;
-const max_rp = buffer_len - min_lookahead;
-
-const Self = @This();
-
-buffer: [buffer_len]u8 = undefined,
-wp: usize = 0, // write position
-rp: usize = 0, // read position
-fp: isize = 0, // last flush position, tokens are build from fp..rp
-
-/// Returns number of bytes written, or 0 if buffer is full and need to slide.
-pub fn write(self: *Self, buf: []const u8) usize {
- if (self.rp >= max_rp) return 0; // need to slide
-
- const n = @min(buf.len, buffer_len - self.wp);
- @memcpy(self.buffer[self.wp .. self.wp + n], buf[0..n]);
- self.wp += n;
- return n;
-}
-
-/// Slide buffer for hist_len.
-/// Drops old history, preserves between hist_len and hist_len - min_lookahead.
-/// Returns number of bytes removed.
-pub fn slide(self: *Self) u16 {
- assert(self.rp >= max_rp and self.wp >= self.rp);
- const n = self.wp - hist_len;
- @memcpy(self.buffer[0..n], self.buffer[hist_len..self.wp]);
- self.rp -= hist_len;
- self.wp -= hist_len;
- self.fp -= hist_len;
- return @intCast(n);
-}
-
-/// Data from the current position (read position). Those part of the buffer is
-/// not converted to tokens yet.
-fn lookahead(self: *Self) []const u8 {
- assert(self.wp >= self.rp);
- return self.buffer[self.rp..self.wp];
-}
-
-/// Returns part of the lookahead buffer. If should_flush is set no lookahead is
-/// preserved otherwise preserves enough data for the longest match. Returns
-/// null if there is not enough data.
-pub fn activeLookahead(self: *Self, should_flush: bool) ?[]const u8 {
- const min: usize = if (should_flush) 0 else min_lookahead;
- const lh = self.lookahead();
- return if (lh.len > min) lh else null;
-}
-
-/// Advances read position, shrinks lookahead.
-pub fn advance(self: *Self, n: u16) void {
- assert(self.wp >= self.rp + n);
- self.rp += n;
-}
-
-/// Returns writable part of the buffer, where new uncompressed data can be
-/// written.
-pub fn writable(self: *Self) []u8 {
- return self.buffer[self.wp..];
-}
-
-/// Notification of what part of writable buffer is filled with data.
-pub fn written(self: *Self, n: usize) void {
- self.wp += n;
-}
-
-/// Finds match length between previous and current position.
-/// Used in hot path!
-pub fn match(self: *Self, prev_pos: u16, curr_pos: u16, min_len: u16) u16 {
- const max_len: usize = @min(self.wp - curr_pos, consts.match.max_length);
- // lookahead buffers from previous and current positions
- const prev_lh = self.buffer[prev_pos..][0..max_len];
- const curr_lh = self.buffer[curr_pos..][0..max_len];
-
- // If we already have match (min_len > 0),
- // test the first byte above previous len a[min_len] != b[min_len]
- // and then all the bytes from that position to zero.
- // That is likely positions to find difference than looping from first bytes.
- var i: usize = min_len;
- if (i > 0) {
- if (max_len <= i) return 0;
- while (true) {
- if (prev_lh[i] != curr_lh[i]) return 0;
- if (i == 0) break;
- i -= 1;
- }
- i = min_len;
- }
- while (i < max_len) : (i += 1)
- if (prev_lh[i] != curr_lh[i]) break;
- return if (i >= consts.match.min_length) @intCast(i) else 0;
-}
-
-/// Current position of non-compressed data. Data before rp are already converted
-/// to tokens.
-pub fn pos(self: *Self) u16 {
- return @intCast(self.rp);
-}
-
-/// Notification that token list is cleared.
-pub fn flush(self: *Self) void {
- self.fp = @intCast(self.rp);
-}
-
-/// Part of the buffer since last flush or null if there was slide in between (so
-/// fp becomes negative).
-pub fn tokensBuffer(self: *Self) ?[]const u8 {
- assert(self.fp <= self.rp);
- if (self.fp < 0) return null;
- return self.buffer[@intCast(self.fp)..self.rp];
-}
-
-test match {
- const data = "Blah blah blah blah blah!";
- var win: Self = .{};
- try expect(win.write(data) == data.len);
- try expect(win.wp == data.len);
- try expect(win.rp == 0);
-
- // length between l symbols
- try expect(win.match(1, 6, 0) == 18);
- try expect(win.match(1, 11, 0) == 13);
- try expect(win.match(1, 16, 0) == 8);
- try expect(win.match(1, 21, 0) == 0);
-
- // position 15 = "blah blah!"
- // position 20 = "blah!"
- try expect(win.match(15, 20, 0) == 4);
- try expect(win.match(15, 20, 3) == 4);
- try expect(win.match(15, 20, 4) == 0);
-}
-
-test slide {
- var win: Self = .{};
- win.wp = Self.buffer_len - 11;
- win.rp = Self.buffer_len - 111;
- win.buffer[win.rp] = 0xab;
- try expect(win.lookahead().len == 100);
- try expect(win.tokensBuffer().?.len == win.rp);
-
- const n = win.slide();
- try expect(n == 32757);
- try expect(win.buffer[win.rp] == 0xab);
- try expect(win.rp == Self.hist_len - 111);
- try expect(win.wp == Self.hist_len - 11);
- try expect(win.lookahead().len == 100);
- try expect(win.tokensBuffer() == null);
-}
diff --git a/lib/std/compress/flate/Token.zig b/lib/std/compress/flate/Token.zig
index a9641f6adc886a8a9ffdefbdf6f319f696476259..293a786cef7e62e9640471f76da036fb8ee1f7ca 100644
--- a/lib/std/compress/flate/Token.zig
+++ b/lib/std/compress/flate/Token.zig
@@ -6,7 +6,7 @@ const std = @import("std");
const assert = std.debug.assert;
const print = std.debug.print;
const expect = std.testing.expect;
-const consts = @import("consts.zig").match;
+const match = std.compress.flate.match;
const Token = @This();
@@ -26,11 +26,11 @@ pub fn literal(t: Token) u8 {
}
pub fn distance(t: Token) u16 {
- return @as(u16, t.dist) + consts.min_distance;
+ return @as(u16, t.dist) + match.min_distance;
}
pub fn length(t: Token) u16 {
- return @as(u16, t.len_lit) + consts.base_length;
+ return @as(u16, t.len_lit) + match.base_length;
}
pub fn initLiteral(lit: u8) Token {
@@ -40,12 +40,12 @@ pub fn initLiteral(lit: u8) Token {
// distance range 1 - 32768, stored in dist as 0 - 32767 (u15)
// length range 3 - 258, stored in len_lit as 0 - 255 (u8)
pub fn initMatch(dist: u16, len: u16) Token {
- assert(len >= consts.min_length and len <= consts.max_length);
- assert(dist >= consts.min_distance and dist <= consts.max_distance);
+ assert(len >= match.min_length and len <= match.max_length);
+ assert(dist >= match.min_distance and dist <= match.max_distance);
return .{
.kind = .match,
- .dist = @intCast(dist - consts.min_distance),
- .len_lit = @intCast(len - consts.base_length),
+ .dist = @intCast(dist - match.min_distance),
+ .len_lit = @intCast(len - match.base_length),
};
}
diff --git a/lib/std/compress/flate/bit_reader.zig b/lib/std/compress/flate/bit_reader.zig
deleted file mode 100644
index 1e41f081c1c38e48eb87112ffef71732624583c9..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/bit_reader.zig
+++ /dev/null
@@ -1,422 +0,0 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const testing = std.testing;
-
-pub fn bitReader(comptime T: type, reader: anytype) BitReader(T, @TypeOf(reader)) {
- return BitReader(T, @TypeOf(reader)).init(reader);
-}
-
-pub fn BitReader64(comptime ReaderType: type) type {
- return BitReader(u64, ReaderType);
-}
-
-pub fn BitReader32(comptime ReaderType: type) type {
- return BitReader(u32, ReaderType);
-}
-
-/// Bit reader used during inflate (decompression). Has internal buffer of 64
-/// bits which shifts right after bits are consumed. Uses forward_reader to fill
-/// that internal buffer when needed.
-///
-/// readF is the core function. Supports few different ways of getting bits
-/// controlled by flags. In hot path we try to avoid checking whether we need to
-/// fill buffer from forward_reader by calling fill in advance and readF with
-/// buffered flag set.
-///
-pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
- assert(T == u32 or T == u64);
- const t_bytes: usize = @sizeOf(T);
- const Tshift = if (T == u64) u6 else u5;
-
- return struct {
- // Underlying reader used for filling internal bits buffer
- forward_reader: ReaderType = undefined,
- // Internal buffer of 64 bits
- bits: T = 0,
- // Number of bits in the buffer
- nbits: u32 = 0,
-
- const Self = @This();
-
- pub const Error = ReaderType.Error || error{EndOfStream};
-
- pub fn init(rdr: ReaderType) Self {
- var self = Self{ .forward_reader = rdr };
- self.fill(1) catch {};
- return self;
- }
-
- /// Try to have `nice` bits are available in buffer. Reads from
- /// forward reader if there is no `nice` bits in buffer. Returns error
- /// if end of forward stream is reached and internal buffer is empty.
- /// It will not error if less than `nice` bits are in buffer, only when
- /// all bits are exhausted. During inflate we usually know what is the
- /// maximum bits for the next step but usually that step will need less
- /// bits to decode. So `nice` is not hard limit, it will just try to have
- /// that number of bits available. If end of forward stream is reached
- /// it may be some extra zero bits in buffer.
- pub inline fn fill(self: *Self, nice: u6) !void {
- if (self.nbits >= nice and nice != 0) {
- return; // We have enough bits
- }
- // Read more bits from forward reader
-
- // Number of empty bytes in bits, round nbits to whole bytes.
- const empty_bytes =
- @as(u8, if (self.nbits & 0x7 == 0) t_bytes else t_bytes - 1) - // 8 for 8, 16, 24..., 7 otherwise
- (self.nbits >> 3); // 0 for 0-7, 1 for 8-16, ... same as / 8
-
- var buf: [t_bytes]u8 = [_]u8{0} ** t_bytes;
- const bytes_read = self.forward_reader.readAll(buf[0..empty_bytes]) catch 0;
- if (bytes_read > 0) {
- const u: T = std.mem.readInt(T, buf[0..t_bytes], .little);
- self.bits |= u << @as(Tshift, @intCast(self.nbits));
- self.nbits += 8 * @as(u8, @intCast(bytes_read));
- return;
- }
-
- if (self.nbits == 0)
- return error.EndOfStream;
- }
-
- /// Read exactly buf.len bytes into buf.
- pub fn readAll(self: *Self, buf: []u8) !void {
- assert(self.alignBits() == 0); // internal bits must be at byte boundary
-
- // First read from internal bits buffer.
- var n: usize = 0;
- while (self.nbits > 0 and n < buf.len) {
- buf[n] = try self.readF(u8, flag.buffered);
- n += 1;
- }
- // Then use forward reader for all other bytes.
- try self.forward_reader.readNoEof(buf[n..]);
- }
-
- pub const flag = struct {
- pub const peek: u3 = 0b001; // dont advance internal buffer, just get bits, leave them in buffer
- pub const buffered: u3 = 0b010; // assume that there is no need to fill, fill should be called before
- pub const reverse: u3 = 0b100; // bit reverse read bits
- };
-
- /// Alias for readF(U, 0).
- pub fn read(self: *Self, comptime U: type) !U {
- return self.readF(U, 0);
- }
-
- /// Alias for readF with flag.peak set.
- pub inline fn peekF(self: *Self, comptime U: type, comptime how: u3) !U {
- return self.readF(U, how | flag.peek);
- }
-
- /// Read with flags provided.
- pub fn readF(self: *Self, comptime U: type, comptime how: u3) !U {
- if (U == T) {
- assert(how == 0);
- assert(self.alignBits() == 0);
- try self.fill(@bitSizeOf(T));
- if (self.nbits != @bitSizeOf(T)) return error.EndOfStream;
- const v = self.bits;
- self.nbits = 0;
- self.bits = 0;
- return v;
- }
- const n: Tshift = @bitSizeOf(U);
- switch (how) {
- 0 => { // `normal` read
- try self.fill(n); // ensure that there are n bits in the buffer
- const u: U = @truncate(self.bits); // get n bits
- try self.shift(n); // advance buffer for n
- return u;
- },
- (flag.peek) => { // no shift, leave bits in the buffer
- try self.fill(n);
- return @truncate(self.bits);
- },
- flag.buffered => { // no fill, assume that buffer has enough bits
- const u: U = @truncate(self.bits);
- try self.shift(n);
- return u;
- },
- (flag.reverse) => { // same as 0 with bit reverse
- try self.fill(n);
- const u: U = @truncate(self.bits);
- try self.shift(n);
- return @bitReverse(u);
- },
- (flag.peek | flag.reverse) => {
- try self.fill(n);
- return @bitReverse(@as(U, @truncate(self.bits)));
- },
- (flag.buffered | flag.reverse) => {
- const u: U = @truncate(self.bits);
- try self.shift(n);
- return @bitReverse(u);
- },
- (flag.peek | flag.buffered) => {
- return @truncate(self.bits);
- },
- (flag.peek | flag.buffered | flag.reverse) => {
- return @bitReverse(@as(U, @truncate(self.bits)));
- },
- }
- }
-
- /// Read n number of bits.
- /// Only buffered flag can be used in how.
- pub fn readN(self: *Self, n: u4, comptime how: u3) !u16 {
- switch (how) {
- 0 => {
- try self.fill(n);
- },
- flag.buffered => {},
- else => unreachable,
- }
- const mask: u16 = (@as(u16, 1) << n) - 1;
- const u: u16 = @as(u16, @truncate(self.bits)) & mask;
- try self.shift(n);
- return u;
- }
-
- /// Advance buffer for n bits.
- pub fn shift(self: *Self, n: Tshift) !void {
- if (n > self.nbits) return error.EndOfStream;
- self.bits >>= n;
- self.nbits -= n;
- }
-
- /// Skip n bytes.
- pub fn skipBytes(self: *Self, n: u16) !void {
- for (0..n) |_| {
- try self.fill(8);
- try self.shift(8);
- }
- }
-
- // Number of bits to align stream to the byte boundary.
- fn alignBits(self: *Self) u3 {
- return @intCast(self.nbits & 0x7);
- }
-
- /// Align stream to the byte boundary.
- pub fn alignToByte(self: *Self) void {
- const ab = self.alignBits();
- if (ab > 0) self.shift(ab) catch unreachable;
- }
-
- /// Skip zero terminated string.
- pub fn skipStringZ(self: *Self) !void {
- while (true) {
- if (try self.readF(u8, 0) == 0) break;
- }
- }
-
- /// Read deflate fixed fixed code.
- /// Reads first 7 bits, and then maybe 1 or 2 more to get full 7,8 or 9 bit code.
- /// ref: https://datatracker.ietf.org/doc/html/rfc1951#page-12
- /// Lit Value Bits Codes
- /// --------- ---- -----
- /// 0 - 143 8 00110000 through
- /// 10111111
- /// 144 - 255 9 110010000 through
- /// 111111111
- /// 256 - 279 7 0000000 through
- /// 0010111
- /// 280 - 287 8 11000000 through
- /// 11000111
- pub fn readFixedCode(self: *Self) !u16 {
- try self.fill(7 + 2);
- const code7 = try self.readF(u7, flag.buffered | flag.reverse);
- if (code7 <= 0b0010_111) { // 7 bits, 256-279, codes 0000_000 - 0010_111
- return @as(u16, code7) + 256;
- } else if (code7 <= 0b1011_111) { // 8 bits, 0-143, codes 0011_0000 through 1011_1111
- return (@as(u16, code7) << 1) + @as(u16, try self.readF(u1, flag.buffered)) - 0b0011_0000;
- } else if (code7 <= 0b1100_011) { // 8 bit, 280-287, codes 1100_0000 - 1100_0111
- return (@as(u16, code7 - 0b1100000) << 1) + try self.readF(u1, flag.buffered) + 280;
- } else { // 9 bit, 144-255, codes 1_1001_0000 - 1_1111_1111
- return (@as(u16, code7 - 0b1100_100) << 2) + @as(u16, try self.readF(u2, flag.buffered | flag.reverse)) + 144;
- }
- }
- };
-}
-
-test "readF" {
- var fbs = std.io.fixedBufferStream(&[_]u8{ 0xf3, 0x48, 0xcd, 0xc9, 0x00, 0x00 });
- var br = bitReader(u64, fbs.reader());
- const F = BitReader64(@TypeOf(fbs.reader())).flag;
-
- try testing.expectEqual(@as(u8, 48), br.nbits);
- try testing.expectEqual(@as(u64, 0xc9cd48f3), br.bits);
-
- try testing.expect(try br.readF(u1, 0) == 0b0000_0001);
- try testing.expect(try br.readF(u2, 0) == 0b0000_0001);
- try testing.expectEqual(@as(u8, 48 - 3), br.nbits);
- try testing.expectEqual(@as(u3, 5), br.alignBits());
-
- try testing.expect(try br.readF(u8, F.peek) == 0b0001_1110);
- try testing.expect(try br.readF(u9, F.peek) == 0b1_0001_1110);
- try br.shift(9);
- try testing.expectEqual(@as(u8, 36), br.nbits);
- try testing.expectEqual(@as(u3, 4), br.alignBits());
-
- try testing.expect(try br.readF(u4, 0) == 0b0100);
- try testing.expectEqual(@as(u8, 32), br.nbits);
- try testing.expectEqual(@as(u3, 0), br.alignBits());
-
- try br.shift(1);
- try testing.expectEqual(@as(u3, 7), br.alignBits());
- try br.shift(1);
- try testing.expectEqual(@as(u3, 6), br.alignBits());
- br.alignToByte();
- try testing.expectEqual(@as(u3, 0), br.alignBits());
-
- try testing.expectEqual(@as(u64, 0xc9), br.bits);
- try testing.expectEqual(@as(u16, 0x9), try br.readN(4, 0));
- try testing.expectEqual(@as(u16, 0xc), try br.readN(4, 0));
-}
-
-test "read block type 1 data" {
- inline for ([_]type{ u64, u32 }) |T| {
- const data = [_]u8{
- 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1
- 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00,
- 0x0c, 0x01, 0x02, 0x03, //
- 0xaa, 0xbb, 0xcc, 0xdd,
- };
- var fbs = std.io.fixedBufferStream(&data);
- var br = bitReader(T, fbs.reader());
- const F = BitReader(T, @TypeOf(fbs.reader())).flag;
-
- try testing.expectEqual(@as(u1, 1), try br.readF(u1, 0)); // bfinal
- try testing.expectEqual(@as(u2, 1), try br.readF(u2, 0)); // block_type
-
- for ("Hello world\n") |c| {
- try testing.expectEqual(@as(u8, c), try br.readF(u8, F.reverse) - 0x30);
- }
- try testing.expectEqual(@as(u7, 0), try br.readF(u7, 0)); // end of block
- br.alignToByte();
- try testing.expectEqual(@as(u32, 0x0302010c), try br.readF(u32, 0));
- try testing.expectEqual(@as(u16, 0xbbaa), try br.readF(u16, 0));
- try testing.expectEqual(@as(u16, 0xddcc), try br.readF(u16, 0));
- }
-}
-
-test "shift/fill" {
- const data = [_]u8{
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- };
- var fbs = std.io.fixedBufferStream(&data);
- var br = bitReader(u64, fbs.reader());
-
- try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits);
- try br.shift(8);
- try testing.expectEqual(@as(u64, 0x00_08_07_06_05_04_03_02), br.bits);
- try br.fill(60); // fill with 1 byte
- try testing.expectEqual(@as(u64, 0x01_08_07_06_05_04_03_02), br.bits);
- try br.shift(8 * 4 + 4);
- try testing.expectEqual(@as(u64, 0x00_00_00_00_00_10_80_70), br.bits);
-
- try br.fill(60); // fill with 4 bytes (shift by 4)
- try testing.expectEqual(@as(u64, 0x00_50_40_30_20_10_80_70), br.bits);
- try testing.expectEqual(@as(u8, 8 * 7 + 4), br.nbits);
-
- try br.shift(@intCast(br.nbits)); // clear buffer
- try br.fill(8); // refill with the rest of the bytes
- try testing.expectEqual(@as(u64, 0x00_00_00_00_00_08_07_06), br.bits);
-}
-
-test "readAll" {
- inline for ([_]type{ u64, u32 }) |T| {
- const data = [_]u8{
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- };
- var fbs = std.io.fixedBufferStream(&data);
- var br = bitReader(T, fbs.reader());
-
- switch (T) {
- u64 => try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits),
- u32 => try testing.expectEqual(@as(u32, 0x04_03_02_01), br.bits),
- else => unreachable,
- }
-
- var out: [16]u8 = undefined;
- try br.readAll(out[0..]);
- try testing.expect(br.nbits == 0);
- try testing.expect(br.bits == 0);
-
- try testing.expectEqualSlices(u8, data[0..16], &out);
- }
-}
-
-test "readFixedCode" {
- inline for ([_]type{ u64, u32 }) |T| {
- const fixed_codes = @import("huffman_encoder.zig").fixed_codes;
-
- var fbs = std.io.fixedBufferStream(&fixed_codes);
- var rdr = bitReader(T, fbs.reader());
-
- for (0..286) |c| {
- try testing.expectEqual(c, try rdr.readFixedCode());
- }
- try testing.expect(rdr.nbits == 0);
- }
-}
-
-test "u32 leaves no bits on u32 reads" {
- const data = [_]u8{
- 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- };
- var fbs = std.io.fixedBufferStream(&data);
- var br = bitReader(u32, fbs.reader());
-
- _ = try br.read(u3);
- try testing.expectEqual(29, br.nbits);
- br.alignToByte();
- try testing.expectEqual(24, br.nbits);
- try testing.expectEqual(0x04_03_02_01, try br.read(u32));
- try testing.expectEqual(0, br.nbits);
- try testing.expectEqual(0x08_07_06_05, try br.read(u32));
- try testing.expectEqual(0, br.nbits);
-
- _ = try br.read(u9);
- try testing.expectEqual(23, br.nbits);
- br.alignToByte();
- try testing.expectEqual(16, br.nbits);
- try testing.expectEqual(0x0e_0d_0c_0b, try br.read(u32));
- try testing.expectEqual(0, br.nbits);
-}
-
-test "u64 need fill after alignToByte" {
- const data = [_]u8{
- 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- };
-
- // without fill
- var fbs = std.io.fixedBufferStream(&data);
- var br = bitReader(u64, fbs.reader());
- _ = try br.read(u23);
- try testing.expectEqual(41, br.nbits);
- br.alignToByte();
- try testing.expectEqual(40, br.nbits);
- try testing.expectEqual(0x06_05_04_03, try br.read(u32));
- try testing.expectEqual(8, br.nbits);
- try testing.expectEqual(0x0a_09_08_07, try br.read(u32));
- try testing.expectEqual(32, br.nbits);
-
- // fill after align ensures all bits filled
- fbs.reset();
- br = bitReader(u64, fbs.reader());
- _ = try br.read(u23);
- try testing.expectEqual(41, br.nbits);
- br.alignToByte();
- try br.fill(0);
- try testing.expectEqual(64, br.nbits);
- try testing.expectEqual(0x06_05_04_03, try br.read(u32));
- try testing.expectEqual(32, br.nbits);
- try testing.expectEqual(0x0a_09_08_07, try br.read(u32));
- try testing.expectEqual(0, br.nbits);
-}
diff --git a/lib/std/compress/flate/bit_writer.zig b/lib/std/compress/flate/bit_writer.zig
deleted file mode 100644
index b5d84c7e2af7b9c13371d27973dbd84926e65e5f..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/bit_writer.zig
+++ /dev/null
@@ -1,99 +0,0 @@
-const std = @import("std");
-const assert = std.debug.assert;
-
-/// Bit writer for use in deflate (compression).
-///
-/// Has internal bits buffer of 64 bits and internal bytes buffer of 248 bytes.
-/// When we accumulate 48 bits 6 bytes are moved to the bytes buffer. When we
-/// accumulate 240 bytes they are flushed to the underlying inner_writer.
-///
-pub fn BitWriter(comptime WriterType: type) type {
- // buffer_flush_size indicates the buffer size
- // after which bytes are flushed to the writer.
- // Should preferably be a multiple of 6, since
- // we accumulate 6 bytes between writes to the buffer.
- const buffer_flush_size = 240;
-
- // buffer_size is the actual output byte buffer size.
- // It must have additional headroom for a flush
- // which can contain up to 8 bytes.
- const buffer_size = buffer_flush_size + 8;
-
- return struct {
- inner_writer: WriterType,
-
- // Data waiting to be written is bytes[0 .. nbytes]
- // and then the low nbits of bits. Data is always written
- // sequentially into the bytes array.
- bits: u64 = 0,
- nbits: u32 = 0, // number of bits
- bytes: [buffer_size]u8 = undefined,
- nbytes: u32 = 0, // number of bytes
-
- const Self = @This();
-
- pub const Error = WriterType.Error || error{UnfinishedBits};
-
- pub fn init(writer: WriterType) Self {
- return .{ .inner_writer = writer };
- }
-
- pub fn setWriter(self: *Self, new_writer: WriterType) void {
- //assert(self.bits == 0 and self.nbits == 0 and self.nbytes == 0);
- self.inner_writer = new_writer;
- }
-
- pub fn flush(self: *Self) Error!void {
- var n = self.nbytes;
- while (self.nbits != 0) {
- self.bytes[n] = @as(u8, @truncate(self.bits));
- self.bits >>= 8;
- if (self.nbits > 8) { // Avoid underflow
- self.nbits -= 8;
- } else {
- self.nbits = 0;
- }
- n += 1;
- }
- self.bits = 0;
- _ = try self.inner_writer.write(self.bytes[0..n]);
- self.nbytes = 0;
- }
-
- pub fn writeBits(self: *Self, b: u32, nb: u32) Error!void {
- self.bits |= @as(u64, @intCast(b)) << @as(u6, @intCast(self.nbits));
- self.nbits += nb;
- if (self.nbits < 48)
- return;
-
- var n = self.nbytes;
- std.mem.writeInt(u64, self.bytes[n..][0..8], self.bits, .little);
- n += 6;
- if (n >= buffer_flush_size) {
- _ = try self.inner_writer.write(self.bytes[0..n]);
- n = 0;
- }
- self.nbytes = n;
- self.bits >>= 48;
- self.nbits -= 48;
- }
-
- pub fn writeBytes(self: *Self, bytes: []const u8) Error!void {
- var n = self.nbytes;
- if (self.nbits & 7 != 0) {
- return error.UnfinishedBits;
- }
- while (self.nbits != 0) {
- self.bytes[n] = @as(u8, @truncate(self.bits));
- self.bits >>= 8;
- self.nbits -= 8;
- n += 1;
- }
- if (n != 0) {
- _ = try self.inner_writer.write(self.bytes[0..n]);
- }
- self.nbytes = 0;
- _ = try self.inner_writer.write(bytes);
- }
- };
-}
diff --git a/lib/std/compress/flate/block_writer.zig b/lib/std/compress/flate/block_writer.zig
deleted file mode 100644
index fa0d299e8432223d16c0bac74c32aac208018fda..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/block_writer.zig
+++ /dev/null
@@ -1,706 +0,0 @@
-const std = @import("std");
-const io = std.io;
-const assert = std.debug.assert;
-
-const hc = @import("huffman_encoder.zig");
-const consts = @import("consts.zig").huffman;
-const Token = @import("Token.zig");
-const BitWriter = @import("bit_writer.zig").BitWriter;
-
-pub fn blockWriter(writer: anytype) BlockWriter(@TypeOf(writer)) {
- return BlockWriter(@TypeOf(writer)).init(writer);
-}
-
-/// Accepts list of tokens, decides what is best block type to write. What block
-/// type will provide best compression. Writes header and body of the block.
-///
-pub fn BlockWriter(comptime WriterType: type) type {
- const BitWriterType = BitWriter(WriterType);
- return struct {
- const codegen_order = consts.codegen_order;
- const end_code_mark = 255;
- const Self = @This();
-
- pub const Error = BitWriterType.Error;
- bit_writer: BitWriterType,
-
- codegen_freq: [consts.codegen_code_count]u16 = undefined,
- literal_freq: [consts.max_num_lit]u16 = undefined,
- distance_freq: [consts.distance_code_count]u16 = undefined,
- codegen: [consts.max_num_lit + consts.distance_code_count + 1]u8 = undefined,
- literal_encoding: hc.LiteralEncoder = .{},
- distance_encoding: hc.DistanceEncoder = .{},
- codegen_encoding: hc.CodegenEncoder = .{},
- fixed_literal_encoding: hc.LiteralEncoder,
- fixed_distance_encoding: hc.DistanceEncoder,
- huff_distance: hc.DistanceEncoder,
-
- pub fn init(writer: WriterType) Self {
- return .{
- .bit_writer = BitWriterType.init(writer),
- .fixed_literal_encoding = hc.fixedLiteralEncoder(),
- .fixed_distance_encoding = hc.fixedDistanceEncoder(),
- .huff_distance = hc.huffmanDistanceEncoder(),
- };
- }
-
- /// Flush intrenal bit buffer to the writer.
- /// Should be called only when bit stream is at byte boundary.
- ///
- /// That is after final block; when last byte could be incomplete or
- /// after stored block; which is aligned to the byte boundary (it has x
- /// padding bits after first 3 bits).
- pub fn flush(self: *Self) Error!void {
- try self.bit_writer.flush();
- }
-
- pub fn setWriter(self: *Self, new_writer: WriterType) void {
- self.bit_writer.setWriter(new_writer);
- }
-
- fn writeCode(self: *Self, c: hc.HuffCode) Error!void {
- try self.bit_writer.writeBits(c.code, c.len);
- }
-
- // RFC 1951 3.2.7 specifies a special run-length encoding for specifying
- // the literal and distance lengths arrays (which are concatenated into a single
- // array). This method generates that run-length encoding.
- //
- // The result is written into the codegen array, and the frequencies
- // of each code is written into the codegen_freq array.
- // Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
- // information. Code bad_code is an end marker
- //
- // num_literals: The number of literals in literal_encoding
- // num_distances: The number of distances in distance_encoding
- // lit_enc: The literal encoder to use
- // dist_enc: The distance encoder to use
- fn generateCodegen(
- self: *Self,
- num_literals: u32,
- num_distances: u32,
- lit_enc: *hc.LiteralEncoder,
- dist_enc: *hc.DistanceEncoder,
- ) void {
- for (self.codegen_freq, 0..) |_, i| {
- self.codegen_freq[i] = 0;
- }
-
- // Note that we are using codegen both as a temporary variable for holding
- // a copy of the frequencies, and as the place where we put the result.
- // This is fine because the output is always shorter than the input used
- // so far.
- var codegen = &self.codegen; // cache
- // Copy the concatenated code sizes to codegen. Put a marker at the end.
- var cgnl = codegen[0..num_literals];
- for (cgnl, 0..) |_, i| {
- cgnl[i] = @as(u8, @intCast(lit_enc.codes[i].len));
- }
-
- cgnl = codegen[num_literals .. num_literals + num_distances];
- for (cgnl, 0..) |_, i| {
- cgnl[i] = @as(u8, @intCast(dist_enc.codes[i].len));
- }
- codegen[num_literals + num_distances] = end_code_mark;
-
- var size = codegen[0];
- var count: i32 = 1;
- var out_index: u32 = 0;
- var in_index: u32 = 1;
- while (size != end_code_mark) : (in_index += 1) {
- // INVARIANT: We have seen "count" copies of size that have not yet
- // had output generated for them.
- const next_size = codegen[in_index];
- if (next_size == size) {
- count += 1;
- continue;
- }
- // We need to generate codegen indicating "count" of size.
- if (size != 0) {
- codegen[out_index] = size;
- out_index += 1;
- self.codegen_freq[size] += 1;
- count -= 1;
- while (count >= 3) {
- var n: i32 = 6;
- if (n > count) {
- n = count;
- }
- codegen[out_index] = 16;
- out_index += 1;
- codegen[out_index] = @as(u8, @intCast(n - 3));
- out_index += 1;
- self.codegen_freq[16] += 1;
- count -= n;
- }
- } else {
- while (count >= 11) {
- var n: i32 = 138;
- if (n > count) {
- n = count;
- }
- codegen[out_index] = 18;
- out_index += 1;
- codegen[out_index] = @as(u8, @intCast(n - 11));
- out_index += 1;
- self.codegen_freq[18] += 1;
- count -= n;
- }
- if (count >= 3) {
- // 3 <= count <= 10
- codegen[out_index] = 17;
- out_index += 1;
- codegen[out_index] = @as(u8, @intCast(count - 3));
- out_index += 1;
- self.codegen_freq[17] += 1;
- count = 0;
- }
- }
- count -= 1;
- while (count >= 0) : (count -= 1) {
- codegen[out_index] = size;
- out_index += 1;
- self.codegen_freq[size] += 1;
- }
- // Set up invariant for next time through the loop.
- size = next_size;
- count = 1;
- }
- // Marker indicating the end of the codegen.
- codegen[out_index] = end_code_mark;
- }
-
- const DynamicSize = struct {
- size: u32,
- num_codegens: u32,
- };
-
- // dynamicSize returns the size of dynamically encoded data in bits.
- fn dynamicSize(
- self: *Self,
- lit_enc: *hc.LiteralEncoder, // literal encoder
- dist_enc: *hc.DistanceEncoder, // distance encoder
- extra_bits: u32,
- ) DynamicSize {
- var num_codegens = self.codegen_freq.len;
- while (num_codegens > 4 and self.codegen_freq[codegen_order[num_codegens - 1]] == 0) {
- num_codegens -= 1;
- }
- const header = 3 + 5 + 5 + 4 + (3 * num_codegens) +
- self.codegen_encoding.bitLength(self.codegen_freq[0..]) +
- self.codegen_freq[16] * 2 +
- self.codegen_freq[17] * 3 +
- self.codegen_freq[18] * 7;
- const size = header +
- lit_enc.bitLength(&self.literal_freq) +
- dist_enc.bitLength(&self.distance_freq) +
- extra_bits;
-
- return DynamicSize{
- .size = @as(u32, @intCast(size)),
- .num_codegens = @as(u32, @intCast(num_codegens)),
- };
- }
-
- // fixedSize returns the size of dynamically encoded data in bits.
- fn fixedSize(self: *Self, extra_bits: u32) u32 {
- return 3 +
- self.fixed_literal_encoding.bitLength(&self.literal_freq) +
- self.fixed_distance_encoding.bitLength(&self.distance_freq) +
- extra_bits;
- }
-
- const StoredSize = struct {
- size: u32,
- storable: bool,
- };
-
- // storedSizeFits calculates the stored size, including header.
- // The function returns the size in bits and whether the block
- // fits inside a single block.
- fn storedSizeFits(in: ?[]const u8) StoredSize {
- if (in == null) {
- return .{ .size = 0, .storable = false };
- }
- if (in.?.len <= consts.max_store_block_size) {
- return .{ .size = @as(u32, @intCast((in.?.len + 5) * 8)), .storable = true };
- }
- return .{ .size = 0, .storable = false };
- }
-
- // Write the header of a dynamic Huffman block to the output stream.
- //
- // num_literals: The number of literals specified in codegen
- // num_distances: The number of distances specified in codegen
- // num_codegens: The number of codegens used in codegen
- // eof: Is it the end-of-file? (end of stream)
- fn dynamicHeader(
- self: *Self,
- num_literals: u32,
- num_distances: u32,
- num_codegens: u32,
- eof: bool,
- ) Error!void {
- const first_bits: u32 = if (eof) 5 else 4;
- try self.bit_writer.writeBits(first_bits, 3);
- try self.bit_writer.writeBits(num_literals - 257, 5);
- try self.bit_writer.writeBits(num_distances - 1, 5);
- try self.bit_writer.writeBits(num_codegens - 4, 4);
-
- var i: u32 = 0;
- while (i < num_codegens) : (i += 1) {
- const value = self.codegen_encoding.codes[codegen_order[i]].len;
- try self.bit_writer.writeBits(value, 3);
- }
-
- i = 0;
- while (true) {
- const code_word: u32 = @as(u32, @intCast(self.codegen[i]));
- i += 1;
- if (code_word == end_code_mark) {
- break;
- }
- try self.writeCode(self.codegen_encoding.codes[@as(u32, @intCast(code_word))]);
-
- switch (code_word) {
- 16 => {
- try self.bit_writer.writeBits(self.codegen[i], 2);
- i += 1;
- },
- 17 => {
- try self.bit_writer.writeBits(self.codegen[i], 3);
- i += 1;
- },
- 18 => {
- try self.bit_writer.writeBits(self.codegen[i], 7);
- i += 1;
- },
- else => {},
- }
- }
- }
-
- fn storedHeader(self: *Self, length: usize, eof: bool) Error!void {
- assert(length <= 65535);
- const flag: u32 = if (eof) 1 else 0;
- try self.bit_writer.writeBits(flag, 3);
- try self.flush();
- const l: u16 = @intCast(length);
- try self.bit_writer.writeBits(l, 16);
- try self.bit_writer.writeBits(~l, 16);
- }
-
- fn fixedHeader(self: *Self, eof: bool) Error!void {
- // Indicate that we are a fixed Huffman block
- var value: u32 = 2;
- if (eof) {
- value = 3;
- }
- try self.bit_writer.writeBits(value, 3);
- }
-
- // Write a block of tokens with the smallest encoding. Will choose block type.
- // The original input can be supplied, and if the huffman encoded data
- // is larger than the original bytes, the data will be written as a
- // stored block.
- // If the input is null, the tokens will always be Huffman encoded.
- pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8) Error!void {
- const lit_and_dist = self.indexTokens(tokens);
- const num_literals = lit_and_dist.num_literals;
- const num_distances = lit_and_dist.num_distances;
-
- var extra_bits: u32 = 0;
- const ret = storedSizeFits(input);
- const stored_size = ret.size;
- const storable = ret.storable;
-
- if (storable) {
- // We only bother calculating the costs of the extra bits required by
- // the length of distance fields (which will be the same for both fixed
- // and dynamic encoding), if we need to compare those two encodings
- // against stored encoding.
- var length_code: u16 = Token.length_codes_start + 8;
- while (length_code < num_literals) : (length_code += 1) {
- // First eight length codes have extra size = 0.
- extra_bits += @as(u32, @intCast(self.literal_freq[length_code])) *
- @as(u32, @intCast(Token.lengthExtraBits(length_code)));
- }
- var distance_code: u16 = 4;
- while (distance_code < num_distances) : (distance_code += 1) {
- // First four distance codes have extra size = 0.
- extra_bits += @as(u32, @intCast(self.distance_freq[distance_code])) *
- @as(u32, @intCast(Token.distanceExtraBits(distance_code)));
- }
- }
-
- // Figure out smallest code.
- // Fixed Huffman baseline.
- var literal_encoding = &self.fixed_literal_encoding;
- var distance_encoding = &self.fixed_distance_encoding;
- var size = self.fixedSize(extra_bits);
-
- // Dynamic Huffman?
- var num_codegens: u32 = 0;
-
- // Generate codegen and codegenFrequencies, which indicates how to encode
- // the literal_encoding and the distance_encoding.
- self.generateCodegen(
- num_literals,
- num_distances,
- &self.literal_encoding,
- &self.distance_encoding,
- );
- self.codegen_encoding.generate(self.codegen_freq[0..], 7);
- const dynamic_size = self.dynamicSize(
- &self.literal_encoding,
- &self.distance_encoding,
- extra_bits,
- );
- const dyn_size = dynamic_size.size;
- num_codegens = dynamic_size.num_codegens;
-
- if (dyn_size < size) {
- size = dyn_size;
- literal_encoding = &self.literal_encoding;
- distance_encoding = &self.distance_encoding;
- }
-
- // Stored bytes?
- if (storable and stored_size < size) {
- try self.storedBlock(input.?, eof);
- return;
- }
-
- // Huffman.
- if (@intFromPtr(literal_encoding) == @intFromPtr(&self.fixed_literal_encoding)) {
- try self.fixedHeader(eof);
- } else {
- try self.dynamicHeader(num_literals, num_distances, num_codegens, eof);
- }
-
- // Write the tokens.
- try self.writeTokens(tokens, &literal_encoding.codes, &distance_encoding.codes);
- }
-
- pub fn storedBlock(self: *Self, input: []const u8, eof: bool) Error!void {
- try self.storedHeader(input.len, eof);
- try self.bit_writer.writeBytes(input);
- }
-
- // writeBlockDynamic encodes a block using a dynamic Huffman table.
- // This should be used if the symbols used have a disproportionate
- // histogram distribution.
- // If input is supplied and the compression savings are below 1/16th of the
- // input size the block is stored.
- fn dynamicBlock(
- self: *Self,
- tokens: []const Token,
- eof: bool,
- input: ?[]const u8,
- ) Error!void {
- const total_tokens = self.indexTokens(tokens);
- const num_literals = total_tokens.num_literals;
- const num_distances = total_tokens.num_distances;
-
- // Generate codegen and codegenFrequencies, which indicates how to encode
- // the literal_encoding and the distance_encoding.
- self.generateCodegen(
- num_literals,
- num_distances,
- &self.literal_encoding,
- &self.distance_encoding,
- );
- self.codegen_encoding.generate(self.codegen_freq[0..], 7);
- const dynamic_size = self.dynamicSize(&self.literal_encoding, &self.distance_encoding, 0);
- const size = dynamic_size.size;
- const num_codegens = dynamic_size.num_codegens;
-
- // Store bytes, if we don't get a reasonable improvement.
-
- const stored_size = storedSizeFits(input);
- const ssize = stored_size.size;
- const storable = stored_size.storable;
- if (storable and ssize < (size + (size >> 4))) {
- try self.storedBlock(input.?, eof);
- return;
- }
-
- // Write Huffman table.
- try self.dynamicHeader(num_literals, num_distances, num_codegens, eof);
-
- // Write the tokens.
- try self.writeTokens(tokens, &self.literal_encoding.codes, &self.distance_encoding.codes);
- }
-
- const TotalIndexedTokens = struct {
- num_literals: u32,
- num_distances: u32,
- };
-
- // Indexes a slice of tokens followed by an end_block_marker, and updates
- // literal_freq and distance_freq, and generates literal_encoding
- // and distance_encoding.
- // The number of literal and distance tokens is returned.
- fn indexTokens(self: *Self, tokens: []const Token) TotalIndexedTokens {
- var num_literals: u32 = 0;
- var num_distances: u32 = 0;
-
- for (self.literal_freq, 0..) |_, i| {
- self.literal_freq[i] = 0;
- }
- for (self.distance_freq, 0..) |_, i| {
- self.distance_freq[i] = 0;
- }
-
- for (tokens) |t| {
- if (t.kind == Token.Kind.literal) {
- self.literal_freq[t.literal()] += 1;
- continue;
- }
- self.literal_freq[t.lengthCode()] += 1;
- self.distance_freq[t.distanceCode()] += 1;
- }
- // add end_block_marker token at the end
- self.literal_freq[consts.end_block_marker] += 1;
-
- // get the number of literals
- num_literals = @as(u32, @intCast(self.literal_freq.len));
- while (self.literal_freq[num_literals - 1] == 0) {
- num_literals -= 1;
- }
- // get the number of distances
- num_distances = @as(u32, @intCast(self.distance_freq.len));
- while (num_distances > 0 and self.distance_freq[num_distances - 1] == 0) {
- num_distances -= 1;
- }
- if (num_distances == 0) {
- // We haven't found a single match. If we want to go with the dynamic encoding,
- // we should count at least one distance to be sure that the distance huffman tree could be encoded.
- self.distance_freq[0] = 1;
- num_distances = 1;
- }
- self.literal_encoding.generate(&self.literal_freq, 15);
- self.distance_encoding.generate(&self.distance_freq, 15);
- return TotalIndexedTokens{
- .num_literals = num_literals,
- .num_distances = num_distances,
- };
- }
-
- // Writes a slice of tokens to the output followed by and end_block_marker.
- // codes for literal and distance encoding must be supplied.
- fn writeTokens(
- self: *Self,
- tokens: []const Token,
- le_codes: []hc.HuffCode,
- oe_codes: []hc.HuffCode,
- ) Error!void {
- for (tokens) |t| {
- if (t.kind == Token.Kind.literal) {
- try self.writeCode(le_codes[t.literal()]);
- continue;
- }
-
- // Write the length
- const le = t.lengthEncoding();
- try self.writeCode(le_codes[le.code]);
- if (le.extra_bits > 0) {
- try self.bit_writer.writeBits(le.extra_length, le.extra_bits);
- }
-
- // Write the distance
- const oe = t.distanceEncoding();
- try self.writeCode(oe_codes[oe.code]);
- if (oe.extra_bits > 0) {
- try self.bit_writer.writeBits(oe.extra_distance, oe.extra_bits);
- }
- }
- // add end_block_marker at the end
- try self.writeCode(le_codes[consts.end_block_marker]);
- }
-
- // Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
- // if the results only gains very little from compression.
- pub fn huffmanBlock(self: *Self, input: []const u8, eof: bool) Error!void {
- // Add everything as literals
- histogram(input, &self.literal_freq);
-
- self.literal_freq[consts.end_block_marker] = 1;
-
- const num_literals = consts.end_block_marker + 1;
- self.distance_freq[0] = 1;
- const num_distances = 1;
-
- self.literal_encoding.generate(&self.literal_freq, 15);
-
- // Figure out smallest code.
- // Always use dynamic Huffman or Store
- var num_codegens: u32 = 0;
-
- // Generate codegen and codegenFrequencies, which indicates how to encode
- // the literal_encoding and the distance_encoding.
- self.generateCodegen(
- num_literals,
- num_distances,
- &self.literal_encoding,
- &self.huff_distance,
- );
- self.codegen_encoding.generate(self.codegen_freq[0..], 7);
- const dynamic_size = self.dynamicSize(&self.literal_encoding, &self.huff_distance, 0);
- const size = dynamic_size.size;
- num_codegens = dynamic_size.num_codegens;
-
- // Store bytes, if we don't get a reasonable improvement.
- const stored_size_ret = storedSizeFits(input);
- const ssize = stored_size_ret.size;
- const storable = stored_size_ret.storable;
-
- if (storable and ssize < (size + (size >> 4))) {
- try self.storedBlock(input, eof);
- return;
- }
-
- // Huffman.
- try self.dynamicHeader(num_literals, num_distances, num_codegens, eof);
- const encoding = self.literal_encoding.codes[0..257];
-
- for (input) |t| {
- const c = encoding[t];
- try self.bit_writer.writeBits(c.code, c.len);
- }
- try self.writeCode(encoding[consts.end_block_marker]);
- }
-
- // histogram accumulates a histogram of b in h.
- fn histogram(b: []const u8, h: *[286]u16) void {
- // Clear histogram
- for (h, 0..) |_, i| {
- h[i] = 0;
- }
-
- var lh = h.*[0..256];
- for (b) |t| {
- lh[t] += 1;
- }
- }
- };
-}
-
-// tests
-const expect = std.testing.expect;
-const fmt = std.fmt;
-const testing = std.testing;
-const ArrayList = std.ArrayList;
-
-const TestCase = @import("testdata/block_writer.zig").TestCase;
-const testCases = @import("testdata/block_writer.zig").testCases;
-
-// tests if the writeBlock encoding has changed.
-test "write" {
- inline for (0..testCases.len) |i| {
- try testBlock(testCases[i], .write_block);
- }
-}
-
-// tests if the writeBlockDynamic encoding has changed.
-test "dynamicBlock" {
- inline for (0..testCases.len) |i| {
- try testBlock(testCases[i], .write_dyn_block);
- }
-}
-
-test "huffmanBlock" {
- inline for (0..testCases.len) |i| {
- try testBlock(testCases[i], .write_huffman_block);
- }
- try testBlock(.{
- .tokens = &[_]Token{},
- .input = "huffman-rand-max.input",
- .want = "huffman-rand-max.{s}.expect",
- }, .write_huffman_block);
-}
-
-const TestFn = enum {
- write_block,
- write_dyn_block, // write dynamic block
- write_huffman_block,
-
- fn to_s(self: TestFn) []const u8 {
- return switch (self) {
- .write_block => "wb",
- .write_dyn_block => "dyn",
- .write_huffman_block => "huff",
- };
- }
-
- fn write(
- comptime self: TestFn,
- bw: anytype,
- tok: []const Token,
- input: ?[]const u8,
- final: bool,
- ) !void {
- switch (self) {
- .write_block => try bw.write(tok, final, input),
- .write_dyn_block => try bw.dynamicBlock(tok, final, input),
- .write_huffman_block => try bw.huffmanBlock(input.?, final),
- }
- try bw.flush();
- }
-};
-
-// testBlock tests a block against its references
-//
-// size
-// 64K [file-name].input - input non compressed file
-// 8.1K [file-name].golden -
-// 78 [file-name].dyn.expect - output with writeBlockDynamic
-// 78 [file-name].wb.expect - output with writeBlock
-// 8.1K [file-name].huff.expect - output with writeBlockHuff
-// 78 [file-name].dyn.expect-noinput - output with writeBlockDynamic when input is null
-// 78 [file-name].wb.expect-noinput - output with writeBlock when input is null
-//
-// wb - writeBlock
-// dyn - writeBlockDynamic
-// huff - writeBlockHuff
-//
-fn testBlock(comptime tc: TestCase, comptime tfn: TestFn) !void {
- if (tc.input.len != 0 and tc.want.len != 0) {
- const want_name = comptime fmt.comptimePrint(tc.want, .{tfn.to_s()});
- const input = @embedFile("testdata/block_writer/" ++ tc.input);
- const want = @embedFile("testdata/block_writer/" ++ want_name);
- try testWriteBlock(tfn, input, want, tc.tokens);
- }
-
- if (tfn == .write_huffman_block) {
- return;
- }
-
- const want_name_no_input = comptime fmt.comptimePrint(tc.want_no_input, .{tfn.to_s()});
- const want = @embedFile("testdata/block_writer/" ++ want_name_no_input);
- try testWriteBlock(tfn, null, want, tc.tokens);
-}
-
-// Uses writer function `tfn` to write `tokens`, tests that we got `want` as output.
-fn testWriteBlock(comptime tfn: TestFn, input: ?[]const u8, want: []const u8, tokens: []const Token) !void {
- var buf = ArrayList(u8).init(testing.allocator);
- var bw = blockWriter(buf.writer());
- try tfn.write(&bw, tokens, input, false);
- var got = buf.items;
- try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
- try expect(got[0] & 0b0000_0001 == 0); // bfinal is not set
- //
- // Test if the writer produces the same output after reset.
- buf.deinit();
- buf = ArrayList(u8).init(testing.allocator);
- defer buf.deinit();
- bw.setWriter(buf.writer());
-
- try tfn.write(&bw, tokens, input, true);
- try bw.flush();
- got = buf.items;
-
- try expect(got[0] & 1 == 1); // bfinal is set
- buf.items[0] &= 0b1111_1110; // remove bfinal bit, so we can run test slices
- try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
-}
diff --git a/lib/std/compress/flate/consts.zig b/lib/std/compress/flate/consts.zig
deleted file mode 100644
index b17083461bce6444fc1800602263196571a0e67e..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/consts.zig
+++ /dev/null
@@ -1,49 +0,0 @@
-pub const deflate = struct {
- // Number of tokens to accumulate in deflate before starting block encoding.
- //
- // In zlib this depends on memlevel: 6 + memlevel, where default memlevel is
- // 8 and max 9 that gives 14 or 15 bits.
- pub const tokens = 1 << 15;
-};
-
-pub const match = struct {
- pub const base_length = 3; // smallest match length per the RFC section 3.2.5
- pub const min_length = 4; // min length used in this algorithm
- pub const max_length = 258;
-
- pub const min_distance = 1;
- pub const max_distance = 32768;
-};
-
-pub const history = struct {
- pub const len = match.max_distance;
-};
-
-pub const lookup = struct {
- pub const bits = 15;
- pub const len = 1 << bits;
- pub const shift = 32 - bits;
-};
-
-pub const huffman = struct {
- // The odd order in which the codegen code sizes are written.
- pub const codegen_order = [_]u32{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
- // The number of codegen codes.
- pub const codegen_code_count = 19;
-
- // The largest distance code.
- pub const distance_code_count = 30;
-
- // Maximum number of literals.
- pub const max_num_lit = 286;
-
- // Max number of frequencies used for a Huffman Code
- // Possible lengths are codegen_code_count (19), distance_code_count (30) and max_num_lit (286).
- // The largest of these is max_num_lit.
- pub const max_num_frequencies = max_num_lit;
-
- // Biggest block size for uncompressed block.
- pub const max_store_block_size = 65535;
- // The special code used to mark the end of a block.
- pub const end_block_marker = 256;
-};
diff --git a/lib/std/compress/flate/container.zig b/lib/std/compress/flate/container.zig
deleted file mode 100644
index fe6dec446d5cb83f499eb0d239f652f395cc01f5..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/container.zig
+++ /dev/null
@@ -1,208 +0,0 @@
-//! Container of the deflate bit stream body. Container adds header before
-//! deflate bit stream and footer after. It can bi gzip, zlib or raw (no header,
-//! no footer, raw bit stream).
-//!
-//! Zlib format is defined in rfc 1950. Header has 2 bytes and footer 4 bytes
-//! addler 32 checksum.
-//!
-//! Gzip format is defined in rfc 1952. Header has 10+ bytes and footer 4 bytes
-//! crc32 checksum and 4 bytes of uncompressed data length.
-//!
-//!
-//! rfc 1950: https://datatracker.ietf.org/doc/html/rfc1950#page-4
-//! rfc 1952: https://datatracker.ietf.org/doc/html/rfc1952#page-5
-//!
-
-const std = @import("std");
-
-pub const Container = enum {
- raw, // no header or footer
- gzip, // gzip header and footer
- zlib, // zlib header and footer
-
- pub fn size(w: Container) usize {
- return headerSize(w) + footerSize(w);
- }
-
- pub fn headerSize(w: Container) usize {
- return switch (w) {
- .gzip => 10,
- .zlib => 2,
- .raw => 0,
- };
- }
-
- pub fn footerSize(w: Container) usize {
- return switch (w) {
- .gzip => 8,
- .zlib => 4,
- .raw => 0,
- };
- }
-
- pub const list = [_]Container{ .raw, .gzip, .zlib };
-
- pub const Error = error{
- BadGzipHeader,
- BadZlibHeader,
- WrongGzipChecksum,
- WrongGzipSize,
- WrongZlibChecksum,
- };
-
- pub fn writeHeader(comptime wrap: Container, writer: anytype) !void {
- switch (wrap) {
- .gzip => {
- // GZIP 10 byte header (https://datatracker.ietf.org/doc/html/rfc1952#page-5):
- // - ID1 (IDentification 1), always 0x1f
- // - ID2 (IDentification 2), always 0x8b
- // - CM (Compression Method), always 8 = deflate
- // - FLG (Flags), all set to 0
- // - 4 bytes, MTIME (Modification time), not used, all set to zero
- // - XFL (eXtra FLags), all set to zero
- // - OS (Operating System), 03 = Unix
- const gzipHeader = [_]u8{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 };
- try writer.writeAll(&gzipHeader);
- },
- .zlib => {
- // ZLIB has a two-byte header (https://datatracker.ietf.org/doc/html/rfc1950#page-4):
- // 1st byte:
- // - First four bits is the CINFO (compression info), which is 7 for the default deflate window size.
- // - The next four bits is the CM (compression method), which is 8 for deflate.
- // 2nd byte:
- // - Two bits is the FLEVEL (compression level). Values are: 0=fastest, 1=fast, 2=default, 3=best.
- // - The next bit, FDICT, is set if a dictionary is given.
- // - The final five FCHECK bits form a mod-31 checksum.
- //
- // CINFO = 7, CM = 8, FLEVEL = 0b10, FDICT = 0, FCHECK = 0b11100
- const zlibHeader = [_]u8{ 0x78, 0b10_0_11100 };
- try writer.writeAll(&zlibHeader);
- },
- .raw => {},
- }
- }
-
- pub fn writeFooter(comptime wrap: Container, hasher: *Hasher(wrap), writer: anytype) !void {
- var bits: [4]u8 = undefined;
- switch (wrap) {
- .gzip => {
- // GZIP 8 bytes footer
- // - 4 bytes, CRC32 (CRC-32)
- // - 4 bytes, ISIZE (Input SIZE) - size of the original (uncompressed) input data modulo 2^32
- std.mem.writeInt(u32, &bits, hasher.chksum(), .little);
- try writer.writeAll(&bits);
-
- std.mem.writeInt(u32, &bits, hasher.bytesRead(), .little);
- try writer.writeAll(&bits);
- },
- .zlib => {
- // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
- // 4 bytes of ADLER32 (Adler-32 checksum)
- // Checksum value of the uncompressed data (excluding any
- // dictionary data) computed according to Adler-32
- // algorithm.
- std.mem.writeInt(u32, &bits, hasher.chksum(), .big);
- try writer.writeAll(&bits);
- },
- .raw => {},
- }
- }
-
- pub fn parseHeader(comptime wrap: Container, reader: anytype) !void {
- switch (wrap) {
- .gzip => try parseGzipHeader(reader),
- .zlib => try parseZlibHeader(reader),
- .raw => {},
- }
- }
-
- fn parseGzipHeader(reader: anytype) !void {
- const magic1 = try reader.read(u8);
- const magic2 = try reader.read(u8);
- const method = try reader.read(u8);
- const flags = try reader.read(u8);
- try reader.skipBytes(6); // mtime(4), xflags, os
- if (magic1 != 0x1f or magic2 != 0x8b or method != 0x08)
- return error.BadGzipHeader;
- // Flags description: https://www.rfc-editor.org/rfc/rfc1952.html#page-5
- if (flags != 0) {
- if (flags & 0b0000_0100 != 0) { // FEXTRA
- const extra_len = try reader.read(u16);
- try reader.skipBytes(extra_len);
- }
- if (flags & 0b0000_1000 != 0) { // FNAME
- try reader.skipStringZ();
- }
- if (flags & 0b0001_0000 != 0) { // FCOMMENT
- try reader.skipStringZ();
- }
- if (flags & 0b0000_0010 != 0) { // FHCRC
- try reader.skipBytes(2);
- }
- }
- }
-
- fn parseZlibHeader(reader: anytype) !void {
- const cm = try reader.read(u4);
- const cinfo = try reader.read(u4);
- _ = try reader.read(u8);
- if (cm != 8 or cinfo > 7) {
- return error.BadZlibHeader;
- }
- }
-
- pub fn parseFooter(comptime wrap: Container, hasher: *Hasher(wrap), reader: anytype) !void {
- switch (wrap) {
- .gzip => {
- try reader.fill(0);
- if (try reader.read(u32) != hasher.chksum()) return error.WrongGzipChecksum;
- if (try reader.read(u32) != hasher.bytesRead()) return error.WrongGzipSize;
- },
- .zlib => {
- const chksum: u32 = @byteSwap(hasher.chksum());
- if (try reader.read(u32) != chksum) return error.WrongZlibChecksum;
- },
- .raw => {},
- }
- }
-
- pub fn Hasher(comptime wrap: Container) type {
- const HasherType = switch (wrap) {
- .gzip => std.hash.Crc32,
- .zlib => std.hash.Adler32,
- .raw => struct {
- pub fn init() @This() {
- return .{};
- }
- },
- };
-
- return struct {
- hasher: HasherType = HasherType.init(),
- bytes: usize = 0,
-
- const Self = @This();
-
- pub fn update(self: *Self, buf: []const u8) void {
- switch (wrap) {
- .raw => {},
- else => {
- self.hasher.update(buf);
- self.bytes += buf.len;
- },
- }
- }
-
- pub fn chksum(self: *Self) u32 {
- switch (wrap) {
- .raw => return 0,
- else => return self.hasher.final(),
- }
- }
-
- pub fn bytesRead(self: *Self) u32 {
- return @truncate(self.bytes);
- }
- };
- }
-};
diff --git a/lib/std/compress/flate/deflate.zig b/lib/std/compress/flate/deflate.zig
deleted file mode 100644
index fd9323600020e6841981b7ceb536e318044aba2a..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/deflate.zig
+++ /dev/null
@@ -1,744 +0,0 @@
-const std = @import("std");
-const io = std.io;
-const assert = std.debug.assert;
-const testing = std.testing;
-const expect = testing.expect;
-const print = std.debug.print;
-
-const Token = @import("Token.zig");
-const consts = @import("consts.zig");
-const BlockWriter = @import("block_writer.zig").BlockWriter;
-const Container = @import("container.zig").Container;
-const SlidingWindow = @import("SlidingWindow.zig");
-const Lookup = @import("Lookup.zig");
-
-pub const Options = struct {
- level: Level = .default,
-};
-
-/// Trades between speed and compression size.
-/// Starts with level 4: in [zlib](https://github.com/madler/zlib/blob/abd3d1a28930f89375d4b41408b39f6c1be157b2/deflate.c#L115C1-L117C43)
-/// levels 1-3 are using different algorithm to perform faster but with less
-/// compression. That is not implemented here.
-pub const Level = enum(u4) {
- // zig fmt: off
- fast = 0xb, level_4 = 4,
- level_5 = 5,
- default = 0xc, level_6 = 6,
- level_7 = 7,
- level_8 = 8,
- best = 0xd, level_9 = 9,
- // zig fmt: on
-};
-
-/// Algorithm knobs for each level.
-const LevelArgs = struct {
- good: u16, // Do less lookups if we already have match of this length.
- nice: u16, // Stop looking for better match if we found match with at least this length.
- lazy: u16, // Don't do lazy match find if got match with at least this length.
- chain: u16, // How many lookups for previous match to perform.
-
- pub fn get(level: Level) LevelArgs {
- // zig fmt: off
- return switch (level) {
- .fast, .level_4 => .{ .good = 4, .lazy = 4, .nice = 16, .chain = 16 },
- .level_5 => .{ .good = 8, .lazy = 16, .nice = 32, .chain = 32 },
- .default, .level_6 => .{ .good = 8, .lazy = 16, .nice = 128, .chain = 128 },
- .level_7 => .{ .good = 8, .lazy = 32, .nice = 128, .chain = 256 },
- .level_8 => .{ .good = 32, .lazy = 128, .nice = 258, .chain = 1024 },
- .best, .level_9 => .{ .good = 32, .lazy = 258, .nice = 258, .chain = 4096 },
- };
- // zig fmt: on
- }
-};
-
-/// Compress plain data from reader into compressed stream written to writer.
-pub fn compress(comptime container: Container, reader: anytype, writer: anytype, options: Options) !void {
- var c = try compressor(container, writer, options);
- try c.compress(reader);
- try c.finish();
-}
-
-/// Create compressor for writer type.
-pub fn compressor(comptime container: Container, writer: anytype, options: Options) !Compressor(
- container,
- @TypeOf(writer),
-) {
- return try Compressor(container, @TypeOf(writer)).init(writer, options);
-}
-
-/// Compressor type.
-pub fn Compressor(comptime container: Container, comptime WriterType: type) type {
- const TokenWriterType = BlockWriter(WriterType);
- return Deflate(container, WriterType, TokenWriterType);
-}
-
-/// Default compression algorithm. Has two steps: tokenization and token
-/// encoding.
-///
-/// Tokenization takes uncompressed input stream and produces list of tokens.
-/// Each token can be literal (byte of data) or match (backrefernce to previous
-/// data with length and distance). Tokenization accumulators 32K tokens, when
-/// full or `flush` is called tokens are passed to the `block_writer`. Level
-/// defines how hard (how slow) it tries to find match.
-///
-/// Block writer will decide which type of deflate block to write (stored, fixed,
-/// dynamic) and encode tokens to the output byte stream. Client has to call
-/// `finish` to write block with the final bit set.
-///
-/// Container defines type of header and footer which can be gzip, zlib or raw.
-/// They all share same deflate body. Raw has no header or footer just deflate
-/// body.
-///
-/// Compression algorithm explained in rfc-1951 (slightly edited for this case):
-///
-/// The compressor uses a chained hash table `lookup` to find duplicated
-/// strings, using a hash function that operates on 4-byte sequences. At any
-/// given point during compression, let XYZW be the next 4 input bytes
-/// (lookahead) to be examined (not necessarily all different, of course).
-/// First, the compressor examines the hash chain for XYZW. If the chain is
-/// empty, the compressor simply writes out X as a literal byte and advances
-/// one byte in the input. If the hash chain is not empty, indicating that the
-/// sequence XYZW (or, if we are unlucky, some other 4 bytes with the same
-/// hash function value) has occurred recently, the compressor compares all
-/// strings on the XYZW hash chain with the actual input data sequence
-/// starting at the current point, and selects the longest match.
-///
-/// To improve overall compression, the compressor defers the selection of
-/// matches ("lazy matching"): after a match of length N has been found, the
-/// compressor searches for a longer match starting at the next input byte. If
-/// it finds a longer match, it truncates the previous match to a length of
-/// one (thus producing a single literal byte) and then emits the longer
-/// match. Otherwise, it emits the original match, and, as described above,
-/// advances N bytes before continuing.
-///
-///
-/// Allocates statically ~400K (192K lookup, 128K tokens, 64K window).
-///
-/// Deflate function accepts BlockWriterType so we can change that in test to test
-/// just tokenization part.
-///
-fn Deflate(comptime container: Container, comptime WriterType: type, comptime BlockWriterType: type) type {
- return struct {
- lookup: Lookup = .{},
- win: SlidingWindow = .{},
- tokens: Tokens = .{},
- wrt: WriterType,
- block_writer: BlockWriterType,
- level: LevelArgs,
- hasher: container.Hasher() = .{},
-
- // Match and literal at the previous position.
- // Used for lazy match finding in processWindow.
- prev_match: ?Token = null,
- prev_literal: ?u8 = null,
-
- const Self = @This();
-
- pub fn init(wrt: WriterType, options: Options) !Self {
- const self = Self{
- .wrt = wrt,
- .block_writer = BlockWriterType.init(wrt),
- .level = LevelArgs.get(options.level),
- };
- try container.writeHeader(self.wrt);
- return self;
- }
-
- const FlushOption = enum { none, flush, final };
-
- // Process data in window and create tokens. If token buffer is full
- // flush tokens to the token writer. In the case of `flush` or `final`
- // option it will process all data from the window. In the `none` case
- // it will preserve some data for the next match.
- fn tokenize(self: *Self, flush_opt: FlushOption) !void {
- // flush - process all data from window
- const should_flush = (flush_opt != .none);
-
- // While there is data in active lookahead buffer.
- while (self.win.activeLookahead(should_flush)) |lh| {
- var step: u16 = 1; // 1 in the case of literal, match length otherwise
- const pos: u16 = self.win.pos();
- const literal = lh[0]; // literal at current position
- const min_len: u16 = if (self.prev_match) |m| m.length() else 0;
-
- // Try to find match at least min_len long.
- if (self.findMatch(pos, lh, min_len)) |match| {
- // Found better match than previous.
- try self.addPrevLiteral();
-
- // Is found match length good enough?
- if (match.length() >= self.level.lazy) {
- // Don't try to lazy find better match, use this.
- step = try self.addMatch(match);
- } else {
- // Store this match.
- self.prev_literal = literal;
- self.prev_match = match;
- }
- } else {
- // There is no better match at current pos then it was previous.
- // Write previous match or literal.
- if (self.prev_match) |m| {
- // Write match from previous position.
- step = try self.addMatch(m) - 1; // we already advanced 1 from previous position
- } else {
- // No match at previous position.
- // Write previous literal if any, and remember this literal.
- try self.addPrevLiteral();
- self.prev_literal = literal;
- }
- }
- // Advance window and add hashes.
- self.windowAdvance(step, lh, pos);
- }
-
- if (should_flush) {
- // In the case of flushing, last few lookahead buffers were smaller then min match len.
- // So only last literal can be unwritten.
- assert(self.prev_match == null);
- try self.addPrevLiteral();
- self.prev_literal = null;
-
- try self.flushTokens(flush_opt);
- }
- }
-
- fn windowAdvance(self: *Self, step: u16, lh: []const u8, pos: u16) void {
- // current position is already added in findMatch
- self.lookup.bulkAdd(lh[1..], step - 1, pos + 1);
- self.win.advance(step);
- }
-
- // Add previous literal (if any) to the tokens list.
- fn addPrevLiteral(self: *Self) !void {
- if (self.prev_literal) |l| try self.addToken(Token.initLiteral(l));
- }
-
- // Add match to the tokens list, reset prev pointers.
- // Returns length of the added match.
- fn addMatch(self: *Self, m: Token) !u16 {
- try self.addToken(m);
- self.prev_literal = null;
- self.prev_match = null;
- return m.length();
- }
-
- fn addToken(self: *Self, token: Token) !void {
- self.tokens.add(token);
- if (self.tokens.full()) try self.flushTokens(.none);
- }
-
- // Finds largest match in the history window with the data at current pos.
- fn findMatch(self: *Self, pos: u16, lh: []const u8, min_len: u16) ?Token {
- var len: u16 = min_len;
- // Previous location with the same hash (same 4 bytes).
- var prev_pos = self.lookup.add(lh, pos);
- // Last found match.
- var match: ?Token = null;
-
- // How much back-references to try, performance knob.
- var chain: usize = self.level.chain;
- if (len >= self.level.good) {
- // If we've got a match that's good enough, only look in 1/4 the chain.
- chain >>= 2;
- }
-
- // Hot path loop!
- while (prev_pos > 0 and chain > 0) : (chain -= 1) {
- const distance = pos - prev_pos;
- if (distance > consts.match.max_distance)
- break;
-
- const new_len = self.win.match(prev_pos, pos, len);
- if (new_len > len) {
- match = Token.initMatch(@intCast(distance), new_len);
- if (new_len >= self.level.nice) {
- // The match is good enough that we don't try to find a better one.
- return match;
- }
- len = new_len;
- }
- prev_pos = self.lookup.prev(prev_pos);
- }
-
- return match;
- }
-
- fn flushTokens(self: *Self, flush_opt: FlushOption) !void {
- // Pass tokens to the token writer
- try self.block_writer.write(self.tokens.tokens(), flush_opt == .final, self.win.tokensBuffer());
- // Stored block ensures byte alignment.
- // It has 3 bits (final, block_type) and then padding until byte boundary.
- // After that everything is aligned to the boundary in the stored block.
- // Empty stored block is Ob000 + (0-7) bits of padding + 0x00 0x00 0xFF 0xFF.
- // Last 4 bytes are byte aligned.
- if (flush_opt == .flush) {
- try self.block_writer.storedBlock("", false);
- }
- if (flush_opt != .none) {
- // Safe to call only when byte aligned or it is OK to add
- // padding bits (on last byte of the final block).
- try self.block_writer.flush();
- }
- // Reset internal tokens store.
- self.tokens.reset();
- // Notify win that tokens are flushed.
- self.win.flush();
- }
-
- // Slide win and if needed lookup tables.
- fn slide(self: *Self) void {
- const n = self.win.slide();
- self.lookup.slide(n);
- }
-
- /// Compresses as much data as possible, stops when the reader becomes
- /// empty. It will introduce some output latency (reading input without
- /// producing all output) because some data are still in internal
- /// buffers.
- ///
- /// It is up to the caller to call flush (if needed) or finish (required)
- /// when is need to output any pending data or complete stream.
- ///
- pub fn compress(self: *Self, reader: anytype) !void {
- while (true) {
- // Fill window from reader
- const buf = self.win.writable();
- if (buf.len == 0) {
- try self.tokenize(.none);
- self.slide();
- continue;
- }
- const n = try reader.readAll(buf);
- self.hasher.update(buf[0..n]);
- self.win.written(n);
- // Process window
- try self.tokenize(.none);
- // Exit when no more data in reader
- if (n < buf.len) break;
- }
- }
-
- /// Flushes internal buffers to the output writer. Outputs empty stored
- /// block to sync bit stream to the byte boundary, so that the
- /// decompressor can get all input data available so far.
- ///
- /// It is useful mainly in compressed network protocols, to ensure that
- /// deflate bit stream can be used as byte stream. May degrade
- /// compression so it should be used only when necessary.
- ///
- /// Completes the current deflate block and follows it with an empty
- /// stored block that is three zero bits plus filler bits to the next
- /// byte, followed by four bytes (00 00 ff ff).
- ///
- pub fn flush(self: *Self) !void {
- try self.tokenize(.flush);
- }
-
- /// Completes deflate bit stream by writing any pending data as deflate
- /// final deflate block. HAS to be called once all data are written to
- /// the compressor as a signal that next block has to have final bit
- /// set.
- ///
- pub fn finish(self: *Self) !void {
- try self.tokenize(.final);
- try container.writeFooter(&self.hasher, self.wrt);
- }
-
- /// Use another writer while preserving history. Most probably flush
- /// should be called on old writer before setting new.
- pub fn setWriter(self: *Self, new_writer: WriterType) void {
- self.block_writer.setWriter(new_writer);
- self.wrt = new_writer;
- }
-
- // Writer interface
-
- pub const Writer = io.GenericWriter(*Self, Error, write);
- pub const Error = BlockWriterType.Error;
-
- /// Write `input` of uncompressed data.
- /// See compress.
- pub fn write(self: *Self, input: []const u8) !usize {
- var fbs = io.fixedBufferStream(input);
- try self.compress(fbs.reader());
- return input.len;
- }
-
- pub fn writer(self: *Self) Writer {
- return .{ .context = self };
- }
- };
-}
-
-// Tokens store
-const Tokens = struct {
- list: [consts.deflate.tokens]Token = undefined,
- pos: usize = 0,
-
- fn add(self: *Tokens, t: Token) void {
- self.list[self.pos] = t;
- self.pos += 1;
- }
-
- fn full(self: *Tokens) bool {
- return self.pos == self.list.len;
- }
-
- fn reset(self: *Tokens) void {
- self.pos = 0;
- }
-
- fn tokens(self: *Tokens) []const Token {
- return self.list[0..self.pos];
- }
-};
-
-/// Creates huffman only deflate blocks. Disables Lempel-Ziv match searching and
-/// only performs Huffman entropy encoding. Results in faster compression, much
-/// less memory requirements during compression but bigger compressed sizes.
-pub const huffman = struct {
- pub fn compress(comptime container: Container, reader: anytype, writer: anytype) !void {
- var c = try huffman.compressor(container, writer);
- try c.compress(reader);
- try c.finish();
- }
-
- pub fn Compressor(comptime container: Container, comptime WriterType: type) type {
- return SimpleCompressor(.huffman, container, WriterType);
- }
-
- pub fn compressor(comptime container: Container, writer: anytype) !huffman.Compressor(container, @TypeOf(writer)) {
- return try huffman.Compressor(container, @TypeOf(writer)).init(writer);
- }
-};
-
-/// Creates store blocks only. Data are not compressed only packed into deflate
-/// store blocks. That adds 9 bytes of header for each block. Max stored block
-/// size is 64K. Block is emitted when flush is called on on finish.
-pub const store = struct {
- pub fn compress(comptime container: Container, reader: anytype, writer: anytype) !void {
- var c = try store.compressor(container, writer);
- try c.compress(reader);
- try c.finish();
- }
-
- pub fn Compressor(comptime container: Container, comptime WriterType: type) type {
- return SimpleCompressor(.store, container, WriterType);
- }
-
- pub fn compressor(comptime container: Container, writer: anytype) !store.Compressor(container, @TypeOf(writer)) {
- return try store.Compressor(container, @TypeOf(writer)).init(writer);
- }
-};
-
-const SimpleCompressorKind = enum {
- huffman,
- store,
-};
-
-fn simpleCompressor(
- comptime kind: SimpleCompressorKind,
- comptime container: Container,
- writer: anytype,
-) !SimpleCompressor(kind, container, @TypeOf(writer)) {
- return try SimpleCompressor(kind, container, @TypeOf(writer)).init(writer);
-}
-
-fn SimpleCompressor(
- comptime kind: SimpleCompressorKind,
- comptime container: Container,
- comptime WriterType: type,
-) type {
- const BlockWriterType = BlockWriter(WriterType);
- return struct {
- buffer: [65535]u8 = undefined, // because store blocks are limited to 65535 bytes
- wp: usize = 0,
-
- wrt: WriterType,
- block_writer: BlockWriterType,
- hasher: container.Hasher() = .{},
-
- const Self = @This();
-
- pub fn init(wrt: WriterType) !Self {
- const self = Self{
- .wrt = wrt,
- .block_writer = BlockWriterType.init(wrt),
- };
- try container.writeHeader(self.wrt);
- return self;
- }
-
- pub fn flush(self: *Self) !void {
- try self.flushBuffer(false);
- try self.block_writer.storedBlock("", false);
- try self.block_writer.flush();
- }
-
- pub fn finish(self: *Self) !void {
- try self.flushBuffer(true);
- try self.block_writer.flush();
- try container.writeFooter(&self.hasher, self.wrt);
- }
-
- fn flushBuffer(self: *Self, final: bool) !void {
- const buf = self.buffer[0..self.wp];
- switch (kind) {
- .huffman => try self.block_writer.huffmanBlock(buf, final),
- .store => try self.block_writer.storedBlock(buf, final),
- }
- self.wp = 0;
- }
-
- // Writes all data from the input reader of uncompressed data.
- // It is up to the caller to call flush or finish if there is need to
- // output compressed blocks.
- pub fn compress(self: *Self, reader: anytype) !void {
- while (true) {
- // read from rdr into buffer
- const buf = self.buffer[self.wp..];
- if (buf.len == 0) {
- try self.flushBuffer(false);
- continue;
- }
- const n = try reader.readAll(buf);
- self.hasher.update(buf[0..n]);
- self.wp += n;
- if (n < buf.len) break; // no more data in reader
- }
- }
-
- // Writer interface
-
- pub const Writer = io.GenericWriter(*Self, Error, write);
- pub const Error = BlockWriterType.Error;
-
- // Write `input` of uncompressed data.
- pub fn write(self: *Self, input: []const u8) !usize {
- var fbs = io.fixedBufferStream(input);
- try self.compress(fbs.reader());
- return input.len;
- }
-
- pub fn writer(self: *Self) Writer {
- return .{ .context = self };
- }
- };
-}
-
-const builtin = @import("builtin");
-
-test "tokenization" {
- const L = Token.initLiteral;
- const M = Token.initMatch;
-
- const cases = [_]struct {
- data: []const u8,
- tokens: []const Token,
- }{
- .{
- .data = "Blah blah blah blah blah!",
- .tokens = &[_]Token{ L('B'), L('l'), L('a'), L('h'), L(' '), L('b'), M(5, 18), L('!') },
- },
- .{
- .data = "ABCDEABCD ABCDEABCD",
- .tokens = &[_]Token{
- L('A'), L('B'), L('C'), L('D'), L('E'), L('A'), L('B'), L('C'), L('D'), L(' '),
- L('A'), M(10, 8),
- },
- },
- };
-
- for (cases) |c| {
- inline for (Container.list) |container| { // for each wrapping
-
- var cw = io.countingWriter(io.null_writer);
- const cww = cw.writer();
- var df = try Deflate(container, @TypeOf(cww), TestTokenWriter).init(cww, .{});
-
- _ = try df.write(c.data);
- try df.flush();
-
- // df.token_writer.show();
- try expect(df.block_writer.pos == c.tokens.len); // number of tokens written
- try testing.expectEqualSlices(Token, df.block_writer.get(), c.tokens); // tokens match
-
- try testing.expectEqual(container.headerSize(), cw.bytes_written);
- try df.finish();
- try testing.expectEqual(container.size(), cw.bytes_written);
- }
- }
-}
-
-// Tests that tokens written are equal to expected token list.
-const TestTokenWriter = struct {
- const Self = @This();
-
- pos: usize = 0,
- actual: [128]Token = undefined,
-
- pub fn init(_: anytype) Self {
- return .{};
- }
- pub fn write(self: *Self, tokens: []const Token, _: bool, _: ?[]const u8) !void {
- for (tokens) |t| {
- self.actual[self.pos] = t;
- self.pos += 1;
- }
- }
-
- pub fn storedBlock(_: *Self, _: []const u8, _: bool) !void {}
-
- pub fn get(self: *Self) []Token {
- return self.actual[0..self.pos];
- }
-
- pub fn show(self: *Self) void {
- print("\n", .{});
- for (self.get()) |t| {
- t.show();
- }
- }
-
- pub fn flush(_: *Self) !void {}
-};
-
-test "file tokenization" {
- const levels = [_]Level{ .level_4, .level_5, .level_6, .level_7, .level_8, .level_9 };
- const cases = [_]struct {
- data: []const u8, // uncompressed content
- // expected number of tokens producet in deflate tokenization
- tokens_count: [levels.len]usize = .{0} ** levels.len,
- }{
- .{
- .data = @embedFile("testdata/rfc1951.txt"),
- .tokens_count = .{ 7675, 7672, 7599, 7594, 7598, 7599 },
- },
-
- .{
- .data = @embedFile("testdata/block_writer/huffman-null-max.input"),
- .tokens_count = .{ 257, 257, 257, 257, 257, 257 },
- },
- .{
- .data = @embedFile("testdata/block_writer/huffman-pi.input"),
- .tokens_count = .{ 2570, 2564, 2564, 2564, 2564, 2564 },
- },
- .{
- .data = @embedFile("testdata/block_writer/huffman-text.input"),
- .tokens_count = .{ 235, 234, 234, 234, 234, 234 },
- },
- .{
- .data = @embedFile("testdata/fuzz/roundtrip1.input"),
- .tokens_count = .{ 333, 331, 331, 331, 331, 331 },
- },
- .{
- .data = @embedFile("testdata/fuzz/roundtrip2.input"),
- .tokens_count = .{ 334, 334, 334, 334, 334, 334 },
- },
- };
-
- for (cases) |case| { // for each case
- const data = case.data;
-
- for (levels, 0..) |level, i| { // for each compression level
- var original = io.fixedBufferStream(data);
-
- // buffer for decompressed data
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
- const writer = al.writer();
-
- // create compressor
- const WriterType = @TypeOf(writer);
- const TokenWriter = TokenDecoder(@TypeOf(writer));
- var cmp = try Deflate(.raw, WriterType, TokenWriter).init(writer, .{ .level = level });
-
- // Stream uncompressed `original` data to the compressor. It will
- // produce tokens list and pass that list to the TokenDecoder. This
- // TokenDecoder uses CircularBuffer from inflate to convert list of
- // tokens back to the uncompressed stream.
- try cmp.compress(original.reader());
- try cmp.flush();
- const expected_count = case.tokens_count[i];
- const actual = cmp.block_writer.tokens_count;
- if (expected_count == 0) {
- print("actual token count {d}\n", .{actual});
- } else {
- try testing.expectEqual(expected_count, actual);
- }
-
- try testing.expectEqual(data.len, al.items.len);
- try testing.expectEqualSlices(u8, data, al.items);
- }
- }
-}
-
-fn TokenDecoder(comptime WriterType: type) type {
- return struct {
- const CircularBuffer = @import("CircularBuffer.zig");
- hist: CircularBuffer = .{},
- wrt: WriterType,
- tokens_count: usize = 0,
-
- const Self = @This();
-
- pub fn init(wrt: WriterType) Self {
- return .{ .wrt = wrt };
- }
-
- pub fn write(self: *Self, tokens: []const Token, _: bool, _: ?[]const u8) !void {
- self.tokens_count += tokens.len;
- for (tokens) |t| {
- switch (t.kind) {
- .literal => self.hist.write(t.literal()),
- .match => try self.hist.writeMatch(t.length(), t.distance()),
- }
- if (self.hist.free() < 285) try self.flushWin();
- }
- try self.flushWin();
- }
-
- pub fn storedBlock(_: *Self, _: []const u8, _: bool) !void {}
-
- fn flushWin(self: *Self) !void {
- while (true) {
- const buf = self.hist.read();
- if (buf.len == 0) break;
- try self.wrt.writeAll(buf);
- }
- }
-
- pub fn flush(_: *Self) !void {}
- };
-}
-
-test "store simple compressor" {
- const data = "Hello world!";
- const expected = [_]u8{
- 0x1, // block type 0, final bit set
- 0xc, 0x0, // len = 12
- 0xf3, 0xff, // ~len
- 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', //
- //0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
- };
-
- var fbs = std.io.fixedBufferStream(data);
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
-
- var cmp = try store.compressor(.raw, al.writer());
- try cmp.compress(fbs.reader());
- try cmp.finish();
- try testing.expectEqualSlices(u8, &expected, al.items);
-
- fbs.reset();
- try al.resize(0);
-
- // huffman only compresoor will also emit store block for this small sample
- var hc = try huffman.compressor(.raw, al.writer());
- try hc.compress(fbs.reader());
- try hc.finish();
- try testing.expectEqualSlices(u8, &expected, al.items);
-}
diff --git a/lib/std/compress/flate/huffman_decoder.zig b/lib/std/compress/flate/huffman_decoder.zig
deleted file mode 100644
index abff915f761a5d50ca1a43bcae62771688f66bb0..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/huffman_decoder.zig
+++ /dev/null
@@ -1,302 +0,0 @@
-const std = @import("std");
-const testing = std.testing;
-
-pub const Symbol = packed struct {
- pub const Kind = enum(u2) {
- literal,
- end_of_block,
- match,
- };
-
- symbol: u8 = 0, // symbol from alphabet
- code_bits: u4 = 0, // number of bits in code 0-15
- kind: Kind = .literal,
-
- code: u16 = 0, // huffman code of the symbol
- next: u16 = 0, // pointer to the next symbol in linked list
- // it is safe to use 0 as null pointer, when sorted 0 has shortest code and fits into lookup
-
- // Sorting less than function.
- pub fn asc(_: void, a: Symbol, b: Symbol) bool {
- if (a.code_bits == b.code_bits) {
- if (a.kind == b.kind) {
- return a.symbol < b.symbol;
- }
- return @intFromEnum(a.kind) < @intFromEnum(b.kind);
- }
- return a.code_bits < b.code_bits;
- }
-};
-
-pub const LiteralDecoder = HuffmanDecoder(286, 15, 9);
-pub const DistanceDecoder = HuffmanDecoder(30, 15, 9);
-pub const CodegenDecoder = HuffmanDecoder(19, 7, 7);
-
-pub const Error = error{
- InvalidCode,
- OversubscribedHuffmanTree,
- IncompleteHuffmanTree,
- MissingEndOfBlockCode,
-};
-
-/// Creates huffman tree codes from list of code lengths (in `build`).
-///
-/// `find` then finds symbol for code bits. Code can be any length between 1 and
-/// 15 bits. When calling `find` we don't know how many bits will be used to
-/// find symbol. When symbol is returned it has code_bits field which defines
-/// how much we should advance in bit stream.
-///
-/// Lookup table is used to map 15 bit int to symbol. Same symbol is written
-/// many times in this table; 32K places for 286 (at most) symbols.
-/// Small lookup table is optimization for faster search.
-/// It is variation of the algorithm explained in [zlib](https://github.com/madler/zlib/blob/643e17b7498d12ab8d15565662880579692f769d/doc/algorithm.txt#L92)
-/// with difference that we here use statically allocated arrays.
-///
-fn HuffmanDecoder(
- comptime alphabet_size: u16,
- comptime max_code_bits: u4,
- comptime lookup_bits: u4,
-) type {
- const lookup_shift = max_code_bits - lookup_bits;
-
- return struct {
- // all symbols in alaphabet, sorted by code_len, symbol
- symbols: [alphabet_size]Symbol = undefined,
- // lookup table code -> symbol
- lookup: [1 << lookup_bits]Symbol = undefined,
-
- const Self = @This();
-
- /// Generates symbols and lookup tables from list of code lens for each symbol.
- pub fn generate(self: *Self, lens: []const u4) !void {
- try checkCompleteness(lens);
-
- // init alphabet with code_bits
- for (self.symbols, 0..) |_, i| {
- const cb: u4 = if (i < lens.len) lens[i] else 0;
- self.symbols[i] = if (i < 256)
- .{ .kind = .literal, .symbol = @intCast(i), .code_bits = cb }
- else if (i == 256)
- .{ .kind = .end_of_block, .symbol = 0xff, .code_bits = cb }
- else
- .{ .kind = .match, .symbol = @intCast(i - 257), .code_bits = cb };
- }
- std.sort.heap(Symbol, &self.symbols, {}, Symbol.asc);
-
- // reset lookup table
- for (0..self.lookup.len) |i| {
- self.lookup[i] = .{};
- }
-
- // assign code to symbols
- // reference: https://youtu.be/9_YEGLe33NA?list=PLU4IQLU9e_OrY8oASHx0u3IXAL9TOdidm&t=2639
- var code: u16 = 0;
- var idx: u16 = 0;
- for (&self.symbols, 0..) |*sym, pos| {
- if (sym.code_bits == 0) continue; // skip unused
- sym.code = code;
-
- const next_code = code + (@as(u16, 1) << (max_code_bits - sym.code_bits));
- const next_idx = next_code >> lookup_shift;
-
- if (next_idx > self.lookup.len or idx >= self.lookup.len) break;
- if (sym.code_bits <= lookup_bits) {
- // fill small lookup table
- for (idx..next_idx) |j|
- self.lookup[j] = sym.*;
- } else {
- // insert into linked table starting at root
- const root = &self.lookup[idx];
- const root_next = root.next;
- root.next = @intCast(pos);
- sym.next = root_next;
- }
-
- idx = next_idx;
- code = next_code;
- }
- }
-
- /// Given the list of code lengths check that it represents a canonical
- /// Huffman code for n symbols.
- ///
- /// Reference: https://github.com/madler/zlib/blob/5c42a230b7b468dff011f444161c0145b5efae59/contrib/puff/puff.c#L340
- fn checkCompleteness(lens: []const u4) !void {
- if (alphabet_size == 286)
- if (lens[256] == 0) return error.MissingEndOfBlockCode;
-
- var count = [_]u16{0} ** (@as(usize, max_code_bits) + 1);
- var max: usize = 0;
- for (lens) |n| {
- if (n == 0) continue;
- if (n > max) max = n;
- count[n] += 1;
- }
- if (max == 0) // empty tree
- return;
-
- // check for an over-subscribed or incomplete set of lengths
- var left: usize = 1; // one possible code of zero length
- for (1..count.len) |len| {
- left <<= 1; // one more bit, double codes left
- if (count[len] > left)
- return error.OversubscribedHuffmanTree;
- left -= count[len]; // deduct count from possible codes
- }
- if (left > 0) { // left > 0 means incomplete
- // incomplete code ok only for single length 1 code
- if (max_code_bits > 7 and max == count[0] + count[1]) return;
- return error.IncompleteHuffmanTree;
- }
- }
-
- /// Finds symbol for lookup table code.
- pub fn find(self: *Self, code: u16) !Symbol {
- // try to find in lookup table
- const idx = code >> lookup_shift;
- const sym = self.lookup[idx];
- if (sym.code_bits != 0) return sym;
- // if not use linked list of symbols with same prefix
- return self.findLinked(code, sym.next);
- }
-
- inline fn findLinked(self: *Self, code: u16, start: u16) !Symbol {
- var pos = start;
- while (pos > 0) {
- const sym = self.symbols[pos];
- const shift = max_code_bits - sym.code_bits;
- // compare code_bits number of upper bits
- if ((code ^ sym.code) >> shift == 0) return sym;
- pos = sym.next;
- }
- return error.InvalidCode;
- }
- };
-}
-
-test "init/find" {
- // example data from: https://youtu.be/SJPvNi4HrWQ?t=8423
- const code_lens = [_]u4{ 4, 3, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 2 };
- var h: CodegenDecoder = .{};
- try h.generate(&code_lens);
-
- const expected = [_]struct {
- sym: Symbol,
- code: u16,
- }{
- .{
- .code = 0b00_00000,
- .sym = .{ .symbol = 3, .code_bits = 2 },
- },
- .{
- .code = 0b01_00000,
- .sym = .{ .symbol = 18, .code_bits = 2 },
- },
- .{
- .code = 0b100_0000,
- .sym = .{ .symbol = 1, .code_bits = 3 },
- },
- .{
- .code = 0b101_0000,
- .sym = .{ .symbol = 4, .code_bits = 3 },
- },
- .{
- .code = 0b110_0000,
- .sym = .{ .symbol = 17, .code_bits = 3 },
- },
- .{
- .code = 0b1110_000,
- .sym = .{ .symbol = 0, .code_bits = 4 },
- },
- .{
- .code = 0b1111_000,
- .sym = .{ .symbol = 16, .code_bits = 4 },
- },
- };
-
- // unused symbols
- for (0..12) |i| {
- try testing.expectEqual(0, h.symbols[i].code_bits);
- }
- // used, from index 12
- for (expected, 12..) |e, i| {
- try testing.expectEqual(e.sym.symbol, h.symbols[i].symbol);
- try testing.expectEqual(e.sym.code_bits, h.symbols[i].code_bits);
- const sym_from_code = try h.find(e.code);
- try testing.expectEqual(e.sym.symbol, sym_from_code.symbol);
- }
-
- // All possible codes for each symbol.
- // Lookup table has 126 elements, to cover all possible 7 bit codes.
- for (0b0000_000..0b0100_000) |c| // 0..32 (32)
- try testing.expectEqual(3, (try h.find(@intCast(c))).symbol);
-
- for (0b0100_000..0b1000_000) |c| // 32..64 (32)
- try testing.expectEqual(18, (try h.find(@intCast(c))).symbol);
-
- for (0b1000_000..0b1010_000) |c| // 64..80 (16)
- try testing.expectEqual(1, (try h.find(@intCast(c))).symbol);
-
- for (0b1010_000..0b1100_000) |c| // 80..96 (16)
- try testing.expectEqual(4, (try h.find(@intCast(c))).symbol);
-
- for (0b1100_000..0b1110_000) |c| // 96..112 (16)
- try testing.expectEqual(17, (try h.find(@intCast(c))).symbol);
-
- for (0b1110_000..0b1111_000) |c| // 112..120 (8)
- try testing.expectEqual(0, (try h.find(@intCast(c))).symbol);
-
- for (0b1111_000..0b1_0000_000) |c| // 120...128 (8)
- try testing.expectEqual(16, (try h.find(@intCast(c))).symbol);
-}
-
-test "encode/decode literals" {
- const LiteralEncoder = @import("huffman_encoder.zig").LiteralEncoder;
-
- for (1..286) |j| { // for all different number of codes
- var enc: LiteralEncoder = .{};
- // create frequencies
- var freq = [_]u16{0} ** 286;
- freq[256] = 1; // ensure we have end of block code
- for (&freq, 1..) |*f, i| {
- if (i % j == 0)
- f.* = @intCast(i);
- }
-
- // encoder from frequencies
- enc.generate(&freq, 15);
-
- // get code_lens from encoder
- var code_lens = [_]u4{0} ** 286;
- for (code_lens, 0..) |_, i| {
- code_lens[i] = @intCast(enc.codes[i].len);
- }
- // generate decoder from code lens
- var dec: LiteralDecoder = .{};
- try dec.generate(&code_lens);
-
- // expect decoder code to match original encoder code
- for (dec.symbols) |s| {
- if (s.code_bits == 0) continue;
- const c_code: u16 = @bitReverse(@as(u15, @intCast(s.code)));
- const symbol: u16 = switch (s.kind) {
- .literal => s.symbol,
- .end_of_block => 256,
- .match => @as(u16, s.symbol) + 257,
- };
-
- const c = enc.codes[symbol];
- try testing.expect(c.code == c_code);
- }
-
- // find each symbol by code
- for (enc.codes) |c| {
- if (c.len == 0) continue;
-
- const s_code: u15 = @bitReverse(@as(u15, @intCast(c.code)));
- const s = try dec.find(s_code);
- try testing.expect(s.code == s_code);
- try testing.expect(s.code_bits == c.len);
- }
- }
-}
diff --git a/lib/std/compress/flate/huffman_encoder.zig b/lib/std/compress/flate/huffman_encoder.zig
deleted file mode 100644
index 3e92e55a630d8223dc1d874d8d362ea5fc573ea0..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/huffman_encoder.zig
+++ /dev/null
@@ -1,536 +0,0 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const math = std.math;
-const mem = std.mem;
-const sort = std.sort;
-const testing = std.testing;
-
-const consts = @import("consts.zig").huffman;
-
-const LiteralNode = struct {
- literal: u16,
- freq: u16,
-};
-
-// Describes the state of the constructed tree for a given depth.
-const LevelInfo = struct {
- // Our level. for better printing
- level: u32,
-
- // The frequency of the last node at this level
- last_freq: u32,
-
- // The frequency of the next character to add to this level
- next_char_freq: u32,
-
- // The frequency of the next pair (from level below) to add to this level.
- // Only valid if the "needed" value of the next lower level is 0.
- next_pair_freq: u32,
-
- // The number of chains remaining to generate for this level before moving
- // up to the next level
- needed: u32,
-};
-
-// hcode is a huffman code with a bit code and bit length.
-pub const HuffCode = struct {
- code: u16 = 0,
- len: u16 = 0,
-
- // set sets the code and length of an hcode.
- fn set(self: *HuffCode, code: u16, length: u16) void {
- self.len = length;
- self.code = code;
- }
-};
-
-pub fn HuffmanEncoder(comptime size: usize) type {
- return struct {
- codes: [size]HuffCode = undefined,
- // Reusable buffer with the longest possible frequency table.
- freq_cache: [consts.max_num_frequencies + 1]LiteralNode = undefined,
- bit_count: [17]u32 = undefined,
- lns: []LiteralNode = undefined, // sorted by literal, stored to avoid repeated allocation in generate
- lfs: []LiteralNode = undefined, // sorted by frequency, stored to avoid repeated allocation in generate
-
- const Self = @This();
-
- // Update this Huffman Code object to be the minimum code for the specified frequency count.
- //
- // freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
- // max_bits The maximum number of bits to use for any literal.
- pub fn generate(self: *Self, freq: []u16, max_bits: u32) void {
- var list = self.freq_cache[0 .. freq.len + 1];
- // Number of non-zero literals
- var count: u32 = 0;
- // Set list to be the set of all non-zero literals and their frequencies
- for (freq, 0..) |f, i| {
- if (f != 0) {
- list[count] = LiteralNode{ .literal = @as(u16, @intCast(i)), .freq = f };
- count += 1;
- } else {
- list[count] = LiteralNode{ .literal = 0x00, .freq = 0 };
- self.codes[i].len = 0;
- }
- }
- list[freq.len] = LiteralNode{ .literal = 0x00, .freq = 0 };
-
- list = list[0..count];
- if (count <= 2) {
- // Handle the small cases here, because they are awkward for the general case code. With
- // two or fewer literals, everything has bit length 1.
- for (list, 0..) |node, i| {
- // "list" is in order of increasing literal value.
- self.codes[node.literal].set(@as(u16, @intCast(i)), 1);
- }
- return;
- }
- self.lfs = list;
- mem.sort(LiteralNode, self.lfs, {}, byFreq);
-
- // Get the number of literals for each bit count
- const bit_count = self.bitCounts(list, max_bits);
- // And do the assignment
- self.assignEncodingAndSize(bit_count, list);
- }
-
- pub fn bitLength(self: *Self, freq: []u16) u32 {
- var total: u32 = 0;
- for (freq, 0..) |f, i| {
- if (f != 0) {
- total += @as(u32, @intCast(f)) * @as(u32, @intCast(self.codes[i].len));
- }
- }
- return total;
- }
-
- // Return the number of literals assigned to each bit size in the Huffman encoding
- //
- // This method is only called when list.len >= 3
- // The cases of 0, 1, and 2 literals are handled by special case code.
- //
- // list: An array of the literals with non-zero frequencies
- // and their associated frequencies. The array is in order of increasing
- // frequency, and has as its last element a special element with frequency
- // std.math.maxInt(i32)
- //
- // max_bits: The maximum number of bits that should be used to encode any literal.
- // Must be less than 16.
- //
- // Returns an integer array in which array[i] indicates the number of literals
- // that should be encoded in i bits.
- fn bitCounts(self: *Self, list: []LiteralNode, max_bits_to_use: usize) []u32 {
- var max_bits = max_bits_to_use;
- const n = list.len;
- const max_bits_limit = 16;
-
- assert(max_bits < max_bits_limit);
-
- // The tree can't have greater depth than n - 1, no matter what. This
- // saves a little bit of work in some small cases
- max_bits = @min(max_bits, n - 1);
-
- // Create information about each of the levels.
- // A bogus "Level 0" whose sole purpose is so that
- // level1.prev.needed == 0. This makes level1.next_pair_freq
- // be a legitimate value that never gets chosen.
- var levels: [max_bits_limit]LevelInfo = mem.zeroes([max_bits_limit]LevelInfo);
- // leaf_counts[i] counts the number of literals at the left
- // of ancestors of the rightmost node at level i.
- // leaf_counts[i][j] is the number of literals at the left
- // of the level j ancestor.
- var leaf_counts: [max_bits_limit][max_bits_limit]u32 = mem.zeroes([max_bits_limit][max_bits_limit]u32);
-
- {
- var level = @as(u32, 1);
- while (level <= max_bits) : (level += 1) {
- // For every level, the first two items are the first two characters.
- // We initialize the levels as if we had already figured this out.
- levels[level] = LevelInfo{
- .level = level,
- .last_freq = list[1].freq,
- .next_char_freq = list[2].freq,
- .next_pair_freq = list[0].freq + list[1].freq,
- .needed = 0,
- };
- leaf_counts[level][level] = 2;
- if (level == 1) {
- levels[level].next_pair_freq = math.maxInt(i32);
- }
- }
- }
-
- // We need a total of 2*n - 2 items at top level and have already generated 2.
- levels[max_bits].needed = 2 * @as(u32, @intCast(n)) - 4;
-
- {
- var level = max_bits;
- while (true) {
- var l = &levels[level];
- if (l.next_pair_freq == math.maxInt(i32) and l.next_char_freq == math.maxInt(i32)) {
- // We've run out of both leaves and pairs.
- // End all calculations for this level.
- // To make sure we never come back to this level or any lower level,
- // set next_pair_freq impossibly large.
- l.needed = 0;
- levels[level + 1].next_pair_freq = math.maxInt(i32);
- level += 1;
- continue;
- }
-
- const prev_freq = l.last_freq;
- if (l.next_char_freq < l.next_pair_freq) {
- // The next item on this row is a leaf node.
- const next = leaf_counts[level][level] + 1;
- l.last_freq = l.next_char_freq;
- // Lower leaf_counts are the same of the previous node.
- leaf_counts[level][level] = next;
- if (next >= list.len) {
- l.next_char_freq = maxNode().freq;
- } else {
- l.next_char_freq = list[next].freq;
- }
- } else {
- // The next item on this row is a pair from the previous row.
- // next_pair_freq isn't valid until we generate two
- // more values in the level below
- l.last_freq = l.next_pair_freq;
- // Take leaf counts from the lower level, except counts[level] remains the same.
- @memcpy(leaf_counts[level][0..level], leaf_counts[level - 1][0..level]);
- levels[l.level - 1].needed = 2;
- }
-
- l.needed -= 1;
- if (l.needed == 0) {
- // We've done everything we need to do for this level.
- // Continue calculating one level up. Fill in next_pair_freq
- // of that level with the sum of the two nodes we've just calculated on
- // this level.
- if (l.level == max_bits) {
- // All done!
- break;
- }
- levels[l.level + 1].next_pair_freq = prev_freq + l.last_freq;
- level += 1;
- } else {
- // If we stole from below, move down temporarily to replenish it.
- while (levels[level - 1].needed > 0) {
- level -= 1;
- if (level == 0) {
- break;
- }
- }
- }
- }
- }
-
- // Somethings is wrong if at the end, the top level is null or hasn't used
- // all of the leaves.
- assert(leaf_counts[max_bits][max_bits] == n);
-
- var bit_count = self.bit_count[0 .. max_bits + 1];
- var bits: u32 = 1;
- const counts = &leaf_counts[max_bits];
- {
- var level = max_bits;
- while (level > 0) : (level -= 1) {
- // counts[level] gives the number of literals requiring at least "bits"
- // bits to encode.
- bit_count[bits] = counts[level] - counts[level - 1];
- bits += 1;
- if (level == 0) {
- break;
- }
- }
- }
- return bit_count;
- }
-
- // Look at the leaves and assign them a bit count and an encoding as specified
- // in RFC 1951 3.2.2
- fn assignEncodingAndSize(self: *Self, bit_count: []u32, list_arg: []LiteralNode) void {
- var code = @as(u16, 0);
- var list = list_arg;
-
- for (bit_count, 0..) |bits, n| {
- code <<= 1;
- if (n == 0 or bits == 0) {
- continue;
- }
- // The literals list[list.len-bits] .. list[list.len-bits]
- // are encoded using "bits" bits, and get the values
- // code, code + 1, .... The code values are
- // assigned in literal order (not frequency order).
- const chunk = list[list.len - @as(u32, @intCast(bits)) ..];
-
- self.lns = chunk;
- mem.sort(LiteralNode, self.lns, {}, byLiteral);
-
- for (chunk) |node| {
- self.codes[node.literal] = HuffCode{
- .code = bitReverse(u16, code, @as(u5, @intCast(n))),
- .len = @as(u16, @intCast(n)),
- };
- code += 1;
- }
- list = list[0 .. list.len - @as(u32, @intCast(bits))];
- }
- }
- };
-}
-
-fn maxNode() LiteralNode {
- return LiteralNode{
- .literal = math.maxInt(u16),
- .freq = math.maxInt(u16),
- };
-}
-
-pub fn huffmanEncoder(comptime size: u32) HuffmanEncoder(size) {
- return .{};
-}
-
-pub const LiteralEncoder = HuffmanEncoder(consts.max_num_frequencies);
-pub const DistanceEncoder = HuffmanEncoder(consts.distance_code_count);
-pub const CodegenEncoder = HuffmanEncoder(19);
-
-// Generates a HuffmanCode corresponding to the fixed literal table
-pub fn fixedLiteralEncoder() LiteralEncoder {
- var h: LiteralEncoder = undefined;
- var ch: u16 = 0;
-
- while (ch < consts.max_num_frequencies) : (ch += 1) {
- var bits: u16 = undefined;
- var size: u16 = undefined;
- switch (ch) {
- 0...143 => {
- // size 8, 000110000 .. 10111111
- bits = ch + 48;
- size = 8;
- },
- 144...255 => {
- // size 9, 110010000 .. 111111111
- bits = ch + 400 - 144;
- size = 9;
- },
- 256...279 => {
- // size 7, 0000000 .. 0010111
- bits = ch - 256;
- size = 7;
- },
- else => {
- // size 8, 11000000 .. 11000111
- bits = ch + 192 - 280;
- size = 8;
- },
- }
- h.codes[ch] = HuffCode{ .code = bitReverse(u16, bits, @as(u5, @intCast(size))), .len = size };
- }
- return h;
-}
-
-pub fn fixedDistanceEncoder() DistanceEncoder {
- var h: DistanceEncoder = undefined;
- for (h.codes, 0..) |_, ch| {
- h.codes[ch] = HuffCode{ .code = bitReverse(u16, @as(u16, @intCast(ch)), 5), .len = 5 };
- }
- return h;
-}
-
-pub fn huffmanDistanceEncoder() DistanceEncoder {
- var distance_freq = [1]u16{0} ** consts.distance_code_count;
- distance_freq[0] = 1;
- // huff_distance is a static distance encoder used for huffman only encoding.
- // It can be reused since we will not be encoding distance values.
- var h: DistanceEncoder = .{};
- h.generate(distance_freq[0..], 15);
- return h;
-}
-
-fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
- _ = context;
- return a.literal < b.literal;
-}
-
-fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
- _ = context;
- if (a.freq == b.freq) {
- return a.literal < b.literal;
- }
- return a.freq < b.freq;
-}
-
-test "generate a Huffman code from an array of frequencies" {
- var freqs: [19]u16 = [_]u16{
- 8, // 0
- 1, // 1
- 1, // 2
- 2, // 3
- 5, // 4
- 10, // 5
- 9, // 6
- 1, // 7
- 0, // 8
- 0, // 9
- 0, // 10
- 0, // 11
- 0, // 12
- 0, // 13
- 0, // 14
- 0, // 15
- 1, // 16
- 3, // 17
- 5, // 18
- };
-
- var enc = huffmanEncoder(19);
- enc.generate(freqs[0..], 7);
-
- try testing.expectEqual(@as(u32, 141), enc.bitLength(freqs[0..]));
-
- try testing.expectEqual(@as(usize, 3), enc.codes[0].len);
- try testing.expectEqual(@as(usize, 6), enc.codes[1].len);
- try testing.expectEqual(@as(usize, 6), enc.codes[2].len);
- try testing.expectEqual(@as(usize, 5), enc.codes[3].len);
- try testing.expectEqual(@as(usize, 3), enc.codes[4].len);
- try testing.expectEqual(@as(usize, 2), enc.codes[5].len);
- try testing.expectEqual(@as(usize, 2), enc.codes[6].len);
- try testing.expectEqual(@as(usize, 6), enc.codes[7].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[8].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[9].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[10].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[11].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[12].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[13].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[14].len);
- try testing.expectEqual(@as(usize, 0), enc.codes[15].len);
- try testing.expectEqual(@as(usize, 6), enc.codes[16].len);
- try testing.expectEqual(@as(usize, 5), enc.codes[17].len);
- try testing.expectEqual(@as(usize, 3), enc.codes[18].len);
-
- try testing.expectEqual(@as(u16, 0x0), enc.codes[5].code);
- try testing.expectEqual(@as(u16, 0x2), enc.codes[6].code);
- try testing.expectEqual(@as(u16, 0x1), enc.codes[0].code);
- try testing.expectEqual(@as(u16, 0x5), enc.codes[4].code);
- try testing.expectEqual(@as(u16, 0x3), enc.codes[18].code);
- try testing.expectEqual(@as(u16, 0x7), enc.codes[3].code);
- try testing.expectEqual(@as(u16, 0x17), enc.codes[17].code);
- try testing.expectEqual(@as(u16, 0x0f), enc.codes[1].code);
- try testing.expectEqual(@as(u16, 0x2f), enc.codes[2].code);
- try testing.expectEqual(@as(u16, 0x1f), enc.codes[7].code);
- try testing.expectEqual(@as(u16, 0x3f), enc.codes[16].code);
-}
-
-test "generate a Huffman code for the fixed literal table specific to Deflate" {
- const enc = fixedLiteralEncoder();
- for (enc.codes) |c| {
- switch (c.len) {
- 7 => {
- const v = @bitReverse(@as(u7, @intCast(c.code)));
- try testing.expect(v <= 0b0010111);
- },
- 8 => {
- const v = @bitReverse(@as(u8, @intCast(c.code)));
- try testing.expect((v >= 0b000110000 and v <= 0b10111111) or
- (v >= 0b11000000 and v <= 11000111));
- },
- 9 => {
- const v = @bitReverse(@as(u9, @intCast(c.code)));
- try testing.expect(v >= 0b110010000 and v <= 0b111111111);
- },
- else => unreachable,
- }
- }
-}
-
-test "generate a Huffman code for the 30 possible relative distances (LZ77 distances) of Deflate" {
- const enc = fixedDistanceEncoder();
- for (enc.codes) |c| {
- const v = @bitReverse(@as(u5, @intCast(c.code)));
- try testing.expect(v <= 29);
- try testing.expect(c.len == 5);
- }
-}
-
-// Reverse bit-by-bit a N-bit code.
-fn bitReverse(comptime T: type, value: T, n: usize) T {
- const r = @bitReverse(value);
- return r >> @as(math.Log2Int(T), @intCast(@typeInfo(T).int.bits - n));
-}
-
-test bitReverse {
- const ReverseBitsTest = struct {
- in: u16,
- bit_count: u5,
- out: u16,
- };
-
- const reverse_bits_tests = [_]ReverseBitsTest{
- .{ .in = 1, .bit_count = 1, .out = 1 },
- .{ .in = 1, .bit_count = 2, .out = 2 },
- .{ .in = 1, .bit_count = 3, .out = 4 },
- .{ .in = 1, .bit_count = 4, .out = 8 },
- .{ .in = 1, .bit_count = 5, .out = 16 },
- .{ .in = 17, .bit_count = 5, .out = 17 },
- .{ .in = 257, .bit_count = 9, .out = 257 },
- .{ .in = 29, .bit_count = 5, .out = 23 },
- };
-
- for (reverse_bits_tests) |h| {
- const v = bitReverse(u16, h.in, h.bit_count);
- try std.testing.expectEqual(h.out, v);
- }
-}
-
-test "fixedLiteralEncoder codes" {
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
- var bw = std.io.bitWriter(.little, al.writer());
-
- const f = fixedLiteralEncoder();
- for (f.codes) |c| {
- try bw.writeBits(c.code, c.len);
- }
- try testing.expectEqualSlices(u8, &fixed_codes, al.items);
-}
-
-pub const fixed_codes = [_]u8{
- 0b00001100, 0b10001100, 0b01001100, 0b11001100, 0b00101100, 0b10101100, 0b01101100, 0b11101100,
- 0b00011100, 0b10011100, 0b01011100, 0b11011100, 0b00111100, 0b10111100, 0b01111100, 0b11111100,
- 0b00000010, 0b10000010, 0b01000010, 0b11000010, 0b00100010, 0b10100010, 0b01100010, 0b11100010,
- 0b00010010, 0b10010010, 0b01010010, 0b11010010, 0b00110010, 0b10110010, 0b01110010, 0b11110010,
- 0b00001010, 0b10001010, 0b01001010, 0b11001010, 0b00101010, 0b10101010, 0b01101010, 0b11101010,
- 0b00011010, 0b10011010, 0b01011010, 0b11011010, 0b00111010, 0b10111010, 0b01111010, 0b11111010,
- 0b00000110, 0b10000110, 0b01000110, 0b11000110, 0b00100110, 0b10100110, 0b01100110, 0b11100110,
- 0b00010110, 0b10010110, 0b01010110, 0b11010110, 0b00110110, 0b10110110, 0b01110110, 0b11110110,
- 0b00001110, 0b10001110, 0b01001110, 0b11001110, 0b00101110, 0b10101110, 0b01101110, 0b11101110,
- 0b00011110, 0b10011110, 0b01011110, 0b11011110, 0b00111110, 0b10111110, 0b01111110, 0b11111110,
- 0b00000001, 0b10000001, 0b01000001, 0b11000001, 0b00100001, 0b10100001, 0b01100001, 0b11100001,
- 0b00010001, 0b10010001, 0b01010001, 0b11010001, 0b00110001, 0b10110001, 0b01110001, 0b11110001,
- 0b00001001, 0b10001001, 0b01001001, 0b11001001, 0b00101001, 0b10101001, 0b01101001, 0b11101001,
- 0b00011001, 0b10011001, 0b01011001, 0b11011001, 0b00111001, 0b10111001, 0b01111001, 0b11111001,
- 0b00000101, 0b10000101, 0b01000101, 0b11000101, 0b00100101, 0b10100101, 0b01100101, 0b11100101,
- 0b00010101, 0b10010101, 0b01010101, 0b11010101, 0b00110101, 0b10110101, 0b01110101, 0b11110101,
- 0b00001101, 0b10001101, 0b01001101, 0b11001101, 0b00101101, 0b10101101, 0b01101101, 0b11101101,
- 0b00011101, 0b10011101, 0b01011101, 0b11011101, 0b00111101, 0b10111101, 0b01111101, 0b11111101,
- 0b00010011, 0b00100110, 0b01001110, 0b10011010, 0b00111100, 0b01100101, 0b11101010, 0b10110100,
- 0b11101001, 0b00110011, 0b01100110, 0b11001110, 0b10011010, 0b00111101, 0b01100111, 0b11101110,
- 0b10111100, 0b11111001, 0b00001011, 0b00010110, 0b00101110, 0b01011010, 0b10111100, 0b01100100,
- 0b11101001, 0b10110010, 0b11100101, 0b00101011, 0b01010110, 0b10101110, 0b01011010, 0b10111101,
- 0b01100110, 0b11101101, 0b10111010, 0b11110101, 0b00011011, 0b00110110, 0b01101110, 0b11011010,
- 0b10111100, 0b01100101, 0b11101011, 0b10110110, 0b11101101, 0b00111011, 0b01110110, 0b11101110,
- 0b11011010, 0b10111101, 0b01100111, 0b11101111, 0b10111110, 0b11111101, 0b00000111, 0b00001110,
- 0b00011110, 0b00111010, 0b01111100, 0b11100100, 0b11101000, 0b10110001, 0b11100011, 0b00100111,
- 0b01001110, 0b10011110, 0b00111010, 0b01111101, 0b11100110, 0b11101100, 0b10111001, 0b11110011,
- 0b00010111, 0b00101110, 0b01011110, 0b10111010, 0b01111100, 0b11100101, 0b11101010, 0b10110101,
- 0b11101011, 0b00110111, 0b01101110, 0b11011110, 0b10111010, 0b01111101, 0b11100111, 0b11101110,
- 0b10111101, 0b11111011, 0b00001111, 0b00011110, 0b00111110, 0b01111010, 0b11111100, 0b11100100,
- 0b11101001, 0b10110011, 0b11100111, 0b00101111, 0b01011110, 0b10111110, 0b01111010, 0b11111101,
- 0b11100110, 0b11101101, 0b10111011, 0b11110111, 0b00011111, 0b00111110, 0b01111110, 0b11111010,
- 0b11111100, 0b11100101, 0b11101011, 0b10110111, 0b11101111, 0b00111111, 0b01111110, 0b11111110,
- 0b11111010, 0b11111101, 0b11100111, 0b11101111, 0b10111111, 0b11111111, 0b00000000, 0b00100000,
- 0b00001000, 0b00001100, 0b10000001, 0b11000010, 0b11100000, 0b00001000, 0b00100100, 0b00001010,
- 0b10001101, 0b11000001, 0b11100010, 0b11110000, 0b00000100, 0b00100010, 0b10001001, 0b01001100,
- 0b10100001, 0b11010010, 0b11101000, 0b00000011, 0b10000011, 0b01000011, 0b11000011, 0b00100011,
- 0b10100011,
-};
diff --git a/lib/std/compress/flate/inflate.zig b/lib/std/compress/flate/inflate.zig
deleted file mode 100644
index 2fcf3cafd4ad311b5390ce9942f77a4851903407..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/inflate.zig
+++ /dev/null
@@ -1,570 +0,0 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const testing = std.testing;
-
-const hfd = @import("huffman_decoder.zig");
-const BitReader = @import("bit_reader.zig").BitReader;
-const CircularBuffer = @import("CircularBuffer.zig");
-const Container = @import("container.zig").Container;
-const Token = @import("Token.zig");
-const codegen_order = @import("consts.zig").huffman.codegen_order;
-
-/// Decompresses deflate bit stream `reader` and writes uncompressed data to the
-/// `writer` stream.
-pub fn decompress(comptime container: Container, reader: anytype, writer: anytype) !void {
- var d = decompressor(container, reader);
- try d.decompress(writer);
-}
-
-/// Inflate decompressor for the reader type.
-pub fn decompressor(comptime container: Container, reader: anytype) Decompressor(container, @TypeOf(reader)) {
- return Decompressor(container, @TypeOf(reader)).init(reader);
-}
-
-pub fn Decompressor(comptime container: Container, comptime ReaderType: type) type {
- // zlib has 4 bytes footer, lookahead of 4 bytes ensures that we will not overshoot.
- // gzip has 8 bytes footer so we will not overshoot even with 8 bytes of lookahead.
- // For raw deflate there is always possibility of overshot so we use 8 bytes lookahead.
- const lookahead: type = if (container == .zlib) u32 else u64;
- return Inflate(container, lookahead, ReaderType);
-}
-
-/// Inflate decompresses deflate bit stream. Reads compressed data from reader
-/// provided in init. Decompressed data are stored in internal hist buffer and
-/// can be accesses iterable `next` or reader interface.
-///
-/// Container defines header/footer wrapper around deflate bit stream. Can be
-/// gzip or zlib.
-///
-/// Deflate bit stream consists of multiple blocks. Block can be one of three types:
-/// * stored, non compressed, max 64k in size
-/// * fixed, huffman codes are predefined
-/// * dynamic, huffman code tables are encoded at the block start
-///
-/// `step` function runs decoder until internal `hist` buffer is full. Client
-/// than needs to read that data in order to proceed with decoding.
-///
-/// Allocates 74.5K of internal buffers, most important are:
-/// * 64K for history (CircularBuffer)
-/// * ~10K huffman decoders (Literal and DistanceDecoder)
-///
-pub fn Inflate(comptime container: Container, comptime LookaheadType: type, comptime ReaderType: type) type {
- assert(LookaheadType == u32 or LookaheadType == u64);
- const BitReaderType = BitReader(LookaheadType, ReaderType);
-
- return struct {
- //const BitReaderType = BitReader(ReaderType);
- const F = BitReaderType.flag;
-
- bits: BitReaderType = .{},
- hist: CircularBuffer = .{},
- // Hashes, produces checkusm, of uncompressed data for gzip/zlib footer.
- hasher: container.Hasher() = .{},
-
- // dynamic block huffman code decoders
- lit_dec: hfd.LiteralDecoder = .{}, // literals
- dst_dec: hfd.DistanceDecoder = .{}, // distances
-
- // current read state
- bfinal: u1 = 0,
- block_type: u2 = 0b11,
- state: ReadState = .protocol_header,
-
- const ReadState = enum {
- protocol_header,
- block_header,
- block,
- protocol_footer,
- end,
- };
-
- const Self = @This();
-
- pub const Error = BitReaderType.Error || Container.Error || hfd.Error || error{
- InvalidCode,
- InvalidMatch,
- InvalidBlockType,
- WrongStoredBlockNlen,
- InvalidDynamicBlockHeader,
- };
-
- pub fn init(rt: ReaderType) Self {
- return .{ .bits = BitReaderType.init(rt) };
- }
-
- fn blockHeader(self: *Self) !void {
- self.bfinal = try self.bits.read(u1);
- self.block_type = try self.bits.read(u2);
- }
-
- fn storedBlock(self: *Self) !bool {
- self.bits.alignToByte(); // skip padding until byte boundary
- // everything after this is byte aligned in stored block
- var len = try self.bits.read(u16);
- const nlen = try self.bits.read(u16);
- if (len != ~nlen) return error.WrongStoredBlockNlen;
-
- while (len > 0) {
- const buf = self.hist.getWritable(len);
- try self.bits.readAll(buf);
- len -= @intCast(buf.len);
- }
- return true;
- }
-
- fn fixedBlock(self: *Self) !bool {
- while (!self.hist.full()) {
- const code = try self.bits.readFixedCode();
- switch (code) {
- 0...255 => self.hist.write(@intCast(code)),
- 256 => return true, // end of block
- 257...285 => try self.fixedDistanceCode(@intCast(code - 257)),
- else => return error.InvalidCode,
- }
- }
- return false;
- }
-
- // Handles fixed block non literal (length) code.
- // Length code is followed by 5 bits of distance code.
- fn fixedDistanceCode(self: *Self, code: u8) !void {
- try self.bits.fill(5 + 5 + 13);
- const length = try self.decodeLength(code);
- const distance = try self.decodeDistance(try self.bits.readF(u5, F.buffered | F.reverse));
- try self.hist.writeMatch(length, distance);
- }
-
- inline fn decodeLength(self: *Self, code: u8) !u16 {
- if (code > 28) return error.InvalidCode;
- const ml = Token.matchLength(code);
- return if (ml.extra_bits == 0) // 0 - 5 extra bits
- ml.base
- else
- ml.base + try self.bits.readN(ml.extra_bits, F.buffered);
- }
-
- fn decodeDistance(self: *Self, code: u8) !u16 {
- if (code > 29) return error.InvalidCode;
- const md = Token.matchDistance(code);
- return if (md.extra_bits == 0) // 0 - 13 extra bits
- md.base
- else
- md.base + try self.bits.readN(md.extra_bits, F.buffered);
- }
-
- fn dynamicBlockHeader(self: *Self) !void {
- const hlit: u16 = @as(u16, try self.bits.read(u5)) + 257; // number of ll code entries present - 257
- const hdist: u16 = @as(u16, try self.bits.read(u5)) + 1; // number of distance code entries - 1
- const hclen: u8 = @as(u8, try self.bits.read(u4)) + 4; // hclen + 4 code lengths are encoded
-
- if (hlit > 286 or hdist > 30)
- return error.InvalidDynamicBlockHeader;
-
- // lengths for code lengths
- var cl_lens = [_]u4{0} ** 19;
- for (0..hclen) |i| {
- cl_lens[codegen_order[i]] = try self.bits.read(u3);
- }
- var cl_dec: hfd.CodegenDecoder = .{};
- try cl_dec.generate(&cl_lens);
-
- // decoded code lengths
- var dec_lens = [_]u4{0} ** (286 + 30);
- var pos: usize = 0;
- while (pos < hlit + hdist) {
- const sym = try cl_dec.find(try self.bits.peekF(u7, F.reverse));
- try self.bits.shift(sym.code_bits);
- pos += try self.dynamicCodeLength(sym.symbol, &dec_lens, pos);
- }
- if (pos > hlit + hdist) {
- return error.InvalidDynamicBlockHeader;
- }
-
- // literal code lengths to literal decoder
- try self.lit_dec.generate(dec_lens[0..hlit]);
-
- // distance code lengths to distance decoder
- try self.dst_dec.generate(dec_lens[hlit .. hlit + hdist]);
- }
-
- // Decode code length symbol to code length. Writes decoded length into
- // lens slice starting at position pos. Returns number of positions
- // advanced.
- fn dynamicCodeLength(self: *Self, code: u16, lens: []u4, pos: usize) !usize {
- if (pos >= lens.len)
- return error.InvalidDynamicBlockHeader;
-
- switch (code) {
- 0...15 => {
- // Represent code lengths of 0 - 15
- lens[pos] = @intCast(code);
- return 1;
- },
- 16 => {
- // Copy the previous code length 3 - 6 times.
- // The next 2 bits indicate repeat length
- const n: u8 = @as(u8, try self.bits.read(u2)) + 3;
- if (pos == 0 or pos + n > lens.len)
- return error.InvalidDynamicBlockHeader;
- for (0..n) |i| {
- lens[pos + i] = lens[pos + i - 1];
- }
- return n;
- },
- // Repeat a code length of 0 for 3 - 10 times. (3 bits of length)
- 17 => return @as(u8, try self.bits.read(u3)) + 3,
- // Repeat a code length of 0 for 11 - 138 times (7 bits of length)
- 18 => return @as(u8, try self.bits.read(u7)) + 11,
- else => return error.InvalidDynamicBlockHeader,
- }
- }
-
- // In larger archives most blocks are usually dynamic, so decompression
- // performance depends on this function.
- fn dynamicBlock(self: *Self) !bool {
- // Hot path loop!
- while (!self.hist.full()) {
- try self.bits.fill(15); // optimization so other bit reads can be buffered (avoiding one `if` in hot path)
- const sym = try self.decodeSymbol(&self.lit_dec);
-
- switch (sym.kind) {
- .literal => self.hist.write(sym.symbol),
- .match => { // Decode match backreference
- // fill so we can use buffered reads
- if (LookaheadType == u32)
- try self.bits.fill(5 + 15)
- else
- try self.bits.fill(5 + 15 + 13);
- const length = try self.decodeLength(sym.symbol);
- const dsm = try self.decodeSymbol(&self.dst_dec);
- if (LookaheadType == u32) try self.bits.fill(13);
- const distance = try self.decodeDistance(dsm.symbol);
- try self.hist.writeMatch(length, distance);
- },
- .end_of_block => return true,
- }
- }
- return false;
- }
-
- // Peek 15 bits from bits reader (maximum code len is 15 bits). Use
- // decoder to find symbol for that code. We then know how many bits is
- // used. Shift bit reader for that much bits, those bits are used. And
- // return symbol.
- fn decodeSymbol(self: *Self, decoder: anytype) !hfd.Symbol {
- const sym = try decoder.find(try self.bits.peekF(u15, F.buffered | F.reverse));
- try self.bits.shift(sym.code_bits);
- return sym;
- }
-
- fn step(self: *Self) !void {
- switch (self.state) {
- .protocol_header => {
- try container.parseHeader(&self.bits);
- self.state = .block_header;
- },
- .block_header => {
- try self.blockHeader();
- self.state = .block;
- if (self.block_type == 2) try self.dynamicBlockHeader();
- },
- .block => {
- const done = switch (self.block_type) {
- 0 => try self.storedBlock(),
- 1 => try self.fixedBlock(),
- 2 => try self.dynamicBlock(),
- else => return error.InvalidBlockType,
- };
- if (done) {
- self.state = if (self.bfinal == 1) .protocol_footer else .block_header;
- }
- },
- .protocol_footer => {
- self.bits.alignToByte();
- try container.parseFooter(&self.hasher, &self.bits);
- self.state = .end;
- },
- .end => {},
- }
- }
-
- /// Replaces the inner reader with new reader.
- pub fn setReader(self: *Self, new_reader: ReaderType) void {
- self.bits.forward_reader = new_reader;
- if (self.state == .end or self.state == .protocol_footer) {
- self.state = .protocol_header;
- }
- }
-
- // Reads all compressed data from the internal reader and outputs plain
- // (uncompressed) data to the provided writer.
- pub fn decompress(self: *Self, writer: anytype) !void {
- while (try self.next()) |buf| {
- try writer.writeAll(buf);
- }
- }
-
- /// Returns the number of bytes that have been read from the internal
- /// reader but not yet consumed by the decompressor.
- pub fn unreadBytes(self: Self) usize {
- // There can be no error here: the denominator is not zero, and
- // overflow is not possible since the type is unsigned.
- return std.math.divCeil(usize, self.bits.nbits, 8) catch unreachable;
- }
-
- // Iterator interface
-
- /// Can be used in iterator like loop without memcpy to another buffer:
- /// while (try inflate.next()) |buf| { ... }
- pub fn next(self: *Self) Error!?[]const u8 {
- const out = try self.get(0);
- if (out.len == 0) return null;
- return out;
- }
-
- /// Returns decompressed data from internal sliding window buffer.
- /// Returned buffer can be any length between 0 and `limit` bytes. 0
- /// returned bytes means end of stream reached. With limit=0 returns as
- /// much data it can. It newer will be more than 65536 bytes, which is
- /// size of internal buffer.
- pub fn get(self: *Self, limit: usize) Error![]const u8 {
- while (true) {
- const out = self.hist.readAtMost(limit);
- if (out.len > 0) {
- self.hasher.update(out);
- return out;
- }
- if (self.state == .end) return out;
- try self.step();
- }
- }
-
- // Reader interface
-
- pub const Reader = std.io.GenericReader(*Self, Error, read);
-
- /// 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) Error!usize {
- if (buffer.len == 0) return 0;
- const out = try self.get(buffer.len);
- @memcpy(buffer[0..out.len], out);
- return out.len;
- }
-
- pub fn reader(self: *Self) Reader {
- return .{ .context = self };
- }
- };
-}
-
-test "decompress" {
- const cases = [_]struct {
- in: []const u8,
- out: []const u8,
- }{
- // non compressed block (type 0)
- .{
- .in = &[_]u8{
- 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
- 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
- },
- .out = "Hello world\n",
- },
- // fixed code block (type 1)
- .{
- .in = &[_]u8{
- 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1
- 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00,
- },
- .out = "Hello world\n",
- },
- // dynamic block (type 2)
- .{
- .in = &[_]u8{
- 0x3d, 0xc6, 0x39, 0x11, 0x00, 0x00, 0x0c, 0x02, // deflate data block type 2
- 0x30, 0x2b, 0xb5, 0x52, 0x1e, 0xff, 0x96, 0x38,
- 0x16, 0x96, 0x5c, 0x1e, 0x94, 0xcb, 0x6d, 0x01,
- },
- .out = "ABCDEABCD ABCDEABCD",
- },
- };
- for (cases) |c| {
- var fb = std.io.fixedBufferStream(c.in);
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
-
- try decompress(.raw, fb.reader(), al.writer());
- try testing.expectEqualStrings(c.out, al.items);
- }
-}
-
-test "gzip decompress" {
- const cases = [_]struct {
- in: []const u8,
- out: []const u8,
- }{
- // non compressed block (type 0)
- .{
- .in = &[_]u8{
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // gzip header (10 bytes)
- 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
- 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
- 0xd5, 0xe0, 0x39, 0xb7, // gzip footer: checksum
- 0x0c, 0x00, 0x00, 0x00, // gzip footer: size
- },
- .out = "Hello world\n",
- },
- // fixed code block (type 1)
- .{
- .in = &[_]u8{
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, // gzip header (10 bytes)
- 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, // deflate data block type 1
- 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00,
- 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00, // gzip footer (chksum, len)
- },
- .out = "Hello world\n",
- },
- // dynamic block (type 2)
- .{
- .in = &[_]u8{
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // gzip header (10 bytes)
- 0x3d, 0xc6, 0x39, 0x11, 0x00, 0x00, 0x0c, 0x02, // deflate data block type 2
- 0x30, 0x2b, 0xb5, 0x52, 0x1e, 0xff, 0x96, 0x38,
- 0x16, 0x96, 0x5c, 0x1e, 0x94, 0xcb, 0x6d, 0x01,
- 0x17, 0x1c, 0x39, 0xb4, 0x13, 0x00, 0x00, 0x00, // gzip footer (chksum, len)
- },
- .out = "ABCDEABCD ABCDEABCD",
- },
- // gzip header with name
- .{
- .in = &[_]u8{
- 0x1f, 0x8b, 0x08, 0x08, 0xe5, 0x70, 0xb1, 0x65, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e,
- 0x74, 0x78, 0x74, 0x00, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1,
- 0x02, 0x00, 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00,
- },
- .out = "Hello world\n",
- },
- };
- for (cases) |c| {
- var fb = std.io.fixedBufferStream(c.in);
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
-
- try decompress(.gzip, fb.reader(), al.writer());
- try testing.expectEqualStrings(c.out, al.items);
- }
-}
-
-test "zlib decompress" {
- const cases = [_]struct {
- in: []const u8,
- out: []const u8,
- }{
- // non compressed block (type 0)
- .{
- .in = &[_]u8{
- 0x78, 0b10_0_11100, // zlib header (2 bytes)
- 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
- 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
- 0x1c, 0xf2, 0x04, 0x47, // zlib footer: checksum
- },
- .out = "Hello world\n",
- },
- };
- for (cases) |c| {
- var fb = std.io.fixedBufferStream(c.in);
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
-
- try decompress(.zlib, fb.reader(), al.writer());
- try testing.expectEqualStrings(c.out, al.items);
- }
-}
-
-test "fuzzing tests" {
- const cases = [_]struct {
- input: []const u8,
- out: []const u8 = "",
- err: ?anyerror = null,
- }{
- .{ .input = "deflate-stream", .out = @embedFile("testdata/fuzz/deflate-stream.expect") }, // 0
- .{ .input = "empty-distance-alphabet01" },
- .{ .input = "empty-distance-alphabet02" },
- .{ .input = "end-of-stream", .err = error.EndOfStream },
- .{ .input = "invalid-distance", .err = error.InvalidMatch },
- .{ .input = "invalid-tree01", .err = error.IncompleteHuffmanTree }, // 5
- .{ .input = "invalid-tree02", .err = error.IncompleteHuffmanTree },
- .{ .input = "invalid-tree03", .err = error.IncompleteHuffmanTree },
- .{ .input = "lengths-overflow", .err = error.InvalidDynamicBlockHeader },
- .{ .input = "out-of-codes", .err = error.InvalidCode },
- .{ .input = "puff01", .err = error.WrongStoredBlockNlen }, // 10
- .{ .input = "puff02", .err = error.EndOfStream },
- .{ .input = "puff03", .out = &[_]u8{0xa} },
- .{ .input = "puff04", .err = error.InvalidCode },
- .{ .input = "puff05", .err = error.EndOfStream },
- .{ .input = "puff06", .err = error.EndOfStream },
- .{ .input = "puff08", .err = error.InvalidCode },
- .{ .input = "puff09", .out = "P" },
- .{ .input = "puff10", .err = error.InvalidCode },
- .{ .input = "puff11", .err = error.InvalidMatch },
- .{ .input = "puff12", .err = error.InvalidDynamicBlockHeader }, // 20
- .{ .input = "puff13", .err = error.IncompleteHuffmanTree },
- .{ .input = "puff14", .err = error.EndOfStream },
- .{ .input = "puff15", .err = error.IncompleteHuffmanTree },
- .{ .input = "puff16", .err = error.InvalidDynamicBlockHeader },
- .{ .input = "puff17", .err = error.MissingEndOfBlockCode }, // 25
- .{ .input = "fuzz1", .err = error.InvalidDynamicBlockHeader },
- .{ .input = "fuzz2", .err = error.InvalidDynamicBlockHeader },
- .{ .input = "fuzz3", .err = error.InvalidMatch },
- .{ .input = "fuzz4", .err = error.OversubscribedHuffmanTree },
- .{ .input = "puff18", .err = error.OversubscribedHuffmanTree }, // 30
- .{ .input = "puff19", .err = error.OversubscribedHuffmanTree },
- .{ .input = "puff20", .err = error.OversubscribedHuffmanTree },
- .{ .input = "puff21", .err = error.OversubscribedHuffmanTree },
- .{ .input = "puff22", .err = error.OversubscribedHuffmanTree },
- .{ .input = "puff23", .err = error.OversubscribedHuffmanTree }, // 35
- .{ .input = "puff24", .err = error.IncompleteHuffmanTree },
- .{ .input = "puff25", .err = error.OversubscribedHuffmanTree },
- .{ .input = "puff26", .err = error.InvalidDynamicBlockHeader },
- .{ .input = "puff27", .err = error.InvalidDynamicBlockHeader },
- };
-
- inline for (cases, 0..) |c, case_no| {
- var in = std.io.fixedBufferStream(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
- var out = std.ArrayList(u8).init(testing.allocator);
- defer out.deinit();
- errdefer std.debug.print("test case failed {}\n", .{case_no});
-
- if (c.err) |expected_err| {
- try testing.expectError(expected_err, decompress(.raw, in.reader(), out.writer()));
- } else {
- try decompress(.raw, in.reader(), out.writer());
- try testing.expectEqualStrings(c.out, out.items);
- }
- }
-}
-
-test "bug 18966" {
- const input = @embedFile("testdata/fuzz/bug_18966.input");
- const expect = @embedFile("testdata/fuzz/bug_18966.expect");
-
- var in = std.io.fixedBufferStream(input);
- var out = std.ArrayList(u8).init(testing.allocator);
- defer out.deinit();
-
- try decompress(.gzip, in.reader(), out.writer());
- try testing.expectEqualStrings(expect, out.items);
-}
-
-test "bug 19895" {
- const input = &[_]u8{
- 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
- 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
- };
- var in = std.io.fixedBufferStream(input);
- var decomp = decompressor(.raw, in.reader());
- var buf: [0]u8 = undefined;
- try testing.expectEqual(0, try decomp.read(&buf));
-}
diff --git a/lib/std/compress/gzip.zig b/lib/std/compress/gzip.zig
deleted file mode 100644
index e619b575dee3829bfd5ceb53496f9fe640e5dc68..0000000000000000000000000000000000000000
--- a/lib/std/compress/gzip.zig
+++ /dev/null
@@ -1,66 +0,0 @@
-const deflate = @import("flate/deflate.zig");
-const inflate = @import("flate/inflate.zig");
-
-/// Decompress compressed data from reader and write plain data to the writer.
-pub fn decompress(reader: anytype, writer: anytype) !void {
- try inflate.decompress(.gzip, reader, writer);
-}
-
-/// Decompressor type
-pub fn Decompressor(comptime ReaderType: type) type {
- return inflate.Decompressor(.gzip, ReaderType);
-}
-
-/// Create Decompressor which will read compressed data from reader.
-pub fn decompressor(reader: anytype) Decompressor(@TypeOf(reader)) {
- return inflate.decompressor(.gzip, reader);
-}
-
-/// Compression level, trades between speed and compression size.
-pub const Options = deflate.Options;
-
-/// Compress plain data from reader and write compressed data to the writer.
-pub fn compress(reader: anytype, writer: anytype, options: Options) !void {
- try deflate.compress(.gzip, reader, writer, options);
-}
-
-/// Compressor type
-pub fn Compressor(comptime WriterType: type) type {
- return deflate.Compressor(.gzip, WriterType);
-}
-
-/// Create Compressor which outputs compressed data to the writer.
-pub fn compressor(writer: anytype, options: Options) !Compressor(@TypeOf(writer)) {
- return try deflate.compressor(.gzip, writer, options);
-}
-
-/// Huffman only compression. Without Lempel-Ziv match searching. Faster
-/// compression, less memory requirements but bigger compressed sizes.
-pub const huffman = struct {
- pub fn compress(reader: anytype, writer: anytype) !void {
- try deflate.huffman.compress(.gzip, reader, writer);
- }
-
- pub fn Compressor(comptime WriterType: type) type {
- return deflate.huffman.Compressor(.gzip, WriterType);
- }
-
- pub fn compressor(writer: anytype) !huffman.Compressor(@TypeOf(writer)) {
- return deflate.huffman.compressor(.gzip, writer);
- }
-};
-
-// No compression store only. Compressed size is slightly bigger than plain.
-pub const store = struct {
- pub fn compress(reader: anytype, writer: anytype) !void {
- try deflate.store.compress(.gzip, reader, writer);
- }
-
- pub fn Compressor(comptime WriterType: type) type {
- return deflate.store.Compressor(.gzip, WriterType);
- }
-
- pub fn compressor(writer: anytype) !store.Compressor(@TypeOf(writer)) {
- return deflate.store.compressor(.gzip, writer);
- }
-};
diff --git a/lib/std/compress/zlib.zig b/lib/std/compress/zlib.zig
deleted file mode 100644
index 554f6f894bc32d8dc53aa44a1ed2938d03b1f709..0000000000000000000000000000000000000000
--- a/lib/std/compress/zlib.zig
+++ /dev/null
@@ -1,101 +0,0 @@
-const deflate = @import("flate/deflate.zig");
-const inflate = @import("flate/inflate.zig");
-
-/// Decompress compressed data from reader and write plain data to the writer.
-pub fn decompress(reader: anytype, writer: anytype) !void {
- try inflate.decompress(.zlib, reader, writer);
-}
-
-/// Decompressor type
-pub fn Decompressor(comptime ReaderType: type) type {
- return inflate.Decompressor(.zlib, ReaderType);
-}
-
-/// Create Decompressor which will read compressed data from reader.
-pub fn decompressor(reader: anytype) Decompressor(@TypeOf(reader)) {
- return inflate.decompressor(.zlib, reader);
-}
-
-/// Compression level, trades between speed and compression size.
-pub const Options = deflate.Options;
-
-/// Compress plain data from reader and write compressed data to the writer.
-pub fn compress(reader: anytype, writer: anytype, options: Options) !void {
- try deflate.compress(.zlib, reader, writer, options);
-}
-
-/// Compressor type
-pub fn Compressor(comptime WriterType: type) type {
- return deflate.Compressor(.zlib, WriterType);
-}
-
-/// Create Compressor which outputs compressed data to the writer.
-pub fn compressor(writer: anytype, options: Options) !Compressor(@TypeOf(writer)) {
- return try deflate.compressor(.zlib, writer, options);
-}
-
-/// Huffman only compression. Without Lempel-Ziv match searching. Faster
-/// compression, less memory requirements but bigger compressed sizes.
-pub const huffman = struct {
- pub fn compress(reader: anytype, writer: anytype) !void {
- try deflate.huffman.compress(.zlib, reader, writer);
- }
-
- pub fn Compressor(comptime WriterType: type) type {
- return deflate.huffman.Compressor(.zlib, WriterType);
- }
-
- pub fn compressor(writer: anytype) !huffman.Compressor(@TypeOf(writer)) {
- return deflate.huffman.compressor(.zlib, writer);
- }
-};
-
-// No compression store only. Compressed size is slightly bigger than plain.
-pub const store = struct {
- pub fn compress(reader: anytype, writer: anytype) !void {
- try deflate.store.compress(.zlib, reader, writer);
- }
-
- pub fn Compressor(comptime WriterType: type) type {
- return deflate.store.Compressor(.zlib, WriterType);
- }
-
- pub fn compressor(writer: anytype) !store.Compressor(@TypeOf(writer)) {
- return deflate.store.compressor(.zlib, writer);
- }
-};
-
-test "should not overshoot" {
- const std = @import("std");
-
- // Compressed zlib data with extra 4 bytes at the end.
- const data = [_]u8{
- 0x78, 0x9c, 0x73, 0xce, 0x2f, 0xa8, 0x2c, 0xca, 0x4c, 0xcf, 0x28, 0x51, 0x08, 0xcf, 0xcc, 0xc9,
- 0x49, 0xcd, 0x55, 0x28, 0x4b, 0xcc, 0x53, 0x08, 0x4e, 0xce, 0x48, 0xcc, 0xcc, 0xd6, 0x51, 0x08,
- 0xce, 0xcc, 0x4b, 0x4f, 0x2c, 0xc8, 0x2f, 0x4a, 0x55, 0x30, 0xb4, 0xb4, 0x34, 0xd5, 0xb5, 0x34,
- 0x03, 0x00, 0x8b, 0x61, 0x0f, 0xa4, 0x52, 0x5a, 0x94, 0x12,
- };
-
- var stream = std.io.fixedBufferStream(data[0..]);
- const reader = stream.reader();
-
- var dcp = decompressor(reader);
- var out: [128]u8 = undefined;
-
- // Decompress
- var n = try dcp.reader().readAll(out[0..]);
-
- // Expected decompressed data
- try std.testing.expectEqual(46, n);
- try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
-
- // Decompressor don't overshoot underlying reader.
- // It is leaving it at the end of compressed data chunk.
- try std.testing.expectEqual(data.len - 4, stream.getPos());
- try std.testing.expectEqual(0, dcp.unreadBytes());
-
- // 4 bytes after compressed chunk are available in reader.
- n = try reader.readAll(out[0..]);
- try std.testing.expectEqual(n, 4);
- try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
-}
diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig
index 3f1fc41febe14a97eee5990926ab103268015f28..5fa5dd002afdc2171bb13f66afb8442ac9322c28 100644
--- a/lib/std/debug/Dwarf.zig
+++ b/lib/std/debug/Dwarf.zig
@@ -2235,18 +2235,14 @@ pub const ElfModule = struct {
const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
- var section_stream = std.io.fixedBufferStream(section_bytes);
- const section_reader = section_stream.reader();
- const chdr = section_reader.readStruct(elf.Chdr) catch continue;
+ var section_reader: std.Io.Reader = .fixed(section_bytes);
+ const chdr = section_reader.takeStruct(elf.Chdr, endian) catch continue;
if (chdr.ch_type != .ZLIB) continue;
- var zlib_stream = std.compress.zlib.decompressor(section_reader);
-
- const decompressed_section = try gpa.alloc(u8, chdr.ch_size);
+ var zlib_stream: std.compress.flate.Decompress = .init(§ion_reader, .zlib, &.{});
+ const decompressed_section = zlib_stream.reader.allocRemaining(gpa, .unlimited) catch continue;
errdefer gpa.free(decompressed_section);
-
- const read = zlib_stream.reader().readAll(decompressed_section) catch continue;
- assert(read == decompressed_section.len);
+ assert(chdr.ch_size == decompressed_section.len);
break :blk .{
.data = decompressed_section,
diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig
index 838411bebcfc491836f5767c94ad5b8bd9721739..83c1c8b50be542f1fe677401b707ba238bd9b624 100644
--- a/lib/std/http/Client.zig
+++ b/lib/std/http/Client.zig
@@ -405,13 +405,8 @@ pub const RequestTransfer = union(enum) {
/// The decompressor for response messages.
pub const Compression = union(enum) {
- pub const DeflateDecompressor = std.compress.zlib.Decompressor(Request.TransferReader);
- pub const GzipDecompressor = std.compress.gzip.Decompressor(Request.TransferReader);
- // https://github.com/ziglang/zig/issues/18937
- //pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Request.TransferReader, .{});
-
- deflate: DeflateDecompressor,
- gzip: GzipDecompressor,
+ deflate: std.compress.flate.Decompress,
+ gzip: std.compress.flate.Decompress,
// https://github.com/ziglang/zig/issues/18937
//zstd: ZstdDecompressor,
none: void,
diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig
index 886aed72dc1d8594a9d4322fd9268831c361e965..7ec5d5c11f43371c5ea36742be40751df955b563 100644
--- a/lib/std/http/Server.zig
+++ b/lib/std/http/Server.zig
@@ -130,8 +130,8 @@ pub const Request = struct {
pub const DeflateDecompressor = std.compress.zlib.Decompressor(std.io.AnyReader);
pub const GzipDecompressor = std.compress.gzip.Decompressor(std.io.AnyReader);
- deflate: DeflateDecompressor,
- gzip: GzipDecompressor,
+ deflate: std.compress.flate.Decompress,
+ gzip: std.compress.flate.Decompress,
zstd: std.compress.zstd.Decompress,
none: void,
};
diff --git a/lib/std/zip.zig b/lib/std/zip.zig
index e181bc1f6521dd652dbf456b702d151f596dc6ab..b13c1d50101393d8a74864321851f2732f51bf06 100644
--- a/lib/std/zip.zig
+++ b/lib/std/zip.zig
@@ -5,11 +5,10 @@
const builtin = @import("builtin");
const std = @import("std");
-const testing = std.testing;
-
-pub const testutil = @import("zip/test.zig");
-const File = testutil.File;
-const FileStore = testutil.FileStore;
+const File = std.fs.File;
+const is_le = builtin.target.cpu.arch.endian() == .little;
+const Writer = std.io.Writer;
+const Reader = std.io.Reader;
pub const CompressionMethod = enum(u16) {
store = 0,
@@ -95,102 +94,116 @@ pub const EndRecord = extern struct {
central_directory_size: u32 align(1),
central_directory_offset: u32 align(1),
comment_len: u16 align(1),
+
pub fn need_zip64(self: EndRecord) bool {
return isMaxInt(self.record_count_disk) or
isMaxInt(self.record_count_total) or
isMaxInt(self.central_directory_size) or
isMaxInt(self.central_directory_offset);
}
-};
-/// Find and return the end record for the given seekable zip stream.
-/// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and
-/// its context must also have a `.reader()` method that returns an instance of
-/// `std.io.GenericReader`.
-pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord {
- var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined;
- const record_len_max = @min(stream_len, buf.len);
- var loaded_len: u32 = 0;
+ pub const FindBufferError = error{ ZipNoEndRecord, ZipTruncated };
+
+ /// TODO audit this logic
+ pub fn findBuffer(buffer: []const u8) FindBufferError!EndRecord {
+ const pos = std.mem.lastIndexOf(u8, buffer, &end_record_sig) orelse return error.ZipNoEndRecord;
+ if (pos + @sizeOf(EndRecord) > buffer.len) return error.EndOfStream;
+ const record_ptr: *EndRecord = @ptrCast(buffer[pos..][0..@sizeOf(EndRecord)]);
+ var record = record_ptr.*;
+ if (!is_le) std.mem.byteSwapAllFields(EndRecord, &record);
+ return record;
+ }
- var comment_len: u16 = 0;
- while (true) {
- const record_len: u32 = @as(u32, comment_len) + @sizeOf(EndRecord);
- if (record_len > record_len_max)
- return error.ZipNoEndRecord;
+ pub const FindFileError = File.GetEndPosError || File.SeekError || File.ReadError || error{
+ ZipNoEndRecord,
+ EndOfStream,
+ };
- if (record_len > loaded_len) {
- const new_loaded_len = @min(loaded_len + 300, record_len_max);
- const read_len = new_loaded_len - loaded_len;
+ pub fn findFile(fr: *File.Reader) FindFileError!EndRecord {
+ const end_pos = try fr.getSize();
- try seekable_stream.seekTo(stream_len - @as(u64, new_loaded_len));
- const read_buf: []u8 = buf[buf.len - new_loaded_len ..][0..read_len];
- const len = try (if (@TypeOf(seekable_stream.context) == std.fs.File) seekable_stream.context.deprecatedReader() else seekable_stream.context.reader()).readAll(read_buf);
- if (len != read_len)
- return error.ZipTruncated;
- loaded_len = new_loaded_len;
- }
+ var buf: [@sizeOf(EndRecord) + std.math.maxInt(u16)]u8 = undefined;
+ const record_len_max = @min(end_pos, buf.len);
+ var loaded_len: u32 = 0;
+ var comment_len: u16 = 0;
+ while (true) {
+ const record_len: u32 = @as(u32, comment_len) + @sizeOf(EndRecord);
+ if (record_len > record_len_max)
+ return error.ZipNoEndRecord;
- const record_bytes = buf[buf.len - record_len ..][0..@sizeOf(EndRecord)];
- if (std.mem.eql(u8, record_bytes[0..4], &end_record_sig) and
- std.mem.readInt(u16, record_bytes[20..22], .little) == comment_len)
- {
- const record: *align(1) EndRecord = @ptrCast(record_bytes.ptr);
- if (builtin.target.cpu.arch.endian() != .little) {
- std.mem.byteSwapAllFields(@TypeOf(record.*), record);
+ if (record_len > loaded_len) {
+ const new_loaded_len = @min(loaded_len + 300, record_len_max);
+ const read_len = new_loaded_len - loaded_len;
+
+ try fr.seekTo(end_pos - @as(u64, new_loaded_len));
+ const read_buf: []u8 = buf[buf.len - new_loaded_len ..][0..read_len];
+ var br = fr.interface().unbuffered();
+ br.readSlice(read_buf) catch |err| switch (err) {
+ error.ReadFailed => return fr.err.?,
+ error.EndOfStream => return error.EndOfStream,
+ };
+ loaded_len = new_loaded_len;
+ }
+
+ const record_bytes = buf[buf.len - record_len ..][0..@sizeOf(EndRecord)];
+ if (std.mem.eql(u8, record_bytes[0..4], &end_record_sig) and
+ std.mem.readInt(u16, record_bytes[20..22], .little) == comment_len)
+ {
+ const record: *align(1) EndRecord = @ptrCast(record_bytes.ptr);
+ if (!is_le) std.mem.byteSwapAllFields(EndRecord, record);
+ return record.*;
}
- return record.*;
- }
- if (comment_len == std.math.maxInt(u16))
- return error.ZipNoEndRecord;
- comment_len += 1;
+ if (comment_len == std.math.maxInt(u16))
+ return error.ZipNoEndRecord;
+ comment_len += 1;
+ }
}
-}
+};
-/// Decompresses the given data from `reader` into `writer`. Stops early if more
-/// than `uncompressed_size` bytes are processed and verifies that exactly that
-/// number of bytes are decompressed. Returns the CRC-32 of the uncompressed data.
-/// `writer` can be anything with a `writeAll(self: *Self, chunk: []const u8) anyerror!void` method.
-pub fn decompress(
- method: CompressionMethod,
- uncompressed_size: u64,
- reader: anytype,
- writer: anytype,
-) !u32 {
- var hash = std.hash.Crc32.init();
+pub const Decompress = struct {
+ interface: Reader,
+ state: union {
+ inflate: std.compress.flate.Decompress,
+ store: *Reader,
+ },
- var total_uncompressed: u64 = 0;
- switch (method) {
- .store => {
- var buf: [4096]u8 = undefined;
- while (true) {
- const len = try reader.read(&buf);
- if (len == 0) break;
- try writer.writeAll(buf[0..len]);
- hash.update(buf[0..len]);
- total_uncompressed += @intCast(len);
- }
- },
- .deflate => {
- var br = std.io.bufferedReader(reader);
- var decompressor = std.compress.flate.decompressor(br.reader());
- while (try decompressor.next()) |chunk| {
- try writer.writeAll(chunk);
- hash.update(chunk);
- total_uncompressed += @intCast(chunk.len);
- if (total_uncompressed > uncompressed_size)
- return error.ZipUncompressSizeTooSmall;
- }
- if (br.end != br.start)
- return error.ZipDeflateTruncated;
- },
- _ => return error.UnsupportedCompressionMethod,
+ pub fn init(reader: *Reader, method: CompressionMethod, buffer: []u8) Reader {
+ return switch (method) {
+ .store => .{
+ .state = .{ .store = reader },
+ .interface = .{
+ .context = undefined,
+ .vtable = &.{ .stream = streamStore },
+ .buffer = buffer,
+ .end = 0,
+ .seek = 0,
+ },
+ },
+ .deflate => .{
+ .state = .{ .inflate = .init(reader, .raw) },
+ .interface = .{
+ .context = undefined,
+ .vtable = &.{ .stream = streamDeflate },
+ .buffer = buffer,
+ .end = 0,
+ .seek = 0,
+ },
+ },
+ else => unreachable,
+ };
}
- if (total_uncompressed != uncompressed_size)
- return error.ZipUncompressSizeMismatch;
- return hash.final();
-}
+ fn streamStore(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
+ const d: *Decompress = @fieldParentPtr("interface", r);
+ return d.store.read(w, limit);
+ }
+
+ fn streamDeflate(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
+ const d: *Decompress = @fieldParentPtr("interface", r);
+ return std.compress.flate.Decompress.read(&d.inflate, w, limit);
+ }
+};
fn isBadFilename(filename: []const u8) bool {
if (filename.len == 0 or filename[0] == '/')
@@ -253,319 +266,332 @@ fn readZip64FileExtents(comptime T: type, header: T, extents: *FileExtents, data
}
}
-pub fn Iterator(comptime SeekableStream: type) type {
- return struct {
- stream: SeekableStream,
+pub const Iterator = struct {
+ input: *File.Reader,
- cd_record_count: u64,
- cd_zip_offset: u64,
- cd_size: u64,
+ cd_record_count: u64,
+ cd_zip_offset: u64,
+ cd_size: u64,
- cd_record_index: u64 = 0,
- cd_record_offset: u64 = 0,
+ cd_record_index: u64 = 0,
+ cd_record_offset: u64 = 0,
- const Self = @This();
+ pub fn init(input: *File.Reader) !Iterator {
+ const end_record = try EndRecord.findFile(input);
- pub fn init(stream: SeekableStream) !Self {
- const stream_len = try stream.getEndPos();
+ if (!isMaxInt(end_record.record_count_disk) and end_record.record_count_disk > end_record.record_count_total)
+ return error.ZipDiskRecordCountTooLarge;
- const end_record = try findEndRecord(stream, stream_len);
+ if (end_record.disk_number != 0 or end_record.central_directory_disk_number != 0)
+ return error.ZipMultiDiskUnsupported;
- if (!isMaxInt(end_record.record_count_disk) and end_record.record_count_disk > end_record.record_count_total)
- return error.ZipDiskRecordCountTooLarge;
-
- if (end_record.disk_number != 0 or end_record.central_directory_disk_number != 0)
+ {
+ const counts_valid = !isMaxInt(end_record.record_count_disk) and !isMaxInt(end_record.record_count_total);
+ if (counts_valid and end_record.record_count_disk != end_record.record_count_total)
return error.ZipMultiDiskUnsupported;
+ }
- {
- const counts_valid = !isMaxInt(end_record.record_count_disk) and !isMaxInt(end_record.record_count_total);
- if (counts_valid and end_record.record_count_disk != end_record.record_count_total)
- return error.ZipMultiDiskUnsupported;
- }
-
- var result = Self{
- .stream = stream,
- .cd_record_count = end_record.record_count_total,
- .cd_zip_offset = end_record.central_directory_offset,
- .cd_size = end_record.central_directory_size,
- };
- if (!end_record.need_zip64()) return result;
-
- const locator_end_offset: u64 = @as(u64, end_record.comment_len) + @sizeOf(EndRecord) + @sizeOf(EndLocator64);
- if (locator_end_offset > stream_len)
- return error.ZipTruncated;
- try stream.seekTo(stream_len - locator_end_offset);
- const locator = try (if (@TypeOf(stream.context) == std.fs.File) stream.context.deprecatedReader() else stream.context.reader()).readStructEndian(EndLocator64, .little);
- if (!std.mem.eql(u8, &locator.signature, &end_locator64_sig))
- return error.ZipBadLocatorSig;
- if (locator.zip64_disk_count != 0)
- return error.ZipUnsupportedZip64DiskCount;
- if (locator.total_disk_count != 1)
+ var result: Iterator = .{
+ .input = input,
+ .cd_record_count = end_record.record_count_total,
+ .cd_zip_offset = end_record.central_directory_offset,
+ .cd_size = end_record.central_directory_size,
+ };
+ if (!end_record.need_zip64()) return result;
+
+ const locator_end_offset: u64 = @as(u64, end_record.comment_len) + @sizeOf(EndRecord) + @sizeOf(EndLocator64);
+ const stream_len = try input.getSize();
+
+ if (locator_end_offset > stream_len)
+ return error.ZipTruncated;
+ try input.seekTo(stream_len - locator_end_offset);
+ const locator = input.interface.takeStructEndian(EndLocator64, .little) catch |err| switch (err) {
+ error.ReadFailed => return input.err.?,
+ error.EndOfStream => return error.EndOfStream,
+ };
+ if (!std.mem.eql(u8, &locator.signature, &end_locator64_sig))
+ return error.ZipBadLocatorSig;
+ if (locator.zip64_disk_count != 0)
+ return error.ZipUnsupportedZip64DiskCount;
+ if (locator.total_disk_count != 1)
+ return error.ZipMultiDiskUnsupported;
+
+ try input.seekTo(locator.record_file_offset);
+
+ const record64 = input.interface.takeStructEndian(EndRecord64, .little) catch |err| switch (err) {
+ error.ReadFailed => return input.err.?,
+ error.EndOfStream => return error.EndOfStream,
+ };
+
+ if (!std.mem.eql(u8, &record64.signature, &end_record64_sig))
+ return error.ZipBadEndRecord64Sig;
+
+ if (record64.end_record_size < @sizeOf(EndRecord64) - 12)
+ return error.ZipEndRecord64SizeTooSmall;
+ if (record64.end_record_size > @sizeOf(EndRecord64) - 12)
+ return error.ZipEndRecord64UnhandledExtraData;
+
+ if (record64.version_needed_to_extract > 45)
+ return error.ZipUnsupportedVersion;
+
+ {
+ const is_multidisk = record64.disk_number != 0 or
+ record64.central_directory_disk_number != 0 or
+ record64.record_count_disk != record64.record_count_total;
+ if (is_multidisk)
return error.ZipMultiDiskUnsupported;
-
- try stream.seekTo(locator.record_file_offset);
-
- const record64 = try (if (@TypeOf(stream.context) == std.fs.File) stream.context.deprecatedReader() else stream.context.reader()).readStructEndian(EndRecord64, .little);
-
- if (!std.mem.eql(u8, &record64.signature, &end_record64_sig))
- return error.ZipBadEndRecord64Sig;
-
- if (record64.end_record_size < @sizeOf(EndRecord64) - 12)
- return error.ZipEndRecord64SizeTooSmall;
- if (record64.end_record_size > @sizeOf(EndRecord64) - 12)
- return error.ZipEndRecord64UnhandledExtraData;
-
- if (record64.version_needed_to_extract > 45)
- return error.ZipUnsupportedVersion;
-
- {
- const is_multidisk = record64.disk_number != 0 or
- record64.central_directory_disk_number != 0 or
- record64.record_count_disk != record64.record_count_total;
- if (is_multidisk)
- return error.ZipMultiDiskUnsupported;
- }
-
- if (isMaxInt(end_record.record_count_total)) {
- result.cd_record_count = record64.record_count_total;
- } else if (end_record.record_count_total != record64.record_count_total)
- return error.Zip64RecordCountTotalMismatch;
-
- if (isMaxInt(end_record.central_directory_offset)) {
- result.cd_zip_offset = record64.central_directory_offset;
- } else if (end_record.central_directory_offset != record64.central_directory_offset)
- return error.Zip64CentralDirectoryOffsetMismatch;
-
- if (isMaxInt(end_record.central_directory_size)) {
- result.cd_size = record64.central_directory_size;
- } else if (end_record.central_directory_size != record64.central_directory_size)
- return error.Zip64CentralDirectorySizeMismatch;
-
- return result;
}
- pub fn next(self: *Self) !?Entry {
- if (self.cd_record_index == self.cd_record_count) {
- if (self.cd_record_offset != self.cd_size)
- return if (self.cd_size > self.cd_record_offset)
- error.ZipCdOversized
- else
- error.ZipCdUndersized;
-
- return null;
- }
-
- const header_zip_offset = self.cd_zip_offset + self.cd_record_offset;
- try self.stream.seekTo(header_zip_offset);
- const header = try (if (@TypeOf(self.stream.context) == std.fs.File) self.stream.context.deprecatedReader() else self.stream.context.reader()).readStructEndian(CentralDirectoryFileHeader, .little);
- if (!std.mem.eql(u8, &header.signature, ¢ral_file_header_sig))
- return error.ZipBadCdOffset;
-
- self.cd_record_index += 1;
- self.cd_record_offset += @sizeOf(CentralDirectoryFileHeader) + header.filename_len + header.extra_len + header.comment_len;
-
- // Note: checking the version_needed_to_extract doesn't seem to be helpful, i.e. the zip file
- // at https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux.zip
- // has an undocumented version 788 but extracts just fine.
-
- if (header.flags.encrypted)
- return error.ZipEncryptionUnsupported;
- // TODO: check/verify more flags
- if (header.disk_number != 0)
- return error.ZipMultiDiskUnsupported;
+ if (isMaxInt(end_record.record_count_total)) {
+ result.cd_record_count = record64.record_count_total;
+ } else if (end_record.record_count_total != record64.record_count_total)
+ return error.Zip64RecordCountTotalMismatch;
+
+ if (isMaxInt(end_record.central_directory_offset)) {
+ result.cd_zip_offset = record64.central_directory_offset;
+ } else if (end_record.central_directory_offset != record64.central_directory_offset)
+ return error.Zip64CentralDirectoryOffsetMismatch;
+
+ if (isMaxInt(end_record.central_directory_size)) {
+ result.cd_size = record64.central_directory_size;
+ } else if (end_record.central_directory_size != record64.central_directory_size)
+ return error.Zip64CentralDirectorySizeMismatch;
+
+ return result;
+ }
+
+ pub fn next(self: *Iterator) !?Entry {
+ if (self.cd_record_index == self.cd_record_count) {
+ if (self.cd_record_offset != self.cd_size)
+ return if (self.cd_size > self.cd_record_offset)
+ error.ZipCdOversized
+ else
+ error.ZipCdUndersized;
+
+ return null;
+ }
- var extents: FileExtents = .{
- .uncompressed_size = header.uncompressed_size,
- .compressed_size = header.compressed_size,
- .local_file_header_offset = header.local_file_header_offset,
+ const header_zip_offset = self.cd_zip_offset + self.cd_record_offset;
+ const input = self.input;
+ try input.seekTo(header_zip_offset);
+ const header = input.interface.takeStructEndian(CentralDirectoryFileHeader, .little) catch |err| switch (err) {
+ error.ReadFailed => return input.err.?,
+ error.EndOfStream => return error.EndOfStream,
+ };
+ if (!std.mem.eql(u8, &header.signature, ¢ral_file_header_sig))
+ return error.ZipBadCdOffset;
+
+ self.cd_record_index += 1;
+ self.cd_record_offset += @sizeOf(CentralDirectoryFileHeader) + header.filename_len + header.extra_len + header.comment_len;
+
+ // Note: checking the version_needed_to_extract doesn't seem to be helpful, i.e. the zip file
+ // at https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux.zip
+ // has an undocumented version 788 but extracts just fine.
+
+ if (header.flags.encrypted)
+ return error.ZipEncryptionUnsupported;
+ // TODO: check/verify more flags
+ if (header.disk_number != 0)
+ return error.ZipMultiDiskUnsupported;
+
+ var extents: FileExtents = .{
+ .uncompressed_size = header.uncompressed_size,
+ .compressed_size = header.compressed_size,
+ .local_file_header_offset = header.local_file_header_offset,
+ };
+
+ if (header.extra_len > 0) {
+ var extra_buf: [std.math.maxInt(u16)]u8 = undefined;
+ const extra = extra_buf[0..header.extra_len];
+
+ try input.seekTo(header_zip_offset + @sizeOf(CentralDirectoryFileHeader) + header.filename_len);
+ input.interface.readSlice(extra) catch |err| switch (err) {
+ error.ReadFailed => return input.err.?,
+ error.EndOfStream => return error.EndOfStream,
};
- if (header.extra_len > 0) {
- var extra_buf: [std.math.maxInt(u16)]u8 = undefined;
- const extra = extra_buf[0..header.extra_len];
-
- {
- try self.stream.seekTo(header_zip_offset + @sizeOf(CentralDirectoryFileHeader) + header.filename_len);
- const len = try (if (@TypeOf(self.stream.context) == std.fs.File) self.stream.context.deprecatedReader() else self.stream.context.reader()).readAll(extra);
- if (len != extra.len)
- return error.ZipTruncated;
- }
-
- var extra_offset: usize = 0;
- while (extra_offset + 4 <= extra.len) {
- const header_id = std.mem.readInt(u16, extra[extra_offset..][0..2], .little);
- const data_size = std.mem.readInt(u16, extra[extra_offset..][2..4], .little);
- const end = extra_offset + 4 + data_size;
- if (end > extra.len)
- return error.ZipBadExtraFieldSize;
- const data = extra[extra_offset + 4 .. end];
- switch (@as(ExtraHeader, @enumFromInt(header_id))) {
- .zip64_info => try readZip64FileExtents(CentralDirectoryFileHeader, header, &extents, data),
- else => {}, // ignore
- }
- extra_offset = end;
+ var extra_offset: usize = 0;
+ while (extra_offset + 4 <= extra.len) {
+ const header_id = std.mem.readInt(u16, extra[extra_offset..][0..2], .little);
+ const data_size = std.mem.readInt(u16, extra[extra_offset..][2..4], .little);
+ const end = extra_offset + 4 + data_size;
+ if (end > extra.len)
+ return error.ZipBadExtraFieldSize;
+ const data = extra[extra_offset + 4 .. end];
+ switch (@as(ExtraHeader, @enumFromInt(header_id))) {
+ .zip64_info => try readZip64FileExtents(CentralDirectoryFileHeader, header, &extents, data),
+ else => {}, // ignore
}
+ extra_offset = end;
}
-
- return .{
- .version_needed_to_extract = header.version_needed_to_extract,
- .flags = header.flags,
- .compression_method = header.compression_method,
- .last_modification_time = header.last_modification_time,
- .last_modification_date = header.last_modification_date,
- .header_zip_offset = header_zip_offset,
- .crc32 = header.crc32,
- .filename_len = header.filename_len,
- .compressed_size = extents.compressed_size,
- .uncompressed_size = extents.uncompressed_size,
- .file_offset = extents.local_file_header_offset,
- };
}
- pub const Entry = struct {
- version_needed_to_extract: u16,
- flags: GeneralPurposeFlags,
- compression_method: CompressionMethod,
- last_modification_time: u16,
- last_modification_date: u16,
- header_zip_offset: u64,
- crc32: u32,
- filename_len: u32,
- compressed_size: u64,
- uncompressed_size: u64,
- file_offset: u64,
-
- pub fn extract(
- self: Entry,
- stream: SeekableStream,
- options: ExtractOptions,
- filename_buf: []u8,
- dest: std.fs.Dir,
- ) !u32 {
- if (filename_buf.len < self.filename_len)
- return error.ZipInsufficientBuffer;
- const filename = filename_buf[0..self.filename_len];
-
+ return .{
+ .version_needed_to_extract = header.version_needed_to_extract,
+ .flags = header.flags,
+ .compression_method = header.compression_method,
+ .last_modification_time = header.last_modification_time,
+ .last_modification_date = header.last_modification_date,
+ .header_zip_offset = header_zip_offset,
+ .crc32 = header.crc32,
+ .filename_len = header.filename_len,
+ .compressed_size = extents.compressed_size,
+ .uncompressed_size = extents.uncompressed_size,
+ .file_offset = extents.local_file_header_offset,
+ };
+ }
+
+ pub const Entry = struct {
+ version_needed_to_extract: u16,
+ flags: GeneralPurposeFlags,
+ compression_method: CompressionMethod,
+ last_modification_time: u16,
+ last_modification_date: u16,
+ header_zip_offset: u64,
+ crc32: u32,
+ filename_len: u32,
+ compressed_size: u64,
+ uncompressed_size: u64,
+ file_offset: u64,
+
+ pub fn extract(
+ self: Entry,
+ stream: *File.Reader,
+ options: ExtractOptions,
+ filename_buf: []u8,
+ dest: std.fs.Dir,
+ ) !u32 {
+ if (filename_buf.len < self.filename_len)
+ return error.ZipInsufficientBuffer;
+ switch (self.compression_method) {
+ .store, .deflate => {},
+ else => return error.UnsupportedCompressionMethod,
+ }
+ const filename = filename_buf[0..self.filename_len];
+ {
try stream.seekTo(self.header_zip_offset + @sizeOf(CentralDirectoryFileHeader));
+ try stream.interface.readSlice(filename);
+ }
- {
- const len = try (if (@TypeOf(stream.context) == std.fs.File) stream.context.deprecatedReader() else stream.context.reader()).readAll(filename);
- if (len != filename.len)
- return error.ZipBadFileOffset;
- }
-
- const local_data_header_offset: u64 = local_data_header_offset: {
- const local_header = blk: {
- try stream.seekTo(self.file_offset);
- break :blk try (if (@TypeOf(stream.context) == std.fs.File) stream.context.deprecatedReader() else stream.context.reader()).readStructEndian(LocalFileHeader, .little);
- };
- if (!std.mem.eql(u8, &local_header.signature, &local_file_header_sig))
- return error.ZipBadFileOffset;
- if (local_header.version_needed_to_extract != self.version_needed_to_extract)
- return error.ZipMismatchVersionNeeded;
- if (local_header.last_modification_time != self.last_modification_time)
- return error.ZipMismatchModTime;
- if (local_header.last_modification_date != self.last_modification_date)
- return error.ZipMismatchModDate;
+ const local_data_header_offset: u64 = local_data_header_offset: {
+ const local_header = blk: {
+ try stream.seekTo(self.file_offset);
+ break :blk try stream.interface.takeStructEndian(LocalFileHeader, .little);
+ };
+ if (!std.mem.eql(u8, &local_header.signature, &local_file_header_sig))
+ return error.ZipBadFileOffset;
+ if (local_header.version_needed_to_extract != self.version_needed_to_extract)
+ return error.ZipMismatchVersionNeeded;
+ if (local_header.last_modification_time != self.last_modification_time)
+ return error.ZipMismatchModTime;
+ if (local_header.last_modification_date != self.last_modification_date)
+ return error.ZipMismatchModDate;
- if (@as(u16, @bitCast(local_header.flags)) != @as(u16, @bitCast(self.flags)))
- return error.ZipMismatchFlags;
- if (local_header.crc32 != 0 and local_header.crc32 != self.crc32)
- return error.ZipMismatchCrc32;
- var extents: FileExtents = .{
- .uncompressed_size = local_header.uncompressed_size,
- .compressed_size = local_header.compressed_size,
- .local_file_header_offset = 0,
- };
- if (local_header.extra_len > 0) {
- var extra_buf: [std.math.maxInt(u16)]u8 = undefined;
- const extra = extra_buf[0..local_header.extra_len];
+ if (@as(u16, @bitCast(local_header.flags)) != @as(u16, @bitCast(self.flags)))
+ return error.ZipMismatchFlags;
+ if (local_header.crc32 != 0 and local_header.crc32 != self.crc32)
+ return error.ZipMismatchCrc32;
+ var extents: FileExtents = .{
+ .uncompressed_size = local_header.uncompressed_size,
+ .compressed_size = local_header.compressed_size,
+ .local_file_header_offset = 0,
+ };
+ if (local_header.extra_len > 0) {
+ var extra_buf: [std.math.maxInt(u16)]u8 = undefined;
+ const extra = extra_buf[0..local_header.extra_len];
- {
- try stream.seekTo(self.file_offset + @sizeOf(LocalFileHeader) + local_header.filename_len);
- const len = try (if (@TypeOf(stream.context) == std.fs.File) stream.context.deprecatedReader() else stream.context.reader()).readAll(extra);
- if (len != extra.len)
- return error.ZipTruncated;
- }
+ {
+ try stream.seekTo(self.file_offset + @sizeOf(LocalFileHeader) + local_header.filename_len);
+ try stream.interface.readSlice(extra);
+ }
- var extra_offset: usize = 0;
- while (extra_offset + 4 <= local_header.extra_len) {
- const header_id = std.mem.readInt(u16, extra[extra_offset..][0..2], .little);
- const data_size = std.mem.readInt(u16, extra[extra_offset..][2..4], .little);
- const end = extra_offset + 4 + data_size;
- if (end > local_header.extra_len)
- return error.ZipBadExtraFieldSize;
- const data = extra[extra_offset + 4 .. end];
- switch (@as(ExtraHeader, @enumFromInt(header_id))) {
- .zip64_info => try readZip64FileExtents(LocalFileHeader, local_header, &extents, data),
- else => {}, // ignore
- }
- extra_offset = end;
+ var extra_offset: usize = 0;
+ while (extra_offset + 4 <= local_header.extra_len) {
+ const header_id = std.mem.readInt(u16, extra[extra_offset..][0..2], .little);
+ const data_size = std.mem.readInt(u16, extra[extra_offset..][2..4], .little);
+ const end = extra_offset + 4 + data_size;
+ if (end > local_header.extra_len)
+ return error.ZipBadExtraFieldSize;
+ const data = extra[extra_offset + 4 .. end];
+ switch (@as(ExtraHeader, @enumFromInt(header_id))) {
+ .zip64_info => try readZip64FileExtents(LocalFileHeader, local_header, &extents, data),
+ else => {}, // ignore
}
+ extra_offset = end;
}
+ }
- if (extents.compressed_size != 0 and
- extents.compressed_size != self.compressed_size)
- return error.ZipMismatchCompLen;
- if (extents.uncompressed_size != 0 and
- extents.uncompressed_size != self.uncompressed_size)
- return error.ZipMismatchUncompLen;
+ if (extents.compressed_size != 0 and
+ extents.compressed_size != self.compressed_size)
+ return error.ZipMismatchCompLen;
+ if (extents.uncompressed_size != 0 and
+ extents.uncompressed_size != self.uncompressed_size)
+ return error.ZipMismatchUncompLen;
- if (local_header.filename_len != self.filename_len)
- return error.ZipMismatchFilenameLen;
+ if (local_header.filename_len != self.filename_len)
+ return error.ZipMismatchFilenameLen;
- break :local_data_header_offset @as(u64, local_header.filename_len) +
- @as(u64, local_header.extra_len);
- };
+ break :local_data_header_offset @as(u64, local_header.filename_len) +
+ @as(u64, local_header.extra_len);
+ };
- if (isBadFilename(filename))
- return error.ZipBadFilename;
+ if (isBadFilename(filename))
+ return error.ZipBadFilename;
- if (options.allow_backslashes) {
- std.mem.replaceScalar(u8, filename, '\\', '/');
- } else {
- if (std.mem.indexOfScalar(u8, filename, '\\')) |_|
- return error.ZipFilenameHasBackslash;
- }
+ if (options.allow_backslashes) {
+ std.mem.replaceScalar(u8, filename, '\\', '/');
+ } else {
+ if (std.mem.indexOfScalar(u8, filename, '\\')) |_|
+ return error.ZipFilenameHasBackslash;
+ }
- // All entries that end in '/' are directories
- if (filename[filename.len - 1] == '/') {
- if (self.uncompressed_size != 0)
- return error.ZipBadDirectorySize;
- try dest.makePath(filename[0 .. filename.len - 1]);
- return std.hash.Crc32.hash(&.{});
- }
+ // All entries that end in '/' are directories
+ if (filename[filename.len - 1] == '/') {
+ if (self.uncompressed_size != 0)
+ return error.ZipBadDirectorySize;
+ try dest.makePath(filename[0 .. filename.len - 1]);
+ return std.hash.Crc32.hash(&.{});
+ }
- const out_file = blk: {
- if (std.fs.path.dirname(filename)) |dirname| {
- var parent_dir = try dest.makeOpenPath(dirname, .{});
- defer parent_dir.close();
+ const out_file = blk: {
+ if (std.fs.path.dirname(filename)) |dirname| {
+ var parent_dir = try dest.makeOpenPath(dirname, .{});
+ defer parent_dir.close();
- const basename = std.fs.path.basename(filename);
- break :blk try parent_dir.createFile(basename, .{ .exclusive = true });
- }
- break :blk try dest.createFile(filename, .{ .exclusive = true });
- };
- defer out_file.close();
- const local_data_file_offset: u64 =
- @as(u64, self.file_offset) +
- @as(u64, @sizeOf(LocalFileHeader)) +
- local_data_header_offset;
- try stream.seekTo(local_data_file_offset);
- var limited_reader = std.io.limitedReader((if (@TypeOf(stream.context) == std.fs.File) stream.context.deprecatedReader() else stream.context.reader()), self.compressed_size);
- const crc = try decompress(
- self.compression_method,
- self.uncompressed_size,
- limited_reader.reader(),
- out_file.deprecatedWriter(),
- );
- if (limited_reader.bytes_left != 0)
- return error.ZipDecompressTruncated;
- return crc;
- }
- };
+ const basename = std.fs.path.basename(filename);
+ break :blk try parent_dir.createFile(basename, .{ .exclusive = true });
+ }
+ break :blk try dest.createFile(filename, .{ .exclusive = true });
+ };
+ defer out_file.close();
+ var file_writer = out_file.writer();
+ var file_bw = file_writer.writer(&.{});
+ const local_data_file_offset: u64 =
+ @as(u64, self.file_offset) +
+ @as(u64, @sizeOf(LocalFileHeader)) +
+ local_data_header_offset;
+ try stream.seekTo(local_data_file_offset);
+ var limited_file_reader = stream.interface.limited(.limited(self.compressed_size));
+ var file_read_buffer: [1000]u8 = undefined;
+ var decompress_read_buffer: [1000]u8 = undefined;
+ var limited_br = limited_file_reader.reader().buffered(&file_read_buffer);
+ var decompress: Decompress = undefined;
+ var decompress_br = decompress.readable(&limited_br, self.compression_method, &decompress_read_buffer);
+ const start_out = file_bw.count;
+ var hash_writer = file_bw.hashed(std.hash.Crc32.init());
+ var hash_bw = hash_writer.writer(&.{});
+ decompress_br.readAll(&hash_bw, .limited(self.uncompressed_size)) catch |err| switch (err) {
+ error.ReadFailed => return stream.err.?,
+ error.WriteFailed => return file_writer.err.?,
+ error.EndOfStream => return error.ZipDecompressTruncated,
+ };
+ if (limited_file_reader.remaining.nonzero()) return error.ZipDecompressTruncated;
+ const written = file_bw.count - start_out;
+ if (written != self.uncompressed_size) return error.ZipUncompressSizeMismatch;
+ return hash_writer.hasher.final();
+ }
};
-}
+};
// returns true if `filename` starts with `root` followed by a forward slash
fn filenameInRoot(filename: []const u8, root: []const u8) bool {
@@ -614,17 +640,13 @@ pub const ExtractOptions = struct {
diagnostics: ?*Diagnostics = null,
};
-/// Extract the zipped files inside `seekable_stream` to the given `dest` directory.
-/// Note that `seekable_stream` must be an instance of `std.io.SeekableStream` and
-/// its context must also have a `.reader()` method that returns an instance of
-/// `std.io.GenericReader`.
-pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptions) !void {
- const SeekableStream = @TypeOf(seekable_stream);
- var iter = try Iterator(SeekableStream).init(seekable_stream);
+/// Extract the zipped files to the given `dest` directory.
+pub fn extract(dest: std.fs.Dir, fr: *File.Reader, options: ExtractOptions) !void {
+ var iter = try Iterator.init(fr);
var filename_buf: [std.fs.max_path_bytes]u8 = undefined;
while (try iter.next()) |entry| {
- const crc32 = try entry.extract(seekable_stream, options, &filename_buf, dest);
+ const crc32 = try entry.extract(fr, options, &filename_buf, dest);
if (crc32 != entry.crc32)
return error.ZipCrcMismatch;
if (options.diagnostics) |d| {
@@ -633,173 +655,6 @@ pub fn extract(dest: std.fs.Dir, seekable_stream: anytype, options: ExtractOptio
}
}
-fn testZip(options: ExtractOptions, comptime files: []const File, write_opt: testutil.WriteZipOptions) !void {
- var store: [files.len]FileStore = undefined;
- try testZipWithStore(options, files, write_opt, &store);
-}
-fn testZipWithStore(
- options: ExtractOptions,
- test_files: []const File,
- write_opt: testutil.WriteZipOptions,
- store: []FileStore,
-) !void {
- var zip_buf: [4096]u8 = undefined;
- var fbs = try testutil.makeZipWithStore(&zip_buf, test_files, write_opt, store);
-
- var tmp = testing.tmpDir(.{ .no_follow = true });
- defer tmp.cleanup();
- try extract(tmp.dir, fbs.seekableStream(), options);
- try testutil.expectFiles(test_files, tmp.dir, .{});
-}
-fn testZipError(expected_error: anyerror, file: File, options: ExtractOptions) !void {
- var zip_buf: [4096]u8 = undefined;
- var store: [1]FileStore = undefined;
- var fbs = try testutil.makeZipWithStore(&zip_buf, &[_]File{file}, .{}, &store);
- var tmp = testing.tmpDir(.{ .no_follow = true });
- defer tmp.cleanup();
- try testing.expectError(expected_error, extract(tmp.dir, fbs.seekableStream(), options));
-}
-
-test "zip one file" {
- try testZip(.{}, &[_]File{
- .{ .name = "onefile.txt", .content = "Just a single file\n", .compression = .store },
- }, .{});
-}
-test "zip multiple files" {
- try testZip(.{ .allow_backslashes = true }, &[_]File{
- .{ .name = "foo", .content = "a foo file\n", .compression = .store },
- .{ .name = "subdir/bar", .content = "bar is this right?\nanother newline\n", .compression = .store },
- .{ .name = "subdir\\whoa", .content = "you can do backslashes", .compression = .store },
- .{ .name = "subdir/another/baz", .content = "bazzy mc bazzerson", .compression = .store },
- }, .{});
-}
-test "zip deflated" {
- try testZip(.{}, &[_]File{
- .{ .name = "deflateme", .content = "This is a deflated file.\nIt should be smaller in the Zip file1\n", .compression = .deflate },
- // TODO: re-enable this if/when we add support for deflate64
- //.{ .name = "deflateme64", .content = "The 64k version of deflate!\n", .compression = .deflate64 },
- .{ .name = "raw", .content = "Not all files need to be deflated in the same Zip.\n", .compression = .store },
- }, .{});
-}
-test "zip verify filenames" {
- // no empty filenames
- try testZipError(error.ZipBadFilename, .{ .name = "", .content = "", .compression = .store }, .{});
- // no absolute paths
- try testZipError(error.ZipBadFilename, .{ .name = "/", .content = "", .compression = .store }, .{});
- try testZipError(error.ZipBadFilename, .{ .name = "/foo", .content = "", .compression = .store }, .{});
- try testZipError(error.ZipBadFilename, .{ .name = "/foo/bar", .content = "", .compression = .store }, .{});
- // no '..' components
- try testZipError(error.ZipBadFilename, .{ .name = "..", .content = "", .compression = .store }, .{});
- try testZipError(error.ZipBadFilename, .{ .name = "foo/..", .content = "", .compression = .store }, .{});
- try testZipError(error.ZipBadFilename, .{ .name = "foo/bar/..", .content = "", .compression = .store }, .{});
- try testZipError(error.ZipBadFilename, .{ .name = "foo/bar/../", .content = "", .compression = .store }, .{});
- // no backslashes
- try testZipError(error.ZipFilenameHasBackslash, .{ .name = "foo\\bar", .content = "", .compression = .store }, .{});
-}
-
-test "zip64" {
- const test_files = [_]File{
- .{ .name = "fram", .content = "fram foo fro fraba", .compression = .store },
- .{ .name = "subdir/barro", .content = "aljdk;jal;jfd;lajkf", .compression = .store },
- };
-
- try testZip(.{}, &test_files, .{
- .end = .{
- .zip64 = .{},
- .record_count_disk = std.math.maxInt(u16), // trigger zip64
- },
- });
- try testZip(.{}, &test_files, .{
- .end = .{
- .zip64 = .{},
- .record_count_total = std.math.maxInt(u16), // trigger zip64
- },
- });
- try testZip(.{}, &test_files, .{
- .end = .{
- .zip64 = .{},
- .record_count_disk = std.math.maxInt(u16), // trigger zip64
- .record_count_total = std.math.maxInt(u16), // trigger zip64
- },
- });
- try testZip(.{}, &test_files, .{
- .end = .{
- .zip64 = .{},
- .central_directory_size = std.math.maxInt(u32), // trigger zip64
- },
- });
- try testZip(.{}, &test_files, .{
- .end = .{
- .zip64 = .{},
- .central_directory_offset = std.math.maxInt(u32), // trigger zip64
- },
- });
- try testZip(.{}, &test_files, .{
- .end = .{
- .zip64 = .{},
- .central_directory_offset = std.math.maxInt(u32), // trigger zip64
- },
- .local_header = .{
- .zip64 = .{ // trigger local header zip64
- .data_size = 16,
- },
- .compressed_size = std.math.maxInt(u32),
- .uncompressed_size = std.math.maxInt(u32),
- .extra_len = 20,
- },
- });
-}
-
-test "bad zip files" {
- var tmp = testing.tmpDir(.{ .no_follow = true });
- defer tmp.cleanup();
- var zip_buf: [4096]u8 = undefined;
-
- const file_a = [_]File{.{ .name = "a", .content = "", .compression = .store }};
-
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .sig = [_]u8{ 1, 2, 3, 4 } } });
- try testing.expectError(error.ZipNoEndRecord, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .comment_len = 1 } });
- try testing.expectError(error.ZipNoEndRecord, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .comment = "a", .comment_len = 0 } });
- try testing.expectError(error.ZipNoEndRecord, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .disk_number = 1 } });
- try testing.expectError(error.ZipMultiDiskUnsupported, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .central_directory_disk_number = 1 } });
- try testing.expectError(error.ZipMultiDiskUnsupported, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .record_count_disk = 1 } });
- try testing.expectError(error.ZipDiskRecordCountTooLarge, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &.{}, .{ .end = .{ .central_directory_size = 1 } });
- try testing.expectError(error.ZipCdOversized, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &file_a, .{ .end = .{ .central_directory_size = 0 } });
- try testing.expectError(error.ZipCdUndersized, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &file_a, .{ .end = .{ .central_directory_offset = 0 } });
- try testing.expectError(error.ZipBadCdOffset, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
- {
- var fbs = try testutil.makeZip(&zip_buf, &file_a, .{
- .end = .{
- .zip64 = .{ .locator_sig = [_]u8{ 1, 2, 3, 4 } },
- .central_directory_size = std.math.maxInt(u32), // trigger 64
- },
- });
- try testing.expectError(error.ZipBadLocatorSig, extract(tmp.dir, fbs.seekableStream(), .{}));
- }
+test {
+ _ = @import("zip/test.zig");
}
--
2.54.0
From a4f05a4588100c5e7f311dd5319e97e394f109a4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 27 Jul 2025 10:25:46 -0700
Subject: [PATCH 067/110] delete flate implementation
---
lib/std/compress/flate.zig | 208 ++--
lib/std/compress/flate/BlockWriter.zig | 259 ++---
lib/std/compress/flate/Compress.zig | 977 +++---------------
lib/std/compress/flate/Decompress.zig | 5 +-
lib/std/compress/flate/HuffmanEncoder.zig | 475 +++++++++
.../compress/flate/testdata/block_writer.zig | 606 -----------
.../block_writer/huffman-null-max.dyn.expect | Bin 78 -> 0 bytes
.../huffman-null-max.dyn.expect-noinput | Bin 78 -> 0 bytes
.../block_writer/huffman-null-max.huff.expect | Bin 8204 -> 0 bytes
.../block_writer/huffman-null-max.input | Bin 65535 -> 0 bytes
.../block_writer/huffman-null-max.wb.expect | Bin 78 -> 0 bytes
.../huffman-null-max.wb.expect-noinput | Bin 78 -> 0 bytes
.../block_writer/huffman-pi.dyn.expect | Bin 1696 -> 0 bytes
.../huffman-pi.dyn.expect-noinput | Bin 1696 -> 0 bytes
.../block_writer/huffman-pi.huff.expect | Bin 1606 -> 0 bytes
.../testdata/block_writer/huffman-pi.input | 1 -
.../block_writer/huffman-pi.wb.expect | Bin 1696 -> 0 bytes
.../block_writer/huffman-pi.wb.expect-noinput | Bin 1696 -> 0 bytes
.../block_writer/huffman-rand-1k.dyn.expect | Bin 1005 -> 0 bytes
.../huffman-rand-1k.dyn.expect-noinput | Bin 1054 -> 0 bytes
.../block_writer/huffman-rand-1k.huff.expect | Bin 1005 -> 0 bytes
.../block_writer/huffman-rand-1k.input | Bin 1000 -> 0 bytes
.../block_writer/huffman-rand-1k.wb.expect | Bin 1005 -> 0 bytes
.../huffman-rand-1k.wb.expect-noinput | Bin 1054 -> 0 bytes
.../huffman-rand-limit.dyn.expect | Bin 229 -> 0 bytes
.../huffman-rand-limit.dyn.expect-noinput | Bin 229 -> 0 bytes
.../huffman-rand-limit.huff.expect | Bin 252 -> 0 bytes
.../block_writer/huffman-rand-limit.input | 4 -
.../block_writer/huffman-rand-limit.wb.expect | Bin 186 -> 0 bytes
.../huffman-rand-limit.wb.expect-noinput | Bin 186 -> 0 bytes
.../block_writer/huffman-rand-max.huff.expect | Bin 65540 -> 0 bytes
.../block_writer/huffman-rand-max.input | Bin 65535 -> 0 bytes
.../block_writer/huffman-shifts.dyn.expect | Bin 32 -> 0 bytes
.../huffman-shifts.dyn.expect-noinput | Bin 32 -> 0 bytes
.../block_writer/huffman-shifts.huff.expect | Bin 1812 -> 0 bytes
.../block_writer/huffman-shifts.input | 2 -
.../block_writer/huffman-shifts.wb.expect | Bin 32 -> 0 bytes
.../huffman-shifts.wb.expect-noinput | Bin 32 -> 0 bytes
.../huffman-text-shift.dyn.expect | Bin 231 -> 0 bytes
.../huffman-text-shift.dyn.expect-noinput | Bin 231 -> 0 bytes
.../huffman-text-shift.huff.expect | Bin 231 -> 0 bytes
.../block_writer/huffman-text-shift.input | 14 -
.../block_writer/huffman-text-shift.wb.expect | Bin 231 -> 0 bytes
.../huffman-text-shift.wb.expect-noinput | Bin 231 -> 0 bytes
.../block_writer/huffman-text.dyn.expect | Bin 217 -> 0 bytes
.../huffman-text.dyn.expect-noinput | Bin 217 -> 0 bytes
.../block_writer/huffman-text.huff.expect | Bin 219 -> 0 bytes
.../testdata/block_writer/huffman-text.input | 14 -
.../block_writer/huffman-text.wb.expect | Bin 217 -> 0 bytes
.../huffman-text.wb.expect-noinput | Bin 217 -> 0 bytes
.../block_writer/huffman-zero.dyn.expect | Bin 17 -> 0 bytes
.../huffman-zero.dyn.expect-noinput | Bin 17 -> 0 bytes
.../block_writer/huffman-zero.huff.expect | Bin 51 -> 0 bytes
.../testdata/block_writer/huffman-zero.input | 1 -
.../block_writer/huffman-zero.wb.expect | Bin 6 -> 0 bytes
.../huffman-zero.wb.expect-noinput | Bin 6 -> 0 bytes
.../null-long-match.dyn.expect-noinput | Bin 206 -> 0 bytes
.../null-long-match.wb.expect-noinput | Bin 206 -> 0 bytes
58 files changed, 787 insertions(+), 1779 deletions(-)
create mode 100644 lib/std/compress/flate/HuffmanEncoder.zig
delete mode 100644 lib/std/compress/flate/testdata/block_writer.zig
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-null-max.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-null-max.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-null-max.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-null-max.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-null-max.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-null-max.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-pi.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-pi.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-pi.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-pi.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-pi.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-pi.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-max.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-rand-max.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-shifts.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-shifts.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-shifts.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-shifts.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-shifts.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-shifts.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text-shift.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text-shift.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text-shift.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text-shift.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text-shift.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text-shift.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-text.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-zero.dyn.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-zero.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-zero.huff.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-zero.input
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-zero.wb.expect
delete mode 100644 lib/std/compress/flate/testdata/block_writer/huffman-zero.wb.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/null-long-match.dyn.expect-noinput
delete mode 100644 lib/std/compress/flate/testdata/block_writer/null-long-match.wb.expect-noinput
diff --git a/lib/std/compress/flate.zig b/lib/std/compress/flate.zig
index 5a54643f45f2d79e6b7d49e88f9a6269a727be1c..73f98271a4e0671ddb24b26da9c0ed3182bc5d1b 100644
--- a/lib/std/compress/flate.zig
+++ b/lib/std/compress/flate.zig
@@ -1,7 +1,7 @@
const builtin = @import("builtin");
const std = @import("../std.zig");
const testing = std.testing;
-const Writer = std.io.Writer;
+const Writer = std.Io.Writer;
/// Container of the deflate bit stream body. Container adds header before
/// deflate bit stream and footer after. It can bi gzip, zlib or raw (no header,
@@ -77,7 +77,7 @@ pub const Container = enum {
raw: void,
gzip: struct {
crc: std.hash.Crc32 = .init(),
- count: usize = 0,
+ count: u32 = 0,
},
zlib: std.hash.Adler32,
@@ -98,7 +98,7 @@ pub const Container = enum {
.raw => {},
.gzip => |*gzip| {
gzip.update(buf);
- gzip.count += buf.len;
+ gzip.count +%= buf.len;
},
.zlib => |*zlib| {
zlib.update(buf);
@@ -148,35 +148,9 @@ pub const Compress = @import("flate/Compress.zig");
/// decompression and correctly produces the original full-size data or file.
pub const Decompress = @import("flate/Decompress.zig");
-/// Huffman only compression. Without Lempel-Ziv match searching. Faster
-/// compression, less memory requirements but bigger compressed sizes.
-pub const huffman = struct {
- // The odd order in which the codegen code sizes are written.
- pub const codegen_order = [_]u32{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
- // The number of codegen codes.
- pub const codegen_code_count = 19;
-
- // The largest distance code.
- pub const distance_code_count = 30;
-
- // Maximum number of literals.
- pub const max_num_lit = 286;
-
- // Max number of frequencies used for a Huffman Code
- // Possible lengths are codegen_code_count (19), distance_code_count (30) and max_num_lit (286).
- // The largest of these is max_num_lit.
- pub const max_num_frequencies = max_num_lit;
-
- // Biggest block size for uncompressed block.
- pub const max_store_block_size = 65535;
- // The special code used to mark the end of a block.
- pub const end_block_marker = 256;
-};
-
-test {
- _ = Compress;
- _ = Decompress;
-}
+/// Compression without Lempel-Ziv match searching. Faster compression, less
+/// memory requirements but bigger compressed sizes.
+pub const HuffmanEncoder = @import("flate/HuffmanEncoder.zig");
test "compress/decompress" {
const print = std.debug.print;
@@ -217,12 +191,11 @@ test "compress/decompress" {
},
};
- for (cases, 0..) |case, case_no| { // for each case
+ for (cases, 0..) |case, case_no| {
const data = case.data;
- for (levels, 0..) |level, i| { // for each compression level
-
- inline for (Container.list) |container| { // for each wrapping
+ for (levels, 0..) |level, i| {
+ for (Container.list) |container| {
var compressed_size: usize = if (case.gzip_sizes[i] > 0)
case.gzip_sizes[i] - Container.gzip.size() + container.size()
else
@@ -230,21 +203,21 @@ test "compress/decompress" {
// compress original stream to compressed stream
{
- var original: std.io.Reader = .fixed(data);
var compressed: Writer = .fixed(&cmp_buf);
- var compress: Compress = .init(&original, &.{}, .{ .container = .raw, .level = level });
- const n = try compress.reader.streamRemaining(&compressed);
+ var compress: Compress = .init(&compressed, &.{}, .{ .container = .raw, .level = level });
+ try compress.writer.writeAll(data);
+ try compress.end();
+
if (compressed_size == 0) {
if (container == .gzip)
print("case {d} gzip level {} compressed size: {d}\n", .{ case_no, level, compressed.pos });
compressed_size = compressed.end;
}
- try testing.expectEqual(compressed_size, n);
try testing.expectEqual(compressed_size, compressed.end);
}
// decompress compressed stream to decompressed stream
{
- var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var compressed: std.Io.Reader = .fixed(cmp_buf[0..compressed_size]);
var decompressed: Writer = .fixed(&dcm_buf);
var decompress: Decompress = .init(&compressed, container, &.{});
_ = try decompress.reader.streamRemaining(&decompressed);
@@ -266,7 +239,7 @@ test "compress/decompress" {
}
// decompressor reader interface
{
- var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var compressed: std.Io.Reader = .fixed(cmp_buf[0..compressed_size]);
var decompress: Decompress = .init(&compressed, container, &.{});
const n = try decompress.reader.readSliceShort(&dcm_buf);
try testing.expectEqual(data.len, n);
@@ -276,7 +249,7 @@ test "compress/decompress" {
}
// huffman only compression
{
- inline for (Container.list) |container| { // for each wrapping
+ for (Container.list) |container| {
var compressed_size: usize = if (case.huffman_only_size > 0)
case.huffman_only_size - Container.gzip.size() + container.size()
else
@@ -284,7 +257,7 @@ test "compress/decompress" {
// compress original stream to compressed stream
{
- var original: std.io.Reader = .fixed(data);
+ var original: std.Io.Reader = .fixed(data);
var compressed: Writer = .fixed(&cmp_buf);
var cmp = try Compress.Huffman.init(container, &compressed);
try cmp.compress(original.reader());
@@ -298,7 +271,7 @@ test "compress/decompress" {
}
// decompress compressed stream to decompressed stream
{
- var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var compressed: std.Io.Reader = .fixed(cmp_buf[0..compressed_size]);
var decompress: Decompress = .init(&compressed, container, &.{});
var decompressed: Writer = .fixed(&dcm_buf);
_ = try decompress.reader.streamRemaining(&decompressed);
@@ -309,7 +282,7 @@ test "compress/decompress" {
// store only
{
- inline for (Container.list) |container| { // for each wrapping
+ for (Container.list) |container| {
var compressed_size: usize = if (case.store_size > 0)
case.store_size - Container.gzip.size() + container.size()
else
@@ -317,7 +290,7 @@ test "compress/decompress" {
// compress original stream to compressed stream
{
- var original: std.io.Reader = .fixed(data);
+ var original: std.Io.Reader = .fixed(data);
var compressed: Writer = .fixed(&cmp_buf);
var cmp = try Compress.SimpleCompressor(.store, container).init(&compressed);
try cmp.compress(original.reader());
@@ -332,7 +305,7 @@ test "compress/decompress" {
}
// decompress compressed stream to decompressed stream
{
- var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
+ var compressed: std.Io.Reader = .fixed(cmp_buf[0..compressed_size]);
var decompress: Decompress = .init(&compressed, container, &.{});
var decompressed: Writer = .fixed(&dcm_buf);
_ = try decompress.reader.streamRemaining(&decompressed);
@@ -344,13 +317,13 @@ test "compress/decompress" {
}
fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {
- var in: std.io.Reader = .fixed(compressed);
- var aw: std.io.Writer.Allocating = .init(testing.allocator);
+ var in: std.Io.Reader = .fixed(compressed);
+ var aw: std.Io.Writer.Allocating = .init(testing.allocator);
defer aw.deinit();
var decompress: Decompress = .init(&in, container, &.{});
_ = try decompress.reader.streamRemaining(&aw.writer);
- try testing.expectEqualSlices(u8, expected_plain, aw.items);
+ try testing.expectEqualSlices(u8, expected_plain, aw.getWritten());
}
test "don't read past deflate stream's end" {
@@ -483,17 +456,12 @@ test "public interface" {
var buffer1: [64]u8 = undefined;
var buffer2: [64]u8 = undefined;
- // TODO These used to be functions, need to migrate the tests
- const decompress = void;
- const compress = void;
- const store = void;
-
// decompress
{
var plain: Writer = .fixed(&buffer2);
-
- var in: std.io.Reader = .fixed(gzip_data);
- try decompress(&in, &plain);
+ var in: std.Io.Reader = .fixed(gzip_data);
+ var d: Decompress = .init(&in, .raw, &.{});
+ _ = try d.reader.streamRemaining(&plain);
try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
@@ -502,11 +470,13 @@ test "public interface" {
var plain: Writer = .fixed(&buffer2);
var compressed: Writer = .fixed(&buffer1);
- var in: std.io.Reader = .fixed(plain_data);
- try compress(&in, &compressed, .{});
+ var cmp: Compress = .init(&compressed, &.{}, .{});
+ try cmp.writer.writeAll(plain_data);
+ try cmp.end();
- var r: std.io.Reader = .fixed(&buffer1);
- try decompress(&r, &plain);
+ var r: std.Io.Reader = .fixed(&buffer1);
+ var d: Decompress = .init(&r, .raw, &.{});
+ _ = try d.reader.streamRemaining(&plain);
try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
@@ -515,12 +485,11 @@ test "public interface" {
var plain: Writer = .fixed(&buffer2);
var compressed: Writer = .fixed(&buffer1);
- var in: std.io.Reader = .fixed(plain_data);
- var cmp = try Compress(&compressed, .{});
- try cmp.compress(&in);
- try cmp.finish();
+ var cmp: Compress = .init(&compressed, &.{}, .{});
+ try cmp.writer.writeAll(plain_data);
+ try cmp.end();
- var r: std.io.Reader = .fixed(&buffer1);
+ var r: std.Io.Reader = .fixed(&buffer1);
var dcp = Decompress(&r);
try dcp.decompress(&plain);
try testing.expectEqualSlices(u8, plain_data, plain.buffered());
@@ -533,11 +502,12 @@ test "public interface" {
var plain: Writer = .fixed(&buffer2);
var compressed: Writer = .fixed(&buffer1);
- var in: std.io.Reader = .fixed(plain_data);
- try huffman.compress(&in, &compressed);
+ var in: std.Io.Reader = .fixed(plain_data);
+ try HuffmanEncoder.compress(&in, &compressed);
- var r: std.io.Reader = .fixed(&buffer1);
- try decompress(&r, &plain);
+ var r: std.Io.Reader = .fixed(&buffer1);
+ var d: Decompress = .init(&r, .raw, &.{});
+ _ = try d.reader.streamRemaining(&plain);
try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
@@ -546,47 +516,50 @@ test "public interface" {
var plain: Writer = .fixed(&buffer2);
var compressed: Writer = .fixed(&buffer1);
- var in: std.io.Reader = .fixed(plain_data);
- var cmp = try huffman.Compressor(&compressed);
+ var in: std.Io.Reader = .fixed(plain_data);
+ var cmp = try HuffmanEncoder.Compressor(&compressed);
try cmp.compress(&in);
try cmp.finish();
- var r: std.io.Reader = .fixed(&buffer1);
- try decompress(&r, &plain);
+ var r: std.Io.Reader = .fixed(&buffer1);
+ var d: Decompress = .init(&r, .raw, &.{});
+ _ = try d.reader.streamRemaining(&plain);
try testing.expectEqualSlices(u8, plain_data, plain.buffered());
}
}
- // store
- {
- // store compress/decompress
- {
- var plain: Writer = .fixed(&buffer2);
- var compressed: Writer = .fixed(&buffer1);
+ // TODO
+ //{
+ // // store compress/decompress
+ // {
+ // var plain: Writer = .fixed(&buffer2);
+ // var compressed: Writer = .fixed(&buffer1);
- var in: std.io.Reader = .fixed(plain_data);
- try store.compress(&in, &compressed);
+ // var in: std.Io.Reader = .fixed(plain_data);
+ // try store.compress(&in, &compressed);
- var r: std.io.Reader = .fixed(&buffer1);
- try decompress(&r, &plain);
- try testing.expectEqualSlices(u8, plain_data, plain.buffered());
- }
+ // var r: std.Io.Reader = .fixed(&buffer1);
+ // var d: Decompress = .init(&r, .raw, &.{});
+ // _ = try d.reader.streamRemaining(&plain);
+ // try testing.expectEqualSlices(u8, plain_data, plain.buffered());
+ // }
- // store compressor/decompressor
- {
- var plain: Writer = .fixed(&buffer2);
- var compressed: Writer = .fixed(&buffer1);
+ // // store compressor/decompressor
+ // {
+ // var plain: Writer = .fixed(&buffer2);
+ // var compressed: Writer = .fixed(&buffer1);
- var in: std.io.Reader = .fixed(plain_data);
- var cmp = try store.compressor(&compressed);
- try cmp.compress(&in);
- try cmp.finish();
+ // var in: std.Io.Reader = .fixed(plain_data);
+ // var cmp = try store.compressor(&compressed);
+ // try cmp.compress(&in);
+ // try cmp.finish();
- var r: std.io.Reader = .fixed(&buffer1);
- try decompress(&r, &plain);
- try testing.expectEqualSlices(u8, plain_data, plain.buffered());
- }
- }
+ // var r: std.Io.Reader = .fixed(&buffer1);
+ // var d: Decompress = .init(&r, .raw, &.{});
+ // _ = try d.reader.streamRemaining(&plain);
+ // try testing.expectEqualSlices(u8, plain_data, plain.buffered());
+ // }
+ //}
}
pub const match = struct {
@@ -615,26 +588,33 @@ test "zlib should not overshoot" {
0x03, 0x00, 0x8b, 0x61, 0x0f, 0xa4, 0x52, 0x5a, 0x94, 0x12,
};
- var stream: std.io.Reader = .fixed(&data);
- const reader = stream.reader();
+ var reader: std.Io.Reader = .fixed(&data);
- var dcp = Decompress.init(reader);
+ var decompress: Decompress = .init(&reader, .zlib, &.{});
var out: [128]u8 = undefined;
- // Decompress
- var n = try dcp.reader().readAll(out[0..]);
+ {
+ const n = try decompress.reader.readSliceShort(out[0..]);
- // Expected decompressed data
- try std.testing.expectEqual(46, n);
- try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
+ // Expected decompressed data
+ try std.testing.expectEqual(46, n);
+ try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
- // Decompressor don't overshoot underlying reader.
- // It is leaving it at the end of compressed data chunk.
- try std.testing.expectEqual(data.len - 4, stream.getPos());
- try std.testing.expectEqual(0, dcp.unreadBytes());
+ // Decompressor don't overshoot underlying reader.
+ // It is leaving it at the end of compressed data chunk.
+ try std.testing.expectEqual(data.len - 4, reader.seek);
+ // TODO what was this testing, exactly?
+ //try std.testing.expectEqual(0, decompress.unreadBytes());
+ }
// 4 bytes after compressed chunk are available in reader.
- n = try reader.readAll(out[0..]);
+ const n = try reader.readSliceShort(out[0..]);
try std.testing.expectEqual(n, 4);
try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
}
+
+test {
+ _ = HuffmanEncoder;
+ _ = Compress;
+ _ = Decompress;
+}
diff --git a/lib/std/compress/flate/BlockWriter.zig b/lib/std/compress/flate/BlockWriter.zig
index d1eb3a068ee434cd609a99b4a3981f0d06bcae99..b3af65051a56b9fdeed03026a61c334253f07f18 100644
--- a/lib/std/compress/flate/BlockWriter.zig
+++ b/lib/std/compress/flate/BlockWriter.zig
@@ -8,32 +8,33 @@ const Writer = std.io.Writer;
const BlockWriter = @This();
const flate = @import("../flate.zig");
const Compress = flate.Compress;
-const huffman = flate.huffman;
+const HuffmanEncoder = flate.HuffmanEncoder;
const Token = @import("Token.zig");
-const codegen_order = huffman.codegen_order;
+const codegen_order = HuffmanEncoder.codegen_order;
const end_code_mark = 255;
output: *Writer,
-codegen_freq: [huffman.codegen_code_count]u16 = undefined,
-literal_freq: [huffman.max_num_lit]u16 = undefined,
-distance_freq: [huffman.distance_code_count]u16 = undefined,
-codegen: [huffman.max_num_lit + huffman.distance_code_count + 1]u8 = undefined,
-literal_encoding: Compress.LiteralEncoder = .{},
-distance_encoding: Compress.DistanceEncoder = .{},
-codegen_encoding: Compress.CodegenEncoder = .{},
-fixed_literal_encoding: Compress.LiteralEncoder,
-fixed_distance_encoding: Compress.DistanceEncoder,
-huff_distance: Compress.DistanceEncoder,
+codegen_freq: [HuffmanEncoder.codegen_code_count]u16,
+literal_freq: [HuffmanEncoder.max_num_lit]u16,
+distance_freq: [HuffmanEncoder.distance_code_count]u16,
+codegen: [HuffmanEncoder.max_num_lit + HuffmanEncoder.distance_code_count + 1]u8,
+literal_encoding: HuffmanEncoder,
+distance_encoding: HuffmanEncoder,
+codegen_encoding: HuffmanEncoder,
+fixed_literal_encoding: HuffmanEncoder,
+fixed_distance_encoding: HuffmanEncoder,
+huff_distance: HuffmanEncoder,
-pub fn init(output: *Writer) BlockWriter {
- return .{
- .output = output,
- .fixed_literal_encoding = Compress.fixedLiteralEncoder(),
- .fixed_distance_encoding = Compress.fixedDistanceEncoder(),
- .huff_distance = Compress.huffmanDistanceEncoder(),
- };
+fixed_literal_codes: [HuffmanEncoder.max_num_frequencies]HuffmanEncoder.Code,
+fixed_distance_codes: [HuffmanEncoder.distance_code_count]HuffmanEncoder.Code,
+distance_codes: [HuffmanEncoder.distance_code_count]HuffmanEncoder.Code,
+
+pub fn init(bw: *BlockWriter) void {
+ bw.fixed_literal_encoding = .fixedLiteralEncoder(&bw.fixed_literal_codes);
+ bw.fixed_distance_encoding = .fixedDistanceEncoder(&bw.fixed_distance_codes);
+ bw.huff_distance = .huffmanDistanceEncoder(&bw.distance_codes);
}
/// Flush intrenal bit buffer to the writer.
@@ -46,27 +47,23 @@ pub fn flush(self: *BlockWriter) Writer.Error!void {
try self.bit_writer.flush();
}
-pub fn setWriter(self: *BlockWriter, new_writer: *Writer) void {
- self.bit_writer.setWriter(new_writer);
-}
-
fn writeCode(self: *BlockWriter, c: Compress.HuffCode) Writer.Error!void {
try self.bit_writer.writeBits(c.code, c.len);
}
-// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
-// the literal and distance lengths arrays (which are concatenated into a single
-// array). This method generates that run-length encoding.
-//
-// The result is written into the codegen array, and the frequencies
-// of each code is written into the codegen_freq array.
-// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
-// information. Code bad_code is an end marker
-//
-// num_literals: The number of literals in literal_encoding
-// num_distances: The number of distances in distance_encoding
-// lit_enc: The literal encoder to use
-// dist_enc: The distance encoder to use
+/// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
+/// the literal and distance lengths arrays (which are concatenated into a single
+/// array). This method generates that run-length encoding.
+///
+/// The result is written into the codegen array, and the frequencies
+/// of each code is written into the codegen_freq array.
+/// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
+/// information. Code bad_code is an end marker
+///
+/// num_literals: The number of literals in literal_encoding
+/// num_distances: The number of distances in distance_encoding
+/// lit_enc: The literal encoder to use
+/// dist_enc: The distance encoder to use
fn generateCodegen(
self: *BlockWriter,
num_literals: u32,
@@ -167,7 +164,7 @@ const DynamicSize = struct {
num_codegens: u32,
};
-// dynamicSize returns the size of dynamically encoded data in bits.
+/// dynamicSize returns the size of dynamically encoded data in bits.
fn dynamicSize(
self: *BlockWriter,
lit_enc: *Compress.LiteralEncoder, // literal encoder
@@ -194,7 +191,7 @@ fn dynamicSize(
};
}
-// fixedSize returns the size of dynamically encoded data in bits.
+/// fixedSize returns the size of dynamically encoded data in bits.
fn fixedSize(self: *BlockWriter, extra_bits: u32) u32 {
return 3 +
self.fixed_literal_encoding.bitLength(&self.literal_freq) +
@@ -207,25 +204,25 @@ const StoredSize = struct {
storable: bool,
};
-// storedSizeFits calculates the stored size, including header.
-// The function returns the size in bits and whether the block
-// fits inside a single block.
+/// storedSizeFits calculates the stored size, including header.
+/// The function returns the size in bits and whether the block
+/// fits inside a single block.
fn storedSizeFits(in: ?[]const u8) StoredSize {
if (in == null) {
return .{ .size = 0, .storable = false };
}
- if (in.?.len <= huffman.max_store_block_size) {
+ if (in.?.len <= HuffmanEncoder.max_store_block_size) {
return .{ .size = @as(u32, @intCast((in.?.len + 5) * 8)), .storable = true };
}
return .{ .size = 0, .storable = false };
}
-// Write the header of a dynamic Huffman block to the output stream.
-//
-// num_literals: The number of literals specified in codegen
-// num_distances: The number of distances specified in codegen
-// num_codegens: The number of codegens used in codegen
-// eof: Is it the end-of-file? (end of stream)
+/// Write the header of a dynamic Huffman block to the output stream.
+///
+/// num_literals: The number of literals specified in codegen
+/// num_distances: The number of distances specified in codegen
+/// num_codegens: The number of codegens used in codegen
+/// eof: Is it the end-of-file? (end of stream)
fn dynamicHeader(
self: *BlockWriter,
num_literals: u32,
@@ -291,11 +288,11 @@ fn fixedHeader(self: *BlockWriter, eof: bool) Writer.Error!void {
try self.bit_writer.writeBits(value, 3);
}
-// Write a block of tokens with the smallest encoding. Will choose block type.
-// The original input can be supplied, and if the huffman encoded data
-// is larger than the original bytes, the data will be written as a
-// stored block.
-// If the input is null, the tokens will always be Huffman encoded.
+/// Write a block of tokens with the smallest encoding. Will choose block type.
+/// The original input can be supplied, and if the huffman encoded data
+/// is larger than the original bytes, the data will be written as a
+/// stored block.
+/// If the input is null, the tokens will always be Huffman encoded.
pub fn write(self: *BlockWriter, tokens: []const Token, eof: bool, input: ?[]const u8) Writer.Error!void {
const lit_and_dist = self.indexTokens(tokens);
const num_literals = lit_and_dist.num_literals;
@@ -379,11 +376,11 @@ pub fn storedBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Erro
try self.bit_writer.writeBytes(input);
}
-// writeBlockDynamic encodes a block using a dynamic Huffman table.
-// This should be used if the symbols used have a disproportionate
-// histogram distribution.
-// If input is supplied and the compression savings are below 1/16th of the
-// input size the block is stored.
+/// writeBlockDynamic encodes a block using a dynamic Huffman table.
+/// This should be used if the symbols used have a disproportionate
+/// histogram distribution.
+/// If input is supplied and the compression savings are below 1/16th of the
+/// input size the block is stored.
fn dynamicBlock(
self: *BlockWriter,
tokens: []const Token,
@@ -429,10 +426,10 @@ const TotalIndexedTokens = struct {
num_distances: u32,
};
-// Indexes a slice of tokens followed by an end_block_marker, and updates
-// literal_freq and distance_freq, and generates literal_encoding
-// and distance_encoding.
-// The number of literal and distance tokens is returned.
+/// Indexes a slice of tokens followed by an end_block_marker, and updates
+/// literal_freq and distance_freq, and generates literal_encoding
+/// and distance_encoding.
+/// The number of literal and distance tokens is returned.
fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
var num_literals: u32 = 0;
var num_distances: u32 = 0;
@@ -453,7 +450,7 @@ fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
self.distance_freq[t.distanceCode()] += 1;
}
// add end_block_marker token at the end
- self.literal_freq[huffman.end_block_marker] += 1;
+ self.literal_freq[HuffmanEncoder.end_block_marker] += 1;
// get the number of literals
num_literals = @as(u32, @intCast(self.literal_freq.len));
@@ -479,8 +476,8 @@ fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
};
}
-// Writes a slice of tokens to the output followed by and end_block_marker.
-// codes for literal and distance encoding must be supplied.
+/// Writes a slice of tokens to the output followed by and end_block_marker.
+/// codes for literal and distance encoding must be supplied.
fn writeTokens(
self: *BlockWriter,
tokens: []const Token,
@@ -508,18 +505,18 @@ fn writeTokens(
}
}
// add end_block_marker at the end
- try self.writeCode(le_codes[huffman.end_block_marker]);
+ try self.writeCode(le_codes[HuffmanEncoder.end_block_marker]);
}
-// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
-// if the results only gains very little from compression.
+/// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
+/// if the results only gains very little from compression.
pub fn huffmanBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Error!void {
// Add everything as literals
histogram(input, &self.literal_freq);
- self.literal_freq[huffman.end_block_marker] = 1;
+ self.literal_freq[HuffmanEncoder.end_block_marker] = 1;
- const num_literals = huffman.end_block_marker + 1;
+ const num_literals = HuffmanEncoder.end_block_marker + 1;
self.distance_freq[0] = 1;
const num_distances = 1;
@@ -560,10 +557,9 @@ pub fn huffmanBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Err
const c = encoding[t];
try self.bit_writer.writeBits(c.code, c.len);
}
- try self.writeCode(encoding[huffman.end_block_marker]);
+ try self.writeCode(encoding[HuffmanEncoder.end_block_marker]);
}
-// histogram accumulates a histogram of b in h.
fn histogram(b: []const u8, h: *[286]u16) void {
// Clear histogram
for (h, 0..) |_, i| {
@@ -575,122 +571,3 @@ fn histogram(b: []const u8, h: *[286]u16) void {
lh[t] += 1;
}
}
-
-// tests
-const expect = std.testing.expect;
-const fmt = std.fmt;
-const testing = std.testing;
-const ArrayList = std.ArrayList;
-
-const TestCase = @import("testdata/block_writer.zig").TestCase;
-const testCases = @import("testdata/block_writer.zig").testCases;
-
-// tests if the writeBlock encoding has changed.
-test "write" {
- inline for (0..testCases.len) |i| {
- try testBlock(testCases[i], .write_block);
- }
-}
-
-// tests if the writeBlockDynamic encoding has changed.
-test "dynamicBlock" {
- inline for (0..testCases.len) |i| {
- try testBlock(testCases[i], .write_dyn_block);
- }
-}
-
-test "huffmanBlock" {
- inline for (0..testCases.len) |i| {
- try testBlock(testCases[i], .write_huffman_block);
- }
- try testBlock(.{
- .tokens = &[_]Token{},
- .input = "huffman-rand-max.input",
- .want = "huffman-rand-max.{s}.expect",
- }, .write_huffman_block);
-}
-
-const TestFn = enum {
- write_block,
- write_dyn_block, // write dynamic block
- write_huffman_block,
-
- fn to_s(self: TestFn) []const u8 {
- return switch (self) {
- .write_block => "wb",
- .write_dyn_block => "dyn",
- .write_huffman_block => "huff",
- };
- }
-
- fn write(
- comptime self: TestFn,
- bw: anytype,
- tok: []const Token,
- input: ?[]const u8,
- final: bool,
- ) !void {
- switch (self) {
- .write_block => try bw.write(tok, final, input),
- .write_dyn_block => try bw.dynamicBlock(tok, final, input),
- .write_huffman_block => try bw.huffmanBlock(input.?, final),
- }
- try bw.flush();
- }
-};
-
-// testBlock tests a block against its references
-//
-// size
-// 64K [file-name].input - input non compressed file
-// 8.1K [file-name].golden -
-// 78 [file-name].dyn.expect - output with writeBlockDynamic
-// 78 [file-name].wb.expect - output with writeBlock
-// 8.1K [file-name].huff.expect - output with writeBlockHuff
-// 78 [file-name].dyn.expect-noinput - output with writeBlockDynamic when input is null
-// 78 [file-name].wb.expect-noinput - output with writeBlock when input is null
-//
-// wb - writeBlock
-// dyn - writeBlockDynamic
-// huff - writeBlockHuff
-//
-fn testBlock(comptime tc: TestCase, comptime tfn: TestFn) !void {
- if (tc.input.len != 0 and tc.want.len != 0) {
- const want_name = comptime fmt.comptimePrint(tc.want, .{tfn.to_s()});
- const input = @embedFile("testdata/block_writer/" ++ tc.input);
- const want = @embedFile("testdata/block_writer/" ++ want_name);
- try testWriteBlock(tfn, input, want, tc.tokens);
- }
-
- if (tfn == .write_huffman_block) {
- return;
- }
-
- const want_name_no_input = comptime fmt.comptimePrint(tc.want_no_input, .{tfn.to_s()});
- const want = @embedFile("testdata/block_writer/" ++ want_name_no_input);
- try testWriteBlock(tfn, null, want, tc.tokens);
-}
-
-// Uses writer function `tfn` to write `tokens`, tests that we got `want` as output.
-fn testWriteBlock(comptime tfn: TestFn, input: ?[]const u8, want: []const u8, tokens: []const Token) !void {
- var buf = ArrayList(u8).init(testing.allocator);
- var bw: BlockWriter = .init(buf.writer());
- try tfn.write(&bw, tokens, input, false);
- var got = buf.items;
- try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
- try expect(got[0] & 0b0000_0001 == 0); // bfinal is not set
- //
- // Test if the writer produces the same output after reset.
- buf.deinit();
- buf = ArrayList(u8).init(testing.allocator);
- defer buf.deinit();
- bw.setWriter(buf.writer());
-
- try tfn.write(&bw, tokens, input, true);
- try bw.flush();
- got = buf.items;
-
- try expect(got[0] & 1 == 1); // bfinal is set
- buf.items[0] &= 0b1111_1110; // remove bfinal bit, so we can run test slices
- try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
-}
diff --git a/lib/std/compress/flate/Compress.zig b/lib/std/compress/flate/Compress.zig
index 4d827fd590e87deb51e5682d1b63396d8a51830b..f38f7b27035baacfa873a0ce933db48444810d99 100644
--- a/lib/std/compress/flate/Compress.zig
+++ b/lib/std/compress/flate/Compress.zig
@@ -39,6 +39,7 @@
//!
//!
//! Allocates statically ~400K (192K lookup, 128K tokens, 64K window).
+
const builtin = @import("builtin");
const std = @import("std");
const assert = std.debug.assert;
@@ -47,7 +48,6 @@ const expect = testing.expect;
const mem = std.mem;
const math = std.math;
const Writer = std.Io.Writer;
-const Reader = std.Io.Reader;
const Compress = @This();
const Token = @import("Token.zig");
@@ -55,22 +55,24 @@ const BlockWriter = @import("BlockWriter.zig");
const flate = @import("../flate.zig");
const Container = flate.Container;
const Lookup = @import("Lookup.zig");
-const huffman = flate.huffman;
+const HuffmanEncoder = flate.HuffmanEncoder;
+const LiteralNode = HuffmanEncoder.LiteralNode;
lookup: Lookup = .{},
tokens: Tokens = .{},
-/// Asserted to have a buffer capacity of at least `flate.max_window_len`.
-input: *Reader,
block_writer: BlockWriter,
level: LevelArgs,
hasher: Container.Hasher,
-reader: Reader,
+writer: Writer,
+state: State,
// Match and literal at the previous position.
// Used for lazy match finding in processWindow.
prev_match: ?Token = null,
prev_literal: ?u8 = null,
+pub const State = enum { header, middle, ended };
+
/// Trades between speed and compression size.
/// Starts with level 4: in [zlib](https://github.com/madler/zlib/blob/abd3d1a28930f89375d4b41408b39f6c1be157b2/deflate.c#L115C1-L117C43)
/// levels 1-3 are using different algorithm to perform faster but with less
@@ -118,188 +120,34 @@ pub const Options = struct {
container: Container = .raw,
};
-pub fn init(input: *Reader, buffer: []u8, options: Options) Compress {
+pub fn init(output: *Writer, buffer: []u8, options: Options) Compress {
return .{
- .input = input,
- .block_writer = undefined,
+ .block_writer = .{
+ .output = output,
+ .codegen_freq = undefined,
+ .literal_freq = undefined,
+ .distance_freq = undefined,
+ .codegen = undefined,
+ .literal_encoding = undefined,
+ .distance_encoding = undefined,
+ .codegen_encoding = undefined,
+ .fixed_literal_encoding = undefined,
+ .fixed_distance_encoding = undefined,
+ .huff_distance = undefined,
+ .fixed_literal_codes = undefined,
+ .fixed_distance_codes = undefined,
+ .distance_codes = undefined,
+ },
.level = .get(options.level),
.hasher = .init(options.container),
.state = .header,
- .reader = .{
+ .writer = .{
.buffer = buffer,
- .stream = stream,
+ .vtable = &.{ .drain = drain },
},
};
}
-const FlushOption = enum { none, flush, final };
-
-/// Process data in window and create tokens. If token buffer is full
-/// flush tokens to the token writer.
-///
-/// Returns number of bytes consumed from `lh`.
-fn tokenizeSlice(c: *Compress, bw: *Writer, limit: std.Io.Limit, lh: []const u8) !usize {
- _ = bw;
- _ = limit;
- if (true) @panic("TODO");
- var step: u16 = 1; // 1 in the case of literal, match length otherwise
- const pos: u16 = c.win.pos();
- const literal = lh[0]; // literal at current position
- const min_len: u16 = if (c.prev_match) |m| m.length() else 0;
-
- // Try to find match at least min_len long.
- if (c.findMatch(pos, lh, min_len)) |match| {
- // Found better match than previous.
- try c.addPrevLiteral();
-
- // Is found match length good enough?
- if (match.length() >= c.level.lazy) {
- // Don't try to lazy find better match, use this.
- step = try c.addMatch(match);
- } else {
- // Store this match.
- c.prev_literal = literal;
- c.prev_match = match;
- }
- } else {
- // There is no better match at current pos then it was previous.
- // Write previous match or literal.
- if (c.prev_match) |m| {
- // Write match from previous position.
- step = try c.addMatch(m) - 1; // we already advanced 1 from previous position
- } else {
- // No match at previous position.
- // Write previous literal if any, and remember this literal.
- try c.addPrevLiteral();
- c.prev_literal = literal;
- }
- }
- // Advance window and add hashes.
- c.windowAdvance(step, lh, pos);
-}
-
-fn windowAdvance(self: *Compress, step: u16, lh: []const u8, pos: u16) void {
- // current position is already added in findMatch
- self.lookup.bulkAdd(lh[1..], step - 1, pos + 1);
- self.win.advance(step);
-}
-
-// Add previous literal (if any) to the tokens list.
-fn addPrevLiteral(self: *Compress) !void {
- if (self.prev_literal) |l| try self.addToken(Token.initLiteral(l));
-}
-
-// Add match to the tokens list, reset prev pointers.
-// Returns length of the added match.
-fn addMatch(self: *Compress, m: Token) !u16 {
- try self.addToken(m);
- self.prev_literal = null;
- self.prev_match = null;
- return m.length();
-}
-
-fn addToken(self: *Compress, token: Token) !void {
- self.tokens.add(token);
- if (self.tokens.full()) try self.flushTokens(.none);
-}
-
-// Finds largest match in the history window with the data at current pos.
-fn findMatch(self: *Compress, pos: u16, lh: []const u8, min_len: u16) ?Token {
- var len: u16 = min_len;
- // Previous location with the same hash (same 4 bytes).
- var prev_pos = self.lookup.add(lh, pos);
- // Last found match.
- var match: ?Token = null;
-
- // How much back-references to try, performance knob.
- var chain: usize = self.level.chain;
- if (len >= self.level.good) {
- // If we've got a match that's good enough, only look in 1/4 the chain.
- chain >>= 2;
- }
-
- // Hot path loop!
- while (prev_pos > 0 and chain > 0) : (chain -= 1) {
- const distance = pos - prev_pos;
- if (distance > flate.match.max_distance)
- break;
-
- const new_len = self.win.match(prev_pos, pos, len);
- if (new_len > len) {
- match = Token.initMatch(@intCast(distance), new_len);
- if (new_len >= self.level.nice) {
- // The match is good enough that we don't try to find a better one.
- return match;
- }
- len = new_len;
- }
- prev_pos = self.lookup.prev(prev_pos);
- }
-
- return match;
-}
-
-fn flushTokens(self: *Compress, flush_opt: FlushOption) !void {
- // Pass tokens to the token writer
- try self.block_writer.write(self.tokens.tokens(), flush_opt == .final, self.win.tokensBuffer());
- // Stored block ensures byte alignment.
- // It has 3 bits (final, block_type) and then padding until byte boundary.
- // After that everything is aligned to the boundary in the stored block.
- // Empty stored block is Ob000 + (0-7) bits of padding + 0x00 0x00 0xFF 0xFF.
- // Last 4 bytes are byte aligned.
- if (flush_opt == .flush) {
- try self.block_writer.storedBlock("", false);
- }
- if (flush_opt != .none) {
- // Safe to call only when byte aligned or it is OK to add
- // padding bits (on last byte of the final block).
- try self.block_writer.flush();
- }
- // Reset internal tokens store.
- self.tokens.reset();
- // Notify win that tokens are flushed.
- self.win.flush();
-}
-
-// Slide win and if needed lookup tables.
-fn slide(self: *Compress) void {
- const n = self.win.slide();
- self.lookup.slide(n);
-}
-
-/// Flushes internal buffers to the output writer. Outputs empty stored
-/// block to sync bit stream to the byte boundary, so that the
-/// decompressor can get all input data available so far.
-///
-/// It is useful mainly in compressed network protocols, to ensure that
-/// deflate bit stream can be used as byte stream. May degrade
-/// compression so it should be used only when necessary.
-///
-/// Completes the current deflate block and follows it with an empty
-/// stored block that is three zero bits plus filler bits to the next
-/// byte, followed by four bytes (00 00 ff ff).
-///
-pub fn flush(c: *Compress) !void {
- try c.tokenize(.flush);
-}
-
-/// Completes deflate bit stream by writing any pending data as deflate
-/// final deflate block. HAS to be called once all data are written to
-/// the compressor as a signal that next block has to have final bit
-/// set.
-///
-pub fn finish(c: *Compress) !void {
- _ = c;
- @panic("TODO");
-}
-
-/// Use another writer while preserving history. Most probably flush
-/// should be called on old writer before setting new.
-pub fn setWriter(self: *Compress, new_writer: *Writer) void {
- self.block_writer.setWriter(new_writer);
- self.output = new_writer;
-}
-
// Tokens store
const Tokens = struct {
list: [n_tokens]Token = undefined,
@@ -323,528 +171,111 @@ const Tokens = struct {
}
};
-/// Creates huffman only deflate blocks. Disables Lempel-Ziv match searching and
-/// only performs Huffman entropy encoding. Results in faster compression, much
-/// less memory requirements during compression but bigger compressed sizes.
-pub const Huffman = SimpleCompressor(.huffman, .raw);
-
-/// Creates store blocks only. Data are not compressed only packed into deflate
-/// store blocks. That adds 9 bytes of header for each block. Max stored block
-/// size is 64K. Block is emitted when flush is called on on finish.
-pub const store = struct {
- pub fn Compressor(comptime container: Container, comptime WriterType: type) type {
- return SimpleCompressor(.store, container, WriterType);
- }
-
- pub fn compressor(comptime container: Container, writer: anytype) !store.Compressor(container, @TypeOf(writer)) {
- return try store.Compressor(container, @TypeOf(writer)).init(writer);
- }
-};
-
-const SimpleCompressorKind = enum {
- huffman,
- store,
-};
-
-fn simpleCompressor(
- comptime kind: SimpleCompressorKind,
- comptime container: Container,
- writer: anytype,
-) !SimpleCompressor(kind, container, @TypeOf(writer)) {
- return try SimpleCompressor(kind, container, @TypeOf(writer)).init(writer);
-}
-
-fn SimpleCompressor(
- comptime kind: SimpleCompressorKind,
- comptime container: Container,
- comptime WriterType: type,
-) type {
- const BlockWriterType = BlockWriter(WriterType);
- return struct {
- buffer: [65535]u8 = undefined, // because store blocks are limited to 65535 bytes
- wp: usize = 0,
-
- output: WriterType,
- block_writer: BlockWriterType,
- hasher: container.Hasher() = .{},
-
- const Self = @This();
-
- pub fn init(output: WriterType) !Self {
- const self = Self{
- .output = output,
- .block_writer = BlockWriterType.init(output),
- };
- try container.writeHeader(self.output);
- return self;
- }
-
- pub fn flush(self: *Self) !void {
- try self.flushBuffer(false);
- try self.block_writer.storedBlock("", false);
- try self.block_writer.flush();
- }
-
- pub fn finish(self: *Self) !void {
- try self.flushBuffer(true);
- try self.block_writer.flush();
- try container.writeFooter(&self.hasher, self.output);
- }
-
- fn flushBuffer(self: *Self, final: bool) !void {
- const buf = self.buffer[0..self.wp];
- switch (kind) {
- .huffman => try self.block_writer.huffmanBlock(buf, final),
- .store => try self.block_writer.storedBlock(buf, final),
- }
- self.wp = 0;
- }
- };
-}
-
-const LiteralNode = struct {
- literal: u16,
- freq: u16,
-};
-
-// Describes the state of the constructed tree for a given depth.
-const LevelInfo = struct {
- // Our level. for better printing
- level: u32,
-
- // The frequency of the last node at this level
- last_freq: u32,
-
- // The frequency of the next character to add to this level
- next_char_freq: u32,
-
- // The frequency of the next pair (from level below) to add to this level.
- // Only valid if the "needed" value of the next lower level is 0.
- next_pair_freq: u32,
-
- // The number of chains remaining to generate for this level before moving
- // up to the next level
- needed: u32,
-};
-
-// hcode is a huffman code with a bit code and bit length.
-pub const HuffCode = struct {
- code: u16 = 0,
- len: u16 = 0,
-
- // set sets the code and length of an hcode.
- fn set(self: *HuffCode, code: u16, length: u16) void {
- self.len = length;
- self.code = code;
- }
-};
-
-pub fn HuffmanEncoder(comptime size: usize) type {
- return struct {
- codes: [size]HuffCode = undefined,
- // Reusable buffer with the longest possible frequency table.
- freq_cache: [huffman.max_num_frequencies + 1]LiteralNode = undefined,
- bit_count: [17]u32 = undefined,
- lns: []LiteralNode = undefined, // sorted by literal, stored to avoid repeated allocation in generate
- lfs: []LiteralNode = undefined, // sorted by frequency, stored to avoid repeated allocation in generate
-
- const Self = @This();
-
- // Update this Huffman Code object to be the minimum code for the specified frequency count.
- //
- // freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
- // max_bits The maximum number of bits to use for any literal.
- pub fn generate(self: *Self, freq: []u16, max_bits: u32) void {
- var list = self.freq_cache[0 .. freq.len + 1];
- // Number of non-zero literals
- var count: u32 = 0;
- // Set list to be the set of all non-zero literals and their frequencies
- for (freq, 0..) |f, i| {
- if (f != 0) {
- list[count] = LiteralNode{ .literal = @as(u16, @intCast(i)), .freq = f };
- count += 1;
- } else {
- list[count] = LiteralNode{ .literal = 0x00, .freq = 0 };
- self.codes[i].len = 0;
- }
- }
- list[freq.len] = LiteralNode{ .literal = 0x00, .freq = 0 };
-
- list = list[0..count];
- if (count <= 2) {
- // Handle the small cases here, because they are awkward for the general case code. With
- // two or fewer literals, everything has bit length 1.
- for (list, 0..) |node, i| {
- // "list" is in order of increasing literal value.
- self.codes[node.literal].set(@as(u16, @intCast(i)), 1);
- }
- return;
- }
- self.lfs = list;
- mem.sort(LiteralNode, self.lfs, {}, byFreq);
-
- // Get the number of literals for each bit count
- const bit_count = self.bitCounts(list, max_bits);
- // And do the assignment
- self.assignEncodingAndSize(bit_count, list);
- }
-
- pub fn bitLength(self: *Self, freq: []u16) u32 {
- var total: u32 = 0;
- for (freq, 0..) |f, i| {
- if (f != 0) {
- total += @as(u32, @intCast(f)) * @as(u32, @intCast(self.codes[i].len));
- }
- }
- return total;
- }
-
- // Return the number of literals assigned to each bit size in the Huffman encoding
- //
- // This method is only called when list.len >= 3
- // The cases of 0, 1, and 2 literals are handled by special case code.
- //
- // list: An array of the literals with non-zero frequencies
- // and their associated frequencies. The array is in order of increasing
- // frequency, and has as its last element a special element with frequency
- // `math.maxInt(i32)`
- //
- // max_bits: The maximum number of bits that should be used to encode any literal.
- // Must be less than 16.
- //
- // Returns an integer array in which array[i] indicates the number of literals
- // that should be encoded in i bits.
- fn bitCounts(self: *Self, list: []LiteralNode, max_bits_to_use: usize) []u32 {
- var max_bits = max_bits_to_use;
- const n = list.len;
- const max_bits_limit = 16;
-
- assert(max_bits < max_bits_limit);
-
- // The tree can't have greater depth than n - 1, no matter what. This
- // saves a little bit of work in some small cases
- max_bits = @min(max_bits, n - 1);
-
- // Create information about each of the levels.
- // A bogus "Level 0" whose sole purpose is so that
- // level1.prev.needed == 0. This makes level1.next_pair_freq
- // be a legitimate value that never gets chosen.
- var levels: [max_bits_limit]LevelInfo = mem.zeroes([max_bits_limit]LevelInfo);
- // leaf_counts[i] counts the number of literals at the left
- // of ancestors of the rightmost node at level i.
- // leaf_counts[i][j] is the number of literals at the left
- // of the level j ancestor.
- var leaf_counts: [max_bits_limit][max_bits_limit]u32 = mem.zeroes([max_bits_limit][max_bits_limit]u32);
-
- {
- var level = @as(u32, 1);
- while (level <= max_bits) : (level += 1) {
- // For every level, the first two items are the first two characters.
- // We initialize the levels as if we had already figured this out.
- levels[level] = LevelInfo{
- .level = level,
- .last_freq = list[1].freq,
- .next_char_freq = list[2].freq,
- .next_pair_freq = list[0].freq + list[1].freq,
- .needed = 0,
- };
- leaf_counts[level][level] = 2;
- if (level == 1) {
- levels[level].next_pair_freq = math.maxInt(i32);
- }
- }
- }
-
- // We need a total of 2*n - 2 items at top level and have already generated 2.
- levels[max_bits].needed = 2 * @as(u32, @intCast(n)) - 4;
-
- {
- var level = max_bits;
- while (true) {
- var l = &levels[level];
- if (l.next_pair_freq == math.maxInt(i32) and l.next_char_freq == math.maxInt(i32)) {
- // We've run out of both leaves and pairs.
- // End all calculations for this level.
- // To make sure we never come back to this level or any lower level,
- // set next_pair_freq impossibly large.
- l.needed = 0;
- levels[level + 1].next_pair_freq = math.maxInt(i32);
- level += 1;
- continue;
- }
-
- const prev_freq = l.last_freq;
- if (l.next_char_freq < l.next_pair_freq) {
- // The next item on this row is a leaf node.
- const next = leaf_counts[level][level] + 1;
- l.last_freq = l.next_char_freq;
- // Lower leaf_counts are the same of the previous node.
- leaf_counts[level][level] = next;
- if (next >= list.len) {
- l.next_char_freq = maxNode().freq;
- } else {
- l.next_char_freq = list[next].freq;
- }
- } else {
- // The next item on this row is a pair from the previous row.
- // next_pair_freq isn't valid until we generate two
- // more values in the level below
- l.last_freq = l.next_pair_freq;
- // Take leaf counts from the lower level, except counts[level] remains the same.
- @memcpy(leaf_counts[level][0..level], leaf_counts[level - 1][0..level]);
- levels[l.level - 1].needed = 2;
- }
-
- l.needed -= 1;
- if (l.needed == 0) {
- // We've done everything we need to do for this level.
- // Continue calculating one level up. Fill in next_pair_freq
- // of that level with the sum of the two nodes we've just calculated on
- // this level.
- if (l.level == max_bits) {
- // All done!
- break;
- }
- levels[l.level + 1].next_pair_freq = prev_freq + l.last_freq;
- level += 1;
- } else {
- // If we stole from below, move down temporarily to replenish it.
- while (levels[level - 1].needed > 0) {
- level -= 1;
- if (level == 0) {
- break;
- }
- }
- }
- }
- }
-
- // Somethings is wrong if at the end, the top level is null or hasn't used
- // all of the leaves.
- assert(leaf_counts[max_bits][max_bits] == n);
-
- var bit_count = self.bit_count[0 .. max_bits + 1];
- var bits: u32 = 1;
- const counts = &leaf_counts[max_bits];
- {
- var level = max_bits;
- while (level > 0) : (level -= 1) {
- // counts[level] gives the number of literals requiring at least "bits"
- // bits to encode.
- bit_count[bits] = counts[level] - counts[level - 1];
- bits += 1;
- if (level == 0) {
- break;
- }
- }
- }
- return bit_count;
- }
-
- // Look at the leaves and assign them a bit count and an encoding as specified
- // in RFC 1951 3.2.2
- fn assignEncodingAndSize(self: *Self, bit_count: []u32, list_arg: []LiteralNode) void {
- var code = @as(u16, 0);
- var list = list_arg;
-
- for (bit_count, 0..) |bits, n| {
- code <<= 1;
- if (n == 0 or bits == 0) {
- continue;
- }
- // The literals list[list.len-bits] .. list[list.len-bits]
- // are encoded using "bits" bits, and get the values
- // code, code + 1, .... The code values are
- // assigned in literal order (not frequency order).
- const chunk = list[list.len - @as(u32, @intCast(bits)) ..];
-
- self.lns = chunk;
- mem.sort(LiteralNode, self.lns, {}, byLiteral);
-
- for (chunk) |node| {
- self.codes[node.literal] = HuffCode{
- .code = bitReverse(u16, code, @as(u5, @intCast(n))),
- .len = @as(u16, @intCast(n)),
- };
- code += 1;
- }
- list = list[0 .. list.len - @as(u32, @intCast(bits))];
- }
- }
- };
-}
-
-fn maxNode() LiteralNode {
- return LiteralNode{
- .literal = math.maxInt(u16),
- .freq = math.maxInt(u16),
- };
-}
-
-pub fn huffmanEncoder(comptime size: u32) HuffmanEncoder(size) {
- return .{};
-}
-
-pub const LiteralEncoder = HuffmanEncoder(huffman.max_num_frequencies);
-pub const DistanceEncoder = HuffmanEncoder(huffman.distance_code_count);
-pub const CodegenEncoder = HuffmanEncoder(19);
-
-// Generates a HuffmanCode corresponding to the fixed literal table
-pub fn fixedLiteralEncoder() LiteralEncoder {
- var h: LiteralEncoder = undefined;
- var ch: u16 = 0;
-
- while (ch < huffman.max_num_frequencies) : (ch += 1) {
- var bits: u16 = undefined;
- var size: u16 = undefined;
- switch (ch) {
- 0...143 => {
- // size 8, 000110000 .. 10111111
- bits = ch + 48;
- size = 8;
- },
- 144...255 => {
- // size 9, 110010000 .. 111111111
- bits = ch + 400 - 144;
- size = 9;
- },
- 256...279 => {
- // size 7, 0000000 .. 0010111
- bits = ch - 256;
- size = 7;
- },
- else => {
- // size 8, 11000000 .. 11000111
- bits = ch + 192 - 280;
- size = 8;
- },
- }
- h.codes[ch] = HuffCode{ .code = bitReverse(u16, bits, @as(u5, @intCast(size))), .len = size };
- }
- return h;
-}
-
-pub fn fixedDistanceEncoder() DistanceEncoder {
- var h: DistanceEncoder = undefined;
- for (h.codes, 0..) |_, ch| {
- h.codes[ch] = HuffCode{ .code = bitReverse(u16, @as(u16, @intCast(ch)), 5), .len = 5 };
- }
- return h;
-}
-
-pub fn huffmanDistanceEncoder() DistanceEncoder {
- var distance_freq = [1]u16{0} ** huffman.distance_code_count;
- distance_freq[0] = 1;
- // huff_distance is a static distance encoder used for huffman only encoding.
- // It can be reused since we will not be encoding distance values.
- var h: DistanceEncoder = .{};
- h.generate(distance_freq[0..], 15);
- return h;
-}
-
-fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
- _ = context;
- return a.literal < b.literal;
-}
-
-fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
- _ = context;
- if (a.freq == b.freq) {
- return a.literal < b.literal;
- }
- return a.freq < b.freq;
-}
-
-fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
- const c: *Compress = @fieldParentPtr("reader", r);
+fn drain(me: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
+ _ = data;
+ _ = splat;
+ const c: *Compress = @fieldParentPtr("writer", me);
+ const out = c.block_writer.output;
switch (c.state) {
- .header => |i| {
+ .header => {
+ c.state = .middle;
const header = c.hasher.container().header();
- const n = try w.write(header[i..]);
- if (header.len - i - n == 0) {
- c.state = .middle;
- } else {
- c.state.header += n;
- }
- return n;
+ try out.writeAll(header);
+ return header.len;
},
- .middle => {
- c.input.fillMore() catch |err| switch (err) {
- error.EndOfStream => {
- c.state = .final;
- return 0;
- },
- else => |e| return e,
- };
- const buffer_contents = c.input.buffered();
- const min_lookahead = flate.match.min_length + flate.match.max_length;
- const history_plus_lookahead_len = flate.history_len + min_lookahead;
- if (buffer_contents.len < history_plus_lookahead_len) return 0;
- const lookahead = buffer_contents[flate.history_len..];
- const start = w.count;
- const n = try c.tokenizeSlice(w, limit, lookahead) catch |err| switch (err) {
- error.WriteFailed => return error.WriteFailed,
- };
- c.hasher.update(lookahead[0..n]);
- c.input.toss(n);
- return w.count - start;
- },
- .final => {
- const buffer_contents = c.input.buffered();
- const start = w.count;
- const n = c.tokenizeSlice(w, limit, buffer_contents) catch |err| switch (err) {
- error.WriteFailed => return error.WriteFailed,
- };
- if (buffer_contents.len - n == 0) {
- c.hasher.update(buffer_contents);
- c.input.tossAll();
- {
- // In the case of flushing, last few lookahead buffers were
- // smaller than min match len, so only last literal can be
- // unwritten.
- assert(c.prev_match == null);
- try c.addPrevLiteral();
- c.prev_literal = null;
+ .middle => {},
+ .ended => unreachable,
+ }
+
+ const buffered = me.buffered();
+ const min_lookahead = flate.match.min_length + flate.match.max_length;
+ const history_plus_lookahead_len = flate.history_len + min_lookahead;
+ if (buffered.len < history_plus_lookahead_len) return 0;
+ const lookahead = buffered[flate.history_len..];
+
+ _ = lookahead;
+ // TODO tokenize
+ //c.hasher.update(lookahead[0..n]);
+ @panic("TODO");
+}
+
+pub fn end(c: *Compress) !void {
+ try endUnflushed(c);
+ try c.output.flush();
+}
+
+pub fn endUnflushed(c: *Compress) !void {
+ while (c.writer.end != 0) _ = try drain(&c.writer, &.{""}, 1);
+ c.state = .ended;
+
+ const out = c.block_writer.output;
+
+ // TODO flush tokens
- try c.flushTokens(.final);
- }
- switch (c.hasher) {
- .gzip => |*gzip| {
- // GZIP 8 bytes footer
- // - 4 bytes, CRC32 (CRC-32)
- // - 4 bytes, ISIZE (Input SIZE) - size of the original (uncompressed) input data modulo 2^32
- comptime assert(c.footer_buffer.len == 8);
- std.mem.writeInt(u32, c.footer_buffer[0..4], gzip.final(), .little);
- std.mem.writeInt(u32, c.footer_buffer[4..8], gzip.bytes_read, .little);
- c.state = .{ .footer = 0 };
- },
- .zlib => |*zlib| {
- // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
- // 4 bytes of ADLER32 (Adler-32 checksum)
- // Checksum value of the uncompressed data (excluding any
- // dictionary data) computed according to Adler-32
- // algorithm.
- comptime assert(c.footer_buffer.len == 8);
- std.mem.writeInt(u32, c.footer_buffer[4..8], zlib.final, .big);
- c.state = .{ .footer = 4 };
- },
- .raw => {
- c.state = .ended;
- },
- }
- }
- return w.count - start;
+ switch (c.hasher) {
+ .gzip => |*gzip| {
+ // GZIP 8 bytes footer
+ // - 4 bytes, CRC32 (CRC-32)
+ // - 4 bytes, ISIZE (Input SIZE) - size of the original (uncompressed) input data modulo 2^32
+ const footer = try out.writableArray(8);
+ std.mem.writeInt(u32, footer[0..4], gzip.crc.final(), .little);
+ std.mem.writeInt(u32, footer[4..8], @truncate(gzip.count), .little);
},
- .ended => return error.EndOfStream,
- .footer => |i| {
- const remaining = c.footer_buffer[i..];
- const n = try w.write(limit.slice(remaining));
- c.state = if (n == remaining) .ended else .{ .footer = i - n };
- return n;
+ .zlib => |*zlib| {
+ // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
+ // 4 bytes of ADLER32 (Adler-32 checksum)
+ // Checksum value of the uncompressed data (excluding any
+ // dictionary data) computed according to Adler-32
+ // algorithm.
+ std.mem.writeInt(u32, try out.writableArray(4), zlib.final, .big);
},
+ .raw => {},
}
}
+pub const Simple = struct {
+ /// Note that store blocks are limited to 65535 bytes.
+ buffer: []u8,
+ wp: usize,
+ block_writer: BlockWriter,
+ hasher: Container.Hasher,
+ strategy: Strategy,
+
+ pub const Strategy = enum { huffman, store };
+
+ pub fn init(out: *Writer, buffer: []u8, container: Container) !Simple {
+ const self: Simple = .{
+ .buffer = buffer,
+ .wp = 0,
+ .block_writer = .init(out),
+ .hasher = .init(container),
+ };
+ try container.writeHeader(self.out);
+ return self;
+ }
+
+ pub fn flush(self: *Simple) !void {
+ try self.flushBuffer(false);
+ try self.block_writer.storedBlock("", false);
+ try self.block_writer.flush();
+ }
+
+ pub fn finish(self: *Simple) !void {
+ try self.flushBuffer(true);
+ try self.block_writer.flush();
+ try self.hasher.container().writeFooter(&self.hasher, self.out);
+ }
+
+ fn flushBuffer(self: *Simple, final: bool) !void {
+ const buf = self.buffer[0..self.wp];
+ switch (self.strategy) {
+ .huffman => try self.block_writer.huffmanBlock(buf, final),
+ .store => try self.block_writer.storedBlock(buf, final),
+ }
+ self.wp = 0;
+ }
+};
+
test "generate a Huffman code from an array of frequencies" {
var freqs: [19]u16 = [_]u16{
8, // 0
@@ -868,7 +299,8 @@ test "generate a Huffman code from an array of frequencies" {
5, // 18
};
- var enc = huffmanEncoder(19);
+ var codes: [19]HuffmanEncoder.Code = undefined;
+ var enc: HuffmanEncoder = .{ .codes = &codes };
enc.generate(freqs[0..], 7);
try testing.expectEqual(@as(u32, 141), enc.bitLength(freqs[0..]));
@@ -906,120 +338,6 @@ test "generate a Huffman code from an array of frequencies" {
try testing.expectEqual(@as(u16, 0x3f), enc.codes[16].code);
}
-test "generate a Huffman code for the fixed literal table specific to Deflate" {
- const enc = fixedLiteralEncoder();
- for (enc.codes) |c| {
- switch (c.len) {
- 7 => {
- const v = @bitReverse(@as(u7, @intCast(c.code)));
- try testing.expect(v <= 0b0010111);
- },
- 8 => {
- const v = @bitReverse(@as(u8, @intCast(c.code)));
- try testing.expect((v >= 0b000110000 and v <= 0b10111111) or
- (v >= 0b11000000 and v <= 11000111));
- },
- 9 => {
- const v = @bitReverse(@as(u9, @intCast(c.code)));
- try testing.expect(v >= 0b110010000 and v <= 0b111111111);
- },
- else => unreachable,
- }
- }
-}
-
-test "generate a Huffman code for the 30 possible relative distances (LZ77 distances) of Deflate" {
- const enc = fixedDistanceEncoder();
- for (enc.codes) |c| {
- const v = @bitReverse(@as(u5, @intCast(c.code)));
- try testing.expect(v <= 29);
- try testing.expect(c.len == 5);
- }
-}
-
-// Reverse bit-by-bit a N-bit code.
-fn bitReverse(comptime T: type, value: T, n: usize) T {
- const r = @bitReverse(value);
- return r >> @as(math.Log2Int(T), @intCast(@typeInfo(T).int.bits - n));
-}
-
-test bitReverse {
- const ReverseBitsTest = struct {
- in: u16,
- bit_count: u5,
- out: u16,
- };
-
- const reverse_bits_tests = [_]ReverseBitsTest{
- .{ .in = 1, .bit_count = 1, .out = 1 },
- .{ .in = 1, .bit_count = 2, .out = 2 },
- .{ .in = 1, .bit_count = 3, .out = 4 },
- .{ .in = 1, .bit_count = 4, .out = 8 },
- .{ .in = 1, .bit_count = 5, .out = 16 },
- .{ .in = 17, .bit_count = 5, .out = 17 },
- .{ .in = 257, .bit_count = 9, .out = 257 },
- .{ .in = 29, .bit_count = 5, .out = 23 },
- };
-
- for (reverse_bits_tests) |h| {
- const v = bitReverse(u16, h.in, h.bit_count);
- try std.testing.expectEqual(h.out, v);
- }
-}
-
-test "fixedLiteralEncoder codes" {
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
- var bw = std.Io.bitWriter(.little, al.writer());
-
- const f = fixedLiteralEncoder();
- for (f.codes) |c| {
- try bw.writeBits(c.code, c.len);
- }
- try testing.expectEqualSlices(u8, &fixed_codes, al.items);
-}
-
-pub const fixed_codes = [_]u8{
- 0b00001100, 0b10001100, 0b01001100, 0b11001100, 0b00101100, 0b10101100, 0b01101100, 0b11101100,
- 0b00011100, 0b10011100, 0b01011100, 0b11011100, 0b00111100, 0b10111100, 0b01111100, 0b11111100,
- 0b00000010, 0b10000010, 0b01000010, 0b11000010, 0b00100010, 0b10100010, 0b01100010, 0b11100010,
- 0b00010010, 0b10010010, 0b01010010, 0b11010010, 0b00110010, 0b10110010, 0b01110010, 0b11110010,
- 0b00001010, 0b10001010, 0b01001010, 0b11001010, 0b00101010, 0b10101010, 0b01101010, 0b11101010,
- 0b00011010, 0b10011010, 0b01011010, 0b11011010, 0b00111010, 0b10111010, 0b01111010, 0b11111010,
- 0b00000110, 0b10000110, 0b01000110, 0b11000110, 0b00100110, 0b10100110, 0b01100110, 0b11100110,
- 0b00010110, 0b10010110, 0b01010110, 0b11010110, 0b00110110, 0b10110110, 0b01110110, 0b11110110,
- 0b00001110, 0b10001110, 0b01001110, 0b11001110, 0b00101110, 0b10101110, 0b01101110, 0b11101110,
- 0b00011110, 0b10011110, 0b01011110, 0b11011110, 0b00111110, 0b10111110, 0b01111110, 0b11111110,
- 0b00000001, 0b10000001, 0b01000001, 0b11000001, 0b00100001, 0b10100001, 0b01100001, 0b11100001,
- 0b00010001, 0b10010001, 0b01010001, 0b11010001, 0b00110001, 0b10110001, 0b01110001, 0b11110001,
- 0b00001001, 0b10001001, 0b01001001, 0b11001001, 0b00101001, 0b10101001, 0b01101001, 0b11101001,
- 0b00011001, 0b10011001, 0b01011001, 0b11011001, 0b00111001, 0b10111001, 0b01111001, 0b11111001,
- 0b00000101, 0b10000101, 0b01000101, 0b11000101, 0b00100101, 0b10100101, 0b01100101, 0b11100101,
- 0b00010101, 0b10010101, 0b01010101, 0b11010101, 0b00110101, 0b10110101, 0b01110101, 0b11110101,
- 0b00001101, 0b10001101, 0b01001101, 0b11001101, 0b00101101, 0b10101101, 0b01101101, 0b11101101,
- 0b00011101, 0b10011101, 0b01011101, 0b11011101, 0b00111101, 0b10111101, 0b01111101, 0b11111101,
- 0b00010011, 0b00100110, 0b01001110, 0b10011010, 0b00111100, 0b01100101, 0b11101010, 0b10110100,
- 0b11101001, 0b00110011, 0b01100110, 0b11001110, 0b10011010, 0b00111101, 0b01100111, 0b11101110,
- 0b10111100, 0b11111001, 0b00001011, 0b00010110, 0b00101110, 0b01011010, 0b10111100, 0b01100100,
- 0b11101001, 0b10110010, 0b11100101, 0b00101011, 0b01010110, 0b10101110, 0b01011010, 0b10111101,
- 0b01100110, 0b11101101, 0b10111010, 0b11110101, 0b00011011, 0b00110110, 0b01101110, 0b11011010,
- 0b10111100, 0b01100101, 0b11101011, 0b10110110, 0b11101101, 0b00111011, 0b01110110, 0b11101110,
- 0b11011010, 0b10111101, 0b01100111, 0b11101111, 0b10111110, 0b11111101, 0b00000111, 0b00001110,
- 0b00011110, 0b00111010, 0b01111100, 0b11100100, 0b11101000, 0b10110001, 0b11100011, 0b00100111,
- 0b01001110, 0b10011110, 0b00111010, 0b01111101, 0b11100110, 0b11101100, 0b10111001, 0b11110011,
- 0b00010111, 0b00101110, 0b01011110, 0b10111010, 0b01111100, 0b11100101, 0b11101010, 0b10110101,
- 0b11101011, 0b00110111, 0b01101110, 0b11011110, 0b10111010, 0b01111101, 0b11100111, 0b11101110,
- 0b10111101, 0b11111011, 0b00001111, 0b00011110, 0b00111110, 0b01111010, 0b11111100, 0b11100100,
- 0b11101001, 0b10110011, 0b11100111, 0b00101111, 0b01011110, 0b10111110, 0b01111010, 0b11111101,
- 0b11100110, 0b11101101, 0b10111011, 0b11110111, 0b00011111, 0b00111110, 0b01111110, 0b11111010,
- 0b11111100, 0b11100101, 0b11101011, 0b10110111, 0b11101111, 0b00111111, 0b01111110, 0b11111110,
- 0b11111010, 0b11111101, 0b11100111, 0b11101111, 0b10111111, 0b11111111, 0b00000000, 0b00100000,
- 0b00001000, 0b00001100, 0b10000001, 0b11000010, 0b11100000, 0b00001000, 0b00100100, 0b00001010,
- 0b10001101, 0b11000001, 0b11100010, 0b11110000, 0b00000100, 0b00100010, 0b10001001, 0b01001100,
- 0b10100001, 0b11010010, 0b11101000, 0b00000011, 0b10000011, 0b01000011, 0b11000011, 0b00100011,
- 0b10100011,
-};
-
test "tokenization" {
const L = Token.initLiteral;
const M = Token.initMatch;
@@ -1133,7 +451,7 @@ test "file tokenization" {
const data = case.data;
for (levels, 0..) |level, i| { // for each compression level
- var original: Reader = .fixed(data);
+ var original: std.Io.Reader = .fixed(data);
// buffer for decompressed data
var al = std.ArrayList(u8).init(testing.allocator);
@@ -1198,32 +516,33 @@ const TokenDecoder = struct {
};
test "store simple compressor" {
- const data = "Hello world!";
- const expected = [_]u8{
- 0x1, // block type 0, final bit set
- 0xc, 0x0, // len = 12
- 0xf3, 0xff, // ~len
- 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', //
- //0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
- };
+ if (true) return error.SkipZigTest;
+ //const data = "Hello world!";
+ //const expected = [_]u8{
+ // 0x1, // block type 0, final bit set
+ // 0xc, 0x0, // len = 12
+ // 0xf3, 0xff, // ~len
+ // 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', //
+ // //0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
+ //};
- var fbs: Reader = .fixed(data);
- var al = std.ArrayList(u8).init(testing.allocator);
- defer al.deinit();
+ //var fbs: std.Io.Reader = .fixed(data);
+ //var al = std.ArrayList(u8).init(testing.allocator);
+ //defer al.deinit();
- var cmp = try store.compressor(.raw, al.writer());
- try cmp.compress(&fbs);
- try cmp.finish();
- try testing.expectEqualSlices(u8, &expected, al.items);
+ //var cmp = try store.compressor(.raw, al.writer());
+ //try cmp.compress(&fbs);
+ //try cmp.finish();
+ //try testing.expectEqualSlices(u8, &expected, al.items);
- fbs = .fixed(data);
- try al.resize(0);
+ //fbs = .fixed(data);
+ //try al.resize(0);
- // huffman only compresoor will also emit store block for this small sample
- var hc = try huffman.compressor(.raw, al.writer());
- try hc.compress(&fbs);
- try hc.finish();
- try testing.expectEqualSlices(u8, &expected, al.items);
+ //// huffman only compresoor will also emit store block for this small sample
+ //var hc = try huffman.compressor(.raw, al.writer());
+ //try hc.compress(&fbs);
+ //try hc.finish();
+ //try testing.expectEqualSlices(u8, &expected, al.items);
}
test "sliding window match" {
diff --git a/lib/std/compress/flate/Decompress.zig b/lib/std/compress/flate/Decompress.zig
index 6cb595376378d13b2fdd73673a79e4c991fd663e..ed9c0f37983fbcfcd25e6073a90495789e7fb5f0 100644
--- a/lib/std/compress/flate/Decompress.zig
+++ b/lib/std/compress/flate/Decompress.zig
@@ -620,10 +620,9 @@ test "init/find" {
}
test "encode/decode literals" {
- const LiteralEncoder = std.compress.flate.Compress.LiteralEncoder;
-
+ var codes: [flate.HuffmanEncoder.max_num_frequencies]flate.HuffmanEncoder.Code = undefined;
for (1..286) |j| { // for all different number of codes
- var enc: LiteralEncoder = .{};
+ var enc: flate.HuffmanEncoder = .{ .codes = &codes };
// create frequencies
var freq = [_]u16{0} ** 286;
freq[256] = 1; // ensure we have end of block code
diff --git a/lib/std/compress/flate/HuffmanEncoder.zig b/lib/std/compress/flate/HuffmanEncoder.zig
new file mode 100644
index 0000000000000000000000000000000000000000..bdcaf7580121d183586d25588d07f29b03ca887e
--- /dev/null
+++ b/lib/std/compress/flate/HuffmanEncoder.zig
@@ -0,0 +1,475 @@
+const HuffmanEncoder = @This();
+const std = @import("std");
+const assert = std.debug.assert;
+const testing = std.testing;
+
+codes: []Code,
+// Reusable buffer with the longest possible frequency table.
+freq_cache: [max_num_frequencies + 1]LiteralNode,
+bit_count: [17]u32,
+lns: []LiteralNode, // sorted by literal, stored to avoid repeated allocation in generate
+lfs: []LiteralNode, // sorted by frequency, stored to avoid repeated allocation in generate
+
+pub const LiteralNode = struct {
+ literal: u16,
+ freq: u16,
+
+ pub fn max() LiteralNode {
+ return .{
+ .literal = std.math.maxInt(u16),
+ .freq = std.math.maxInt(u16),
+ };
+ }
+};
+
+pub const Code = struct {
+ code: u16 = 0,
+ len: u16 = 0,
+};
+
+/// The odd order in which the codegen code sizes are written.
+pub const codegen_order = [_]u32{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+/// The number of codegen codes.
+pub const codegen_code_count = 19;
+
+/// The largest distance code.
+pub const distance_code_count = 30;
+
+/// Maximum number of literals.
+pub const max_num_lit = 286;
+
+/// Max number of frequencies used for a Huffman Code
+/// Possible lengths are codegen_code_count (19), distance_code_count (30) and max_num_lit (286).
+/// The largest of these is max_num_lit.
+pub const max_num_frequencies = max_num_lit;
+
+/// Biggest block size for uncompressed block.
+pub const max_store_block_size = 65535;
+/// The special code used to mark the end of a block.
+pub const end_block_marker = 256;
+
+/// Update this Huffman Code object to be the minimum code for the specified frequency count.
+///
+/// freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
+/// max_bits The maximum number of bits to use for any literal.
+pub fn generate(self: *HuffmanEncoder, freq: []u16, max_bits: u32) void {
+ var list = self.freq_cache[0 .. freq.len + 1];
+ // Number of non-zero literals
+ var count: u32 = 0;
+ // Set list to be the set of all non-zero literals and their frequencies
+ for (freq, 0..) |f, i| {
+ if (f != 0) {
+ list[count] = LiteralNode{ .literal = @as(u16, @intCast(i)), .freq = f };
+ count += 1;
+ } else {
+ list[count] = LiteralNode{ .literal = 0x00, .freq = 0 };
+ self.codes[i].len = 0;
+ }
+ }
+ list[freq.len] = LiteralNode{ .literal = 0x00, .freq = 0 };
+
+ list = list[0..count];
+ if (count <= 2) {
+ // Handle the small cases here, because they are awkward for the general case code. With
+ // two or fewer literals, everything has bit length 1.
+ for (list, 0..) |node, i| {
+ // "list" is in order of increasing literal value.
+ self.codes[node.literal] = .{
+ .code = @intCast(i),
+ .len = 1,
+ };
+ }
+ return;
+ }
+ self.lfs = list;
+ std.mem.sort(LiteralNode, self.lfs, {}, byFreq);
+
+ // Get the number of literals for each bit count
+ const bit_count = self.bitCounts(list, max_bits);
+ // And do the assignment
+ self.assignEncodingAndSize(bit_count, list);
+}
+
+pub fn bitLength(self: *HuffmanEncoder, freq: []u16) u32 {
+ var total: u32 = 0;
+ for (freq, 0..) |f, i| {
+ if (f != 0) {
+ total += @as(u32, @intCast(f)) * @as(u32, @intCast(self.codes[i].len));
+ }
+ }
+ return total;
+}
+
+/// Return the number of literals assigned to each bit size in the Huffman encoding
+///
+/// This method is only called when list.len >= 3
+/// The cases of 0, 1, and 2 literals are handled by special case code.
+///
+/// list: An array of the literals with non-zero frequencies
+/// and their associated frequencies. The array is in order of increasing
+/// frequency, and has as its last element a special element with frequency
+/// `math.maxInt(i32)`
+///
+/// max_bits: The maximum number of bits that should be used to encode any literal.
+/// Must be less than 16.
+///
+/// Returns an integer array in which array[i] indicates the number of literals
+/// that should be encoded in i bits.
+fn bitCounts(self: *HuffmanEncoder, list: []LiteralNode, max_bits_to_use: usize) []u32 {
+ var max_bits = max_bits_to_use;
+ const n = list.len;
+ const max_bits_limit = 16;
+
+ assert(max_bits < max_bits_limit);
+
+ // The tree can't have greater depth than n - 1, no matter what. This
+ // saves a little bit of work in some small cases
+ max_bits = @min(max_bits, n - 1);
+
+ // Create information about each of the levels.
+ // A bogus "Level 0" whose sole purpose is so that
+ // level1.prev.needed == 0. This makes level1.next_pair_freq
+ // be a legitimate value that never gets chosen.
+ var levels: [max_bits_limit]LevelInfo = std.mem.zeroes([max_bits_limit]LevelInfo);
+ // leaf_counts[i] counts the number of literals at the left
+ // of ancestors of the rightmost node at level i.
+ // leaf_counts[i][j] is the number of literals at the left
+ // of the level j ancestor.
+ var leaf_counts: [max_bits_limit][max_bits_limit]u32 = @splat(0);
+
+ {
+ var level = @as(u32, 1);
+ while (level <= max_bits) : (level += 1) {
+ // For every level, the first two items are the first two characters.
+ // We initialize the levels as if we had already figured this out.
+ levels[level] = LevelInfo{
+ .level = level,
+ .last_freq = list[1].freq,
+ .next_char_freq = list[2].freq,
+ .next_pair_freq = list[0].freq + list[1].freq,
+ .needed = 0,
+ };
+ leaf_counts[level][level] = 2;
+ if (level == 1) {
+ levels[level].next_pair_freq = std.math.maxInt(i32);
+ }
+ }
+ }
+
+ // We need a total of 2*n - 2 items at top level and have already generated 2.
+ levels[max_bits].needed = 2 * @as(u32, @intCast(n)) - 4;
+
+ {
+ var level = max_bits;
+ while (true) {
+ var l = &levels[level];
+ if (l.next_pair_freq == std.math.maxInt(i32) and l.next_char_freq == std.math.maxInt(i32)) {
+ // We've run out of both leaves and pairs.
+ // End all calculations for this level.
+ // To make sure we never come back to this level or any lower level,
+ // set next_pair_freq impossibly large.
+ l.needed = 0;
+ levels[level + 1].next_pair_freq = std.math.maxInt(i32);
+ level += 1;
+ continue;
+ }
+
+ const prev_freq = l.last_freq;
+ if (l.next_char_freq < l.next_pair_freq) {
+ // The next item on this row is a leaf node.
+ const next = leaf_counts[level][level] + 1;
+ l.last_freq = l.next_char_freq;
+ // Lower leaf_counts are the same of the previous node.
+ leaf_counts[level][level] = next;
+ if (next >= list.len) {
+ l.next_char_freq = LiteralNode.max().freq;
+ } else {
+ l.next_char_freq = list[next].freq;
+ }
+ } else {
+ // The next item on this row is a pair from the previous row.
+ // next_pair_freq isn't valid until we generate two
+ // more values in the level below
+ l.last_freq = l.next_pair_freq;
+ // Take leaf counts from the lower level, except counts[level] remains the same.
+ @memcpy(leaf_counts[level][0..level], leaf_counts[level - 1][0..level]);
+ levels[l.level - 1].needed = 2;
+ }
+
+ l.needed -= 1;
+ if (l.needed == 0) {
+ // We've done everything we need to do for this level.
+ // Continue calculating one level up. Fill in next_pair_freq
+ // of that level with the sum of the two nodes we've just calculated on
+ // this level.
+ if (l.level == max_bits) {
+ // All done!
+ break;
+ }
+ levels[l.level + 1].next_pair_freq = prev_freq + l.last_freq;
+ level += 1;
+ } else {
+ // If we stole from below, move down temporarily to replenish it.
+ while (levels[level - 1].needed > 0) {
+ level -= 1;
+ if (level == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Somethings is wrong if at the end, the top level is null or hasn't used
+ // all of the leaves.
+ assert(leaf_counts[max_bits][max_bits] == n);
+
+ var bit_count = self.bit_count[0 .. max_bits + 1];
+ var bits: u32 = 1;
+ const counts = &leaf_counts[max_bits];
+ {
+ var level = max_bits;
+ while (level > 0) : (level -= 1) {
+ // counts[level] gives the number of literals requiring at least "bits"
+ // bits to encode.
+ bit_count[bits] = counts[level] - counts[level - 1];
+ bits += 1;
+ if (level == 0) {
+ break;
+ }
+ }
+ }
+ return bit_count;
+}
+
+/// Look at the leaves and assign them a bit count and an encoding as specified
+/// in RFC 1951 3.2.2
+fn assignEncodingAndSize(self: *HuffmanEncoder, bit_count: []u32, list_arg: []LiteralNode) void {
+ var code = @as(u16, 0);
+ var list = list_arg;
+
+ for (bit_count, 0..) |bits, n| {
+ code <<= 1;
+ if (n == 0 or bits == 0) {
+ continue;
+ }
+ // The literals list[list.len-bits] .. list[list.len-bits]
+ // are encoded using "bits" bits, and get the values
+ // code, code + 1, .... The code values are
+ // assigned in literal order (not frequency order).
+ const chunk = list[list.len - @as(u32, @intCast(bits)) ..];
+
+ self.lns = chunk;
+ std.mem.sort(LiteralNode, self.lns, {}, byLiteral);
+
+ for (chunk) |node| {
+ self.codes[node.literal] = .{
+ .code = bitReverse(u16, code, @as(u5, @intCast(n))),
+ .len = @as(u16, @intCast(n)),
+ };
+ code += 1;
+ }
+ list = list[0 .. list.len - @as(u32, @intCast(bits))];
+ }
+}
+
+fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
+ _ = context;
+ if (a.freq == b.freq) {
+ return a.literal < b.literal;
+ }
+ return a.freq < b.freq;
+}
+
+/// Describes the state of the constructed tree for a given depth.
+const LevelInfo = struct {
+ /// Our level. for better printing
+ level: u32,
+ /// The frequency of the last node at this level
+ last_freq: u32,
+ /// The frequency of the next character to add to this level
+ next_char_freq: u32,
+ /// The frequency of the next pair (from level below) to add to this level.
+ /// Only valid if the "needed" value of the next lower level is 0.
+ next_pair_freq: u32,
+ /// The number of chains remaining to generate for this level before moving
+ /// up to the next level
+ needed: u32,
+};
+
+fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
+ _ = context;
+ return a.literal < b.literal;
+}
+
+/// Reverse bit-by-bit a N-bit code.
+fn bitReverse(comptime T: type, value: T, n: usize) T {
+ const r = @bitReverse(value);
+ return r >> @as(std.math.Log2Int(T), @intCast(@typeInfo(T).int.bits - n));
+}
+
+test bitReverse {
+ const ReverseBitsTest = struct {
+ in: u16,
+ bit_count: u5,
+ out: u16,
+ };
+
+ const reverse_bits_tests = [_]ReverseBitsTest{
+ .{ .in = 1, .bit_count = 1, .out = 1 },
+ .{ .in = 1, .bit_count = 2, .out = 2 },
+ .{ .in = 1, .bit_count = 3, .out = 4 },
+ .{ .in = 1, .bit_count = 4, .out = 8 },
+ .{ .in = 1, .bit_count = 5, .out = 16 },
+ .{ .in = 17, .bit_count = 5, .out = 17 },
+ .{ .in = 257, .bit_count = 9, .out = 257 },
+ .{ .in = 29, .bit_count = 5, .out = 23 },
+ };
+
+ for (reverse_bits_tests) |h| {
+ const v = bitReverse(u16, h.in, h.bit_count);
+ try std.testing.expectEqual(h.out, v);
+ }
+}
+
+/// Generates a HuffmanCode corresponding to the fixed literal table
+pub fn fixedLiteralEncoder(codes: *[max_num_frequencies]Code) HuffmanEncoder {
+ var h: HuffmanEncoder = undefined;
+ h.codes = codes;
+ var ch: u16 = 0;
+
+ while (ch < max_num_frequencies) : (ch += 1) {
+ var bits: u16 = undefined;
+ var size: u16 = undefined;
+ switch (ch) {
+ 0...143 => {
+ // size 8, 000110000 .. 10111111
+ bits = ch + 48;
+ size = 8;
+ },
+ 144...255 => {
+ // size 9, 110010000 .. 111111111
+ bits = ch + 400 - 144;
+ size = 9;
+ },
+ 256...279 => {
+ // size 7, 0000000 .. 0010111
+ bits = ch - 256;
+ size = 7;
+ },
+ else => {
+ // size 8, 11000000 .. 11000111
+ bits = ch + 192 - 280;
+ size = 8;
+ },
+ }
+ h.codes[ch] = .{ .code = bitReverse(u16, bits, @as(u5, @intCast(size))), .len = size };
+ }
+ return h;
+}
+
+pub fn fixedDistanceEncoder(codes: *[distance_code_count]Code) HuffmanEncoder {
+ var h: HuffmanEncoder = undefined;
+ h.codes = codes;
+ for (h.codes, 0..) |_, ch| {
+ h.codes[ch] = .{ .code = bitReverse(u16, @as(u16, @intCast(ch)), 5), .len = 5 };
+ }
+ return h;
+}
+
+pub fn huffmanDistanceEncoder(codes: *[distance_code_count]Code) HuffmanEncoder {
+ var distance_freq: [distance_code_count]u16 = @splat(0);
+ distance_freq[0] = 1;
+ // huff_distance is a static distance encoder used for huffman only encoding.
+ // It can be reused since we will not be encoding distance values.
+ var h: HuffmanEncoder = .{};
+ h.codes = codes;
+ h.generate(distance_freq[0..], 15);
+ return h;
+}
+
+test "generate a Huffman code for the fixed literal table specific to Deflate" {
+ const enc = fixedLiteralEncoder();
+ for (enc.codes) |c| {
+ switch (c.len) {
+ 7 => {
+ const v = @bitReverse(@as(u7, @intCast(c.code)));
+ try testing.expect(v <= 0b0010111);
+ },
+ 8 => {
+ const v = @bitReverse(@as(u8, @intCast(c.code)));
+ try testing.expect((v >= 0b000110000 and v <= 0b10111111) or
+ (v >= 0b11000000 and v <= 11000111));
+ },
+ 9 => {
+ const v = @bitReverse(@as(u9, @intCast(c.code)));
+ try testing.expect(v >= 0b110010000 and v <= 0b111111111);
+ },
+ else => unreachable,
+ }
+ }
+}
+
+test "generate a Huffman code for the 30 possible relative distances (LZ77 distances) of Deflate" {
+ var codes: [distance_code_count]Code = undefined;
+ const enc = fixedDistanceEncoder(&codes);
+ for (enc.codes) |c| {
+ const v = @bitReverse(@as(u5, @intCast(c.code)));
+ try testing.expect(v <= 29);
+ try testing.expect(c.len == 5);
+ }
+}
+
+test "fixedLiteralEncoder codes" {
+ var al = std.ArrayList(u8).init(testing.allocator);
+ defer al.deinit();
+ var bw = std.Io.bitWriter(.little, al.writer());
+
+ var codes: [max_num_frequencies]Code = undefined;
+ const f = fixedLiteralEncoder(&codes);
+ for (f.codes) |c| {
+ try bw.writeBits(c.code, c.len);
+ }
+ try testing.expectEqualSlices(u8, &fixed_codes, al.items);
+}
+
+pub const fixed_codes = [_]u8{
+ 0b00001100, 0b10001100, 0b01001100, 0b11001100, 0b00101100, 0b10101100, 0b01101100, 0b11101100,
+ 0b00011100, 0b10011100, 0b01011100, 0b11011100, 0b00111100, 0b10111100, 0b01111100, 0b11111100,
+ 0b00000010, 0b10000010, 0b01000010, 0b11000010, 0b00100010, 0b10100010, 0b01100010, 0b11100010,
+ 0b00010010, 0b10010010, 0b01010010, 0b11010010, 0b00110010, 0b10110010, 0b01110010, 0b11110010,
+ 0b00001010, 0b10001010, 0b01001010, 0b11001010, 0b00101010, 0b10101010, 0b01101010, 0b11101010,
+ 0b00011010, 0b10011010, 0b01011010, 0b11011010, 0b00111010, 0b10111010, 0b01111010, 0b11111010,
+ 0b00000110, 0b10000110, 0b01000110, 0b11000110, 0b00100110, 0b10100110, 0b01100110, 0b11100110,
+ 0b00010110, 0b10010110, 0b01010110, 0b11010110, 0b00110110, 0b10110110, 0b01110110, 0b11110110,
+ 0b00001110, 0b10001110, 0b01001110, 0b11001110, 0b00101110, 0b10101110, 0b01101110, 0b11101110,
+ 0b00011110, 0b10011110, 0b01011110, 0b11011110, 0b00111110, 0b10111110, 0b01111110, 0b11111110,
+ 0b00000001, 0b10000001, 0b01000001, 0b11000001, 0b00100001, 0b10100001, 0b01100001, 0b11100001,
+ 0b00010001, 0b10010001, 0b01010001, 0b11010001, 0b00110001, 0b10110001, 0b01110001, 0b11110001,
+ 0b00001001, 0b10001001, 0b01001001, 0b11001001, 0b00101001, 0b10101001, 0b01101001, 0b11101001,
+ 0b00011001, 0b10011001, 0b01011001, 0b11011001, 0b00111001, 0b10111001, 0b01111001, 0b11111001,
+ 0b00000101, 0b10000101, 0b01000101, 0b11000101, 0b00100101, 0b10100101, 0b01100101, 0b11100101,
+ 0b00010101, 0b10010101, 0b01010101, 0b11010101, 0b00110101, 0b10110101, 0b01110101, 0b11110101,
+ 0b00001101, 0b10001101, 0b01001101, 0b11001101, 0b00101101, 0b10101101, 0b01101101, 0b11101101,
+ 0b00011101, 0b10011101, 0b01011101, 0b11011101, 0b00111101, 0b10111101, 0b01111101, 0b11111101,
+ 0b00010011, 0b00100110, 0b01001110, 0b10011010, 0b00111100, 0b01100101, 0b11101010, 0b10110100,
+ 0b11101001, 0b00110011, 0b01100110, 0b11001110, 0b10011010, 0b00111101, 0b01100111, 0b11101110,
+ 0b10111100, 0b11111001, 0b00001011, 0b00010110, 0b00101110, 0b01011010, 0b10111100, 0b01100100,
+ 0b11101001, 0b10110010, 0b11100101, 0b00101011, 0b01010110, 0b10101110, 0b01011010, 0b10111101,
+ 0b01100110, 0b11101101, 0b10111010, 0b11110101, 0b00011011, 0b00110110, 0b01101110, 0b11011010,
+ 0b10111100, 0b01100101, 0b11101011, 0b10110110, 0b11101101, 0b00111011, 0b01110110, 0b11101110,
+ 0b11011010, 0b10111101, 0b01100111, 0b11101111, 0b10111110, 0b11111101, 0b00000111, 0b00001110,
+ 0b00011110, 0b00111010, 0b01111100, 0b11100100, 0b11101000, 0b10110001, 0b11100011, 0b00100111,
+ 0b01001110, 0b10011110, 0b00111010, 0b01111101, 0b11100110, 0b11101100, 0b10111001, 0b11110011,
+ 0b00010111, 0b00101110, 0b01011110, 0b10111010, 0b01111100, 0b11100101, 0b11101010, 0b10110101,
+ 0b11101011, 0b00110111, 0b01101110, 0b11011110, 0b10111010, 0b01111101, 0b11100111, 0b11101110,
+ 0b10111101, 0b11111011, 0b00001111, 0b00011110, 0b00111110, 0b01111010, 0b11111100, 0b11100100,
+ 0b11101001, 0b10110011, 0b11100111, 0b00101111, 0b01011110, 0b10111110, 0b01111010, 0b11111101,
+ 0b11100110, 0b11101101, 0b10111011, 0b11110111, 0b00011111, 0b00111110, 0b01111110, 0b11111010,
+ 0b11111100, 0b11100101, 0b11101011, 0b10110111, 0b11101111, 0b00111111, 0b01111110, 0b11111110,
+ 0b11111010, 0b11111101, 0b11100111, 0b11101111, 0b10111111, 0b11111111, 0b00000000, 0b00100000,
+ 0b00001000, 0b00001100, 0b10000001, 0b11000010, 0b11100000, 0b00001000, 0b00100100, 0b00001010,
+ 0b10001101, 0b11000001, 0b11100010, 0b11110000, 0b00000100, 0b00100010, 0b10001001, 0b01001100,
+ 0b10100001, 0b11010010, 0b11101000, 0b00000011, 0b10000011, 0b01000011, 0b11000011, 0b00100011,
+ 0b10100011,
+};
diff --git a/lib/std/compress/flate/testdata/block_writer.zig b/lib/std/compress/flate/testdata/block_writer.zig
deleted file mode 100644
index cb8f3028d12db71be89136ef0425880dc2ab20b8..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/testdata/block_writer.zig
+++ /dev/null
@@ -1,606 +0,0 @@
-const Token = @import("../Token.zig");
-
-pub const TestCase = struct {
- tokens: []const Token,
- input: []const u8 = "", // File name of input data matching the tokens.
- want: []const u8 = "", // File name of data with the expected output with input available.
- want_no_input: []const u8 = "", // File name of the expected output when no input is available.
-};
-
-pub const testCases = blk: {
- @setEvalBranchQuota(4096 * 2);
-
- const L = Token.initLiteral;
- const M = Token.initMatch;
- const ml = M(1, 258); // Maximum length token. Used to reduce the size of writeBlockTests
-
- break :blk &[_]TestCase{
- TestCase{
- .input = "huffman-null-max.input",
- .want = "huffman-null-max.{s}.expect",
- .want_no_input = "huffman-null-max.{s}.expect-noinput",
- .tokens = &[_]Token{
- L(0x0), ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, L(0x0), L(0x0),
- },
- },
- TestCase{
- .input = "huffman-pi.input",
- .want = "huffman-pi.{s}.expect",
- .want_no_input = "huffman-pi.{s}.expect-noinput",
- .tokens = &[_]Token{
- L('3'), L('.'), L('1'), L('4'), L('1'), L('5'), L('9'), L('2'),
- L('6'), L('5'), L('3'), L('5'), L('8'), L('9'), L('7'), L('9'),
- L('3'), L('2'), L('3'), L('8'), L('4'), L('6'), L('2'), L('6'),
- L('4'), L('3'), L('3'), L('8'), L('3'), L('2'), L('7'), L('9'),
- L('5'), L('0'), L('2'), L('8'), L('8'), L('4'), L('1'), L('9'),
- L('7'), L('1'), L('6'), L('9'), L('3'), L('9'), L('9'), L('3'),
- L('7'), L('5'), L('1'), L('0'), L('5'), L('8'), L('2'), L('0'),
- L('9'), L('7'), L('4'), L('9'), L('4'), L('4'), L('5'), L('9'),
- L('2'), L('3'), L('0'), L('7'), L('8'), L('1'), L('6'), L('4'),
- L('0'), L('6'), L('2'), L('8'), L('6'), L('2'), L('0'), L('8'),
- L('9'), L('9'), L('8'), L('6'), L('2'), L('8'), L('0'), L('3'),
- L('4'), L('8'), L('2'), L('5'), L('3'), L('4'), L('2'), L('1'),
- L('1'), L('7'), L('0'), L('6'), L('7'), L('9'), L('8'), L('2'),
- L('1'), L('4'), L('8'), L('0'), L('8'), L('6'), L('5'), L('1'),
- L('3'), L('2'), L('8'), L('2'), L('3'), L('0'), L('6'), L('6'),
- L('4'), L('7'), L('0'), L('9'), L('3'), L('8'), L('4'), L('4'),
- L('6'), L('0'), L('9'), L('5'), L('5'), L('0'), L('5'), L('8'),
- L('2'), L('2'), L('3'), L('1'), L('7'), L('2'), L('5'), L('3'),
- L('5'), L('9'), L('4'), L('0'), L('8'), L('1'), L('2'), L('8'),
- L('4'), L('8'), L('1'), L('1'), L('1'), L('7'), L('4'), M(127, 4),
- L('4'), L('1'), L('0'), L('2'), L('7'), L('0'), L('1'), L('9'),
- L('3'), L('8'), L('5'), L('2'), L('1'), L('1'), L('0'), L('5'),
- L('5'), L('5'), L('9'), L('6'), L('4'), L('4'), L('6'), L('2'),
- L('2'), L('9'), L('4'), L('8'), L('9'), L('5'), L('4'), L('9'),
- L('3'), L('0'), L('3'), L('8'), L('1'), M(19, 4), L('2'), L('8'),
- L('8'), L('1'), L('0'), L('9'), L('7'), L('5'), L('6'), L('6'),
- L('5'), L('9'), L('3'), L('3'), L('4'), L('4'), L('6'), M(72, 4),
- L('7'), L('5'), L('6'), L('4'), L('8'), L('2'), L('3'), L('3'),
- L('7'), L('8'), L('6'), L('7'), L('8'), L('3'), L('1'), L('6'),
- L('5'), L('2'), L('7'), L('1'), L('2'), L('0'), L('1'), L('9'),
- L('0'), L('9'), L('1'), L('4'), M(27, 4), L('5'), L('6'), L('6'),
- L('9'), L('2'), L('3'), L('4'), L('6'), M(179, 4), L('6'), L('1'),
- L('0'), L('4'), L('5'), L('4'), L('3'), L('2'), L('6'), M(51, 4),
- L('1'), L('3'), L('3'), L('9'), L('3'), L('6'), L('0'), L('7'),
- L('2'), L('6'), L('0'), L('2'), L('4'), L('9'), L('1'), L('4'),
- L('1'), L('2'), L('7'), L('3'), L('7'), L('2'), L('4'), L('5'),
- L('8'), L('7'), L('0'), L('0'), L('6'), L('6'), L('0'), L('6'),
- L('3'), L('1'), L('5'), L('5'), L('8'), L('8'), L('1'), L('7'),
- L('4'), L('8'), L('8'), L('1'), L('5'), L('2'), L('0'), L('9'),
- L('2'), L('0'), L('9'), L('6'), L('2'), L('8'), L('2'), L('9'),
- L('2'), L('5'), L('4'), L('0'), L('9'), L('1'), L('7'), L('1'),
- L('5'), L('3'), L('6'), L('4'), L('3'), L('6'), L('7'), L('8'),
- L('9'), L('2'), L('5'), L('9'), L('0'), L('3'), L('6'), L('0'),
- L('0'), L('1'), L('1'), L('3'), L('3'), L('0'), L('5'), L('3'),
- L('0'), L('5'), L('4'), L('8'), L('8'), L('2'), L('0'), L('4'),
- L('6'), L('6'), L('5'), L('2'), L('1'), L('3'), L('8'), L('4'),
- L('1'), L('4'), L('6'), L('9'), L('5'), L('1'), L('9'), L('4'),
- L('1'), L('5'), L('1'), L('1'), L('6'), L('0'), L('9'), L('4'),
- L('3'), L('3'), L('0'), L('5'), L('7'), L('2'), L('7'), L('0'),
- L('3'), L('6'), L('5'), L('7'), L('5'), L('9'), L('5'), L('9'),
- L('1'), L('9'), L('5'), L('3'), L('0'), L('9'), L('2'), L('1'),
- L('8'), L('6'), L('1'), L('1'), L('7'), M(234, 4), L('3'), L('2'),
- M(10, 4), L('9'), L('3'), L('1'), L('0'), L('5'), L('1'), L('1'),
- L('8'), L('5'), L('4'), L('8'), L('0'), L('7'), M(271, 4), L('3'),
- L('7'), L('9'), L('9'), L('6'), L('2'), L('7'), L('4'), L('9'),
- L('5'), L('6'), L('7'), L('3'), L('5'), L('1'), L('8'), L('8'),
- L('5'), L('7'), L('5'), L('2'), L('7'), L('2'), L('4'), L('8'),
- L('9'), L('1'), L('2'), L('2'), L('7'), L('9'), L('3'), L('8'),
- L('1'), L('8'), L('3'), L('0'), L('1'), L('1'), L('9'), L('4'),
- L('9'), L('1'), L('2'), L('9'), L('8'), L('3'), L('3'), L('6'),
- L('7'), L('3'), L('3'), L('6'), L('2'), L('4'), L('4'), L('0'),
- L('6'), L('5'), L('6'), L('6'), L('4'), L('3'), L('0'), L('8'),
- L('6'), L('0'), L('2'), L('1'), L('3'), L('9'), L('4'), L('9'),
- L('4'), L('6'), L('3'), L('9'), L('5'), L('2'), L('2'), L('4'),
- L('7'), L('3'), L('7'), L('1'), L('9'), L('0'), L('7'), L('0'),
- L('2'), L('1'), L('7'), L('9'), L('8'), M(154, 5), L('7'), L('0'),
- L('2'), L('7'), L('7'), L('0'), L('5'), L('3'), L('9'), L('2'),
- L('1'), L('7'), L('1'), L('7'), L('6'), L('2'), L('9'), L('3'),
- L('1'), L('7'), L('6'), L('7'), L('5'), M(563, 5), L('7'), L('4'),
- L('8'), L('1'), M(7, 4), L('6'), L('6'), L('9'), L('4'), L('0'),
- M(488, 4), L('0'), L('0'), L('0'), L('5'), L('6'), L('8'), L('1'),
- L('2'), L('7'), L('1'), L('4'), L('5'), L('2'), L('6'), L('3'),
- L('5'), L('6'), L('0'), L('8'), L('2'), L('7'), L('7'), L('8'),
- L('5'), L('7'), L('7'), L('1'), L('3'), L('4'), L('2'), L('7'),
- L('5'), L('7'), L('7'), L('8'), L('9'), L('6'), M(298, 4), L('3'),
- L('6'), L('3'), L('7'), L('1'), L('7'), L('8'), L('7'), L('2'),
- L('1'), L('4'), L('6'), L('8'), L('4'), L('4'), L('0'), L('9'),
- L('0'), L('1'), L('2'), L('2'), L('4'), L('9'), L('5'), L('3'),
- L('4'), L('3'), L('0'), L('1'), L('4'), L('6'), L('5'), L('4'),
- L('9'), L('5'), L('8'), L('5'), L('3'), L('7'), L('1'), L('0'),
- L('5'), L('0'), L('7'), L('9'), M(203, 4), L('6'), M(340, 4), L('8'),
- L('9'), L('2'), L('3'), L('5'), L('4'), M(458, 4), L('9'), L('5'),
- L('6'), L('1'), L('1'), L('2'), L('1'), L('2'), L('9'), L('0'),
- L('2'), L('1'), L('9'), L('6'), L('0'), L('8'), L('6'), L('4'),
- L('0'), L('3'), L('4'), L('4'), L('1'), L('8'), L('1'), L('5'),
- L('9'), L('8'), L('1'), L('3'), L('6'), L('2'), L('9'), L('7'),
- L('7'), L('4'), M(117, 4), L('0'), L('9'), L('9'), L('6'), L('0'),
- L('5'), L('1'), L('8'), L('7'), L('0'), L('7'), L('2'), L('1'),
- L('1'), L('3'), L('4'), L('9'), M(1, 5), L('8'), L('3'), L('7'),
- L('2'), L('9'), L('7'), L('8'), L('0'), L('4'), L('9'), L('9'),
- M(731, 4), L('9'), L('7'), L('3'), L('1'), L('7'), L('3'), L('2'),
- L('8'), M(395, 4), L('6'), L('3'), L('1'), L('8'), L('5'), M(770, 4),
- M(745, 4), L('4'), L('5'), L('5'), L('3'), L('4'), L('6'), L('9'),
- L('0'), L('8'), L('3'), L('0'), L('2'), L('6'), L('4'), L('2'),
- L('5'), L('2'), L('2'), L('3'), L('0'), M(740, 4), M(616, 4), L('8'),
- L('5'), L('0'), L('3'), L('5'), L('2'), L('6'), L('1'), L('9'),
- L('3'), L('1'), L('1'), M(531, 4), L('1'), L('0'), L('1'), L('0'),
- L('0'), L('0'), L('3'), L('1'), L('3'), L('7'), L('8'), L('3'),
- L('8'), L('7'), L('5'), L('2'), L('8'), L('8'), L('6'), L('5'),
- L('8'), L('7'), L('5'), L('3'), L('3'), L('2'), L('0'), L('8'),
- L('3'), L('8'), L('1'), L('4'), L('2'), L('0'), L('6'), M(321, 4),
- M(300, 4), L('1'), L('4'), L('7'), L('3'), L('0'), L('3'), L('5'),
- L('9'), M(815, 5), L('9'), L('0'), L('4'), L('2'), L('8'), L('7'),
- L('5'), L('5'), L('4'), L('6'), L('8'), L('7'), L('3'), L('1'),
- L('1'), L('5'), L('9'), L('5'), M(854, 4), L('3'), L('8'), L('8'),
- L('2'), L('3'), L('5'), L('3'), L('7'), L('8'), L('7'), L('5'),
- M(896, 5), L('9'), M(315, 4), L('1'), M(329, 4), L('8'), L('0'), L('5'),
- L('3'), M(395, 4), L('2'), L('2'), L('6'), L('8'), L('0'), L('6'),
- L('6'), L('1'), L('3'), L('0'), L('0'), L('1'), L('9'), L('2'),
- L('7'), L('8'), L('7'), L('6'), L('6'), L('1'), L('1'), L('1'),
- L('9'), L('5'), L('9'), M(568, 4), L('6'), M(293, 5), L('8'), L('9'),
- L('3'), L('8'), L('0'), L('9'), L('5'), L('2'), L('5'), L('7'),
- L('2'), L('0'), L('1'), L('0'), L('6'), L('5'), L('4'), L('8'),
- L('5'), L('8'), L('6'), L('3'), L('2'), L('7'), M(155, 4), L('9'),
- L('3'), L('6'), L('1'), L('5'), L('3'), M(545, 4), M(349, 5), L('2'),
- L('3'), L('0'), L('3'), L('0'), L('1'), L('9'), L('5'), L('2'),
- L('0'), L('3'), L('5'), L('3'), L('0'), L('1'), L('8'), L('5'),
- L('2'), M(370, 4), M(118, 4), L('3'), L('6'), L('2'), L('2'), L('5'),
- L('9'), L('9'), L('4'), L('1'), L('3'), M(597, 4), L('4'), L('9'),
- L('7'), L('2'), L('1'), L('7'), M(223, 4), L('3'), L('4'), L('7'),
- L('9'), L('1'), L('3'), L('1'), L('5'), L('1'), L('5'), L('5'),
- L('7'), L('4'), L('8'), L('5'), L('7'), L('2'), L('4'), L('2'),
- L('4'), L('5'), L('4'), L('1'), L('5'), L('0'), L('6'), L('9'),
- M(320, 4), L('8'), L('2'), L('9'), L('5'), L('3'), L('3'), L('1'),
- L('1'), L('6'), L('8'), L('6'), L('1'), L('7'), L('2'), L('7'),
- L('8'), M(824, 4), L('9'), L('0'), L('7'), L('5'), L('0'), L('9'),
- M(270, 4), L('7'), L('5'), L('4'), L('6'), L('3'), L('7'), L('4'),
- L('6'), L('4'), L('9'), L('3'), L('9'), L('3'), L('1'), L('9'),
- L('2'), L('5'), L('5'), L('0'), L('6'), L('0'), L('4'), L('0'),
- L('0'), L('9'), M(620, 4), L('1'), L('6'), L('7'), L('1'), L('1'),
- L('3'), L('9'), L('0'), L('0'), L('9'), L('8'), M(822, 4), L('4'),
- L('0'), L('1'), L('2'), L('8'), L('5'), L('8'), L('3'), L('6'),
- L('1'), L('6'), L('0'), L('3'), L('5'), L('6'), L('3'), L('7'),
- L('0'), L('7'), L('6'), L('6'), L('0'), L('1'), L('0'), L('4'),
- M(371, 4), L('8'), L('1'), L('9'), L('4'), L('2'), L('9'), M(1055, 5),
- M(240, 4), M(652, 4), L('7'), L('8'), L('3'), L('7'), L('4'), M(1193, 4),
- L('8'), L('2'), L('5'), L('5'), L('3'), L('7'), M(522, 5), L('2'),
- L('6'), L('8'), M(47, 4), L('4'), L('0'), L('4'), L('7'), M(466, 4),
- L('4'), M(1206, 4), M(910, 4), L('8'), L('4'), M(937, 4), L('6'), M(800, 6),
- L('3'), L('3'), L('1'), L('3'), L('6'), L('7'), L('7'), L('0'),
- L('2'), L('8'), L('9'), L('8'), L('9'), L('1'), L('5'), L('2'),
- M(99, 4), L('5'), L('2'), L('1'), L('6'), L('2'), L('0'), L('5'),
- L('6'), L('9'), L('6'), M(1042, 4), L('0'), L('5'), L('8'), M(1144, 4),
- L('5'), M(1177, 4), L('5'), L('1'), L('1'), M(522, 4), L('8'), L('2'),
- L('4'), L('3'), L('0'), L('0'), L('3'), L('5'), L('5'), L('8'),
- L('7'), L('6'), L('4'), L('0'), L('2'), L('4'), L('7'), L('4'),
- L('9'), L('6'), L('4'), L('7'), L('3'), L('2'), L('6'), L('3'),
- M(1087, 4), L('9'), L('9'), L('2'), M(1100, 4), L('4'), L('2'), L('6'),
- L('9'), M(710, 6), L('7'), M(471, 4), L('4'), M(1342, 4), M(1054, 4), L('9'),
- L('3'), L('4'), L('1'), L('7'), M(430, 4), L('1'), L('2'), M(43, 4),
- L('4'), M(415, 4), L('1'), L('5'), L('0'), L('3'), L('0'), L('2'),
- L('8'), L('6'), L('1'), L('8'), L('2'), L('9'), L('7'), L('4'),
- L('5'), L('5'), L('5'), L('7'), L('0'), L('6'), L('7'), L('4'),
- M(310, 4), L('5'), L('0'), L('5'), L('4'), L('9'), L('4'), L('5'),
- L('8'), M(454, 4), L('9'), M(82, 4), L('5'), L('6'), M(493, 4), L('7'),
- L('2'), L('1'), L('0'), L('7'), L('9'), M(346, 4), L('3'), L('0'),
- M(267, 4), L('3'), L('2'), L('1'), L('1'), L('6'), L('5'), L('3'),
- L('4'), L('4'), L('9'), L('8'), L('7'), L('2'), L('0'), L('2'),
- L('7'), M(284, 4), L('0'), L('2'), L('3'), L('6'), L('4'), M(559, 4),
- L('5'), L('4'), L('9'), L('9'), L('1'), L('1'), L('9'), L('8'),
- M(1049, 4), L('4'), M(284, 4), L('5'), L('3'), L('5'), L('6'), L('6'),
- L('3'), L('6'), L('9'), M(1105, 4), L('2'), L('6'), L('5'), M(741, 4),
- L('7'), L('8'), L('6'), L('2'), L('5'), L('5'), L('1'), M(987, 4),
- L('1'), L('7'), L('5'), L('7'), L('4'), L('6'), L('7'), L('2'),
- L('8'), L('9'), L('0'), L('9'), L('7'), L('7'), L('7'), L('7'),
- M(1108, 5), L('0'), L('0'), L('0'), M(1534, 4), L('7'), L('0'), M(1248, 4),
- L('6'), M(1002, 4), L('4'), L('9'), L('1'), M(1055, 4), M(664, 4), L('2'),
- L('1'), L('4'), L('7'), L('7'), L('2'), L('3'), L('5'), L('0'),
- L('1'), L('4'), L('1'), L('4'), M(1604, 4), L('3'), L('5'), L('6'),
- M(1200, 4), L('1'), L('6'), L('1'), L('3'), L('6'), L('1'), L('1'),
- L('5'), L('7'), L('3'), L('5'), L('2'), L('5'), M(1285, 4), L('3'),
- L('4'), M(92, 4), L('1'), L('8'), M(1148, 4), L('8'), L('4'), M(1512, 4),
- L('3'), L('3'), L('2'), L('3'), L('9'), L('0'), L('7'), L('3'),
- L('9'), L('4'), L('1'), L('4'), L('3'), L('3'), L('3'), L('4'),
- L('5'), L('4'), L('7'), L('7'), L('6'), L('2'), L('4'), M(579, 4),
- L('2'), L('5'), L('1'), L('8'), L('9'), L('8'), L('3'), L('5'),
- L('6'), L('9'), L('4'), L('8'), L('5'), L('5'), L('6'), L('2'),
- L('0'), L('9'), L('9'), L('2'), L('1'), L('9'), L('2'), L('2'),
- L('2'), L('1'), L('8'), L('4'), L('2'), L('7'), M(575, 4), L('2'),
- M(187, 4), L('6'), L('8'), L('8'), L('7'), L('6'), L('7'), L('1'),
- L('7'), L('9'), L('0'), M(86, 4), L('0'), M(263, 5), L('6'), L('6'),
- M(1000, 4), L('8'), L('8'), L('6'), L('2'), L('7'), L('2'), M(1757, 4),
- L('1'), L('7'), L('8'), L('6'), L('0'), L('8'), L('5'), L('7'),
- M(116, 4), L('3'), M(765, 5), L('7'), L('9'), L('7'), L('6'), L('6'),
- L('8'), L('1'), M(702, 4), L('0'), L('0'), L('9'), L('5'), L('3'),
- L('8'), L('8'), M(1593, 4), L('3'), M(1702, 4), L('0'), L('6'), L('8'),
- L('0'), L('0'), L('6'), L('4'), L('2'), L('2'), L('5'), L('1'),
- L('2'), L('5'), L('2'), M(1404, 4), L('7'), L('3'), L('9'), L('2'),
- M(664, 4), M(1141, 4), L('4'), M(1716, 5), L('8'), L('6'), L('2'), L('6'),
- L('9'), L('4'), L('5'), M(486, 4), L('4'), L('1'), L('9'), L('6'),
- L('5'), L('2'), L('8'), L('5'), L('0'), M(154, 4), M(925, 4), L('1'),
- L('8'), L('6'), L('3'), M(447, 4), L('4'), M(341, 5), L('2'), L('0'),
- L('3'), L('9'), M(1420, 4), L('4'), L('5'), M(701, 4), L('2'), L('3'),
- L('7'), M(1069, 4), L('6'), M(1297, 4), L('5'), L('6'), M(1593, 4), L('7'),
- L('1'), L('9'), L('1'), L('7'), L('2'), L('8'), M(370, 4), L('7'),
- L('6'), L('4'), L('6'), L('5'), L('7'), L('5'), L('7'), L('3'),
- L('9'), M(258, 4), L('3'), L('8'), L('9'), M(1865, 4), L('8'), L('3'),
- L('2'), L('6'), L('4'), L('5'), L('9'), L('9'), L('5'), L('8'),
- M(1704, 4), L('0'), L('4'), L('7'), L('8'), M(479, 4), M(809, 4), L('9'),
- M(46, 4), L('6'), L('4'), L('0'), L('7'), L('8'), L('9'), L('5'),
- L('1'), M(143, 4), L('6'), L('8'), L('3'), M(304, 4), L('2'), L('5'),
- L('9'), L('5'), L('7'), L('0'), M(1129, 4), L('8'), L('2'), L('2'),
- M(713, 4), L('2'), M(1564, 4), L('4'), L('0'), L('7'), L('7'), L('2'),
- L('6'), L('7'), L('1'), L('9'), L('4'), L('7'), L('8'), M(794, 4),
- L('8'), L('2'), L('6'), L('0'), L('1'), L('4'), L('7'), L('6'),
- L('9'), L('9'), L('0'), L('9'), M(1257, 4), L('0'), L('1'), L('3'),
- L('6'), L('3'), L('9'), L('4'), L('4'), L('3'), M(640, 4), L('3'),
- L('0'), M(262, 4), L('2'), L('0'), L('3'), L('4'), L('9'), L('6'),
- L('2'), L('5'), L('2'), L('4'), L('5'), L('1'), L('7'), M(950, 4),
- L('9'), L('6'), L('5'), L('1'), L('4'), L('3'), L('1'), L('4'),
- L('2'), L('9'), L('8'), L('0'), L('9'), L('1'), L('9'), L('0'),
- L('6'), L('5'), L('9'), L('2'), M(643, 4), L('7'), L('2'), L('2'),
- L('1'), L('6'), L('9'), L('6'), L('4'), L('6'), M(1050, 4), M(123, 4),
- L('5'), M(1295, 4), L('4'), M(1382, 5), L('8'), M(1370, 4), L('9'), L('7'),
- M(1404, 4), L('5'), L('4'), M(1182, 4), M(575, 4), L('7'), M(1627, 4), L('8'),
- L('4'), L('6'), L('8'), L('1'), L('3'), M(141, 4), L('6'), L('8'),
- L('3'), L('8'), L('6'), L('8'), L('9'), L('4'), L('2'), L('7'),
- L('7'), L('4'), L('1'), L('5'), L('5'), L('9'), L('9'), L('1'),
- L('8'), L('5'), M(91, 4), L('2'), L('4'), L('5'), L('9'), L('5'),
- L('3'), L('9'), L('5'), L('9'), L('4'), L('3'), L('1'), M(1464, 4),
- L('7'), M(19, 4), L('6'), L('8'), L('0'), L('8'), L('4'), L('5'),
- M(744, 4), L('7'), L('3'), M(2079, 4), L('9'), L('5'), L('8'), L('4'),
- L('8'), L('6'), L('5'), L('3'), L('8'), M(1769, 4), L('6'), L('2'),
- M(243, 4), L('6'), L('0'), L('9'), M(1207, 4), L('6'), L('0'), L('8'),
- L('0'), L('5'), L('1'), L('2'), L('4'), L('3'), L('8'), L('8'),
- L('4'), M(315, 4), M(12, 4), L('4'), L('1'), L('3'), M(784, 4), L('7'),
- L('6'), L('2'), L('7'), L('8'), M(834, 4), L('7'), L('1'), L('5'),
- M(1436, 4), L('3'), L('5'), L('9'), L('9'), L('7'), L('7'), L('0'),
- L('0'), L('1'), L('2'), L('9'), M(1139, 4), L('8'), L('9'), L('4'),
- L('4'), L('1'), M(632, 4), L('6'), L('8'), L('5'), L('5'), M(96, 4),
- L('4'), L('0'), L('6'), L('3'), M(2279, 4), L('2'), L('0'), L('7'),
- L('2'), L('2'), M(345, 4), M(516, 5), L('4'), L('8'), L('1'), L('5'),
- L('8'), M(518, 4), M(511, 4), M(635, 4), M(665, 4), L('3'), L('9'), L('4'),
- L('5'), L('2'), L('2'), L('6'), L('7'), M(1175, 6), L('8'), M(1419, 4),
- L('2'), L('1'), M(747, 4), L('2'), M(904, 4), L('5'), L('4'), L('6'),
- L('6'), L('6'), M(1308, 4), L('2'), L('3'), L('9'), L('8'), L('6'),
- L('4'), L('5'), L('6'), M(1221, 4), L('1'), L('6'), L('3'), L('5'),
- M(596, 5), M(2066, 4), L('7'), M(2222, 4), L('9'), L('8'), M(1119, 4), L('9'),
- L('3'), L('6'), L('3'), L('4'), M(1884, 4), L('7'), L('4'), L('3'),
- L('2'), L('4'), M(1148, 4), L('1'), L('5'), L('0'), L('7'), L('6'),
- M(1212, 4), L('7'), L('9'), L('4'), L('5'), L('1'), L('0'), L('9'),
- M(63, 4), L('0'), L('9'), L('4'), L('0'), M(1703, 4), L('8'), L('8'),
- L('7'), L('9'), L('7'), L('1'), L('0'), L('8'), L('9'), L('3'),
- M(2289, 4), L('6'), L('9'), L('1'), L('3'), L('6'), L('8'), L('6'),
- L('7'), L('2'), M(604, 4), M(511, 4), L('5'), M(1344, 4), M(1129, 4), M(2050, 4),
- L('1'), L('7'), L('9'), L('2'), L('8'), L('6'), L('8'), M(2253, 4),
- L('8'), L('7'), L('4'), L('7'), M(1951, 5), L('8'), L('2'), L('4'),
- M(2427, 4), L('8'), M(604, 4), L('7'), L('1'), L('4'), L('9'), L('0'),
- L('9'), L('6'), L('7'), L('5'), L('9'), L('8'), M(1776, 4), L('3'),
- L('6'), L('5'), M(309, 4), L('8'), L('1'), M(93, 4), M(1862, 4), M(2359, 4),
- L('6'), L('8'), L('2'), L('9'), M(1407, 4), L('8'), L('7'), L('2'),
- L('2'), L('6'), L('5'), L('8'), L('8'), L('0'), M(1554, 4), L('5'),
- M(586, 4), L('4'), L('2'), L('7'), L('0'), L('4'), L('7'), L('7'),
- L('5'), L('5'), M(2079, 4), L('3'), L('7'), L('9'), L('6'), L('4'),
- L('1'), L('4'), L('5'), L('1'), L('5'), L('2'), M(1534, 4), L('2'),
- L('3'), L('4'), L('3'), L('6'), L('4'), L('5'), L('4'), M(1503, 4),
- L('4'), L('4'), L('4'), L('7'), L('9'), L('5'), M(61, 4), M(1316, 4),
- M(2279, 5), L('4'), L('1'), M(1323, 4), L('3'), M(773, 4), L('5'), L('2'),
- L('3'), L('1'), M(2114, 5), L('1'), L('6'), L('6'), L('1'), M(2227, 4),
- L('5'), L('9'), L('6'), L('9'), L('5'), L('3'), L('6'), L('2'),
- L('3'), L('1'), L('4'), M(1536, 4), L('2'), L('4'), L('8'), L('4'),
- L('9'), L('3'), L('7'), L('1'), L('8'), L('7'), L('1'), L('1'),
- L('0'), L('1'), L('4'), L('5'), L('7'), L('6'), L('5'), L('4'),
- M(1890, 4), L('0'), L('2'), L('7'), L('9'), L('9'), L('3'), L('4'),
- L('4'), L('0'), L('3'), L('7'), L('4'), L('2'), L('0'), L('0'),
- L('7'), M(2368, 4), L('7'), L('8'), L('5'), L('3'), L('9'), L('0'),
- L('6'), L('2'), L('1'), L('9'), M(666, 5), M(838, 4), L('8'), L('4'),
- L('7'), M(979, 5), L('8'), L('3'), L('3'), L('2'), L('1'), L('4'),
- L('4'), L('5'), L('7'), L('1'), M(645, 4), M(1911, 4), L('4'), L('3'),
- L('5'), L('0'), M(2345, 4), M(1129, 4), L('5'), L('3'), L('1'), L('9'),
- L('1'), L('0'), L('4'), L('8'), L('4'), L('8'), L('1'), L('0'),
- L('0'), L('5'), L('3'), L('7'), L('0'), L('6'), M(2237, 4), M(1438, 5),
- M(1922, 5), L('1'), M(1370, 4), L('7'), M(796, 4), L('5'), M(2029, 4), M(1037, 4),
- L('6'), L('3'), M(2013, 5), L('4'), M(2418, 4), M(847, 5), M(1014, 5), L('8'),
- M(1326, 5), M(2184, 5), L('9'), M(392, 4), L('9'), L('1'), M(2255, 4), L('8'),
- L('1'), L('4'), L('6'), L('7'), L('5'), L('1'), M(1580, 4), L('1'),
- L('2'), L('3'), L('9'), M(426, 6), L('9'), L('0'), L('7'), L('1'),
- L('8'), L('6'), L('4'), L('9'), L('4'), L('2'), L('3'), L('1'),
- L('9'), L('6'), L('1'), L('5'), L('6'), M(493, 4), M(1725, 4), L('9'),
- L('5'), M(2343, 4), M(1130, 4), M(284, 4), L('6'), L('0'), L('3'), L('8'),
- M(2598, 4), M(368, 4), M(901, 4), L('6'), L('2'), M(1115, 4), L('5'), M(2125, 4),
- L('6'), L('3'), L('8'), L('9'), L('3'), L('7'), L('7'), L('8'),
- L('7'), M(2246, 4), M(249, 4), L('9'), L('7'), L('9'), L('2'), L('0'),
- L('7'), L('7'), L('3'), M(1496, 4), L('2'), L('1'), L('8'), L('2'),
- L('5'), L('6'), M(2016, 4), L('6'), L('6'), M(1751, 4), L('4'), L('2'),
- M(1663, 5), L('6'), M(1767, 4), L('4'), L('4'), M(37, 4), L('5'), L('4'),
- L('9'), L('2'), L('0'), L('2'), L('6'), L('0'), L('5'), M(2740, 4),
- M(997, 5), L('2'), L('0'), L('1'), L('4'), L('9'), M(1235, 4), L('8'),
- L('5'), L('0'), L('7'), L('3'), M(1434, 4), L('6'), L('6'), L('6'),
- L('0'), M(405, 4), L('2'), L('4'), L('3'), L('4'), L('0'), M(136, 4),
- L('0'), M(1900, 4), L('8'), L('6'), L('3'), M(2391, 4), M(2021, 4), M(1068, 4),
- M(373, 4), L('5'), L('7'), L('9'), L('6'), L('2'), L('6'), L('8'),
- L('5'), L('6'), M(321, 4), L('5'), L('0'), L('8'), M(1316, 4), L('5'),
- L('8'), L('7'), L('9'), L('6'), L('9'), L('9'), M(1810, 4), L('5'),
- L('7'), L('4'), M(2585, 4), L('8'), L('4'), L('0'), M(2228, 4), L('1'),
- L('4'), L('5'), L('9'), L('1'), M(1933, 4), L('7'), L('0'), M(565, 4),
- L('0'), L('1'), M(3048, 4), L('1'), L('2'), M(3189, 4), L('0'), M(964, 4),
- L('3'), L('9'), M(2859, 4), M(275, 4), L('7'), L('1'), L('5'), M(945, 4),
- L('4'), L('2'), L('0'), M(3059, 5), L('9'), M(3011, 4), L('0'), L('7'),
- M(834, 4), M(1942, 4), M(2736, 4), M(3171, 4), L('2'), L('1'), M(2401, 4), L('2'),
- L('5'), L('1'), M(1404, 4), M(2373, 4), L('9'), L('2'), M(435, 4), L('8'),
- L('2'), L('6'), M(2919, 4), L('2'), M(633, 4), L('3'), L('2'), L('1'),
- L('5'), L('7'), L('9'), L('1'), L('9'), L('8'), L('4'), L('1'),
- L('4'), M(2172, 5), L('9'), L('1'), L('6'), L('4'), M(1769, 5), L('9'),
- M(2905, 5), M(2268, 4), L('7'), L('2'), L('2'), M(802, 4), L('5'), M(2213, 4),
- M(322, 4), L('9'), L('1'), L('0'), M(189, 4), M(3164, 4), L('5'), L('2'),
- L('8'), L('0'), L('1'), L('7'), M(562, 4), L('7'), L('1'), L('2'),
- M(2325, 4), L('8'), L('3'), L('2'), M(884, 4), L('1'), M(1418, 4), L('0'),
- L('9'), L('3'), L('5'), L('3'), L('9'), L('6'), L('5'), L('7'),
- M(1612, 4), L('1'), L('0'), L('8'), L('3'), M(106, 4), L('5'), L('1'),
- M(1915, 4), M(3419, 4), L('1'), L('4'), L('4'), L('4'), L('2'), L('1'),
- L('0'), L('0'), M(515, 4), L('0'), L('3'), M(413, 4), L('1'), L('1'),
- L('0'), L('3'), M(3202, 4), M(10, 4), M(39, 4), M(1539, 6), L('5'), L('1'),
- L('6'), M(1498, 4), M(2180, 5), M(2347, 4), L('5'), M(3139, 5), L('8'), L('5'),
- L('1'), L('7'), L('1'), L('4'), L('3'), L('7'), M(1542, 4), M(110, 4),
- L('1'), L('5'), L('5'), L('6'), L('5'), L('0'), L('8'), L('8'),
- M(954, 4), L('9'), L('8'), L('9'), L('8'), L('5'), L('9'), L('9'),
- L('8'), L('2'), L('3'), L('8'), M(464, 4), M(2491, 4), L('3'), M(365, 4),
- M(1087, 4), M(2500, 4), L('8'), M(3590, 5), L('3'), L('2'), M(264, 4), L('5'),
- M(774, 4), L('3'), M(459, 4), L('9'), M(1052, 4), L('9'), L('8'), M(2174, 4),
- L('4'), M(3257, 4), L('7'), M(1612, 4), L('0'), L('7'), M(230, 4), L('4'),
- L('8'), L('1'), L('4'), L('1'), M(1338, 4), L('8'), L('5'), L('9'),
- L('4'), L('6'), L('1'), M(3018, 4), L('8'), L('0'),
- },
- },
- TestCase{
- .input = "huffman-rand-1k.input",
- .want = "huffman-rand-1k.{s}.expect",
- .want_no_input = "huffman-rand-1k.{s}.expect-noinput",
- .tokens = &[_]Token{
- L(0xf8), L(0x8b), L(0x96), L(0x76), L(0x48), L(0xd), L(0x85), L(0x94), L(0x25), L(0x80), L(0xaf), L(0xc2), L(0xfe), L(0x8d),
- L(0xe8), L(0x20), L(0xeb), L(0x17), L(0x86), L(0xc9), L(0xb7), L(0xc5), L(0xde), L(0x6), L(0xea), L(0x7d), L(0x18), L(0x8b),
- L(0xe7), L(0x3e), L(0x7), L(0xda), L(0xdf), L(0xff), L(0x6c), L(0x73), L(0xde), L(0xcc), L(0xe7), L(0x6d), L(0x8d), L(0x4),
- L(0x19), L(0x49), L(0x7f), L(0x47), L(0x1f), L(0x48), L(0x15), L(0xb0), L(0xe8), L(0x9e), L(0xf2), L(0x31), L(0x59), L(0xde),
- L(0x34), L(0xb4), L(0x5b), L(0xe5), L(0xe0), L(0x9), L(0x11), L(0x30), L(0xc2), L(0x88), L(0x5b), L(0x7c), L(0x5d), L(0x14),
- L(0x13), L(0x6f), L(0x23), L(0xa9), L(0xd), L(0xbc), L(0x2d), L(0x23), L(0xbe), L(0xd9), L(0xed), L(0x75), L(0x4), L(0x6c),
- L(0x99), L(0xdf), L(0xfd), L(0x70), L(0x66), L(0xe6), L(0xee), L(0xd9), L(0xb1), L(0x9e), L(0x6e), L(0x83), L(0x59), L(0xd5),
- L(0xd4), L(0x80), L(0x59), L(0x98), L(0x77), L(0x89), L(0x43), L(0x38), L(0xc9), L(0xaf), L(0x30), L(0x32), L(0x9a), L(0x20),
- L(0x1b), L(0x46), L(0x3d), L(0x67), L(0x6e), L(0xd7), L(0x72), L(0x9e), L(0x4e), L(0x21), L(0x4f), L(0xc6), L(0xe0), L(0xd4),
- L(0x7b), L(0x4), L(0x8d), L(0xa5), L(0x3), L(0xf6), L(0x5), L(0x9b), L(0x6b), L(0xdc), L(0x2a), L(0x93), L(0x77), L(0x28),
- L(0xfd), L(0xb4), L(0x62), L(0xda), L(0x20), L(0xe7), L(0x1f), L(0xab), L(0x6b), L(0x51), L(0x43), L(0x39), L(0x2f), L(0xa0),
- L(0x92), L(0x1), L(0x6c), L(0x75), L(0x3e), L(0xf4), L(0x35), L(0xfd), L(0x43), L(0x2e), L(0xf7), L(0xa4), L(0x75), L(0xda),
- L(0xea), L(0x9b), L(0xa), L(0x64), L(0xb), L(0xe0), L(0x23), L(0x29), L(0xbd), L(0xf7), L(0xe7), L(0x83), L(0x3c), L(0xfb),
- L(0xdf), L(0xb3), L(0xae), L(0x4f), L(0xa4), L(0x47), L(0x55), L(0x99), L(0xde), L(0x2f), L(0x96), L(0x6e), L(0x1c), L(0x43),
- L(0x4c), L(0x87), L(0xe2), L(0x7c), L(0xd9), L(0x5f), L(0x4c), L(0x7c), L(0xe8), L(0x90), L(0x3), L(0xdb), L(0x30), L(0x95),
- L(0xd6), L(0x22), L(0xc), L(0x47), L(0xb8), L(0x4d), L(0x6b), L(0xbd), L(0x24), L(0x11), L(0xab), L(0x2c), L(0xd7), L(0xbe),
- L(0x6e), L(0x7a), L(0xd6), L(0x8), L(0xa3), L(0x98), L(0xd8), L(0xdd), L(0x15), L(0x6a), L(0xfa), L(0x93), L(0x30), L(0x1),
- L(0x25), L(0x1d), L(0xa2), L(0x74), L(0x86), L(0x4b), L(0x6a), L(0x95), L(0xe8), L(0xe1), L(0x4e), L(0xe), L(0x76), L(0xb9),
- L(0x49), L(0xa9), L(0x5f), L(0xa0), L(0xa6), L(0x63), L(0x3c), L(0x7e), L(0x7e), L(0x20), L(0x13), L(0x4f), L(0xbb), L(0x66),
- L(0x92), L(0xb8), L(0x2e), L(0xa4), L(0xfa), L(0x48), L(0xcb), L(0xae), L(0xb9), L(0x3c), L(0xaf), L(0xd3), L(0x1f), L(0xe1),
- L(0xd5), L(0x8d), L(0x42), L(0x6d), L(0xf0), L(0xfc), L(0x8c), L(0xc), L(0x0), L(0xde), L(0x40), L(0xab), L(0x8b), L(0x47),
- L(0x97), L(0x4e), L(0xa8), L(0xcf), L(0x8e), L(0xdb), L(0xa6), L(0x8b), L(0x20), L(0x9), L(0x84), L(0x7a), L(0x66), L(0xe5),
- L(0x98), L(0x29), L(0x2), L(0x95), L(0xe6), L(0x38), L(0x32), L(0x60), L(0x3), L(0xe3), L(0x9a), L(0x1e), L(0x54), L(0xe8),
- L(0x63), L(0x80), L(0x48), L(0x9c), L(0xe7), L(0x63), L(0x33), L(0x6e), L(0xa0), L(0x65), L(0x83), L(0xfa), L(0xc6), L(0xba),
- L(0x7a), L(0x43), L(0x71), L(0x5), L(0xf5), L(0x68), L(0x69), L(0x85), L(0x9c), L(0xba), L(0x45), L(0xcd), L(0x6b), L(0xb),
- L(0x19), L(0xd1), L(0xbb), L(0x7f), L(0x70), L(0x85), L(0x92), L(0xd1), L(0xb4), L(0x64), L(0x82), L(0xb1), L(0xe4), L(0x62),
- L(0xc5), L(0x3c), L(0x46), L(0x1f), L(0x92), L(0x31), L(0x1c), L(0x4e), L(0x41), L(0x77), L(0xf7), L(0xe7), L(0x87), L(0xa2),
- L(0xf), L(0x6e), L(0xe8), L(0x92), L(0x3), L(0x6b), L(0xa), L(0xe7), L(0xa9), L(0x3b), L(0x11), L(0xda), L(0x66), L(0x8a),
- L(0x29), L(0xda), L(0x79), L(0xe1), L(0x64), L(0x8d), L(0xe3), L(0x54), L(0xd4), L(0xf5), L(0xef), L(0x64), L(0x87), L(0x3b),
- L(0xf4), L(0xc2), L(0xf4), L(0x71), L(0x13), L(0xa9), L(0xe9), L(0xe0), L(0xa2), L(0x6), L(0x14), L(0xab), L(0x5d), L(0xa7),
- L(0x96), L(0x0), L(0xd6), L(0xc3), L(0xcc), L(0x57), L(0xed), L(0x39), L(0x6a), L(0x25), L(0xcd), L(0x76), L(0xea), L(0xba),
- L(0x3a), L(0xf2), L(0xa1), L(0x95), L(0x5d), L(0xe5), L(0x71), L(0xcf), L(0x9c), L(0x62), L(0x9e), L(0x6a), L(0xfa), L(0xd5),
- L(0x31), L(0xd1), L(0xa8), L(0x66), L(0x30), L(0x33), L(0xaa), L(0x51), L(0x17), L(0x13), L(0x82), L(0x99), L(0xc8), L(0x14),
- L(0x60), L(0x9f), L(0x4d), L(0x32), L(0x6d), L(0xda), L(0x19), L(0x26), L(0x21), L(0xdc), L(0x7e), L(0x2e), L(0x25), L(0x67),
- L(0x72), L(0xca), L(0xf), L(0x92), L(0xcd), L(0xf6), L(0xd6), L(0xcb), L(0x97), L(0x8a), L(0x33), L(0x58), L(0x73), L(0x70),
- L(0x91), L(0x1d), L(0xbf), L(0x28), L(0x23), L(0xa3), L(0xc), L(0xf1), L(0x83), L(0xc3), L(0xc8), L(0x56), L(0x77), L(0x68),
- L(0xe3), L(0x82), L(0xba), L(0xb9), L(0x57), L(0x56), L(0x57), L(0x9c), L(0xc3), L(0xd6), L(0x14), L(0x5), L(0x3c), L(0xb1),
- L(0xaf), L(0x93), L(0xc8), L(0x8a), L(0x57), L(0x7f), L(0x53), L(0xfa), L(0x2f), L(0xaa), L(0x6e), L(0x66), L(0x83), L(0xfa),
- L(0x33), L(0xd1), L(0x21), L(0xab), L(0x1b), L(0x71), L(0xb4), L(0x7c), L(0xda), L(0xfd), L(0xfb), L(0x7f), L(0x20), L(0xab),
- L(0x5e), L(0xd5), L(0xca), L(0xfd), L(0xdd), L(0xe0), L(0xee), L(0xda), L(0xba), L(0xa8), L(0x27), L(0x99), L(0x97), L(0x69),
- L(0xc1), L(0x3c), L(0x82), L(0x8c), L(0xa), L(0x5c), L(0x2d), L(0x5b), L(0x88), L(0x3e), L(0x34), L(0x35), L(0x86), L(0x37),
- L(0x46), L(0x79), L(0xe1), L(0xaa), L(0x19), L(0xfb), L(0xaa), L(0xde), L(0x15), L(0x9), L(0xd), L(0x1a), L(0x57), L(0xff),
- L(0xb5), L(0xf), L(0xf3), L(0x2b), L(0x5a), L(0x6a), L(0x4d), L(0x19), L(0x77), L(0x71), L(0x45), L(0xdf), L(0x4f), L(0xb3),
- L(0xec), L(0xf1), L(0xeb), L(0x18), L(0x53), L(0x3e), L(0x3b), L(0x47), L(0x8), L(0x9a), L(0x73), L(0xa0), L(0x5c), L(0x8c),
- L(0x5f), L(0xeb), L(0xf), L(0x3a), L(0xc2), L(0x43), L(0x67), L(0xb4), L(0x66), L(0x67), L(0x80), L(0x58), L(0xe), L(0xc1),
- L(0xec), L(0x40), L(0xd4), L(0x22), L(0x94), L(0xca), L(0xf9), L(0xe8), L(0x92), L(0xe4), L(0x69), L(0x38), L(0xbe), L(0x67),
- L(0x64), L(0xca), L(0x50), L(0xc7), L(0x6), L(0x67), L(0x42), L(0x6e), L(0xa3), L(0xf0), L(0xb7), L(0x6c), L(0xf2), L(0xe8),
- L(0x5f), L(0xb1), L(0xaf), L(0xe7), L(0xdb), L(0xbb), L(0x77), L(0xb5), L(0xf8), L(0xcb), L(0x8), L(0xc4), L(0x75), L(0x7e),
- L(0xc0), L(0xf9), L(0x1c), L(0x7f), L(0x3c), L(0x89), L(0x2f), L(0xd2), L(0x58), L(0x3a), L(0xe2), L(0xf8), L(0x91), L(0xb6),
- L(0x7b), L(0x24), L(0x27), L(0xe9), L(0xae), L(0x84), L(0x8b), L(0xde), L(0x74), L(0xac), L(0xfd), L(0xd9), L(0xb7), L(0x69),
- L(0x2a), L(0xec), L(0x32), L(0x6f), L(0xf0), L(0x92), L(0x84), L(0xf1), L(0x40), L(0xc), L(0x8a), L(0xbc), L(0x39), L(0x6e),
- L(0x2e), L(0x73), L(0xd4), L(0x6e), L(0x8a), L(0x74), L(0x2a), L(0xdc), L(0x60), L(0x1f), L(0xa3), L(0x7), L(0xde), L(0x75),
- L(0x8b), L(0x74), L(0xc8), L(0xfe), L(0x63), L(0x75), L(0xf6), L(0x3d), L(0x63), L(0xac), L(0x33), L(0x89), L(0xc3), L(0xf0),
- L(0xf8), L(0x2d), L(0x6b), L(0xb4), L(0x9e), L(0x74), L(0x8b), L(0x5c), L(0x33), L(0xb4), L(0xca), L(0xa8), L(0xe4), L(0x99),
- L(0xb6), L(0x90), L(0xa1), L(0xef), L(0xf), L(0xd3), L(0x61), L(0xb2), L(0xc6), L(0x1a), L(0x94), L(0x7c), L(0x44), L(0x55),
- L(0xf4), L(0x45), L(0xff), L(0x9e), L(0xa5), L(0x5a), L(0xc6), L(0xa0), L(0xe8), L(0x2a), L(0xc1), L(0x8d), L(0x6f), L(0x34),
- L(0x11), L(0xb9), L(0xbe), L(0x4e), L(0xd9), L(0x87), L(0x97), L(0x73), L(0xcf), L(0x3d), L(0x23), L(0xae), L(0xd5), L(0x1a),
- L(0x5e), L(0xae), L(0x5d), L(0x6a), L(0x3), L(0xf9), L(0x22), L(0xd), L(0x10), L(0xd9), L(0x47), L(0x69), L(0x15), L(0x3f),
- L(0xee), L(0x52), L(0xa3), L(0x8), L(0xd2), L(0x3c), L(0x51), L(0xf4), L(0xf8), L(0x9d), L(0xe4), L(0x98), L(0x89), L(0xc8),
- L(0x67), L(0x39), L(0xd5), L(0x5e), L(0x35), L(0x78), L(0x27), L(0xe8), L(0x3c), L(0x80), L(0xae), L(0x79), L(0x71), L(0xd2),
- L(0x93), L(0xf4), L(0xaa), L(0x51), L(0x12), L(0x1c), L(0x4b), L(0x1b), L(0xe5), L(0x6e), L(0x15), L(0x6f), L(0xe4), L(0xbb),
- L(0x51), L(0x9b), L(0x45), L(0x9f), L(0xf9), L(0xc4), L(0x8c), L(0x2a), L(0xfb), L(0x1a), L(0xdf), L(0x55), L(0xd3), L(0x48),
- L(0x93), L(0x27), L(0x1), L(0x26), L(0xc2), L(0x6b), L(0x55), L(0x6d), L(0xa2), L(0xfb), L(0x84), L(0x8b), L(0xc9), L(0x9e),
- L(0x28), L(0xc2), L(0xef), L(0x1a), L(0x24), L(0xec), L(0x9b), L(0xae), L(0xbd), L(0x60), L(0xe9), L(0x15), L(0x35), L(0xee),
- L(0x42), L(0xa4), L(0x33), L(0x5b), L(0xfa), L(0xf), L(0xb6), L(0xf7), L(0x1), L(0xa6), L(0x2), L(0x4c), L(0xca), L(0x90),
- L(0x58), L(0x3a), L(0x96), L(0x41), L(0xe7), L(0xcb), L(0x9), L(0x8c), L(0xdb), L(0x85), L(0x4d), L(0xa8), L(0x89), L(0xf3),
- L(0xb5), L(0x8e), L(0xfd), L(0x75), L(0x5b), L(0x4f), L(0xed), L(0xde), L(0x3f), L(0xeb), L(0x38), L(0xa3), L(0xbe), L(0xb0),
- L(0x73), L(0xfc), L(0xb8), L(0x54), L(0xf7), L(0x4c), L(0x30), L(0x67), L(0x2e), L(0x38), L(0xa2), L(0x54), L(0x18), L(0xba),
- L(0x8), L(0xbf), L(0xf2), L(0x39), L(0xd5), L(0xfe), L(0xa5), L(0x41), L(0xc6), L(0x66), L(0x66), L(0xba), L(0x81), L(0xef),
- L(0x67), L(0xe4), L(0xe6), L(0x3c), L(0xc), L(0xca), L(0xa4), L(0xa), L(0x79), L(0xb3), L(0x57), L(0x8b), L(0x8a), L(0x75),
- L(0x98), L(0x18), L(0x42), L(0x2f), L(0x29), L(0xa3), L(0x82), L(0xef), L(0x9f), L(0x86), L(0x6), L(0x23), L(0xe1), L(0x75),
- L(0xfa), L(0x8), L(0xb1), L(0xde), L(0x17), L(0x4a),
- },
- },
- TestCase{
- .input = "huffman-rand-limit.input",
- .want = "huffman-rand-limit.{s}.expect",
- .want_no_input = "huffman-rand-limit.{s}.expect-noinput",
- .tokens = &[_]Token{
- L(0x61), M(1, 74), L(0xa), L(0xf8), L(0x8b), L(0x96), L(0x76), L(0x48), L(0xa), L(0x85), L(0x94), L(0x25), L(0x80),
- L(0xaf), L(0xc2), L(0xfe), L(0x8d), L(0xe8), L(0x20), L(0xeb), L(0x17), L(0x86), L(0xc9), L(0xb7), L(0xc5), L(0xde),
- L(0x6), L(0xea), L(0x7d), L(0x18), L(0x8b), L(0xe7), L(0x3e), L(0x7), L(0xda), L(0xdf), L(0xff), L(0x6c), L(0x73),
- L(0xde), L(0xcc), L(0xe7), L(0x6d), L(0x8d), L(0x4), L(0x19), L(0x49), L(0x7f), L(0x47), L(0x1f), L(0x48), L(0x15),
- L(0xb0), L(0xe8), L(0x9e), L(0xf2), L(0x31), L(0x59), L(0xde), L(0x34), L(0xb4), L(0x5b), L(0xe5), L(0xe0), L(0x9),
- L(0x11), L(0x30), L(0xc2), L(0x88), L(0x5b), L(0x7c), L(0x5d), L(0x14), L(0x13), L(0x6f), L(0x23), L(0xa9), L(0xa),
- L(0xbc), L(0x2d), L(0x23), L(0xbe), L(0xd9), L(0xed), L(0x75), L(0x4), L(0x6c), L(0x99), L(0xdf), L(0xfd), L(0x70),
- L(0x66), L(0xe6), L(0xee), L(0xd9), L(0xb1), L(0x9e), L(0x6e), L(0x83), L(0x59), L(0xd5), L(0xd4), L(0x80), L(0x59),
- L(0x98), L(0x77), L(0x89), L(0x43), L(0x38), L(0xc9), L(0xaf), L(0x30), L(0x32), L(0x9a), L(0x20), L(0x1b), L(0x46),
- L(0x3d), L(0x67), L(0x6e), L(0xd7), L(0x72), L(0x9e), L(0x4e), L(0x21), L(0x4f), L(0xc6), L(0xe0), L(0xd4), L(0x7b),
- L(0x4), L(0x8d), L(0xa5), L(0x3), L(0xf6), L(0x5), L(0x9b), L(0x6b), L(0xdc), L(0x2a), L(0x93), L(0x77), L(0x28),
- L(0xfd), L(0xb4), L(0x62), L(0xda), L(0x20), L(0xe7), L(0x1f), L(0xab), L(0x6b), L(0x51), L(0x43), L(0x39), L(0x2f),
- L(0xa0), L(0x92), L(0x1), L(0x6c), L(0x75), L(0x3e), L(0xf4), L(0x35), L(0xfd), L(0x43), L(0x2e), L(0xf7), L(0xa4),
- L(0x75), L(0xda), L(0xea), L(0x9b), L(0xa),
- },
- },
- TestCase{
- .input = "huffman-shifts.input",
- .want = "huffman-shifts.{s}.expect",
- .want_no_input = "huffman-shifts.{s}.expect-noinput",
- .tokens = &[_]Token{
- L('1'), L('0'), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258),
- M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258),
- M(2, 258), M(2, 76), L(0xd), L(0xa), L('2'), L('3'), M(2, 258), M(2, 258),
- M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 256),
- },
- },
- TestCase{
- .input = "huffman-text-shift.input",
- .want = "huffman-text-shift.{s}.expect",
- .want_no_input = "huffman-text-shift.{s}.expect-noinput",
- .tokens = &[_]Token{
- L('/'), L('/'), L('C'), L('o'), L('p'), L('y'), L('r'), L('i'),
- L('g'), L('h'), L('t'), L('2'), L('0'), L('0'), L('9'), L('T'),
- L('h'), L('G'), L('o'), L('A'), L('u'), L('t'), L('h'), L('o'),
- L('r'), L('.'), L('A'), L('l'), L('l'), M(23, 5), L('r'), L('r'),
- L('v'), L('d'), L('.'), L(0xd), L(0xa), L('/'), L('/'), L('U'),
- L('o'), L('f'), L('t'), L('h'), L('i'), L('o'), L('u'), L('r'),
- L('c'), L('c'), L('o'), L('d'), L('i'), L('g'), L('o'), L('v'),
- L('r'), L('n'), L('d'), L('b'), L('y'), L('B'), L('S'), L('D'),
- L('-'), L('t'), L('y'), L('l'), M(33, 4), L('l'), L('i'), L('c'),
- L('n'), L('t'), L('h'), L('t'), L('c'), L('n'), L('b'), L('f'),
- L('o'), L('u'), L('n'), L('d'), L('i'), L('n'), L('t'), L('h'),
- L('L'), L('I'), L('C'), L('E'), L('N'), L('S'), L('E'), L('f'),
- L('i'), L('l'), L('.'), L(0xd), L(0xa), L(0xd), L(0xa), L('p'),
- L('c'), L('k'), L('g'), L('m'), L('i'), L('n'), M(11, 4), L('i'),
- L('m'), L('p'), L('o'), L('r'), L('t'), L('"'), L('o'), L('"'),
- M(13, 4), L('f'), L('u'), L('n'), L('c'), L('m'), L('i'), L('n'),
- L('('), L(')'), L('{'), L(0xd), L(0xa), L(0x9), L('v'), L('r'),
- L('b'), L('='), L('m'), L('k'), L('('), L('['), L(']'), L('b'),
- L('y'), L('t'), L(','), L('6'), L('5'), L('5'), L('3'), L('5'),
- L(')'), L(0xd), L(0xa), L(0x9), L('f'), L(','), L('_'), L(':'),
- L('='), L('o'), L('.'), L('C'), L('r'), L('t'), L('('), L('"'),
- L('h'), L('u'), L('f'), L('f'), L('m'), L('n'), L('-'), L('n'),
- L('u'), L('l'), L('l'), L('-'), L('m'), L('x'), L('.'), L('i'),
- L('n'), L('"'), M(34, 5), L('.'), L('W'), L('r'), L('i'), L('t'),
- L('('), L('b'), L(')'), L(0xd), L(0xa), L('}'), L(0xd), L(0xa),
- L('A'), L('B'), L('C'), L('D'), L('E'), L('F'), L('G'), L('H'),
- L('I'), L('J'), L('K'), L('L'), L('M'), L('N'), L('O'), L('P'),
- L('Q'), L('R'), L('S'), L('T'), L('U'), L('V'), L('X'), L('x'),
- L('y'), L('z'), L('!'), L('"'), L('#'), L(0xc2), L(0xa4), L('%'),
- L('&'), L('/'), L('?'), L('"'),
- },
- },
- TestCase{
- .input = "huffman-text.input",
- .want = "huffman-text.{s}.expect",
- .want_no_input = "huffman-text.{s}.expect-noinput",
- .tokens = &[_]Token{
- L('/'), L('/'), L(' '), L('z'), L('i'), L('g'), L(' '), L('v'),
- L('0'), L('.'), L('1'), L('0'), L('.'), L('0'), L(0xa), L('/'),
- L('/'), L(' '), L('c'), L('r'), L('e'), L('a'), L('t'), L('e'),
- L(' '), L('a'), L(' '), L('f'), L('i'), L('l'), L('e'), M(5, 4),
- L('l'), L('e'), L('d'), L(' '), L('w'), L('i'), L('t'), L('h'),
- L(' '), L('0'), L('x'), L('0'), L('0'), L(0xa), L('c'), L('o'),
- L('n'), L('s'), L('t'), L(' '), L('s'), L('t'), L('d'), L(' '),
- L('='), L(' '), L('@'), L('i'), L('m'), L('p'), L('o'), L('r'),
- L('t'), L('('), L('"'), L('s'), L('t'), L('d'), L('"'), L(')'),
- L(';'), L(0xa), L(0xa), L('p'), L('u'), L('b'), L(' '), L('f'),
- L('n'), L(' '), L('m'), L('a'), L('i'), L('n'), L('('), L(')'),
- L(' '), L('!'), L('v'), L('o'), L('i'), L('d'), L(' '), L('{'),
- L(0xa), L(' '), L(' '), L(' '), L(' '), L('v'), L('a'), L('r'),
- L(' '), L('b'), L(' '), L('='), L(' '), L('['), L('1'), L(']'),
- L('u'), L('8'), L('{'), L('0'), L('}'), L(' '), L('*'), L('*'),
- L(' '), L('6'), L('5'), L('5'), L('3'), L('5'), L(';'), M(31, 5),
- M(86, 6), L('f'), L(' '), L('='), L(' '), L('t'), L('r'), L('y'),
- M(94, 4), L('.'), L('f'), L('s'), L('.'), L('c'), L('w'), L('d'),
- L('('), L(')'), L('.'), M(144, 6), L('F'), L('i'), L('l'), L('e'),
- L('('), M(43, 5), M(1, 4), L('"'), L('h'), L('u'), L('f'), L('f'),
- L('m'), L('a'), L('n'), L('-'), L('n'), L('u'), L('l'), L('l'),
- L('-'), L('m'), L('a'), L('x'), L('.'), L('i'), L('n'), L('"'),
- L(','), M(31, 9), L('.'), L('{'), L(' '), L('.'), L('r'), L('e'),
- L('a'), L('d'), M(79, 5), L('u'), L('e'), L(' '), L('}'), M(27, 6),
- L(')'), M(108, 6), L('d'), L('e'), L('f'), L('e'), L('r'), L(' '),
- L('f'), L('.'), L('c'), L('l'), L('o'), L('s'), L('e'), L('('),
- M(183, 4), M(22, 4), L('_'), M(124, 7), L('f'), L('.'), L('w'), L('r'),
- L('i'), L('t'), L('e'), L('A'), L('l'), L('l'), L('('), L('b'),
- L('['), L('0'), L('.'), L('.'), L(']'), L(')'), L(';'), L(0xa),
- L('}'), L(0xa),
- },
- },
- TestCase{
- .input = "huffman-zero.input",
- .want = "huffman-zero.{s}.expect",
- .want_no_input = "huffman-zero.{s}.expect-noinput",
- .tokens = &[_]Token{ L(0x30), ml, M(1, 49) },
- },
- TestCase{
- .input = "",
- .want = "",
- .want_no_input = "null-long-match.{s}.expect-noinput",
- .tokens = &[_]Token{
- L(0x0), ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
- ml, ml, ml, M(1, 8),
- },
- },
- };
-};
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-null-max.dyn.expect b/lib/std/compress/flate/testdata/block_writer/huffman-null-max.dyn.expect
deleted file mode 100644
index c08165143f2c570013c4916cbac5addfe9622a55..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 78
ZcmaEJppgLx8W#LrDZUcKq5v#l0|1+Y23i0B
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-null-max.dyn.expect-noinput b/lib/std/compress/flate/testdata/block_writer/huffman-null-max.dyn.expect-noinput
deleted file mode 100644
index c08165143f2c570013c4916cbac5addfe9622a55..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 78
ZcmaEJppgLx8W#LrDZUcKq5v#l0|1+Y23i0B
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-null-max.huff.expect b/lib/std/compress/flate/testdata/block_writer/huffman-null-max.huff.expect
deleted file mode 100644
index db422ca3983d12e71e31979d7b3dddd080dcbca7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 8204
zcmeIuK@9*P3YwAz<>b*1`HT5V8DO@0|pEjFkrxd
z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA
zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj
zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r
z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@
t0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFwg)F00961
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-null-max.wb.expect b/lib/std/compress/flate/testdata/block_writer/huffman-null-max.wb.expect
deleted file mode 100644
index c08165143f2c570013c4916cbac5addfe9622a55..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 78
ZcmaEJppgLx8W#LrDZUcKq5v#l0|1+Y23i0B
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-null-max.wb.expect-noinput b/lib/std/compress/flate/testdata/block_writer/huffman-null-max.wb.expect-noinput
deleted file mode 100644
index c08165143f2c570013c4916cbac5addfe9622a55..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 78
ZcmaEJppgLx8W#LrDZUcKq5v#l0|1+Y23i0B
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-pi.dyn.expect b/lib/std/compress/flate/testdata/block_writer/huffman-pi.dyn.expect
deleted file mode 100644
index e4396ac6fe5e34609ccb7ea0bc359e6adb48c7f4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1696
zcmV;R24DFkmtE59U+}dp3M$E(%$UAJ`ff>rsvsiW8T+$6ZwCa`!Y=s-_luo9MajP$09#>I(F*#bYgkvGSvgH9cjqJxOtZL@E-R
zxap{F9H>K0YPWsSkS2)R*aWKe{#|WqFIuv-wS}!bk75c(Z-9;7Wc4VnBBzs?752D&
zc8p>URDdmkKtvR3uWp%l!&_EmTpc=NETFYQZ$(jWT@;
zgN3|cc@&v*F@uVLa&KnX>Fd2bZUkkwfB)b_MW1tl319U*%S
zvp^|A=dI~L9VRO0%SM^tpIF);2v&
z2UTM|Eu;@^j|Ys3yuqcmNp8%xRb#N#JWo+RBgezuM69fAg{7zjhSjaxj9hCIS<|))
zTLN?jLt7gbXKG}iEUuqR-jG}(yN@N#B)wX
z?|Hml6#3}s*c0K~nJep+6gLc-%e0Zx+0e0@vrzAOGcG64J5tD?3)Gal%l@md3K`X!
zWHzzhS`E>KPF)C!q0$!IOpK<-WbmbF9QLE^nXFo~mu))PKI>??oiY
z2eq0;6HL=Tt81EVym$AC{;?VPYEHwbEH44G@EQbW;L1XcSd)b||Ff@Ei(4Sj++jOm
zBUh^KsO^kc_oqFUViJ1J^cG$3Tj{GxbaP=7I(EAlE=mRs3qthuA%e9rE-#PHFM(mQ
zu6KhDd&6Mrg?qbky>)t9e~*^0hsbjfTxSkFOE@c#rEgM-#Z9ZTpaI9jc6f=dNhXc8
znW%G1wBBCANuz}>6H}+!y>*N6gKL$sTjqM=lH+`zajbQ|_!-Asw+~_~BPZz2`j$Kc
zEhFt1TPE|&golz{9lnon*4~tBl|$aFu;^S(&T%XtkV=$yRZ5cBjJLTgxTv7rS!-y$2B``yh?Bd
zU87(35T;+y=@n~to6Yow&?UtR3gMggy9M(CYsW0orRXZXb1;cR#nNz{C5S6uiE#A#
z)e7C6h_D5sJRBg(Zy^5U!@dY0#$+}dp3M$E(%$UAJ`ff>rsvsiW8T+$6ZwCa`!Y=s-_luo9MajP$09#>I(F*#bYgkvGSvgH9cjqJxOtZL@E-R
zxap{F9H>K0YPWsSkS2)R*aWKe{#|WqFIuv-wS}!bk75c(Z-9;7Wc4VnBBzs?752D&
zc8p>URDdmkKtvR3uWp%l!&_EmTpc=NETFYQZ$(jWT@;
zgN3|cc@&v*F@uVLa&KnX>Fd2bZUkkwfB)b_MW1tl319U*%S
zvp^|A=dI~L9VRO0%SM^tpIF);2v&
z2UTM|Eu;@^j|Ys3yuqcmNp8%xRb#N#JWo+RBgezuM69fAg{7zjhSjaxj9hCIS<|))
zTLN?jLt7gbXKG}iEUuqR-jG}(yN@N#B)wX
z?|Hml6#3}s*c0K~nJep+6gLc-%e0Zx+0e0@vrzAOGcG64J5tD?3)Gal%l@md3K`X!
zWHzzhS`E>KPF)C!q0$!IOpK<-WbmbF9QLE^nXFo~mu))PKI>??oiY
z2eq0;6HL=Tt81EVym$AC{;?VPYEHwbEH44G@EQbW;L1XcSd)b||Ff@Ei(4Sj++jOm
zBUh^KsO^kc_oqFUViJ1J^cG$3Tj{GxbaP=7I(EAlE=mRs3qthuA%e9rE-#PHFM(mQ
zu6KhDd&6Mrg?qbky>)t9e~*^0hsbjfTxSkFOE@c#rEgM-#Z9ZTpaI9jc6f=dNhXc8
znW%G1wBBCANuz}>6H}+!y>*N6gKL$sTjqM=lH+`zajbQ|_!-Asw+~_~BPZz2`j$Kc
zEhFt1TPE|&golz{9lnon*4~tBl|$aFu;^S(&T%XtkV=$yRZ5cBjJLTgxTv7rS!-y$2B``yh?Bd
zU87(35T;+y=@n~to6Yow&?UtR3gMggy9M(CYsW0orRXZXb1;cR#nNz{C5S6uiE#A#
z)e7C6h_D5sJRBg(Zy^5U!@dY0#$WHH?*pN(^^{)UzR+vFm$z5VCPWYJYu^c@?DSYt
z-8UDQT!V^U$bB)7T#vx*u3cr>8Uiz?!&E~$_X|MfTLzZ%-*0cT3{sA
zq<70y?3)+V47+RWm;KF_UZSrB{g
zmdef8;|D3hF@bivQ*X8_PI1sPpD$f?o@apfMsLJqhqe|k1YMzo08jbe==SDWr>dyl
zbP)shg1$9yeHZ}|Ge>&gwccapdqSeq-ZI7R^#yqYl*Kmh`QG!AW7&KY&*GJ(U$x77
zhtAF78c+Fd_HOjLIGnc+=hME>#~}3C0p=D;~UUo6}`h~uDe&Kt8|~ZgH{F>+ga2Ta~_F64W74myu+K;Bjn~5cx>A?@3xm=
zD~x&%_crj<4^cHss+1nx$(uD-yKl03k90IoYxDsL4uhF_c$b&cCRR@G?c@dAF%EZj
z!F%)6tR6euoJ8jypvoJ+WS`#U)OSL47esd%y~SbI85?f~-OJeks!kW?4w}A9wYnXQ
z#VCGziPdN5y&!va_RSkqJ5O&AZJkzk&%0`gmtxsl+-dW8#e*OnqgyUTXk1m3t=~z
ztn{vTymfdO4o{D!cQUlYRmWwqeGkufvuO$~hNp#hD}F4UuGsSZ#)Bo
zS{7a0Tik+o`!pa(^qw3sFrES3(@Kl?cse319lmJa+t;~Qg6}kRGv`+WxC0;tQ
z^43(V;Jpqnv!~9qw9cGMg>~t?=4n;kG^^}-N4rcpck4rWS%+ByUK=%87P!zqw&Du-Yd21qtBOAtHq0U#a6U^d)e2RvTDFj
zOuTP&N14q$sn3scaC}Q(1^H&3hB$x^bpE
zIi9DuEc>E#rU!QFQ=>JWCH8{!VS8Z~&bBFYyH+xcPUe7n!#hcM`>rM^Xy4KM_)Kx5
zki2TG__7!Q#1rr7UbPQ++=Go-Jk{0nK9E~2!@fff7*J<%o;rK&Bj+7=<{m~Dy^a^n
z%aNA}FHD{H7Nia@&I#qym~R%L9*SWY%;?=1bjHV`dqII-U#lI1sQtJ(ksR|Kg?)#h
zi)))#3{+G`K%c;Mv9HIJ(>cT}_o0S-rFJqEXlZt}51yBxZ@`_t^Wt5k%6{p)%T3#2f#2i?tZ=e>*D=b76#
zQqB+Or*e^dSb>Kfz3m1?)`+o+?=5siLoiIXFnQytPJ83(cBeS`0N-vg4csZT8QjzO
zu*WEGcXPz1E
zbw-{bS(C8Srcs-HxkqX99{i*^^NQ#xeY-}))Nk>&?@b!rm^<`o)wwCefUwx8H2e;k
EAEW(DiU0rr
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-pi.input b/lib/std/compress/flate/testdata/block_writer/huffman-pi.input
deleted file mode 100644
index efaed43431adecaa32c5d843afacbef275c58db8..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/testdata/block_writer/huffman-pi.input
+++ /dev/null
@@ -1 +0,0 @@
-3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278865936153381827968230301952035301852968995773622599413891249721775283479131515574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012858361603563707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104752162056966024058038150193511253382430035587640247496473263914199272604269922796782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955321165344987202755960236480665499119881834797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548161361157352552133475741849468438523323907394143334547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786085784383827967976681454100953883786360950680064225125205117392984896084128488626945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645995813390478027590099465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203496252451749399651431429809190659250937221696461515709858387410597885959772975498930161753928468138268683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506016842739452267467678895252138522549954666727823986456596116354886230577456498035593634568174324112515076069479451096596094025228879710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821682998948722658804857564014270477555132379641451523746234364542858444795265867821051141354735739523113427166102135969536231442952484937187110145765403590279934403742007310578539062198387447808478489683321445713868751943506430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675142691239748940907186494231961567945208095146550225231603881930142093762137855956638937787083039069792077346722182562599661501421503068038447734549202605414665925201497442850732518666002132434088190710486331734649651453905796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007230558763176359421873125147120532928191826186125867321579198414848829164470609575270695722091756711672291098169091528017350671274858322287183520935396572512108357915136988209144421006751033467110314126711136990865851639831501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064204675259070915481416549859461637180
\ No newline at end of file
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-pi.wb.expect b/lib/std/compress/flate/testdata/block_writer/huffman-pi.wb.expect
deleted file mode 100644
index e4396ac6fe5e34609ccb7ea0bc359e6adb48c7f4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1696
zcmV;R24DFkmtE59U+}dp3M$E(%$UAJ`ff>rsvsiW8T+$6ZwCa`!Y=s-_luo9MajP$09#>I(F*#bYgkvGSvgH9cjqJxOtZL@E-R
zxap{F9H>K0YPWsSkS2)R*aWKe{#|WqFIuv-wS}!bk75c(Z-9;7Wc4VnBBzs?752D&
zc8p>URDdmkKtvR3uWp%l!&_EmTpc=NETFYQZ$(jWT@;
zgN3|cc@&v*F@uVLa&KnX>Fd2bZUkkwfB)b_MW1tl319U*%S
zvp^|A=dI~L9VRO0%SM^tpIF);2v&
z2UTM|Eu;@^j|Ys3yuqcmNp8%xRb#N#JWo+RBgezuM69fAg{7zjhSjaxj9hCIS<|))
zTLN?jLt7gbXKG}iEUuqR-jG}(yN@N#B)wX
z?|Hml6#3}s*c0K~nJep+6gLc-%e0Zx+0e0@vrzAOGcG64J5tD?3)Gal%l@md3K`X!
zWHzzhS`E>KPF)C!q0$!IOpK<-WbmbF9QLE^nXFo~mu))PKI>??oiY
z2eq0;6HL=Tt81EVym$AC{;?VPYEHwbEH44G@EQbW;L1XcSd)b||Ff@Ei(4Sj++jOm
zBUh^KsO^kc_oqFUViJ1J^cG$3Tj{GxbaP=7I(EAlE=mRs3qthuA%e9rE-#PHFM(mQ
zu6KhDd&6Mrg?qbky>)t9e~*^0hsbjfTxSkFOE@c#rEgM-#Z9ZTpaI9jc6f=dNhXc8
znW%G1wBBCANuz}>6H}+!y>*N6gKL$sTjqM=lH+`zajbQ|_!-Asw+~_~BPZz2`j$Kc
zEhFt1TPE|&golz{9lnon*4~tBl|$aFu;^S(&T%XtkV=$yRZ5cBjJLTgxTv7rS!-y$2B``yh?Bd
zU87(35T;+y=@n~to6Yow&?UtR3gMggy9M(CYsW0orRXZXb1;cR#nNz{C5S6uiE#A#
z)e7C6h_D5sJRBg(Zy^5U!@dY0#$+}dp3M$E(%$UAJ`ff>rsvsiW8T+$6ZwCa`!Y=s-_luo9MajP$09#>I(F*#bYgkvGSvgH9cjqJxOtZL@E-R
zxap{F9H>K0YPWsSkS2)R*aWKe{#|WqFIuv-wS}!bk75c(Z-9;7Wc4VnBBzs?752D&
zc8p>URDdmkKtvR3uWp%l!&_EmTpc=NETFYQZ$(jWT@;
zgN3|cc@&v*F@uVLa&KnX>Fd2bZUkkwfB)b_MW1tl319U*%S
zvp^|A=dI~L9VRO0%SM^tpIF);2v&
z2UTM|Eu;@^j|Ys3yuqcmNp8%xRb#N#JWo+RBgezuM69fAg{7zjhSjaxj9hCIS<|))
zTLN?jLt7gbXKG}iEUuqR-jG}(yN@N#B)wX
z?|Hml6#3}s*c0K~nJep+6gLc-%e0Zx+0e0@vrzAOGcG64J5tD?3)Gal%l@md3K`X!
zWHzzhS`E>KPF)C!q0$!IOpK<-WbmbF9QLE^nXFo~mu))PKI>??oiY
z2eq0;6HL=Tt81EVym$AC{;?VPYEHwbEH44G@EQbW;L1XcSd)b||Ff@Ei(4Sj++jOm
zBUh^KsO^kc_oqFUViJ1J^cG$3Tj{GxbaP=7I(EAlE=mRs3qthuA%e9rE-#PHFM(mQ
zu6KhDd&6Mrg?qbky>)t9e~*^0hsbjfTxSkFOE@c#rEgM-#Z9ZTpaI9jc6f=dNhXc8
znW%G1wBBCANuz}>6H}+!y>*N6gKL$sTjqM=lH+`zajbQ|_!-Asw+~_~BPZz2`j$Kc
zEhFt1TPE|&golz{9lnon*4~tBl|$aFu;^S(&T%XtkV=$yRZ5cBjJLTgxTv7rS!-y$2B``yh?Bd
zU87(35T;+y=@n~to6Yow&?UtR3gMggy9M(CYsW0orRXZXb1;cR#nNz{C5S6uiE#A#
z)e7C6h_D5sJRBg(Zy^5U!@dY0#$lcQ}x5eHD>U|iC=ROD8-~ViL-puE1
zjRYA__oQ{&>YEB=3*aLuz4zyXJp13Xu1};#Rhix|mTnwF
zOo!rp*PZhF=TqnOy;6>9pEFaaeUqI8B!YL)2W
zP7ZdtNvU6;rei#QejpQ1yJnKOE~NTM%dWXRuhSpl)r~@J@cfJn0Ny~Wi$|AEsLzhu
zri&m6gnDM>m?;94<~TB71LK+=ROn-XNSxENOU6sujQmH^hn%vbF>Y9-Bf>bg4ep_N_banGD$o@)BlG0~`IFf*!A
z7ZZY+$P{3oO)_oT873jzel8_va>@^q&Gy#Imx?o3b8wLzzbGT44Do}*$X0h~ljl$J4Xnb
zbD&&|U+WJ#!b4}YW@ms{4#Dg|)FPD1`RJ15X*j-TWXe#-24_NUqwu$E^5|c&ujkvl
zceVJ-2*h=M!1)}1Jc%#TSUTePk+ypzC+V()i{5ms{n@u^D(o_E@REe_Kn#k!Ic_d<
z)NYD&D%@ZnqX*t~i*(5TV|DgDW2`fY!|?bmYqXwpi(E6b%BbX-wveIk57S|?#u}7-
zL{;=f|DL5<#-Qjb!HsV;5xKrj*@u^N&pjiq)f!%|U1|gQA`KAPM`;y5?oy)&(mYZ0
z_?_gKiO6R;)m}AtC+IwYu6c3Nlk}=l5*$k#%8*z(mO5DYDWih#pN0k_;dS~5vECO-S0Dj5
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.dyn.expect-noinput b/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.dyn.expect-noinput
deleted file mode 100644
index 0c24742fde2487e3a454ec3364f15e541693c37c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1054
zcmV+(1mXJxzzaAU2mk=!nayIXbMy_f)7H$mL&SF;F?3`%k8@)&&%@Oe(UOiioadDG
zS>BI}35WJ&PF@*1*&LbA=aF5pFj3x*HIFRrKcto>d1~bp8)vlgPG~al`sLh_uD4>f
zwcquqQs)bz`O{dU_?0E5ZyfOj3vL$R|;Io1R(-}eKi+pE+?-hv`IeFsDFRE4SU5j~y=5(3C6?qYw^br64(dswwJMG1iwh9bz5{6%{CK{d
z?OTrws1RG0;tdgAc^^}S;a;h-Le*Jl$;@?4WVbi2?}j$(yZ8P0lo@^JyA?I@?GEt7oU6m&;AhmaN!WN2o4Ue&a8T%J8g~M#1p4zh)_hxG4z2`Ogny
za;mxRW4Md@6TRsPIrIrmbY`0*@-5uMh;C)*<4Qh|=G6i5GP){GL)z@9EkaXFMahfN
zv?c%P&)d;?j&h!ypwqm%P^YHL3jM3}%*^0B)TTYwcr0m+>#+B{By^cDULDE6Dg;&&
zO=u{r#qY9CX2q~>M2)v~oJjxXwYfA4W6UEykUq9QGg?N01rigUU44BE!qnW&8XUe)
zez=s$?Lpl~RS(YSo`<)!77bHS>Gu?tEqHX60yc4tb%nr56)BI?!K^R=U-@BSOT=w5
zsVIvXfM*tHgqR0EakjC|{oW&au|@y5MGR8cGC-Yn06%^E0PC$!Cb-Z0wr}jN>)ms9
zL;@;_wIK!J>p%w{0>eRLG6F9RY`9EcFXkV~=#m(_eoQp~r+?KjZg}QSSL~iFyscF_
z+e_{^90j}~rTuecm=4dkoD6jMc=)XI@ePwzb~aU<_(Cb{iZR=;j^CkY@49GGUfJU<
zh|_pVqS;)85%YqWL`@t%i6ZSgMZJLK5AGbA<2Z~&Y6y(OZ<17W7CzqwPW|%tkU??k
z4*_!bQ%1vsp<0>Q04?4|yQkkrm{|cY?4524U<_tm@Hqt*?a07|`vlpP7J3xS#y+
zPf2l=uqk^9aS%w&GBx^|J3nR
zNecGe6yILAWA?u!e(Cl<@PcA2?CSjWxPaGt_JWfJ*C8~T`^Pp$vI5uS*J~uVqo@>8
Yxt^P)DKm4sCRYuzNKydW#Fu~kAGX;UBLDyZ
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.huff.expect b/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.huff.expect
deleted file mode 100644
index 09dc798ee37df82176b8b7c9998c88a14207c1ad..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1005
zcmVlcQ}x5eHD>U|iC=ROD8-~ViL-puE1
zjRYA__oQ{&>YEB=3*aLuz4zyXJp13Xu1};#Rhix|mTnwF
zOo!rp*PZhF=TqnOy;6>9pEFaaeUqI8B!YL)2W
zP7ZdtNvU6;rei#QejpQ1yJnKOE~NTM%dWXRuhSpl)r~@J@cfJn0Ny~Wi$|AEsLzhu
zri&m6gnDM>m?;94<~TB71LK+=ROn-XNSxENOU6sujQmH^hn%vbF>Y9-Bf>bg4ep_N_banGD$o@)BlG0~`IFf*!A
z7ZZY+$P{3oO)_oT873jzel8_va>@^q&Gy#Imx?o3b8wLzzbGT44Do}*$X0h~ljl$J4Xnb
zbD&&|U+WJ#!b4}YW@ms{4#Dg|)FPD1`RJ15X*j-TWXe#-24_NUqwu$E^5|c&ujkvl
zceVJ-2*h=M!1)}1Jc%#TSUTePk+ypzC+V()i{5ms{n@u^D(o_E@REe_Kn#k!Ic_d<
z)NYD&D%@ZnqX*t~i*(5TV|DgDW2`fY!|?bmYqXwpi(E6b%BbX-wveIk57S|?#u}7-
zL{;=f|DL5<#-Qjb!HsV;5xKrj*@u^N&pjiq)f!%|U1|gQA`KAPM`;y5?oy)&(mYZ0
z_?_gKiO6R;)m}AtC+IwYu6c3Nlk}=l5*$k#%8*z(mO5DYDWih#pN0k_;dS~5vECO-S0Dj5
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.input b/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.input
deleted file mode 100644
index ce038ebb5bd911cd054b86c044fd26e6003225e2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1000
zcmV&K%;#;51Q|(x
zM;}NPu;`xhFDh+BMJ6ccYFsSUg>Bfi<~bp&jg-~DiA=I+_Co^FF#
z)zpAln0JXoILWUtGMXS8Mm=Y4*K(dtAy3BO)O!Str33Z_n`_)ElXocnv|`#I=O3$U
zQA0T|pppS>bw2bp{X;JIq;=Zrn+jwL;3Fx$_veE=``@#!Pozgxncgp!ZX82QhvIzM
zUrc=HkOSK=mDVB*N4QOEy(AHOADFT(|I5J=Zkm4@Lua&RXMk7^!R$cPB9zMc=#u1VIKF3O%23A!XF_hH@V9L8=wGp~=i9q?wfM^j
z#C3ka`5b>di7(PvI^y_|wtFNe>8^x}-gK<}*|%vb>@sigl7#U<42rxtZZ31wZi;j&
z++ZK02i|pybjbc=b@n}DtTTzj@c1ojw4QW}Tr;%FsN|WpkfHAn(_ym48kBrQRrE#w
zo~2sGpy(>Wjc+s&xxP->hnI8DJtMBw8eXnlY6JNq4G`H!X%#>2QlkjcJW=%co#dE_
z$Y(j#UNv|p=sbX~d2!N{^r}%397`MJZWV9jyHT4(pZUa$D*GDWRnth5CjlnHYgKKc
z`-F?ho+!fa8YJwSuDxLC6*cZcq%&Lk54QIKrUFdLkXSmFLFdZ}jN64xsEPBnj{S98
zPwn16>o}vnuyg#lRQF6UXD&FRR2aGlzw$ZN{-r_2W@fs9?`P!ZJPgXD3VE|vi;8ua
W7(y>8qk`|Bh6W?yb@~Xg-WN)Vp#LfW
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.wb.expect b/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.wb.expect
deleted file mode 100644
index 09dc798ee37df82176b8b7c9998c88a14207c1ad..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1005
zcmVlcQ}x5eHD>U|iC=ROD8-~ViL-puE1
zjRYA__oQ{&>YEB=3*aLuz4zyXJp13Xu1};#Rhix|mTnwF
zOo!rp*PZhF=TqnOy;6>9pEFaaeUqI8B!YL)2W
zP7ZdtNvU6;rei#QejpQ1yJnKOE~NTM%dWXRuhSpl)r~@J@cfJn0Ny~Wi$|AEsLzhu
zri&m6gnDM>m?;94<~TB71LK+=ROn-XNSxENOU6sujQmH^hn%vbF>Y9-Bf>bg4ep_N_banGD$o@)BlG0~`IFf*!A
z7ZZY+$P{3oO)_oT873jzel8_va>@^q&Gy#Imx?o3b8wLzzbGT44Do}*$X0h~ljl$J4Xnb
zbD&&|U+WJ#!b4}YW@ms{4#Dg|)FPD1`RJ15X*j-TWXe#-24_NUqwu$E^5|c&ujkvl
zceVJ-2*h=M!1)}1Jc%#TSUTePk+ypzC+V()i{5ms{n@u^D(o_E@REe_Kn#k!Ic_d<
z)NYD&D%@ZnqX*t~i*(5TV|DgDW2`fY!|?bmYqXwpi(E6b%BbX-wveIk57S|?#u}7-
zL{;=f|DL5<#-Qjb!HsV;5xKrj*@u^N&pjiq)f!%|U1|gQA`KAPM`;y5?oy)&(mYZ0
z_?_gKiO6R;)m}AtC+IwYu6c3Nlk}=l5*$k#%8*z(mO5DYDWih#pN0k_;dS~5vECO-S0Dj5
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.wb.expect-noinput b/lib/std/compress/flate/testdata/block_writer/huffman-rand-1k.wb.expect-noinput
deleted file mode 100644
index 0c24742fde2487e3a454ec3364f15e541693c37c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1054
zcmV+(1mXJxzzaAU2mk=!nayIXbMy_f)7H$mL&SF;F?3`%k8@)&&%@Oe(UOiioadDG
zS>BI}35WJ&PF@*1*&LbA=aF5pFj3x*HIFRrKcto>d1~bp8)vlgPG~al`sLh_uD4>f
zwcquqQs)bz`O{dU_?0E5ZyfOj3vL$R|;Io1R(-}eKi+pE+?-hv`IeFsDFRE4SU5j~y=5(3C6?qYw^br64(dswwJMG1iwh9bz5{6%{CK{d
z?OTrws1RG0;tdgAc^^}S;a;h-Le*Jl$;@?4WVbi2?}j$(yZ8P0lo@^JyA?I@?GEt7oU6m&;AhmaN!WN2o4Ue&a8T%J8g~M#1p4zh)_hxG4z2`Ogny
za;mxRW4Md@6TRsPIrIrmbY`0*@-5uMh;C)*<4Qh|=G6i5GP){GL)z@9EkaXFMahfN
zv?c%P&)d;?j&h!ypwqm%P^YHL3jM3}%*^0B)TTYwcr0m+>#+B{By^cDULDE6Dg;&&
zO=u{r#qY9CX2q~>M2)v~oJjxXwYfA4W6UEykUq9QGg?N01rigUU44BE!qnW&8XUe)
zez=s$?Lpl~RS(YSo`<)!77bHS>Gu?tEqHX60yc4tb%nr56)BI?!K^R=U-@BSOT=w5
zsVIvXfM*tHgqR0EakjC|{oW&au|@y5MGR8cGC-Yn06%^E0PC$!Cb-Z0wr}jN>)ms9
zL;@;_wIK!J>p%w{0>eRLG6F9RY`9EcFXkV~=#m(_eoQp~r+?KjZg}QSSL~iFyscF_
z+e_{^90j}~rTuecm=4dkoD6jMc=)XI@ePwzb~aU<_(Cb{iZR=;j^CkY@49GGUfJU<
zh|_pVqS;)85%YqWL`@t%i6ZSgMZJLK5AGbA<2Z~&Y6y(OZ<17W7CzqwPW|%tkU??k
z4*_!bQ%1vsp<0>Q04?4|yQkkrm{|cY?4524U<_tm@Hqt*?a07|`vlpP7J3xS#y+
zPf2l=uqk^9aS%w&GBx^|J3nR
zNecGe6yILAWA?u!e(Cl<@PcA2?CSjWxPaGt_JWfJ*C8~T`^Pp$vI5uS*J~uVqo@>8
Yxt^P)DKm4sCRYuzNKydW#Fu~kAGX;UBLDyZ
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.dyn.expect b/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.dyn.expect
deleted file mode 100644
index 2d6527934e98300d744c7558a025250f67e0f1c9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 229
zcmVoXRI~IhW&XxGJNu5-
o$p8QV
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.input b/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.input
deleted file mode 100644
index fb5b1be6198e4b2e17d70d67ea06156a06f9ae9e..0000000000000000000000000000000000000000
--- a/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.input
+++ /dev/null
@@ -1,4 +0,0 @@
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-vH
-% ɷ}>lsmIGH1Y4[ 0[|]o#
--#ulpfٱnYԀYwC8ɯ02 F=gnrN!O{k*w(b kQC9/lu>5C.u
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.wb.expect b/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.wb.expect
deleted file mode 100644
index 881e59c9ab9bb356c5f1b8f2e188818bd42dbcf0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 186
zcmV;r07d^wq#oe<(LJrqgR6ClYQy?N|9W32ycTaex&7!pwpX+&C|&*fKV2Rd8oFPOxbQ)>6c^slqt_a&vbUd`qL0Dk3ZG5`Po
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.wb.expect-noinput b/lib/std/compress/flate/testdata/block_writer/huffman-rand-limit.wb.expect-noinput
deleted file mode 100644
index 881e59c9ab9bb356c5f1b8f2e188818bd42dbcf0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 186
zcmV;r07d^wq#oe<(LJrqgR6ClYQy?N|9W32ycTaex&7!pwpX+&C|&*fKV2Rd8oFPOxbQ)>6c^slqt_a&vbUd`qL0Dk3ZG5`Po
diff --git a/lib/std/compress/flate/testdata/block_writer/huffman-rand-max.huff.expect b/lib/std/compress/flate/testdata/block_writer/huffman-rand-max.huff.expect
deleted file mode 100644
index 47d53c89c077d0e62aeaf818154f60921960de5c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 65540
zcmV(pK=8i+|Nj60_=}cyNDYOQC4jHO{*CA$>lcQ}x5eHD>U|iC=ROD8-~ViL-puE1
zjRYA__oQ{&>YEB=3*aLuz4zyXJp13Xu1};#Rhix|mTnwF
zOo!rp*PZhF=TqnOy;6>9pEFaaeUqI8B!YL)2W
zP7ZdtNvU6;rei#QejpQ1yJnKOE~NTM%dWXRuhSpl)r~@J@cfJn0Ny~Wi$|AEsLzhu
zri&m6gnDM>m?;94<~TB71LK+=ROn-XNSxENOU6sujQmH^hn%vbF>Y9-Bf>bg4ep_N_banGD$o@)BlG0~`IFf*!A
z7ZZY+$P{3oO)_oT873jzel8_va>@^q&Gy#Imx?o3b8wLzzbGT44Do}*$X0h~ljl$J4Xnb
zbD&&|U+WJ#!b4}YW@ms{4#Dg|)FPD1`RJ15X*j-TWXe#-24_NUqwu$E^5|c&ujkvl
zceVJ-2*h=M!1)}1Jc%#TSUTePk+ypzC+V()i{5ms{n@u^D(o_E@REe_Kn#k!Ic_d<
z)NYD&D%@ZnqX*t~i*(5TV|DgDW2`fY!|?bmYqXwpi(E6b%BbX-wveIk57S|?#u}7-
zL{;=f|DL5<#-Qjb!HsV;5xKrj*@u^N&pjiq)f!%|U1|gQA`KAPM`;y5?oy)&(mYZ0
z_?_gKiO6R;)m}AtC+IwYu6c3Nlk}=l5*$k#%8*z(mO5DYDWih#pN0k_;dS~5vECO-{Sz67BvqUSnW{Z4qSwk8%vpHvn(29}%TV=C
zHoW^0>~#+!vE}ML_o3Oq>J0sCA))J2%?N)^T+vPSA29CXybmFgXG{)+RryHV(XWGV
z*!JOk+FDxX8)8v|T&yQh!z%Hf9GF$fj!tc+URbObr5q!ZD2Z*V=P6(r@qLP$jo3H}n3C
zBVZ@K;Q%0MI>Yab7)`=^3;o8B9x++Y8BsAZ{pg*WYdMe`U8HPGjk((k3nQoMAQXPu
z3$E{&7ziBb6n~;Dpp%I853jGu-D|VlliNfn0-&leAM!2dUBAg^yr310r
zxw+}MT8v@Jdq@H%5db-3bw~gyz`)9(VSmo@KOADTm)UKO?D{5=*C3(C?oP*LUFOK$
z=^hys;TqN|qn)Tn%$rg!`J92y{NJ5uHr=){&GF9y
zt4*>NtPcrBh6lcR<9Bcq2Ylz1;YLk+vc0^%Mt-yh@O!qVdwFp@xL=V5P`3MlGB1>Rqq+>Y7-oT&p;d^3
zZ~ogY{%hrg`-S^_^F?a=Qd-W-TSh}pl~6?6>I>_&$_18*4jnKQ6`j<+(?9aUHBOkqNpbGxUJ;rHWy&ylQIv
zPQJl>*v?}_ixmi8Q))ysZi;J-&~)agct+P~s4Xeu%ft}mN*wyA)3+6lKZD*2`h>e7
z>k$U!NJB_ap*6|~X3`?S-3T|}yd1x5!kV75kT8>YnLKYVRk>FDqAgq0vqyl*MzZU1
z?(qL4))R@QXc#I(;*fj~*i)N*p4naH8kITw<}nAGY@4C#B!qP$yPf;Q
z>$H6_XGCq$^T=R{O$Q3Juf+>+qR4Emp?Vj=r$8y9(
z_C3Eb^-L3Ztw1AjcuXZ%kbmaSqqA0u*rn;g&jz4L6ErD^4TrB&+6x-<=nh35GIDsY6SW67&X0(&=>rcX1~=`j5`#!HU)T6@YGh4&@}1
z>?#WZ!K%|o$nMcyMDerArf}p&x~thmP6Yo>a8o*JqmcmiU%I+5%Zu<`G_1xG!-~0+J#uPW5vn>GcqZ*0Q8f{+KY`Rf6q)>Xt`nfOBxANZSabMysrUFl
zFw<{su%&7TWf4n*X-4Px99P;)Keq|12z;N~nE|1!?Qr0o!0Maz#cLk^0M!EUEq$8T4NU2R||4psE#ku+CA)8hijH@}!AGBe{R%N}KcmW|%pdiK~a07;I&TdRVupK8_s-TsVG
z=`3E9WZ$DlN|4O3>2Y6rgvF{jpYjl_OG1*xYhWHW%_q$iH*+`~V&}=$@v$p5w`Bpu
z$%wIy$i{8mZ^4+j&Fw^JMf1@sWbi)g`ceV#F0hrGbgE=`+*N9Fz1Rx7RX07CUQiwv
zVCqC?CBszzG9&|*3ij?ktQOXLgKm>FQOhOZEiUmygxTFJ*^yy2h7s6WkE%>_2Kn&<
z>qJn4woxTm6{nSivU3hw9)B(~THM^atj1Oxirb<68?{7m$CV#&u&`r?deElS|Id)Q
zD2Bd+Zh#S2pHEd)C9WqV;uE-CFLM0vVg1HB6K$+axcb+C&B!MeoY&3-b~%X%)X*I0
zIDzqueUF7Y)c)^NBTXFyKJ)W*DvR387=m6f7Tw&Lpdb=A=cp$=CkHzrUXn_fudW6`sVeW(N12g{kRTMvbZ8
zElwE`YtgJSK-0)2M0_=tivB2uE%tYPf*kt|F*>Vrt@ec0F##wSaYfnc{WLJ~w4IT~
zDj~n6k->N8s{t&nK{n))cOXwX8Se8
z&;st9)N$>lX1pZh>_10>>t
zB7yOYbE!fM
znWPz(ERaT7ridH~+BjAN_4kAwRCJD?f4zT#gVke8)H-Dn$mgBJz+$j7CpMWa1Q^@u
z6m}x0qdn>r=@Np_>4?UP8O#kr6nvn^>ioBMHS*>CJ@
zsiy!F8_Ta1NNZ(*`Mx^2A~@&iPm3+8^j?Qu5Nk!8HdpN;{)d+6
z91^{}n+G*vc*u8X#*djSf8~PMTDf<|8)bR}Q78xWM
z*(voo$SVO-yC5!G<6#@S%x2?`R0=z1^r2s(SVz8xZ{1VRS~7U}I1`rkpOui=wTpo_Hy
z+lsCl1W;t>$p}r8K}SX_C0b$S!{)krMFDt^(MaoK%Azf{q;YgF?7voq7JOU^2vF;a
zd_$j8Mka+IlZdrS&vN0Lm9$-*kPR)&k+7A8RGaF4nY{*Jp$F)M>+dD2(Q&_}&4eK5
zKSoS}HLSjtvZr$#fRVp0^bH=@T&2iSkSuSFd5wCGta}iO25r+_7Ip{egm-+~xh+JA
z079ysye}8rj(BDN5pDB#b%&Vfu%uJl$&-C>aOhvf+pFgo=vXp{kMhsEjVWjaq^Hvw
z!~vt}=2WJWZ_0rzp2*{q~`5#p1yw2-@g}|ng*z#hZuq|9U5g!
z2etp1EG_?%HU3V;7z{%-y>p%;01shJ?pxGQQ4`shW;l-pt!Wbq&qrl`Mfj+VzAvft
z_M|lC+*HWBd?#St1@>atD$>O0sp5<+*Lux;DEzY@AoWHj5mu7ssAQzfwtP~jSjGmg
z`nzLV#K7f}yu0c}`W_UkEt^3%uGCpupB*doCo6ayPwQ<;&b}2M2ROhgjChHa4|XCYBI@dnJ(3Op_Nc
zFf@@bdJ0HO5LC*jZ{FPRS2SBKKlp5dG-zRht}3SRDdVsx
z)%nHQs7OBfjF@Gc?j2U}*({(Dc|3!?{8>7mk}c(nC;a-KjTu#FHDq9d?OgObo+!A8
z)~N~+jbAM4QZQ*lW*P!?e;Vy>sD*?mCWQexu2LJiqqbb1@NVCI2{
zY)C*!&bP73^Q8g#*(diZy4WEWMmr2}2)3hkB=B^EX!M0q#(XYPR%hndGzN$ckHc?Te2B*227W<~uI7Qe
zV`YOQK>}2@XKFDIcCRb3uvkwjOP>mw>v^eH@FKPMg#J_ftK01eCbIKNJ?-R_?LLAd
z+DysSOB13^XQfaJb@MJX9s5%J^55Z;QNl08qGztF1HQWJRcWffEC4}WGkQ8{$+s`Z$oKFbb_hXQX3Y@>lv#moE1+y_
zGVz#9GfPve7rI
zEuCmlxD|r??q!eh=b2#O9w(0jWfb5+B=bjERR1dby#~!fu7rH<>kr#&g;mwk=BVyzxJVf}leE9w1ZK21BJ6#w%MCBrxUIc`TW5
zW0KWKK2`X(X)v^lT?`0{c?r-9K{8GXta!g)ew`igN8zZ6tqQS?;d5s?-n5m(!9fjp
z0idOi{nI$(efOO*z%c5R*ABCX7?FF9Ad9G`Yj05Zw2xCDSLAT-ND@2i^mYw%z*=VO2
zn`O_od)N8cp~xN~zjgtr%#*U;G|53`VydMZv7nq
zZzs`N>q*>}qR&DliI%Rr0@VrlhtFK`;?Bd_<~zvScQL;UueU<)d1u62?DToDx-K)O
z4rSj`jVW@6JU#|)oS$|kmD8ZR4vpGB635Crvl^5aHfnrwWMP1f{l%daA?I3hT~!ZZ
zDK_pYT>)Jh*TPA(o&g;Gk}}=+(9a+;wx}Mg6;(Z_e_)<7P&?fa0UIf85w=;O0XVeA
z8qQwO9+9FsaA{vrlW$#D*B-Q(oZGb^*GV45$LN#B7M9NB4N34&-dVPC`8&qkw8=Aq
z{nCiY#(wm|&MJxD;0*RPV3f8LU`l6EKD{Kz%BtU3F;X^SUO&YK-v6n_8b~|-_+dM1I-`8`)3$pBPyxE}V_+`v@Y3$^z
zOs`mDT@QG%Gcds&8LlX7rAa0kUvs=_LTGj7DuwyiJl+|-)X!&12#K&N^kU1dSqD&-
zD-82a9bPuF{=W-_+7cXR(~tQD!+7-+=%aEyHM+ZU2#c)p-qVuN(J
zGiru!dT(#^O4izY)Ye^El4^3fkn0up`g_OZkp+IDM8DJWXkNz&?%Cr^vY0X)*vv%D
zl?jNR(>?LVhnKr`Gj|FxQCMB3PgXKx&~cLNtNWi#Tfr>NaxRNNk0NdeG42l0K~=aw
z-ee5OP2$;LEqwZMA66IjL&f0WiX(Zbd&CrWN)~ulmZu
zsv>50ROtCugW9h;Z_jleB^1JdLWzt2bkEI**=brU|AUpmUIxNu%~peD+%cXIzV^j8
zR8e3A;ZItvRpCd%)r&BrqHY5StQnM4$ebxZVSdoqOJmq2=ee-ing{>~uoxQH>b%8Xrf
zC=Z>0w;uuCSrEf|*-p>;rJCZ3;{2Q+4%4O;7GDFj!`D_N5Z=Vu#3E8Sti1OjFSEdv
za~_n@>gt``-32pDWkJ!W!FwZ6uM?N|X2hZj2VV6t$9aHhD61~_Il_0wP|&-!$uy19
ze@CK9TI^U)9gkc*$8oKUdi%2mun|t5`
z5rK8qSWk^*Ljh;C;ddhy)S;9;kP73Z54b{|sw-n6mD&EBTFmY*|B>tlvx!Or%JqxH
zjFmCwf2ctEj>K0UyhKspjStW7&YJ8prmJ6RGLsb6mrwJ{qs~_2Ox%u-UiHe4D7c~;
zBP0(ns*VUlHsIE-xk`Yd9J=`JYZUXBdV~p9=XV?;yf+fFYQ+@d2QO32=G*lHsl#*>OZBR
zxRmf*iUreZ}Cp-mEKo&L5^0ijLyVj@WG-6C>-Np!JV^eiUK5z)!&&NR~``hAGhS>(Cfmr4Xghl<6$-uTZ{D&)3zaZ
zme2;#vV=M!P&WeoY)hOiux)pQ)d!`wz~v&WpNig?N8|2BDB~>q>H4bV07cDoWd#_G
z(J^^|{Tu6EApAD>eurgPZYm}{ebhQO3{dy_6|BQxbCbWJlx|KDcpojVLlvTy5|SY1
z7tah8fV~iBg#Hj1XT8KgB~L(@Qhd{IiqJq@W30UK`6AyRM4R$g|2rwQF?`50@9RQ;le@}5
zn|~bvcX4*_)K;2w=XCz+#)-x@#Bqx*B9YecQ^7P&!)0z5
zO|IW4vav+jK8GI97YIqx0~tfimf;@u(Kdw<4A?BrF9oS`b}&
z{-M6Bd?C;w4RGChoRv7C$1?@xCvDCs?pc-a)sSpR(We@@vqaWgox3!%mcO1P0I
zq8OuY?F#ym?W5oQrae&`()P*f?WG}^-8R-P!L3gttdWN&_XX#9wL
zA=9&a8#A$zQejT@+wK=S?Lk_ymt5)l6iY2?=iKIN(B$(>c@^CImaAdgI=fY`r
zji10dts2f8Z!d>sbV$u0!Zr
zx6LoK#JpE+|90p2FFtS=NH`TxOhvt7^TPVzE!xo6K6=2Y0*gwaVrwiq*VW0i7T!tT(cKf#5Oy
zG=~!1j#Rj^ZKA@vjc9^8CBKlt*xPqPQfxy6}s!9%0
z*gngWx*rjf-&hbw6`@|f$aoO^g$+e6p@Xe!oExG|Ba}`$-b(4wjXxZ^w!%N!iillu
zB^^+Xw7fFp?yzu%71|ax`fm{;A|yn35}`6^uG;v*7dUaFpE=ZK$++o^Kr>X)emCf&
zl);d*UU9+bHW0*muj3x~U~+o`MCq4bNiC+Dj{t=~n6SnVN~!Q*F|^svZrBaMH0_;#
z6`2yACNKXqGYPBSkcKH01W^rUK7z-XgHcBRzeoy}bVs^D-JdcbBCz=zx-PaMd@_Ip
zm{i*#sau!2B(h1b`^o?%vP=Vz>QC(*+P!eb3*KBVBiDk#lTMCxoDLO5R_tMf41xeA
z!2s8~jurz!v2M1TOuDEG-dTVg0<1MVp;^H+G>23^*+YE%guwE4QjxKIS=~@hET4wn
z9F_FRP*8X|jMKGWWU}hK0#%2RZnxQxC7z_h9>rAcR2SxE-gz8495r`AIxD^5RrflM
zkzP}Fr#UEDEnN1O=J4cM+ruG1TJhX%dc+JGyD7Zt!bG&7!r>;eS>SB}xJJ6JKN^M$
zA#jcwVPhU+fY-zZZWdBwTH^g-6Xon1@1SX+n#j=F!f1SdtVkirubcn}tuVfEz>2$>
z=quL9W!sh30ccwm58QA!Al+>+p}60hEh72KbB#I)Pkrm>ITnQEq$i<#R{^X!@`U!Q
z=YpbfRKqE6d3Y7oEj1JI2fKksGSX`>`%+b0Q+V5rL$KG&7KAu=VGL2=DE*(wxVAO&
zEnzM|uY0K@ODo!NrN`wgjm(I-yuTKRS%#W$zzStX6oWv{#<&7Bj=(kRBx_c@W4dI(
zjB$pEV5R)9nn(hyXNAZ)zQP+a`%ZjztNA2e&4rX82Cgk~ZtJ8XF^%%b7CHWi?m6!1
zLb50hcW|ul&IBKe~Mab{+)j^^|-tA*txF~zV=)v-nSxL}+d?M?#0acYz!eQUHl`R0@VY2q_b)^T6#(ioeQa%J21M1>m!Gsr?O0mpg^^Wi_9EYF%`0)r+SaR<2l2wK
z3k?l=$)YEwD$zzvsXu?0TPc~f3?3kSyBlQ#58VDQ#q)A}&z;G%`|Kr`eD8-#?%QO)
zIYS9=f>{!i94{EJUm2HU!SRg12zS}n)eU#o5iOwEj0hE37Hgygx3ZVojWiClrfNP#
z(jx0(TVzGj)HxM<3z>$iU*^rA*iieFNXX?}&>t5NeB!5rL>djddI)|W7zkCMn;wVQ
zMabTK1$yHp=U%g}!d;z!u_4MWQ}O9WzQ($Xc6zfv@uMRlYEJk9k}67I@M*l2=7Jys
z46Pbk>;f>Z@5}`XCS7%ymB_ny>fS#b;5@1!r`Yn>#I2_jZvVQ|=j#o)5@f*j3wS5&
z0T0Llf5aGtWUXJAk^Z7>3)v_TuIyqZ@@u8y3@a4-)`_Q||WfqQgV{eWpCD`7k
z>%EDmBK)CWjIl`fU)*}^@hA;7AYaE2zhD{Sj^1vWcNVl
zl1M{)9E9%q1$EXdk11@V<=
zTm;^{NqXPLf$`DXX)s}6kWAAB$KP1To=3OQW$lc$w35Qhy$)>HqKpS``C7VqvB*|p
z@juPms!^1Y@C?G-m2qPUf*&*fH-%hgX@g{xv`YIdCIrFCZ*VF{7>aKn9{h$Z*^&Uj
zxDvl<`Iw
zMjUdj#NwO0@RtY>oh|LRt%AEKo?YFNoN`%a6u={@iuYB6gdr{gJ8!t&E2!&<*^W=i
zb|Nq(l;@+hTu_#Ov)8t70TZ~ahrMgqbAyqm$t^Z+lJ%gVMSD9TfbZqPK63W?Q3Tsy
zl@QQRbrdm?%f-GsVz6x{;MUL!bYBp7fGeW5sb%N70_DY)Sq$7V>kbutg^f%|EMF5r
z&>!)qz#WWbFkq!=l=fNnwSnjM1GoQTM)RDBr)X&<)|>K-MGgq3F<%(%F2uZ
zwA4??Zc%6ReRUf8rr6K7sS;mB?r#=?U0w>J>i^kQCK!kTwEnxOi0(lHJW`_4Y4rpcDW}lz?RsJD*+WX6Q&-e&`Or2e?=~cNXWJ>
zp*H3|^**%5qF%6Dmbl%a;%WAa)f$>G4#&9mk53b%73}YR=~re3t6OSejAN0OAriUpsoYcVZ*X?w9iR&8Tt_Rqa
z2=q6vY0EVnPJm{05hHv4zbcXfLuFWhB4dKL28Z_09xaE2xO3d9O|6d|D?0o)vXWY-
zLoTpPsN^BbyQ{K7A+lFlt3(apL+W#66*UZ=WZ$p3}ytAS*GX{_j
z{O%huQx0;3$iqMEse8UgwsHjTGc*s1ij6q(7s(l8#3G|EiC6v-w`AhPdvc`zdgxr7
zWlN-%o0N1JsQZY>aq(bEkT*8d3;MVXXITNLVlVEqHE6qtX%8;Q4tMca9luppTa;S
z_J*H^_V?fPZyi~L#dS3m>q9POn+Cp~X1v^3I+iT8JjDPhh5U8qf?Z6S738GWu+y^G
zX)w2|oThn%Ixlf-e@s4pe^hbVrji&Cl!4kKK@9s;fP{!xhXi4V&IU~4|y=9WgWEDAcR
z|L)wfKB1Vh9lj+N{A%G52G9X_1PZy}ImsG?XiG87I_ktFwIUqzC2=OVV$9EV-{V-e
zjB!
zGlhm==4bNxLHddK2gsS~lih~FX)y6TV<%7pe*BZuAmEFS7^Bvb{}g6p3{Em?&6NJz
zPiDFDEek6kKlAX@vUp;}w7HHzlE|)<0gvq|mCM|7equWXUsEmG85C-BCdI%V`Kzal
z8srgvflKE(8>J~;9CGv(QTe7O6`P~LE_(6)+4)sV*CFj+FveSR?}VZw*~0@pB{!;v
zD|~EU8nz&CA3WYSs~l8O-v+qUk%U7cU#FWl^2PF1m>|*LQjK7jlg-(eAp6rE?dfyZ
z8ziEinWWY@}d{fV9I0ra(p0ikov&!vzyxDwm6Rg!5r;I4F
zh!l#m&3q5YmILjur1k%fr#p%}pkIy8!G>C9;1u^hAwtoRyZRM-r05NCR^Xc-D<}u`
z5G`DU{*=6?yQjB2WhcOoQ97nmW~wD6@iz)}dLm^Zsaocf09h$;q}_QPQSRPZ)-K#o
z9oDLVw-Z?KdrPph2hV_q)Da|I$4(DOf4yg(dQ4haA?{#{_^e+bmf;^L&(AES+KFH{
z?Ibdux!3(!5mwwh$*JRp^&H_!)CylUU=H7R=7i0ZPN@qdEqQHFQueys<)Pp`=VyDuEuz-{(JkSi
z7^4t1gm5Mf*&Y|HbypT*FqD*$hIUEbKy}$`beH|{kF5hzsJ_e(>#n6QA&KBKy`cLN%u`2OBmm8E23Ochyc0BTn~%KM
ziQeHABLewOoqVD*lnZ_6ON)mV4{aS*k@HI{zvF`PkmT2#!o-#!ZShzR+Afs3&Gs_W
z(~m`KhD-Ijn5hg@p&Prg6X=c1|bLwBRnys^?m;MB53a0I9K!h~J8KVshy%
zvTNQ;K};9l`eMNF$*>;~Q>D`x-KGiZd*#L+hl1z;l*!wsB3ndp#k{-+47>3fy)f$m
zU%1Fk+(oJgAnZBMC1T|A$(EH)Qfps3$Zm^9W=)`OoH%?=#ubd!X41IP(0XiM*BHOX
zNZL<=k}~=-l&L68vN>R^6iD>wyf?rfkW|=xtqoVk%f~*M&F$WY95}*?bf2hHUd0pG
z-g(-Hf9{9e3+R10+t8uwWR&64yO4yYMlllx>mNk(AjYo;3E$60LR_oU6fm#qLWX;V
zriJ~dV@9pUV(C~97Hs&hJH%I|#C-jQ!vX{~g^RKo(Qa0ZeXVj
zJ76Wgi8GR}sj0NE8jQBE-~gc55FrwB?87>WNP$YH9lPcc30-%k&jug0$oLK;vLZ>v
zi<_yNJceMKDsN#TQ3d$ntPbKIzseQ?3N7+wlIT%HMFnf>#uett(gU3T8^`9yWL0zr
ztcN4HrTFI)Fb=AJfzgs5<=&cF&^csg#W{Y@R7N-Zc*h!zeP(rgBB~FJ1lJEQ4Lw8sQfZ=Fu))6JEC)NQ8WN
zJCLER?6o9Nz1WfC!iKg?Z|c-i5et(eXx?XxurzJfaDgb}u{(9#P?Qhf!E|L$yYn1`
zS!z-aE+dmuRuD&+AcF>i>#231-&=c|M&Ab>0oR6~B`qw71;Hrk0afQ&)CZIMcQfZp
zJRQ3x9DSfsa{$kNxj+Ie{$dDjW{`uxiQyy;!{A(RXuR3sE!#aZKgx8_Sa8Nf2WrR8
zo|m5-`_PbJpdU>1c8gADk-re9HTn0*R?@v@vX#(`6wLWL^*kocYt+Km_R_8QvEK1N
zo~(>MHy#TNxTs~P$4{>+d(3LRmfNH0h$BeUWw|Y;Wlq+ugA0XJCMLHCHcg$^ipMK4
zjw!4Z_ibg{@1kTFTei{^=$rl5Z@6fZpEeXnSN(}--qqIR#w_^u18b8r3b}5u8jY3A
z5OCkga_t)?xKf(qBY?FO>_%C;5@%*3vo7+EcLL;|CVsk}Qh5n2;1ZOB$qUWmX!bz^
z6?2+wkRHMxz)$<#NfaR9erW*0GO#C3t$POg6199O$V+tV0CY{BRyw(^8DnJ|3Od+q
zy$V&@jF+mU&dTYRh>7Y+ck;&)vwEVs&CG7`b3}PGiq7}gxYA+_;2OOz#?+Wp#SWn!
z$C?`m&rCD4FZ^z%&j7`~Ofd@93*tPOUo^pA+aITLA%v-0Mn*bsWnKfcq_dL%HXZ9&
zYjR7(TD;VCQhp0+8(aSqlt{hEnT%^@ywh>*S}HLBb&$)#@N}^V6ECpAs#DQZe^B~B
zO&?4v=_TJl=cuqLF(0L`{JhH_fNpqQ70?=u>Gc!#V>cHfma9Cy*@6fSNwnEmlC?QZ
zptE|krB!I0-8=ZW3CQl`TdU2NFGxuKN6q!51_hva|42S9a|
zADr<7H#&bv^X3BD4RBXA!pEx18$xI<<5w4bmr+{umUv67xyi@O??B&k>V;QC4-XoG
zlcbHQW++WWmH7%xMoa2EW*ee;GrOm9SL6E+c8}Fr~vO6=ZI?iB+N`t
zZ~*L0Pm>zgXb5xw3DHh1F8W_-F*IzGeUC;#VpQXm9YWzx#4>sbJ5Xi;L9YeSYZbre
zCY^>XF|u0aX31G~8%2`Idov{1!aVW=?L+2w`!#fOnWOH!>^PXzyx?R#XJe}{Hpt}F
zsGSa;s&$kp+o$lb^PDVogXN17_?Jp}eo$}~t(5ULk=rP6vzh%tB@rQ4fEIE8L<9L3
z>LSo&HUXL>5Z!BKl5Za|5;XR0i0b|h`WI*kc)$0B*)mC>gU|4QTbKq6e*L09IgQC7WD_9;zi;MINrpxt>a>_@bNkWhs_g?G(&;HI
z72K-meFJp6aS146qS|2-`nh+QzFtqS7El4WO}xeKpCw)a@T~}J*khe}Gr{nEeKE)D
zTyMv6_XS@(5G*i(O7kw0t;jQf0WC1D0OT7$;I>ykx5UyuavFxH=saQ$G4T|UP1>G0xQ
z&o9O1>Jg>Hv%2ZnT&45`Ms7KMo-S_^#Xgxpjv?2-96D7z#-=jtabQnW^70N_209lS
zwOg5;4>-s6gZI8^=eGxq(tq3f_}oT|IJ8gGXE<+22_s8W5A}6Ct`ZfplF)Z-etI_c
z#A~!NI>00O?CBqrNqj2|avA+_lOb;xsVbOH=&*#4NA`tIv!%5Ig|$l$CBGgFY{9gC
zN8)I}YLCLM>q6d~4Z%I%U10wX7y8UzlJfJpDR&l>*q2IB4y{EcjYdBKEF-%RMncfX
z+uI$UKr18XtZ7a1XS$JAj3%1~%chNC5W!NfoflQmiV6NHd&7B(ZEgTFcAN+y;;@Lf
zH<%n=z{DQR73|NM#>Y+%9n$!rCI%by|9D!J~2Yknxf
z5h#t-aSok2k>xqkQIUXs+1LGD5`#}u-ZhH#_K3w`uQG@hRkLzWpJ-!w_)Tjk#drVO
zyHsl6PccW-R!p)%U%&YlAwaa#y4W^_SPc3e$cTppq0;}T^nGXiJmb}aQona`UkMM^
zv`tfpv=}JKb57&!dX#;9iYRPMfm3-?-Rb2;noxe%sf2
z*$$@b0B14|4TGw`K8*WxIkb=e{{`^MLLP?qW%VF{>jl><0U38R*^ELqaka3pk#<^_>@_vflqO*4!UFMTy&HzDFk
zNI3-zoBKT>CILYa7)Hli;3E|9)aRym#{tg&_sj
zJw@CB>kuV(Ts6|?+Qz|#Tgpn#0`gK8D~AFM(s0FABdRleV4T?InDt8o5^DnMVO=8)
zPn|b!>^~N=YLR@I8Xx{7otX_XF~Z6?xb)gU9B5a=dG+|0q_6?vU=St;%&vxDIJssU
zC^6xXCNq;QUO
z7HsU91Z>nxo+VE(irz>^*XX_ROdz5l&kyVXgnf8;`%8<UovBz`HVYDwLvy`gv{||Xqf|b(8E9_MF*^<8YAMCi|i`o-2(!j?tw0KA@XhmQ2
zx`I}q2l`VmS&6ulOPJ67
z0zM?ydL=0gY>~qhit{g(s-T4uC=-p3qxqW%b37&z&mwiKklLGA{qCOr_@5Bob#phD
zhb``W=1(jZGCl*&~l9^CGyq@
zO>-`-u@TzQ6~@$-RSzWCxmW7Fm-}0uV6U*gJ@fFI_pzO8?U{Y3K(ZYE&3{Kz7Ug__
zGn9OaL54=UtN7csQG*VpNu)~hMqR0%+F0h{Tc+`?R!P?dSL2Zj%=@P`oeX{@%n;8Q
zhdW|$TT($+^5SKJ#TYsB;I^FRZO~C+!Oe%Aj=pZtk&F5qz`gzoi7y2a#|?Gs19SVS
zi1vZ^BApmLYYtvZg#6A3{4NK5Jodu?Mlo7z`I~j?lk$2cYn2y=8{-1O&ZJ`r&}2pF
zD>|}8DH<{aI|DrO+$9~fupHanGr``>Q$^hw)l)`*S`I;gmC>or%#lEK=d|v?4Ue)p
zASTYTSOR@hqb0qVf6+x;+(`_g!qE&2KT5c>V;1uofDN5NcK+zD3D^-@Gs2uScsqJ
zybYn;2>TY#z#DmP+tV-BpT3Tb5-yOos1`Q}TP8<)rf@}s
zeMb$(V7=($JcRp7@}2af+5aBx``B8y26&pe5!P43L_Sv!r6~aC#L^JRgdI_~2{gBo
z=GN>NSNT84-Ih&J{vuEZ9q0Ln*I@`pJ1STTn>})1g6Ac%Gbh_>41`aG@%umS4YqHW
zdgS^>8)603prB8xXuFmE@1WLiej#&R$P_xR9)g
zSP%`5uM-6g@Hg&F%`esu1DEfCo={9!!+9#shF(n
zHobMRowYMGybc<@d6mG%NST297v??=vZH<33)xe;{unEwnzcCkQ3ZYKKw%a(C6L6^
z+q*?!C2aKzmmHcdDgD1(DVi!X6TS~z=EW#WpL+HMZxH$cuc_pHTY1}5Gz$a6infgX
z2Pv{%{nR5*7FCq}F=M8nWKgtRHPFsCP_h1^a-w1%+Tcj-c)qLgq2C6X83~Makw1IO
zp~=Q#Y|t!(TxUP&ts!5O!2SpmcwsUqp3;`abvh&(>g#ghq^|qK&m_jsaQ9C%!B2A7!Ss@)-s|j{jIP!L
zj`ni^h_Epkyi+K=_RexFZ2pqmZ6$PNQM^C1?_PA<5HY%A2utm&pw7#K7J9Fu9Ts
zWzLaG4U}~-1ObxhOE5$^2re{&d+XKKedq{uZ;=sqZ~)52(A2^C|jp|
zIBabt#u$C1%x^-ZqIzc;OxDABdpV^BLg&?cqE<$R3XrPupne9be;@DevI&cNe7y(=
zT}cvXH{Y=@qRAq;S)!&q@@as>I&zWhK~iX&e2
zyp9ri?%E-rz*Bme+KzocSel)qo@VfJZ}F=7uIQ{>5Km36$VoKGg7zOrwk^^4Pt|Df
z3qG^}b>A}$rC!TNu2K96!I+uPbda2r2t2&`3xwe^5n(#Ql@`1al_{I*f4e$e<&5k-
zg=3f?u7h3c+F8x?0H6YQ*}_ujdQFN4P`uYekoBT(vGti!iYHiDn}34^Vkp!z`QU?L
zSRZ>tY?r6a8)xH8S9#xz=v%Wzj+v>LPQdokQUxfYTM8i5`>>{e@os8JCrPZb3m0{DnW<^s-xqf2-v|>7C{Tjmzm^;A0C&~o8rNR*XEpwfnONI%-vFx5+v
zk5O)Z#W)S9HQmdo@$>Bs*8tT>cK5vhkb}JkRW1$SqI{b5x)U5~-MooH@#Pd4Z^_9)
z2~qFI@?c6`P*{6v+HiL$mz`VblL{_AY)c5^c@t-s$o)C_dCRHklXP
zF&b*9Cs?s@4MXsrxP+GuDd3Cr3|v
z_Iif!f>2x0*IH>wfpo_T{vXC?+=8VoM(
zd(JB}GkrH7)Kq`4eg)A7>6cb)Pparo+$NVR!M2DB?-3P;VFyZl&>)Ar6Vy;Tb)Rlw
zO2A&(N|Fvd5%??xjS=886~{X#+zVnjrsyAmbwH?uCRhQPJ#sgE{WP}{jsFgzcF
zPIFO>wz9lTXHfWjO3A0Tn{!%b?d4qH>>
zzSljbK5SQm3OUBY_=ejpW1sHv;O%M6PWo#qw?$sCPU*XVY&2aDPM+NyuI*iGv_S&0
z4z)|shL)i9m4cZ}dhzRI5!EG%Q4tq`F9_W;x=*Yc&qjQ&ho-Q8N>8uh-@(XV|E&n?
z$N1Fz3u-6#KfDtjmRe4u=GWv7d^R?XeMKbVgD(SC_{6AV0N)!hKV;7xHMpm1X_-uA
zCTRFg+rhs*4u4AO70vB
z)E=lh7@=Ic#WQ}tjV>W}1X;_u+!(ono3T~fY`8;sDj9d>x*4>*JBW|G&9!M`WV9C?
zhk;f5&u?O3C1*AooBPL;J*fd5ang$Wz8^L(d=c$J;UZQKS+YEu1;sr=
zZ2#T-tSPQ>HyF_L_P^LVzA`Ww+Erk4F}+jw$7ar7j&Vz;M&fb6O?FY%$*@dk$qQPy
z-XX~i>v@~#OC4>F@q!(Z+5@lCF2|W&y%kBQ0K${;d(m#2+fYCf+8AM$!!OP-o(oE6IDpF+!9v*_^S`rk
zS!rH-IC%}(AIbg|PL+EG1?|=6)9ECwl?C!+MP{L91qran6N-shI%$DC`hh&c{qE?w
zWf2l?RjPQn7%z}PUMByQ85a0>djcA&<}CY%8TN9B3v(4g^G{m%%#^HPFb-IG85~w_
z&jTWj8%2qZ5nZvWcXL$ma*epwU8d>o;HqVh`N?2==uKi$j8t^Ak4<|c$JH?qQxm5Q
zNP$g&hm77@x(T{RB7x4mk)%JU9Yydh%DZ@C)r0}zx>LUqF*a76)C1C@jc$Ooq_z{8
zcbCeeX#xv6y}8A~{ShR60dEi^yxUyrh$?_bW{296pi|7UfjY!Zodf=;O-?j+>15Ix
zvDG-^#+Ca#i;p3~A%jR8mP*K3>{(`jm
zf2nq`da3m5M_~We+0?k`T)7KrWpwkf6?_rp|OA~Kd
zmr%d9P?XB2osSv6OTr~o75pv>)8^&R;PiCzx+z3Nc+4DMF1Lq(+bQf1q&CP6MdIRQ
zyi*98uc`hIaleHc&jhY~%-s8{E{OgV28
z+_ic*IE#qKTbFum{21e)v<#WnuQ*R3FDfC83pvy5dpGXH-|sBU|>ZMPnpqS@ZxUB;bU(}Y7Pw)tCF4)E|yJuT$)+1-?ec<
zY;Ocwre_y@-~5GAY6k^n^JN62h`^Wed_AN!O<}S-vB#m%!FX`dY)T?
z+bnSb?G3`}VLZ+waEJk^QR>_5`CClBh5Dd
z)*y_NAEuq@*6BuWKArHMMj`ESAvfC2I=DC24I1yBIwAihl$gI7B@##8#y^=FF}=UP
z2yJuBZDXb*CKm{usUh4*p)mDzeqpefd3?iPzb$2$)-~U9-bR;8HbMl7RN4}H4WOI>
z|F)Ta!ug}=i?+qleKtn6u=Qn?wD}doK(6MiyXiH0TiamS5j~zN4g+LVjz3cYaC_`|
zlfi#Nyr0+6#KOx=*titigvi1yu^Y}Vn09}dWz1n^>~eFt5y8YcQwvOZ;4r++@Q-Uy
z01d^Mx+mrB!4~0MH3y6I=Wb^4wQ_7N2o?V)&*3*Yc|_4rT7Ob1vybR6-f;f^shdBY
z=o_u7w5f~`OW^-7xVnXoF&&?+cPOUXxk%*flV)wSJPp>#d(Dn40{iGNX~L6Nt;*-h
zNaAPTd^rkOg1H%o*&hUMT
zb4sn5wxMC+L>84HOQW3Yd2|M^PF`@~Lzkh}Yjg}P1AX6|=~~(Ofl&yY5SOd5BSR&f
zG%dj)$AM7}jB>0FBb>1q3OPF;SJR(55D#*B*Nk-UyGR1BdVc)4dQZG*%j8|JY`^~B
zct9pH??6}XrCLH-GI2#C9fFGfQKSOj^T>~2p-J1Zf-vc>6ZDwuaQ;1<$UI#6)tn-gYLZ0)A
zm8v8WEK!5TJ_~9lcOnxuz&L(*IG@aK+#;7nJ>8fXr&i&XC8?h;P}MG3YW=}-O6oDV
zYb@ctz6(E10#eO+3~Gwaj6*GzMv9KTyA0Psh=$^I5K8Mg8uCG`q(b~H$^gPmI%k63ZzXV}
z3{1Eg6|E#|+Pe&b)s5x~D}mZoefFXs*M-u!d|Rr_pz`uMLJN4N;{S&%aybs9XXKO|
zEB^_4?eYQ^yV{x{ycHOhIvc-B3>V&|JMWyld9k09oVUBq^R|IV
z@Abfm^s3=z)qC$;MZUe)Hphmq|t4q}P6{PZ(B!PhsB>k-bomCayWZPFIPG
zs&fC?K%!Ir6;bx5GLI&a2;@RbuJK!IS57Bb?+mO+ep_OCvA(39@MZZm@EPpAI6{)n
zRH@RYl^G(+irYHjeIL8|QgwrFpvw&x1}(oahEiVYm}6H3L&I<4{Qd;|yATVjT2*~A
zvgnp_nqh=DsBUN{b2Knsd6%PY;74C-E)AJgYEl&A+gpBLDK1~%3CZt%YG!w;iG4Rg
zP;fJ&0~vaMHT-r9j1P3FsaE&zKjJ1uFI-kmFRN@kH%$_{y6p+<*)HdbWcCI2O7}D|g~ii=~Ln
z{b@WlrRhdB-k$HWJu(d@l|DdBP@)3_`}8dGTj|+YW0bVq16N)~Xm*RQTuM=I&kg*y
zd~gp)D%zDsuJ&65agv9IP~W?zcrl}`fXm;bHr_umTsIf`^&$W3fsYEby5G@1k>fP5
z%q)&zV`-Q>sFMtG-*juB;t`3%`-I$G1hc-6>gs!eZvP!1VVjUlBU4GrHVM5tdn0F_
zBqO5w75-9dRtWOcyCV`*dUM|v9Eul^mECjPa_9c6X#Ta&1ga#|0Ps%
zJ-zgmw_Ii#LUGYis&5Tx)00hZpDGX{-v#SNeQ|1P_w?WFPjZ^tkNw(BDsUjl@A25(
z5O{M&KJT14q3f2LxYb2`UFtmdR29+CYIu2zx^>*|G>eeAi9~jUmGa&xLi!5WTUFu)
zRWl2V;io;?GKepx$~EgS)IG3A17baDeAF8PS=l0)p5hpg>w`APFGfmw;{!f}XVm$4
z7>g5OWqbbD@j5-Y27id5#UME5@#aKv$E_F#k-Wdy@fA8fpIEP)!m0n8(`#F8Fb7#%
zc?d@Cq7+7h^-$SfM4$)JAivvP@(6gXtN+PLPZrI_`;=U(9R&Sx5>jDd!>p9
ze?!BGvCfR_2aA|`P}YXmx!@>09NnASQXuK}kRJ9vi+*PGiER<2pE!%4Om)yE$W;+Q
zCa9$O2fp0}T^6969soz-!=b!D>W7y0&I$2vE(xeBrB`U=ts~Z$sS(mI#XX5dorX`A
z(CaqpRPGi4$F!i&$rAA^l2(0&kWV3+Y*R)gB}$ieMvOBs!X#RKOfWsKz!CJ$bYOIr
zy*gE-eCF0O>#6EP%P}H~dmVi2`zJfYMOb73JLyW=gUfSPJWyR^q=p!8{vI=U{+$jt
z<#q&6A8JH>eZ8C>HkVi(4tSYacq3HW
zIwTNgdvn`~GiR$?Hfjhg=~$p>jKjwkNbdioM_Ecj+)%4c;jEl+GcuAt;B)JCWXQ
z(n}U&P$$8EYYmU*09rb@6wxMbu6BD!A<}Rrh9tk(qLFEcN}Cl5p_4{OOlQWm+cG8l
z=py;*h@?&pn5NxtEK*f)l3^YSWJxp)6iQOk7jHT@)7(0zdcrk=8oBt6Z(|*WC7HOL
zh&=xpQa;I{|3Zmwfx;a1f(abW?f6eBR($UbUDL9Yi1xqXW;a+-J1hb+k^2~XLZ8k|
zTZWCIHh+~v(b=#Z30hef2&f=~gPmSc(V70YC;EBeF>kW;N;5yYR(#YPzf{Gh^+FDg
zJG9Ene5We=H8_WbR-R*kZX{;ke?hapxd^Ve+3*h@IBeVl?!qA-gg&vOlJwwY!kv}3
zUax=*9_nEKqz#WvGD9{XX>BCVF6>DhJhHdAM%$8u_@OI(5f^&O>Q9rO{E^@&9Jnzk
zBCf5~r%*H01LLuKV3pwu?Gwbr)GGc6z$OAtW=|S4bRLCPQCJV_~RUEH(?(7Pnz+w0FP^eQL;b-B!U)m
zRGGBZ1~^TGY-3!*9$y4?Mtok=?z!-|h3oB#Dj(yxU}?YC?i$-bSX(Yh{C8C}QG
z%N>n3Nn@116pGX*M%d|3vVAtTD!FieL)(F?B8Le$ds}w%Wm`&UVb
z)F$3bji6M#iVgmn)lHn6nQ$dyZ*Z+`REf^7Chi?89-WunGZ;<^T
zW6206?G!r0SC$QH+d-~TS1mY=o)p-%0sv49OmoE|=FxSs8}BIi0eJq2ma-9wy4IT=
zGEMNq&g`{i>U8PAE;D4XBBZ=4W^`}281o8nCo{}l1*IzMDLoZD8>im5tMjK8C<2~L
z;E1;!^92f;7If)x-C!QFSSlbK)+S6+@}P|si-A$*9gIF0Cg`FX|ZR)Tlv^vH6E1JuBY^E
z1Xr44s53pj0PMfKZK)Sw$YruWb_G!Rv<&CsC=Q&y(
zk+jxgOTbD~V*A4Ep!HgkJZQ88G%!16QV6&BLFXyGf(^
z<+vPW%TRF{Dg}{FadJdNNI^
zYkOe)Wa=E+xTZBLq0-oR)TkFA&0yfq7D3wp0Rs)F3JT
z6eOWlh!BP=;^as;5{J_X=Mf~$f1UZ*9WpC#e!?j@DJd`hO?9dzHmKDq=;{#-6cccI
zTd=yh#K?+dnT%1d{eX?DS-hyxN$Fu}L9QGHH4L900$&}&7Q>0bR!%#!Tqvr@Mkj#c
zL8y0Z9W5OKWU?qrLNP*u3V%b{m=f{BzoehZLXT>JqDz?-VUVrrh_43Nk0I%6>fptj
z1BeASWD(j5b6Q;`JPfCPR8{g*sE5Pa(f)ZsJB0`YMf)C_ho5K(f8pe51A3)AfsTRy
zzMG#TS83`i-PhEt8E4uL$K%`RPWI3V2zXLJRzs|COD6iNsrM$J+8@Tqn!t|NcRGlt
z0yNkNpIjEotsHSR+}ug{q_efV?xnseYF7vcRLJ6Q1kP@-+smom=z=!M3EKdCY%E)(IJ&PBy3|4ZV6Y3O;!v-c
zElvd+_~05#Gkg*Y%pD)`_oP}L59a7n8ZE2I5!F#?%x)Ox9IjRI=%ibt0Drm)2SR4U
zcyycS(;Lf3Z*oxHCL05rW&F^`SpZ0m@`j(J-D
zy3P=8(7s*#RIaB97lJ&qJDaSkMWR(r5pt%C0aAAF^m=WQfSF#fBLc=vZfX<;B0k~h
zPfeMKA_*!fpA>-~?!DPhx+IEqHBLFa&SgmGtxNlF-Mx-oL92=-b16V&`elaQE^*0I
zuAW0-)M$1v07#|gf?6cTKlgo;=eY{jVls}>gKQ01#90E&*&HxZP$Ga)|j_M7@
z`ro?YPJ_NIYyE9IXYbl#5}jiLo(?_Xy>~-Tf}ydhjU~!_CFOkp{OeYcY70KG7sJk_
z{rOr_D84IAH}Xvva^(pF0LT3Ih+}z3DPrh68L?Isnxf$@9XjoWBF}O;qU|Lloj0$2
zMS#%Ci}Z(p;rDcSS(ZCWp-TUs*@w5iaFmLIGPDRQ+SXvqgN4JsNHfmD`~k$_-<5dn
zpr@$<>|6M{58xbo##*XYf)~~_&J-iKYM?aY;FVt%3QWVLfgVF&V1j>DRsmvf0$kIj
zr%)6Es)I6WMVf(YE=T3{k~nMJvk_bqfXo(g!j48QGgG5YSIb#aZ>=%0$TwX;+!*x>
zQJq$iV`~rs<=?}gZ>d@zFk!j4nKM^)03JJ@HI41HhY9{kmk)ay*WYqtB
z288RHK%hboPCB6_4Jt_Sb6lf_qyRP%A-SF6nK2E>YDZ0^S;0sSo`v+IC@~!{znMS4
znhhhO6b~K{$Iq1keZp2Z@!m1^z}}&j;x>x7Uv|@=4Sr+mKkQ?PUvGmu%NkWMiRC>8*QLC2rV&{e`BNqEXU-y87C$oCRMkmX4hVB2R#q57)x`n|E~q_d)xo^T3rZuw=F^M2$z
zl^&k)f&xs+Ww+}t@fCO@Xeumpq^@?0wi?`nbr1S<2N`QVhP3UbNvN+OPC1_r&F
zIgT2~^^^sGcro?>_NCc`KbJq&BJhhk?+o`yFVgB8ywTc=6;&?KJ@ef%yzXsxv>;U0
zd^f+hm;3>Nkqxt0kSOPY9MQC-1;TdjjxPN=D$$nFr$EfcbT%pwm)^P=>bn+?J&!fx
z`Zn|hXD1-eiV4;E7W+ge|FpLjNjSi~I63yAo-^Byd)@AhKyhrD%-8}3YU|xM?`nY5
z@nOEt8EJBmDUuxzsMQ;Ib|~)0YHUglkUewF8n%x>(PJmfDp-gVHYj;4vVtQ6%`95=
zYvB0!J{aS52KSoQ
zf$b)5+F@lr!T%oyno$X{#xUZE=I{i<