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 01/76] 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 16bba08c4e..ab44726091 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 8cf899477f..afbe3fcfa8 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 085ae2c80b..fda6a098d8 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 204abdbbba..bb8fcb6fb4 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 = .{""}, 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 02/76] 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 afbe3fcfa8..bdd059ab37 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 0000000000..8726f61d30 --- /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 0000000000..6788640a80 --- /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 0000000000..fe676a5b25 --- /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 0000000000..d49a2cdcf8 --- /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 = .{""}, +} 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 03/76] Support passing null to `b.dependency()` Both null literals and optionals are supported. --- lib/std/Build.zig | 213 ++++++++++--------- test/standalone/dependency_options/build.zig | 46 +++- 2 files changed, 163 insertions(+), 96 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index e65a71e12b..e5b9e072f7 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, .{ - .name = field.name, - .value = .{ .lazy_path = v.dupeInner(allocator) }, - .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, .{ - .name = field.name, - .value = .{ .lazy_path_list = list }, - .used = false, - }) catch @panic("OOM"); - }, - []const u8 => { - user_input_options.put(field.name, .{ - .name = field.name, - .value = .{ .scalar = v }, - .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, .{ - .name = field.name, - .value = .{ .list = list }, - .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"); - }, - .@"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)), - }, - } + if (field.type == @Type(.null)) continue; + addUserInputOptionFromArg(arena, &map, field, field.type, @field(args, field.name)); } + return map; +} - return user_input_options; +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 = .{ .scalar = if (v) "true" else "false" }, + .used = false, + }) catch @panic("OOM"); + }, + .@"enum", .enum_literal => return if (maybe_value) |v| { + map.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = @tagName(v) }, + .used = false, + }) catch @panic("OOM"); + }, + .comptime_int, .int => return if (maybe_value) |v| { + map.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") }, + .used = false, + }) catch @panic("OOM"); + }, + .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") }, + .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; + }, + }, + else => {}, + }, + } + @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 8726f61d30..27ce63834d 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; } 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 04/76] 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 e5b9e072f7..1c73767009 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 09a1c8f81b..1a717f0bca 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)); 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 05/76] 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 1c73767009..21eb5196ed 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 }, 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 06/76] 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 21eb5196ed..efff88b469 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 27ce63834d..351a82ccdb 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), }); 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 07/76] 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 efff88b469..39aa0f1a4b 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 351a82ccdb..de7b710155 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), }); 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 08/76] 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 39aa0f1a4b..d6b0e68f5d 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 486947768d..2039a4d8c0 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 de7b710155..20e2db1fa2 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 fe676a5b25..c18f92f14d 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"), From c8c59d7ba5d4dbca72c5a529b080a12aa2c164b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 12:30:43 -0700 Subject: [PATCH 09/76] std.json: delete dead API --- lib/std/json.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index f81ac1cd65..c7b7dcf19f 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -69,7 +69,6 @@ pub const ArrayHashMap = @import("json/hashmap.zig").ArrayHashMap; pub const Scanner = @import("json/Scanner.zig"); pub const validate = Scanner.validate; pub const Error = Scanner.Error; -pub const reader = Scanner.reader; pub const default_buffer_size = Scanner.default_buffer_size; pub const Token = Scanner.Token; pub const TokenType = Scanner.TokenType; From 5df52ca0a28d204da0557e88c6c9fe1818bcd6af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 12:31:26 -0700 Subject: [PATCH 10/76] build runner: print newline before summary --- lib/compiler/build_runner.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 693e9b4c70..7402a4c66d 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -708,7 +708,7 @@ fn runStepNames( const total_count = success_count + failure_count + pending_count + skipped_count; ttyconf.setColor(w, .cyan) catch {}; - w.writeAll("Build Summary:") catch {}; + w.writeAll("\nBuild Summary:") catch {}; ttyconf.setColor(w, .reset) catch {}; w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; if (skipped_count > 0) w.print("; {d} skipped", .{skipped_count}) catch {}; From f2a3ac7c0534a74ee544fdf6ef9d2176a8d62389 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 12:49:14 -0700 Subject: [PATCH 11/76] std.fs.File: delete writeFileAll and friends please use File.Writer for these use cases also breaking API changes to std.fs.AtomicFile --- lib/std/Build/Step/Run.zig | 19 ++- lib/std/fs/AtomicFile.zig | 98 ++++++++-------- lib/std/fs/Dir.zig | 180 ++++++++++++----------------- lib/std/fs/File.zig | 107 ----------------- lib/std/fs/test.zig | 39 +++---- src/Builtin.zig | 4 +- src/Compilation.zig | 230 ++++++++++++++++++------------------- src/fmt.zig | 4 +- src/link/MachO.zig | 1 - src/main.zig | 4 +- 10 files changed, 272 insertions(+), 414 deletions(-) diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index e35b602e06..414f7ccff2 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -169,7 +169,7 @@ pub const Output = struct { pub fn create(owner: *std.Build, name: []const u8) *Run { const run = owner.allocator.create(Run) catch @panic("OOM"); run.* = .{ - .step = Step.init(.{ + .step = .init(.{ .id = base_id, .name = name, .owner = owner, @@ -1769,13 +1769,22 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult { child.stdin = null; }, .lazy_path => |lazy_path| { - const path = lazy_path.getPath2(b, &run.step); - const file = b.build_root.handle.openFile(path, .{}) catch |err| { + const path = lazy_path.getPath3(b, &run.step); + const file = path.root_dir.handle.openFile(path.subPathOrDot(), .{}) catch |err| { return run.step.fail("unable to open stdin file: {s}", .{@errorName(err)}); }; defer file.close(); - child.stdin.?.writeFileAll(file, .{}) catch |err| { - return run.step.fail("unable to write file to stdin: {s}", .{@errorName(err)}); + // TODO https://github.com/ziglang/zig/issues/23955 + var buffer: [1024]u8 = undefined; + var file_reader = file.reader(&buffer); + var stdin_writer = child.stdin.?.writer(&.{}); + _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) { + error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{ + path, file_reader.err.?, + }), + error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{ + stdin_writer.err.?, + }), }; child.stdin.?.close(); child.stdin = null; diff --git a/lib/std/fs/AtomicFile.zig b/lib/std/fs/AtomicFile.zig index 17a17f8993..96793aec72 100644 --- a/lib/std/fs/AtomicFile.zig +++ b/lib/std/fs/AtomicFile.zig @@ -1,6 +1,13 @@ -file: File, -// TODO either replace this with rand_buf or use []u16 on Windows -tmp_path_buf: [tmp_path_len:0]u8, +const AtomicFile = @This(); +const std = @import("../std.zig"); +const File = std.fs.File; +const Dir = std.fs.Dir; +const fs = std.fs; +const assert = std.debug.assert; +const posix = std.posix; + +file_writer: File.Writer, +random_integer: u64, dest_basename: []const u8, file_open: bool, file_exists: bool, @@ -9,35 +16,24 @@ dir: Dir, pub const InitError = File.OpenError; -pub const random_bytes_len = 12; -const tmp_path_len = fs.base64_encoder.calcSize(random_bytes_len); - /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function. pub fn init( dest_basename: []const u8, mode: File.Mode, dir: Dir, close_dir_on_deinit: bool, + write_buffer: []u8, ) InitError!AtomicFile { - var rand_buf: [random_bytes_len]u8 = undefined; - var tmp_path_buf: [tmp_path_len:0]u8 = undefined; - while (true) { - std.crypto.random.bytes(rand_buf[0..]); - const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf); - tmp_path_buf[tmp_path.len] = 0; - - const file = dir.createFile( - tmp_path, - .{ .mode = mode, .exclusive = true }, - ) catch |err| switch (err) { + const random_integer = std.crypto.random.int(u64); + const tmp_sub_path = std.fmt.hex(random_integer); + const file = dir.createFile(&tmp_sub_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) { error.PathAlreadyExists => continue, else => |e| return e, }; - - return AtomicFile{ - .file = file, - .tmp_path_buf = tmp_path_buf, + return .{ + .file_writer = file.writer(write_buffer), + .random_integer = random_integer, .dest_basename = dest_basename, .file_open = true, .file_exists = true, @@ -48,41 +44,51 @@ pub fn init( } /// Always call deinit, even after a successful finish(). -pub fn deinit(self: *AtomicFile) void { - if (self.file_open) { - self.file.close(); - self.file_open = false; +pub fn deinit(af: *AtomicFile) void { + if (af.file_open) { + af.file_writer.file.close(); + af.file_open = false; } - if (self.file_exists) { - self.dir.deleteFile(&self.tmp_path_buf) catch {}; - self.file_exists = false; + if (af.file_exists) { + const tmp_sub_path = std.fmt.hex(af.random_integer); + af.dir.deleteFile(&tmp_sub_path) catch {}; + af.file_exists = false; } - if (self.close_dir_on_deinit) { - self.dir.close(); + if (af.close_dir_on_deinit) { + af.dir.close(); } - self.* = undefined; + af.* = undefined; } -pub const FinishError = posix.RenameError; +pub const FlushError = File.WriteError; + +pub fn flush(af: *AtomicFile) FlushError!void { + af.file_writer.interface.flush() catch |err| switch (err) { + error.WriteFailed => return af.file_writer.err.?, + }; +} + +pub const RenameIntoPlaceError = posix.RenameError; /// On Windows, this function introduces a period of time where some file /// system operations on the destination file will result in /// `error.AccessDenied`, including rename operations (such as the one used in /// this function). -pub fn finish(self: *AtomicFile) FinishError!void { - assert(self.file_exists); - if (self.file_open) { - self.file.close(); - self.file_open = false; +pub fn renameIntoPlace(af: *AtomicFile) RenameIntoPlaceError!void { + assert(af.file_exists); + if (af.file_open) { + af.file_writer.file.close(); + af.file_open = false; } - try posix.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename); - self.file_exists = false; + const tmp_sub_path = std.fmt.hex(af.random_integer); + try posix.renameat(af.dir.fd, &tmp_sub_path, af.dir.fd, af.dest_basename); + af.file_exists = false; } -const AtomicFile = @This(); -const std = @import("../std.zig"); -const File = std.fs.File; -const Dir = std.fs.Dir; -const fs = std.fs; -const assert = std.debug.assert; -const posix = std.posix; +pub const FinishError = FlushError || RenameIntoPlaceError; + +/// Combination of `flush` followed by `renameIntoPlace`. +pub fn finish(af: *AtomicFile) FinishError!void { + try af.flush(); + try af.renameIntoPlace(); +} diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 27d97a00cb..16418d216f 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -1,3 +1,20 @@ +const Dir = @This(); +const builtin = @import("builtin"); +const std = @import("../std.zig"); +const File = std.fs.File; +const AtomicFile = std.fs.AtomicFile; +const base64_encoder = fs.base64_encoder; +const posix = std.posix; +const mem = std.mem; +const path = fs.path; +const fs = std.fs; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const linux = std.os.linux; +const windows = std.os.windows; +const native_os = builtin.os.tag; +const have_flock = @TypeOf(posix.system.flock) != void; + fd: Handle, pub const Handle = posix.fd_t; @@ -1862,9 +1879,10 @@ pub fn symLinkW( /// Same as `symLink`, except tries to create the symbolic link until it /// succeeds or encounters an error other than `error.PathAlreadyExists`. -/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). -/// On WASI, both paths should be encoded as valid UTF-8. -/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. +/// +/// * On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// * On WASI, both paths should be encoded as valid UTF-8. +/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding. pub fn atomicSymLink( dir: Dir, target_path: []const u8, @@ -1880,9 +1898,8 @@ pub fn atomicSymLink( const dirname = path.dirname(sym_link_path) orelse "."; - var rand_buf: [AtomicFile.random_bytes_len]u8 = undefined; - - const temp_path_len = dirname.len + 1 + base64_encoder.calcSize(rand_buf.len); + const rand_len = @sizeOf(u64) * 2; + const temp_path_len = dirname.len + 1 + rand_len; var temp_path_buf: [fs.max_path_bytes]u8 = undefined; if (temp_path_len > temp_path_buf.len) return error.NameTooLong; @@ -1892,8 +1909,8 @@ pub fn atomicSymLink( const temp_path = temp_path_buf[0..temp_path_len]; while (true) { - crypto.random.bytes(rand_buf[0..]); - _ = base64_encoder.encode(temp_path[dirname.len + 1 ..], rand_buf[0..]); + const random_integer = std.crypto.random.int(u64); + temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer); if (dir.symLink(target_path, temp_path, flags)) { return dir.rename(temp_path, sym_link_path); @@ -2552,25 +2569,42 @@ pub fn updateFile( try dest_dir.makePath(dirname); } - var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode }); + var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available. + var atomic_file = try dest_dir.atomicFile(dest_path, .{ + .mode = actual_mode, + .write_buffer = &buffer, + }); defer atomic_file.deinit(); - try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size }); - try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime); + var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size); + const dest_writer = &atomic_file.file_writer.interface; + + _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) { + error.ReadFailed => return src_reader.err.?, + error.WriteFailed => return atomic_file.file_writer.err.?, + }; + try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); try atomic_file.finish(); - return PrevStatus.stale; + return .stale; } pub const CopyFileError = File.OpenError || File.StatError || - AtomicFile.InitError || CopyFileRawError || AtomicFile.FinishError; + AtomicFile.InitError || AtomicFile.FinishError || + File.ReadError || File.WriteError; -/// Guaranteed to be atomic. -/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, -/// there is a possibility of power loss or application termination leaving temporary files present -/// in the same directory as dest_path. -/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). -/// On WASI, both paths should be encoded as valid UTF-8. -/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. +/// Atomically creates a new file at `dest_path` within `dest_dir` with the +/// same contents as `source_path` within `source_dir`, overwriting any already +/// existing file. +/// +/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and +/// readily available, there is a possibility of power loss or application +/// termination leaving temporary files present in the same directory as +/// dest_path. +/// +/// On Windows, both paths should be encoded as +/// [WTF-8](https://simonsapin.github.io/wtf-8/). On WASI, both paths should be +/// encoded as valid UTF-8. On other platforms, both paths are an opaque +/// sequence of bytes with no particular encoding. pub fn copyFile( source_dir: Dir, source_path: []const u8, @@ -2578,79 +2612,34 @@ pub fn copyFile( dest_path: []const u8, options: CopyFileOptions, ) CopyFileError!void { - var in_file = try source_dir.openFile(source_path, .{}); - defer in_file.close(); + var file_reader: File.Reader = .init(try source_dir.openFile(source_path, .{}), &.{}); + defer file_reader.file.close(); - var size: ?u64 = null; const mode = options.override_mode orelse blk: { - const st = try in_file.stat(); - size = st.size; + const st = try file_reader.file.stat(); + file_reader.size = st.size; break :blk st.mode; }; - var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode }); + var buffer: [1024]u8 = undefined; // Used only when direct fd-to-fd is not available. + var atomic_file = try dest_dir.atomicFile(dest_path, .{ + .mode = mode, + .write_buffer = &buffer, + }); defer atomic_file.deinit(); - try copy_file(in_file.handle, atomic_file.file.handle, size); + _ = atomic_file.file_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) { + error.ReadFailed => return file_reader.err.?, + error.WriteFailed => return atomic_file.file_writer.err.?, + }; + try atomic_file.finish(); } -const CopyFileRawError = error{SystemResources} || posix.CopyFileRangeError || posix.SendFileError; - -// Transfer all the data between two file descriptors in the most efficient way. -// The copy starts at offset 0, the initial offsets are preserved. -// No metadata is transferred over. -fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRawError!void { - if (builtin.target.os.tag.isDarwin()) { - const rc = posix.system.fcopyfile(fd_in, fd_out, null, .{ .DATA = true }); - switch (posix.errno(rc)) { - .SUCCESS => return, - .INVAL => unreachable, - .NOMEM => return error.SystemResources, - // The source file is not a directory, symbolic link, or regular file. - // Try with the fallback path before giving up. - .OPNOTSUPP => {}, - else => |err| return posix.unexpectedErrno(err), - } - } - - if (native_os == .linux) { - // Try copy_file_range first as that works at the FS level and is the - // most efficient method (if available). - var offset: u64 = 0; - cfr_loop: while (true) { - // The kernel checks the u64 value `offset+count` for overflow, use - // a 32 bit value so that the syscall won't return EINVAL except for - // impossibly large files (> 2^64-1 - 2^32-1). - const amt = try posix.copy_file_range(fd_in, offset, fd_out, offset, std.math.maxInt(u32), 0); - // Terminate as soon as we have copied size bytes or no bytes - if (maybe_size) |s| { - if (s == amt) break :cfr_loop; - } - if (amt == 0) break :cfr_loop; - offset += amt; - } - return; - } - - // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the - // fallback code will copy the contents chunk by chunk. - const empty_iovec = [0]posix.iovec_const{}; - var offset: u64 = 0; - sendfile_loop: while (true) { - const amt = try posix.sendfile(fd_out, fd_in, offset, 0, &empty_iovec, &empty_iovec, 0); - // Terminate as soon as we have copied size bytes or no bytes - if (maybe_size) |s| { - if (s == amt) break :sendfile_loop; - } - if (amt == 0) break :sendfile_loop; - offset += amt; - } -} - pub const AtomicFileOptions = struct { mode: File.Mode = File.default_mode, make_path: bool = false, + write_buffer: []u8, }; /// Directly access the `.file` field, and then call `AtomicFile.finish` to @@ -2668,9 +2657,9 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) else try self.openDir(dirname, .{}); - return AtomicFile.init(fs.path.basename(dest_path), options.mode, dir, true); + return .init(fs.path.basename(dest_path), options.mode, dir, true, options.write_buffer); } else { - return AtomicFile.init(dest_path, options.mode, self, false); + return .init(dest_path, options.mode, self, false, options.write_buffer); } } @@ -2768,30 +2757,3 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v const file: File = .{ .handle = self.fd }; try file.setPermissions(permissions); } - -const Metadata = File.Metadata; -pub const MetadataError = File.MetadataError; - -/// Returns a `Metadata` struct, representing the permissions on the directory -pub fn metadata(self: Dir) MetadataError!Metadata { - const file: File = .{ .handle = self.fd }; - return try file.metadata(); -} - -const Dir = @This(); -const builtin = @import("builtin"); -const std = @import("../std.zig"); -const File = std.fs.File; -const AtomicFile = std.fs.AtomicFile; -const base64_encoder = fs.base64_encoder; -const crypto = std.crypto; -const posix = std.posix; -const mem = std.mem; -const path = fs.path; -const fs = std.fs; -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const linux = std.os.linux; -const windows = std.os.windows; -const native_os = builtin.os.tag; -const have_flock = @TypeOf(posix.system.flock) != void; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 5b7e0aa570..50f2a30876 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1089,113 +1089,6 @@ pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u return total_bytes_copied; } -/// Deprecated in favor of `Writer`. -pub const WriteFileOptions = struct { - in_offset: u64 = 0, - in_len: ?u64 = null, - headers_and_trailers: []posix.iovec_const = &[0]posix.iovec_const{}, - header_count: usize = 0, -}; - -/// Deprecated in favor of `Writer`. -pub const WriteFileError = ReadError || error{EndOfStream} || WriteError; - -/// Deprecated in favor of `Writer`. -pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void { - return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) { - error.Unseekable, - error.FastOpenAlreadyInProgress, - error.MessageTooBig, - error.FileDescriptorNotASocket, - error.NetworkUnreachable, - error.NetworkSubsystemFailed, - error.ConnectionRefused, - => return self.writeFileAllUnseekable(in_file, args), - else => |e| return e, - }; -} - -/// Deprecated in favor of `Writer`. -pub fn writeFileAllUnseekable(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void { - const headers = args.headers_and_trailers[0..args.header_count]; - const trailers = args.headers_and_trailers[args.header_count..]; - try self.writevAll(headers); - try in_file.deprecatedReader().skipBytes(args.in_offset, .{ .buf_size = 4096 }); - var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); - if (args.in_len) |len| { - var stream = std.io.limitedReader(in_file.deprecatedReader(), len); - try fifo.pump(stream.reader(), self.deprecatedWriter()); - } else { - try fifo.pump(in_file.deprecatedReader(), self.deprecatedWriter()); - } - try self.writevAll(trailers); -} - -/// Deprecated in favor of `Writer`. -fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix.SendFileError!void { - const count = blk: { - if (args.in_len) |l| { - if (l == 0) { - return self.writevAll(args.headers_and_trailers); - } else { - break :blk l; - } - } else { - break :blk 0; - } - }; - const headers = args.headers_and_trailers[0..args.header_count]; - const trailers = args.headers_and_trailers[args.header_count..]; - const zero_iovec = &[0]posix.iovec_const{}; - // When reading the whole file, we cannot put the trailers in the sendfile() syscall, - // because we have no way to determine whether a partial write is past the end of the file or not. - const trls = if (count == 0) zero_iovec else trailers; - const offset = args.in_offset; - const out_fd = self.handle; - const in_fd = in_file.handle; - const flags = 0; - var amt: usize = 0; - hdrs: { - var i: usize = 0; - while (i < headers.len) { - amt = try posix.sendfile(out_fd, in_fd, offset, count, headers[i..], trls, flags); - while (amt >= headers[i].len) { - amt -= headers[i].len; - i += 1; - if (i >= headers.len) break :hdrs; - } - headers[i].base += amt; - headers[i].len -= amt; - } - } - if (count == 0) { - var off: u64 = amt; - while (true) { - amt = try posix.sendfile(out_fd, in_fd, offset + off, 0, zero_iovec, zero_iovec, flags); - if (amt == 0) break; - off += amt; - } - } else { - var off: u64 = amt; - while (off < count) { - amt = try posix.sendfile(out_fd, in_fd, offset + off, count - off, zero_iovec, trailers, flags); - off += amt; - } - amt = @as(usize, @intCast(off - count)); - } - var i: usize = 0; - while (i < trailers.len) { - while (amt >= trailers[i].len) { - amt -= trailers[i].len; - i += 1; - if (i >= trailers.len) return; - } - trailers[i].base += amt; - trailers[i].len -= amt; - amt = try posix.writev(self.handle, trailers[i..]); - } -} - /// Deprecated in favor of `Reader`. pub const DeprecatedReader = io.GenericReader(File, ReadError, read); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 50cbccf270..9fe2551738 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1499,32 +1499,18 @@ test "sendfile" { const header2 = "second header\n"; const trailer1 = "trailer1\n"; const trailer2 = "second trailer\n"; - var hdtr = [_]posix.iovec_const{ - .{ - .base = header1, - .len = header1.len, - }, - .{ - .base = header2, - .len = header2.len, - }, - .{ - .base = trailer1, - .len = trailer1.len, - }, - .{ - .base = trailer2, - .len = trailer2.len, - }, - }; + var headers: [2][]const u8 = .{ header1, header2 }; + var trailers: [2][]const u8 = .{ trailer1, trailer2 }; var written_buf: [100]u8 = undefined; - try dest_file.writeFileAll(src_file, .{ - .in_offset = 1, - .in_len = 10, - .headers_and_trailers = &hdtr, - .header_count = 2, - }); + var file_reader = src_file.reader(&.{}); + var fallback_buffer: [50]u8 = undefined; + var file_writer = dest_file.writer(&fallback_buffer); + try file_writer.interface.writeVecAll(&headers); + try file_reader.seekTo(1); + try testing.expectEqual(10, try file_writer.interface.sendFileAll(&file_reader, .limited(10))); + try file_writer.interface.writeVecAll(&trailers); + try file_writer.interface.flush(); const amt = try dest_file.preadAll(&written_buf, 0); try testing.expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]); } @@ -1595,9 +1581,10 @@ test "AtomicFile" { ; { - var af = try ctx.dir.atomicFile(test_out_file, .{}); + var buffer: [100]u8 = undefined; + var af = try ctx.dir.atomicFile(test_out_file, .{ .write_buffer = &buffer }); defer af.deinit(); - try af.file.writeAll(test_content); + try af.file_writer.interface.writeAll(test_content); try af.finish(); } const content = try ctx.dir.readFileAlloc(allocator, test_out_file, 9999); diff --git a/src/Builtin.zig b/src/Builtin.zig index b2cb603f53..b4e05a6089 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -342,9 +342,9 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void { } // `make_path` matters because the dir hasn't actually been created yet. - var af = try root_dir.atomicFile(sub_path, .{ .make_path = true }); + var af = try root_dir.atomicFile(sub_path, .{ .make_path = true, .write_buffer = &.{} }); defer af.deinit(); - try af.file.writeAll(file.source.?); + try af.file_writer.interface.writeAll(file.source.?); af.finish() catch |err| switch (err) { error.AccessDenied => switch (builtin.os.tag) { .windows => { diff --git a/src/Compilation.zig b/src/Compilation.zig index b5597017c4..649288dab2 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3382,7 +3382,7 @@ pub fn saveState(comp: *Compilation) !void { const gpa = comp.gpa; - var bufs = std.ArrayList(std.posix.iovec_const).init(gpa); + var bufs = std.ArrayList([]const u8).init(gpa); defer bufs.deinit(); var pt_headers = std.ArrayList(Header.PerThread).init(gpa); @@ -3421,50 +3421,50 @@ pub fn saveState(comp: *Compilation) !void { try bufs.ensureTotalCapacityPrecise(14 + 8 * pt_headers.items.len); addBuf(&bufs, mem.asBytes(&header)); - addBuf(&bufs, mem.sliceAsBytes(pt_headers.items)); + addBuf(&bufs, @ptrCast(pt_headers.items)); - addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.values())); + addBuf(&bufs, @ptrCast(ip.src_hash_deps.keys())); + addBuf(&bufs, @ptrCast(ip.src_hash_deps.values())); + addBuf(&bufs, @ptrCast(ip.nav_val_deps.keys())); + addBuf(&bufs, @ptrCast(ip.nav_val_deps.values())); + addBuf(&bufs, @ptrCast(ip.nav_ty_deps.keys())); + addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values())); + addBuf(&bufs, @ptrCast(ip.interned_deps.keys())); + addBuf(&bufs, @ptrCast(ip.interned_deps.values())); + addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys())); + addBuf(&bufs, @ptrCast(ip.zon_file_deps.values())); + addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys())); + addBuf(&bufs, @ptrCast(ip.embed_file_deps.values())); + addBuf(&bufs, @ptrCast(ip.namespace_deps.keys())); + addBuf(&bufs, @ptrCast(ip.namespace_deps.values())); + addBuf(&bufs, @ptrCast(ip.namespace_name_deps.keys())); + addBuf(&bufs, @ptrCast(ip.namespace_name_deps.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.first_dependency.keys())); - addBuf(&bufs, mem.sliceAsBytes(ip.first_dependency.values())); - addBuf(&bufs, mem.sliceAsBytes(ip.dep_entries.items)); - addBuf(&bufs, mem.sliceAsBytes(ip.free_dep_entries.items)); + addBuf(&bufs, @ptrCast(ip.first_dependency.keys())); + addBuf(&bufs, @ptrCast(ip.first_dependency.values())); + addBuf(&bufs, @ptrCast(ip.dep_entries.items)); + addBuf(&bufs, @ptrCast(ip.free_dep_entries.items)); for (ip.locals, pt_headers.items) |*local, pt_header| { if (pt_header.intern_pool.limbs_len > 0) { - addBuf(&bufs, mem.sliceAsBytes(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len])); + addBuf(&bufs, @ptrCast(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len])); } if (pt_header.intern_pool.extra_len > 0) { - addBuf(&bufs, mem.sliceAsBytes(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len])); + addBuf(&bufs, @ptrCast(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len])); } if (pt_header.intern_pool.items_len > 0) { - addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len])); + addBuf(&bufs, @ptrCast(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len])); + addBuf(&bufs, @ptrCast(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len])); } if (pt_header.intern_pool.string_bytes_len > 0) { addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]); } if (pt_header.intern_pool.tracked_insts_len > 0) { - addBuf(&bufs, mem.sliceAsBytes(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len])); + addBuf(&bufs, @ptrCast(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len])); } if (pt_header.intern_pool.files_len > 0) { - addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len])); + addBuf(&bufs, @ptrCast(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len])); + addBuf(&bufs, @ptrCast(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len])); } } @@ -3482,95 +3482,95 @@ pub fn saveState(comp: *Compilation) !void { try bufs.ensureUnusedCapacity(85); addBuf(&bufs, wasm.string_bytes.items); // TODO make it well-defined memory layout - //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.func_types.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_functions.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_globals.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_tables.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_memories.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.tag))); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.offset))); + //addBuf(&bufs, @ptrCast(wasm.objects.items)); + addBuf(&bufs, @ptrCast(wasm.func_types.keys())); + addBuf(&bufs, @ptrCast(wasm.object_function_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.object_function_imports.values())); + addBuf(&bufs, @ptrCast(wasm.object_functions.items)); + addBuf(&bufs, @ptrCast(wasm.object_global_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.object_global_imports.values())); + addBuf(&bufs, @ptrCast(wasm.object_globals.items)); + addBuf(&bufs, @ptrCast(wasm.object_table_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.object_table_imports.values())); + addBuf(&bufs, @ptrCast(wasm.object_tables.items)); + addBuf(&bufs, @ptrCast(wasm.object_memory_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.object_memory_imports.values())); + addBuf(&bufs, @ptrCast(wasm.object_memories.items)); + addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.tag))); + addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.offset))); // TODO handle the union safety field - //addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.pointee))); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.addend))); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_init_funcs.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_segments.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_datas.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.values())); + //addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.pointee))); + addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.addend))); + addBuf(&bufs, @ptrCast(wasm.object_init_funcs.items)); + addBuf(&bufs, @ptrCast(wasm.object_data_segments.items)); + addBuf(&bufs, @ptrCast(wasm.object_datas.items)); + addBuf(&bufs, @ptrCast(wasm.object_data_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.object_data_imports.values())); + addBuf(&bufs, @ptrCast(wasm.object_custom_segments.keys())); + addBuf(&bufs, @ptrCast(wasm.object_custom_segments.values())); // TODO make it well-defined memory layout - // addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdats.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.kind))); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.index))); - addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.tag))); - addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.offset))); + // addBuf(&bufs, @ptrCast(wasm.object_comdats.items)); + addBuf(&bufs, @ptrCast(wasm.object_relocations_table.keys())); + addBuf(&bufs, @ptrCast(wasm.object_relocations_table.values())); + addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.kind))); + addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.index))); + addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.tag))); + addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.offset))); // TODO handle the union safety field - //addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.pointee))); - addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.addend))); - addBuf(&bufs, mem.sliceAsBytes(wasm.uav_fixups.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.nav_fixups.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.func_table_fixups.items)); + //addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.pointee))); + addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.addend))); + addBuf(&bufs, @ptrCast(wasm.uav_fixups.items)); + addBuf(&bufs, @ptrCast(wasm.nav_fixups.items)); + addBuf(&bufs, @ptrCast(wasm.func_table_fixups.items)); if (is_obj) { - addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.values())); + addBuf(&bufs, @ptrCast(wasm.navs_obj.keys())); + addBuf(&bufs, @ptrCast(wasm.navs_obj.values())); + addBuf(&bufs, @ptrCast(wasm.uavs_obj.keys())); + addBuf(&bufs, @ptrCast(wasm.uavs_obj.values())); } else { - addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.values())); + addBuf(&bufs, @ptrCast(wasm.navs_exe.keys())); + addBuf(&bufs, @ptrCast(wasm.navs_exe.values())); + addBuf(&bufs, @ptrCast(wasm.uavs_exe.keys())); + addBuf(&bufs, @ptrCast(wasm.uavs_exe.values())); } - addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.keys())); + addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.keys())); + addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.values())); + addBuf(&bufs, @ptrCast(wasm.zcu_funcs.keys())); // TODO handle the union safety field - // addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.data_segments.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.globals.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.tables.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.values())); - addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_indirect_function_set.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_import_set.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_set.keys())); - addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.tag))); + // addBuf(&bufs, @ptrCast(wasm.zcu_funcs.values())); + addBuf(&bufs, @ptrCast(wasm.nav_exports.keys())); + addBuf(&bufs, @ptrCast(wasm.nav_exports.values())); + addBuf(&bufs, @ptrCast(wasm.uav_exports.keys())); + addBuf(&bufs, @ptrCast(wasm.uav_exports.values())); + addBuf(&bufs, @ptrCast(wasm.imports.keys())); + addBuf(&bufs, @ptrCast(wasm.missing_exports.keys())); + addBuf(&bufs, @ptrCast(wasm.function_exports.keys())); + addBuf(&bufs, @ptrCast(wasm.function_exports.values())); + addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.keys())); + addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.values())); + addBuf(&bufs, @ptrCast(wasm.global_exports.items)); + addBuf(&bufs, @ptrCast(wasm.functions.keys())); + addBuf(&bufs, @ptrCast(wasm.function_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.function_imports.values())); + addBuf(&bufs, @ptrCast(wasm.data_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.data_imports.values())); + addBuf(&bufs, @ptrCast(wasm.data_segments.keys())); + addBuf(&bufs, @ptrCast(wasm.globals.keys())); + addBuf(&bufs, @ptrCast(wasm.global_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.global_imports.values())); + addBuf(&bufs, @ptrCast(wasm.tables.keys())); + addBuf(&bufs, @ptrCast(wasm.table_imports.keys())); + addBuf(&bufs, @ptrCast(wasm.table_imports.values())); + addBuf(&bufs, @ptrCast(wasm.zcu_indirect_function_set.keys())); + addBuf(&bufs, @ptrCast(wasm.object_indirect_function_import_set.keys())); + addBuf(&bufs, @ptrCast(wasm.object_indirect_function_set.keys())); + addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.tag))); // TODO handle the union safety field - //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data))); - addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.mir_locals.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items)); - addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items)); + //addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.data))); + addBuf(&bufs, @ptrCast(wasm.mir_extra.items)); + addBuf(&bufs, @ptrCast(wasm.mir_locals.items)); + addBuf(&bufs, @ptrCast(wasm.tag_name_bytes.items)); + addBuf(&bufs, @ptrCast(wasm.tag_name_offs.items)); // TODO add as header fields // entry_resolution: FunctionImport.Resolution @@ -3596,16 +3596,16 @@ pub fn saveState(comp: *Compilation) !void { // Using an atomic file prevents a crash or power failure from corrupting // the previous incremental compilation state. - var af = try lf.emit.root_dir.handle.atomicFile(basename, .{}); + var write_buffer: [1024]u8 = undefined; + var af = try lf.emit.root_dir.handle.atomicFile(basename, .{ .write_buffer = &write_buffer }); defer af.deinit(); - try af.file.pwritevAll(bufs.items, 0); + try af.file_writer.interface.writeVecAll(bufs.items); try af.finish(); } -fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void { - // Even when len=0, the undefined pointer might cause EFAULT. +fn addBuf(list: *std.ArrayList([]const u8), buf: []const u8) void { if (buf.len == 0) return; - list.appendAssumeCapacity(.{ .base = buf.ptr, .len = buf.len }); + list.appendAssumeCapacity(buf); } /// This function is temporally single-threaded. diff --git a/src/fmt.zig b/src/fmt.zig index 23e668d245..92ae22e4bc 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -348,10 +348,10 @@ fn fmtPathFile( try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); fmt.any_error = true; } else { - var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); + var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode, .write_buffer = &.{} }); defer af.deinit(); - try af.file.writeAll(fmt.out_buffer.getWritten()); + try af.file_writer.interface.writeAll(fmt.out_buffer.getWritten()); try af.finish(); try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 734b4b6a04..5b0e8520d1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -612,7 +612,6 @@ pub fn flush( }; const emit = self.base.emit; invalidateKernelCache(emit.root_dir.handle, emit.sub_path) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, else => |e| return diags.fail("failed to invalidate kernel cache: {s}", .{@errorName(e)}), }; } diff --git a/src/main.zig b/src/main.zig index 7ad40e1a68..d3655c9d79 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4624,7 +4624,9 @@ fn cmdTranslateC( fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ path, fs.path.sep_str, out_zig_path, @errorName(err) }); }; defer zig_file.close(); - try fs.File.stdout().writeFileAll(zig_file, .{}); + var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var file_reader = zig_file.reader(&.{}); + _ = try stdout_writer.interface.sendFileAll(&file_reader, .unlimited); return cleanExit(); } } From f1576ef14c5956cdab742aaf31ec4c672b54252b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 16:43:47 -0700 Subject: [PATCH 12/76] objcopy: delete most of it this code is not up to zig project standards tracked by #24522 oh, and fix not adjusting buffer seek position in std.fs.File.Reader --- lib/compiler/objcopy.zig | 969 ++--------------------- lib/std/elf.zig | 275 +++---- lib/std/fs/File.zig | 22 +- test/standalone/stack_iterator/build.zig | 113 +-- tools/gen_stubs.zig | 3 +- 5 files changed, 240 insertions(+), 1142 deletions(-) diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index 52ffe208f6..5908f8b73d 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -13,6 +13,9 @@ const Server = std.zig.Server; var stdin_buffer: [1024]u8 = undefined; var stdout_buffer: [1024]u8 = undefined; +var input_buffer: [1024]u8 = undefined; +var output_buffer: [1024]u8 = undefined; + pub fn main() !void { var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena_instance.deinit(); @@ -145,13 +148,16 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void const input = opt_input orelse fatal("expected input parameter", .{}); const output = opt_output orelse fatal("expected output parameter", .{}); - var in_file = fs.cwd().openFile(input, .{}) catch |err| - fatal("unable to open '{s}': {s}", .{ input, @errorName(err) }); - defer in_file.close(); + const input_file = fs.cwd().openFile(input, .{}) catch |err| fatal("failed to open {s}: {t}", .{ input, err }); + defer input_file.close(); - const elf_hdr = std.elf.Header.read(in_file) catch |err| switch (err) { - error.InvalidElfMagic => fatal("not an ELF file: '{s}'", .{input}), - else => fatal("unable to read '{s}': {s}", .{ input, @errorName(err) }), + const stat = input_file.stat() catch |err| fatal("failed to stat {s}: {t}", .{ input, err }); + + var in: File.Reader = .initSize(input_file, &input_buffer, stat.size); + + const elf_hdr = std.elf.Header.read(&in.interface) catch |err| switch (err) { + error.ReadFailed => fatal("unable to read {s}: {t}", .{ input, in.err.? }), + else => |e| fatal("invalid elf file: {t}", .{e}), }; const in_ofmt = .elf; @@ -168,16 +174,12 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void } }; - const mode = mode: { - if (out_fmt != .elf or only_keep_debug) - break :mode fs.File.default_mode; - if (in_file.stat()) |stat| - break :mode stat.mode - else |_| - break :mode fs.File.default_mode; - }; - var out_file = try fs.cwd().createFile(output, .{ .mode = mode }); - defer out_file.close(); + const mode = if (out_fmt != .elf or only_keep_debug) fs.File.default_mode else stat.mode; + + var output_file = try fs.cwd().createFile(output, .{ .mode = mode }); + defer output_file.close(); + + var out = output_file.writer(&output_buffer); switch (out_fmt) { .hex, .raw => { @@ -192,7 +194,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void if (set_section_flags != null) fatal("zig objcopy: ELF to RAW or HEX copying does not support --set_section_flags", .{}); - try emitElf(arena, in_file, out_file, elf_hdr, .{ + try emitElf(arena, &in, &out, elf_hdr, .{ .ofmt = out_fmt, .only_section = only_section, .pad_to = pad_to, @@ -208,22 +210,13 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void if (pad_to) |_| fatal("zig objcopy: ELF to ELF copying does not support --pad-to", .{}); - try stripElf(arena, in_file, out_file, elf_hdr, .{ - .strip_debug = strip_debug, - .strip_all = strip_all, - .only_keep_debug = only_keep_debug, - .add_debuglink = opt_add_debuglink, - .extract_to = opt_extract, - .compress_debug = compress_debug_sections, - .add_section = add_section, - .set_section_alignment = set_section_alignment, - .set_section_flags = set_section_flags, - }); - return std.process.cleanExit(); + fatal("unimplemented", .{}); }, else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), } + try out.end(); + if (listen) { var stdin_reader = fs.File.stdin().reader(&stdin_buffer); var stdout_writer = fs.File.stdout().writer(&stdout_buffer); @@ -304,12 +297,12 @@ const SetSectionFlags = struct { fn emitElf( arena: Allocator, - in_file: File, - out_file: File, + in: *File.Reader, + out: *File.Writer, elf_hdr: elf.Header, options: EmitRawElfOptions, ) !void { - var binary_elf_output = try BinaryElfOutput.parse(arena, in_file, elf_hdr); + var binary_elf_output = try BinaryElfOutput.parse(arena, in, elf_hdr); defer binary_elf_output.deinit(); if (options.ofmt == .elf) { @@ -328,8 +321,8 @@ fn emitElf( continue; } - try writeBinaryElfSection(in_file, out_file, section); - try padFile(out_file, options.pad_to); + try writeBinaryElfSection(in, out, section); + try padFile(out, options.pad_to); return; } }, @@ -342,10 +335,10 @@ fn emitElf( switch (options.ofmt) { .raw => { for (binary_elf_output.sections.items) |section| { - try out_file.seekTo(section.binaryOffset); - try writeBinaryElfSection(in_file, out_file, section); + try out.seekTo(section.binaryOffset); + try writeBinaryElfSection(in, out, section); } - try padFile(out_file, options.pad_to); + try padFile(out, options.pad_to); }, .hex => { if (binary_elf_output.segments.items.len == 0) return; @@ -353,15 +346,15 @@ fn emitElf( return error.InvalidHexfileAddressRange; } - var hex_writer = HexWriter{ .out_file = out_file }; + var hex_writer = HexWriter{ .out = out }; for (binary_elf_output.segments.items) |segment| { - try hex_writer.writeSegment(segment, in_file); + try hex_writer.writeSegment(segment, in); } if (options.pad_to) |_| { // Padding to a size in hex files isn't applicable return error.InvalidArgument; } - try hex_writer.writeEOF(); + try hex_writer.writeEof(); }, else => unreachable, } @@ -399,7 +392,7 @@ const BinaryElfOutput = struct { self.segments.deinit(self.allocator); } - pub fn parse(allocator: Allocator, elf_file: File, elf_hdr: elf.Header) !Self { + pub fn parse(allocator: Allocator, in: *File.Reader, elf_hdr: elf.Header) !Self { var self: Self = .{ .segments = .{}, .sections = .{}, @@ -412,7 +405,7 @@ const BinaryElfOutput = struct { self.shstrtab = blk: { if (elf_hdr.shstrndx >= elf_hdr.shnum) break :blk null; - var section_headers = elf_hdr.section_header_iterator(&elf_file); + var section_headers = elf_hdr.iterateSectionHeaders(in); var section_counter: usize = 0; while (section_counter < elf_hdr.shstrndx) : (section_counter += 1) { @@ -421,18 +414,13 @@ const BinaryElfOutput = struct { const shstrtab_shdr = (try section_headers.next()).?; - const buffer = try allocator.alloc(u8, @intCast(shstrtab_shdr.sh_size)); - errdefer allocator.free(buffer); - - const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset); - if (num_read != buffer.len) return error.EndOfStream; - - break :blk buffer; + try in.seekTo(shstrtab_shdr.sh_offset); + break :blk try in.interface.readAlloc(allocator, shstrtab_shdr.sh_size); }; errdefer if (self.shstrtab) |shstrtab| allocator.free(shstrtab); - var section_headers = elf_hdr.section_header_iterator(&elf_file); + var section_headers = elf_hdr.iterateSectionHeaders(in); while (try section_headers.next()) |section| { if (sectionValidForOutput(section)) { const newSection = try allocator.create(BinaryElfSection); @@ -451,7 +439,7 @@ const BinaryElfOutput = struct { } } - var program_headers = elf_hdr.program_header_iterator(&elf_file); + var program_headers = elf_hdr.iterateProgramHeaders(in); while (try program_headers.next()) |phdr| { if (phdr.p_type == elf.PT_LOAD) { const newSegment = try allocator.create(BinaryElfSegment); @@ -539,19 +527,17 @@ const BinaryElfOutput = struct { } }; -fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { - try out_file.writeFileAll(elf_file, .{ - .in_offset = section.elfOffset, - .in_len = section.fileSize, - }); +fn writeBinaryElfSection(in: *File.Reader, out: *File.Writer, section: *BinaryElfSection) !void { + try in.seekTo(section.elfOffset); + _ = try out.interface.sendFileAll(in, .limited(section.fileSize)); } const HexWriter = struct { prev_addr: ?u32 = null, - out_file: File, + out: *File.Writer, /// Max data bytes per line of output - const MAX_PAYLOAD_LEN: u8 = 16; + const max_payload_len: u8 = 16; fn addressParts(address: u16) [2]u8 { const msb: u8 = @truncate(address >> 8); @@ -627,13 +613,13 @@ const HexWriter = struct { return (sum ^ 0xFF) +% 1; } - fn write(self: Record, file: File) File.WriteError!void { + fn write(self: Record, out: *File.Writer) !void { const linesep = "\r\n"; // colon, (length, address, type, payload, checksum) as hex, CRLF - const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; + const BUFSIZE = 1 + (1 + 2 + 1 + max_payload_len + 1) * 2 + linesep.len; var outbuf: [BUFSIZE]u8 = undefined; const payload_bytes = self.getPayloadBytes(); - assert(payload_bytes.len <= MAX_PAYLOAD_LEN); + assert(payload_bytes.len <= max_payload_len); const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3X}{4X:0>2}" ++ linesep, .{ @as(u8, @intCast(payload_bytes.len)), @@ -642,38 +628,37 @@ const HexWriter = struct { payload_bytes, self.checksum(), }); - try file.writeAll(line); + try out.interface.writeAll(line); } }; - pub fn writeSegment(self: *HexWriter, segment: *const BinaryElfSegment, elf_file: File) !void { - var buf: [MAX_PAYLOAD_LEN]u8 = undefined; + pub fn writeSegment(self: *HexWriter, segment: *const BinaryElfSegment, in: *File.Reader) !void { + var buf: [max_payload_len]u8 = undefined; var bytes_read: usize = 0; while (bytes_read < segment.fileSize) { const row_address: u32 = @intCast(segment.physicalAddress + bytes_read); const remaining = segment.fileSize - bytes_read; - const to_read: usize = @intCast(@min(remaining, MAX_PAYLOAD_LEN)); - const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read); - if (did_read < to_read) return error.UnexpectedEOF; + const dest = buf[0..@min(remaining, max_payload_len)]; + try in.seekTo(segment.elfOffset + bytes_read); + try in.interface.readSliceAll(dest); + try self.writeDataRow(row_address, dest); - try self.writeDataRow(row_address, buf[0..did_read]); - - bytes_read += did_read; + bytes_read += dest.len; } } - fn writeDataRow(self: *HexWriter, address: u32, data: []const u8) File.WriteError!void { + fn writeDataRow(self: *HexWriter, address: u32, data: []const u8) !void { const record = Record.Data(address, data); if (address > 0xFFFF and (self.prev_addr == null or record.address != self.prev_addr.?)) { - try Record.Address(address).write(self.out_file); + try Record.Address(address).write(self.out); } - try record.write(self.out_file); + try record.write(self.out); self.prev_addr = @intCast(record.address + data.len); } - fn writeEOF(self: HexWriter) File.WriteError!void { - try Record.EOF().write(self.out_file); + fn writeEof(self: HexWriter) !void { + try Record.EOF().write(self.out); } }; @@ -686,9 +671,9 @@ fn containsValidAddressRange(segments: []*BinaryElfSegment) bool { return true; } -fn padFile(f: File, opt_size: ?u64) !void { +fn padFile(out: *File.Writer, opt_size: ?u64) !void { const size = opt_size orelse return; - try f.setEndPos(size); + try out.file.setEndPos(size); } test "HexWriter.Record.Address has correct payload and checksum" { @@ -732,836 +717,6 @@ test "containsValidAddressRange" { try std.testing.expect(containsValidAddressRange(&buf)); } -// ------------- -// ELF to ELF stripping - -const StripElfOptions = struct { - extract_to: ?[]const u8 = null, - add_debuglink: ?[]const u8 = null, - strip_all: bool = false, - strip_debug: bool = false, - only_keep_debug: bool = false, - compress_debug: bool = false, - add_section: ?AddSection, - set_section_alignment: ?SetSectionAlignment, - set_section_flags: ?SetSectionFlags, -}; - -fn stripElf( - allocator: Allocator, - in_file: File, - out_file: File, - elf_hdr: elf.Header, - options: StripElfOptions, -) !void { - const Filter = ElfFileHelper.Filter; - const DebugLink = ElfFileHelper.DebugLink; - - const filter: Filter = filter: { - if (options.only_keep_debug) break :filter .debug; - if (options.strip_all) break :filter .program; - if (options.strip_debug) break :filter .program_and_symbols; - break :filter .all; - }; - - const filter_complement: ?Filter = blk: { - if (options.extract_to) |_| { - break :blk switch (filter) { - .program => .debug_and_symbols, - .debug => .program_and_symbols, - .program_and_symbols => .debug, - .debug_and_symbols => .program, - .all => fatal("zig objcopy: nothing to extract", .{}), - }; - } else { - break :blk null; - } - }; - const debuglink_path = path: { - if (options.add_debuglink) |path| break :path path; - if (options.extract_to) |path| break :path path; - break :path null; - }; - - switch (elf_hdr.is_64) { - inline else => |is_64| { - var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr); - defer elf_file.deinit(); - - if (options.add_section) |user_section| { - for (elf_file.sections) |section| { - if (std.mem.eql(u8, section.name, user_section.section_name)) { - fatal("zig objcopy: unable to add section '{s}'. Section already exists in input", .{user_section.section_name}); - } - } - } - - if (filter_complement) |flt| { - // write the .dbg file and close it, so it can be read back to compute the debuglink checksum. - const path = options.extract_to.?; - const dbg_file = std.fs.cwd().createFile(path, .{}) catch |err| { - fatal("zig objcopy: unable to create '{s}': {s}", .{ path, @errorName(err) }); - }; - defer dbg_file.close(); - - try elf_file.emit(allocator, dbg_file, in_file, .{ .section_filter = flt, .compress_debug = options.compress_debug }); - } - - const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; - try elf_file.emit(allocator, out_file, in_file, .{ - .section_filter = filter, - .debuglink = debuglink, - .compress_debug = options.compress_debug, - .add_section = options.add_section, - .set_section_alignment = options.set_section_alignment, - .set_section_flags = options.set_section_flags, - }); - }, - } -} - -// note: this is "a minimal effort implementation" -// It doesn't support all possibile elf files: some sections type may need fixups, the program header may need fix up, ... -// It was written for a specific use case (strip debug info to a sperate file, for linux 64-bits executables built with `zig` or `zig c++` ) -// It moves and reoders the sections as little as possible to avoid having to do fixups. -// TODO: support non-native endianess - -fn ElfFile(comptime is_64: bool) type { - const Elf_Ehdr = if (is_64) elf.Elf64_Ehdr else elf.Elf32_Ehdr; - const Elf_Phdr = if (is_64) elf.Elf64_Phdr else elf.Elf32_Phdr; - const Elf_Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr; - const Elf_Chdr = if (is_64) elf.Elf64_Chdr else elf.Elf32_Chdr; - const Elf_Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; - const Elf_OffSize = if (is_64) elf.Elf64_Off else elf.Elf32_Off; - - return struct { - raw_elf_header: Elf_Ehdr, - program_segments: []const Elf_Phdr, - sections: []const Section, - arena: std.heap.ArenaAllocator, - - const SectionCategory = ElfFileHelper.SectionCategory; - const section_memory_align: std.mem.Alignment = .of(Elf_Sym); // most restrictive of what we may load in memory - const Section = struct { - section: Elf_Shdr, - name: []const u8 = "", - segment: ?*const Elf_Phdr = null, // if the section is used by a program segment (there can be more than one) - payload: ?[]align(section_memory_align.toByteUnits()) const u8 = null, // if we need the data in memory - category: SectionCategory = .none, // should the section be kept in the exe or stripped to the debug database, or both. - }; - - const Self = @This(); - - pub fn parse(gpa: Allocator, in_file: File, header: elf.Header) !Self { - var arena = std.heap.ArenaAllocator.init(gpa); - errdefer arena.deinit(); - const allocator = arena.allocator(); - - var raw_header: Elf_Ehdr = undefined; - { - const bytes_read = try in_file.preadAll(std.mem.asBytes(&raw_header), 0); - if (bytes_read < @sizeOf(Elf_Ehdr)) - return error.TRUNCATED_ELF; - } - - // program header: list of segments - const program_segments = blk: { - if (@sizeOf(Elf_Phdr) != header.phentsize) - fatal("zig objcopy: unsupported ELF file, unexpected phentsize ({d})", .{header.phentsize}); - - const program_header = try allocator.alloc(Elf_Phdr, header.phnum); - const bytes_read = try in_file.preadAll(std.mem.sliceAsBytes(program_header), header.phoff); - if (bytes_read < @sizeOf(Elf_Phdr) * header.phnum) - return error.TRUNCATED_ELF; - break :blk program_header; - }; - - // section header - const sections = blk: { - if (@sizeOf(Elf_Shdr) != header.shentsize) - fatal("zig objcopy: unsupported ELF file, unexpected shentsize ({d})", .{header.shentsize}); - - const section_header = try allocator.alloc(Section, header.shnum); - - const raw_section_header = try allocator.alloc(Elf_Shdr, header.shnum); - defer allocator.free(raw_section_header); - const bytes_read = try in_file.preadAll(std.mem.sliceAsBytes(raw_section_header), header.shoff); - if (bytes_read < @sizeOf(Elf_Phdr) * header.shnum) - return error.TRUNCATED_ELF; - - for (section_header, raw_section_header) |*section, hdr| { - section.* = .{ .section = hdr }; - } - break :blk section_header; - }; - - // load data to memory for some sections: - // string tables for access - // sections than need modifications when other sections move. - for (sections, 0..) |*section, idx| { - const need_data = switch (section.section.sh_type) { - elf.DT_VERSYM => true, - elf.SHT_SYMTAB, elf.SHT_DYNSYM => true, - else => false, - }; - const need_strings = (idx == header.shstrndx); - - if (need_data or need_strings) { - const buffer = try allocator.alignedAlloc(u8, section_memory_align, @intCast(section.section.sh_size)); - const bytes_read = try in_file.preadAll(buffer, section.section.sh_offset); - if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF; - section.payload = buffer; - } - } - - // fill-in sections info: - // resolve the name - // find if a program segment uses the section - // categorize sections usage (used by program segments, debug datadase, common metadata, symbol table) - for (sections) |*section| { - section.segment = for (program_segments) |*seg| { - if (sectionWithinSegment(section.section, seg.*)) break seg; - } else null; - - if (section.section.sh_name != 0 and header.shstrndx != elf.SHN_UNDEF) - section.name = std.mem.span(@as([*:0]const u8, @ptrCast(§ions[header.shstrndx].payload.?[section.section.sh_name]))); - - const category_from_program: SectionCategory = if (section.segment != null) .exe else .debug; - section.category = switch (section.section.sh_type) { - elf.SHT_NOTE => .common, - elf.SHT_SYMTAB => .symbols, // "strip all" vs "strip only debug" - elf.SHT_DYNSYM => .exe, - elf.SHT_PROGBITS => cat: { - if (std.mem.eql(u8, section.name, ".comment")) break :cat .exe; - if (std.mem.eql(u8, section.name, ".gnu_debuglink")) break :cat .none; - break :cat category_from_program; - }, - elf.SHT_LOPROC...elf.SHT_HIPROC => .common, // don't strip unknown sections - elf.SHT_LOUSER...elf.SHT_HIUSER => .common, // don't strip unknown sections - else => category_from_program, - }; - } - - sections[0].category = .common; // mandatory null section - if (header.shstrndx != elf.SHN_UNDEF) - sections[header.shstrndx].category = .common; // string table for the headers - - // recursively propagate section categories to their linked sections, so that they are kept together - var dirty: u1 = 1; - while (dirty != 0) { - dirty = 0; - - for (sections) |*section| { - if (section.section.sh_link != elf.SHN_UNDEF) - dirty |= ElfFileHelper.propagateCategory(§ions[section.section.sh_link].category, section.category); - if ((section.section.sh_flags & elf.SHF_INFO_LINK) != 0 and section.section.sh_info != elf.SHN_UNDEF) - dirty |= ElfFileHelper.propagateCategory(§ions[section.section.sh_info].category, section.category); - } - } - - return Self{ - .arena = arena, - .raw_elf_header = raw_header, - .program_segments = program_segments, - .sections = sections, - }; - } - - pub fn deinit(self: *Self) void { - self.arena.deinit(); - } - - const Filter = ElfFileHelper.Filter; - const DebugLink = ElfFileHelper.DebugLink; - const EmitElfOptions = struct { - section_filter: Filter = .all, - debuglink: ?DebugLink = null, - compress_debug: bool = false, - add_section: ?AddSection = null, - set_section_alignment: ?SetSectionAlignment = null, - set_section_flags: ?SetSectionFlags = null, - }; - fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { - var arena = std.heap.ArenaAllocator.init(gpa); - defer arena.deinit(); - const allocator = arena.allocator(); - - // when emitting the stripped exe: - // - unused sections are removed - // when emitting the debug file: - // - all sections are kept, but some are emptied and their types is changed to SHT_NOBITS - // the program header is kept unchanged. (`strip` does update it, but `eu-strip` does not, and it still works) - - const Update = struct { - action: ElfFileHelper.Action, - - // remap the indexs after omitting the filtered sections - remap_idx: u16, - - // optionally overrides the payload from the source file - payload: ?[]align(section_memory_align.toByteUnits()) const u8 = null, - section: ?Elf_Shdr = null, - }; - const sections_update = try allocator.alloc(Update, self.sections.len); - const new_shnum = blk: { - var next_idx: u16 = 0; - for (self.sections, sections_update) |section, *update| { - const action = ElfFileHelper.selectAction(section.category, options.section_filter); - const remap_idx = idx: { - if (action == .strip) break :idx elf.SHN_UNDEF; - next_idx += 1; - break :idx next_idx - 1; - }; - update.* = Update{ .action = action, .remap_idx = remap_idx }; - } - - if (options.debuglink != null) - next_idx += 1; - - if (options.add_section != null) { - next_idx += 1; - } - - break :blk next_idx; - }; - - // add a ".gnu_debuglink" to the string table if needed - const debuglink_name: u32 = blk: { - if (options.debuglink == null) break :blk elf.SHN_UNDEF; - if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF) - fatal("zig objcopy: no strtab, cannot add the debuglink section", .{}); // TODO add the section if needed? - - const strtab = &self.sections[self.raw_elf_header.e_shstrndx]; - const update = §ions_update[self.raw_elf_header.e_shstrndx]; - - const name: []const u8 = ".gnu_debuglink"; - const new_offset: u32 = @intCast(strtab.payload.?.len); - const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1); - @memcpy(buf[0..new_offset], strtab.payload.?); - @memcpy(buf[new_offset..][0..name.len], name); - buf[new_offset + name.len] = 0; - - assert(update.action == .keep); - update.payload = buf; - - break :blk new_offset; - }; - - // add user section to the string table if needed - const user_section_name: u32 = blk: { - if (options.add_section == null) break :blk elf.SHN_UNDEF; - if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF) - fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed? - - const strtab = &self.sections[self.raw_elf_header.e_shstrndx]; - const update = §ions_update[self.raw_elf_header.e_shstrndx]; - - const name = options.add_section.?.section_name; - const new_offset: u32 = @intCast(strtab.payload.?.len); - const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1); - @memcpy(buf[0..new_offset], strtab.payload.?); - @memcpy(buf[new_offset..][0..name.len], name); - buf[new_offset + name.len] = 0; - - assert(update.action == .keep); - update.payload = buf; - - break :blk new_offset; - }; - - // maybe compress .debug sections - if (options.compress_debug) { - for (self.sections[1..], sections_update[1..]) |section, *update| { - if (update.action != .keep) continue; - if (!std.mem.startsWith(u8, section.name, ".debug_")) continue; - if ((section.section.sh_flags & elf.SHF_COMPRESSED) != 0) continue; // already compressed - - const chdr = Elf_Chdr{ - .ch_type = elf.COMPRESS.ZLIB, - .ch_size = section.section.sh_size, - .ch_addralign = section.section.sh_addralign, - }; - - const compressed_payload = try ElfFileHelper.tryCompressSection(allocator, in_file, section.section.sh_offset, section.section.sh_size, std.mem.asBytes(&chdr)); - if (compressed_payload) |payload| { - update.payload = payload; - update.section = section.section; - update.section.?.sh_addralign = @alignOf(Elf_Chdr); - update.section.?.sh_size = @intCast(payload.len); - update.section.?.sh_flags |= elf.SHF_COMPRESSED; - } - } - } - - var cmdbuf = std.ArrayList(ElfFileHelper.WriteCmd).init(allocator); - defer cmdbuf.deinit(); - try cmdbuf.ensureUnusedCapacity(3 + new_shnum); - var eof_offset: Elf_OffSize = 0; // track the end of the data written so far. - - // build the updated headers - // nb: updated_elf_header will be updated before the actual write - var updated_elf_header = self.raw_elf_header; - if (updated_elf_header.e_shstrndx != elf.SHN_UNDEF) - updated_elf_header.e_shstrndx = sections_update[updated_elf_header.e_shstrndx].remap_idx; - cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = std.mem.asBytes(&updated_elf_header), .out_offset = 0 } }); - eof_offset = @sizeOf(Elf_Ehdr); - - // program header as-is. - // nb: for only-debug files, removing it appears to work, but is invalid by ELF specifcation. - { - assert(updated_elf_header.e_phoff == @sizeOf(Elf_Ehdr)); - const data = std.mem.sliceAsBytes(self.program_segments); - assert(data.len == @as(usize, updated_elf_header.e_phentsize) * updated_elf_header.e_phnum); - cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_phoff } }); - eof_offset = updated_elf_header.e_phoff + @as(Elf_OffSize, @intCast(data.len)); - } - - // update sections and queue payload writes - const updated_section_header = blk: { - const dest_sections = try allocator.alloc(Elf_Shdr, new_shnum); - - { - // the ELF format doesn't specify the order for all sections. - // this code only supports when they are in increasing file order. - var offset: u64 = eof_offset; - for (self.sections[1..]) |section| { - if (section.section.sh_type == elf.SHT_NOBITS) - continue; - if (section.section.sh_offset < offset) { - fatal("zig objcopy: unsupported ELF file", .{}); - } - offset = section.section.sh_offset; - } - } - - dest_sections[0] = self.sections[0].section; - - var dest_section_idx: u32 = 1; - for (self.sections[1..], sections_update[1..]) |section, update| { - if (update.action == .strip) continue; - assert(update.remap_idx == dest_section_idx); - - const src = if (update.section) |*s| s else §ion.section; - const dest = &dest_sections[dest_section_idx]; - const payload = if (update.payload) |data| data else section.payload; - dest_section_idx += 1; - - dest.* = src.*; - - if (src.sh_link != elf.SHN_UNDEF) - dest.sh_link = sections_update[src.sh_link].remap_idx; - if ((src.sh_flags & elf.SHF_INFO_LINK) != 0 and src.sh_info != elf.SHN_UNDEF) - dest.sh_info = sections_update[src.sh_info].remap_idx; - - if (payload) |data| - dest.sh_size = @intCast(data.len); - - const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign; - dest.sh_offset = std.mem.alignForward(Elf_OffSize, eof_offset, addralign); - if (src.sh_offset != dest.sh_offset and section.segment != null and update.action != .empty and dest.sh_type != elf.SHT_NOTE and dest.sh_type != elf.SHT_NOBITS) { - if (src.sh_offset > dest.sh_offset) { - dest.sh_offset = src.sh_offset; // add padding to avoid modifing the program segments - } else { - fatal("zig objcopy: cannot adjust program segments", .{}); - } - } - assert(dest.sh_addr % addralign == dest.sh_offset % addralign); - - if (update.action == .empty) - dest.sh_type = elf.SHT_NOBITS; - - if (dest.sh_type != elf.SHT_NOBITS) { - if (payload) |src_data| { - // update sections payload and write - const dest_data = switch (src.sh_type) { - elf.DT_VERSYM => dst_data: { - const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len); - @memcpy(data, src_data); - - const defs = @as([*]elf.Verdef, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(elf.Verdef)]; - for (defs) |*def| switch (def.ndx) { - .LOCAL, .GLOBAL => {}, - else => def.ndx = @enumFromInt(sections_update[src.sh_info].remap_idx), - }; - - break :dst_data data; - }, - elf.SHT_SYMTAB, elf.SHT_DYNSYM => dst_data: { - const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len); - @memcpy(data, src_data); - - const syms = @as([*]Elf_Sym, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(Elf_Sym)]; - for (syms) |*sym| { - if (sym.st_shndx != elf.SHN_UNDEF and sym.st_shndx < elf.SHN_LORESERVE) - sym.st_shndx = sections_update[sym.st_shndx].remap_idx; - } - - break :dst_data data; - }, - else => src_data, - }; - - assert(dest_data.len == dest.sh_size); - cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = dest_data, .out_offset = dest.sh_offset } }); - eof_offset = dest.sh_offset + dest.sh_size; - } else { - // direct contents copy - cmdbuf.appendAssumeCapacity(.{ .copy_range = .{ .in_offset = src.sh_offset, .len = dest.sh_size, .out_offset = dest.sh_offset } }); - eof_offset = dest.sh_offset + dest.sh_size; - } - } else { - // account for alignment padding even in empty sections to keep logical section order - eof_offset = dest.sh_offset; - } - } - - // add a ".gnu_debuglink" section - if (options.debuglink) |link| { - const payload = payload: { - const crc_offset = std.mem.alignForward(usize, link.name.len + 1, 4); - const buf = try allocator.alignedAlloc(u8, .@"4", crc_offset + 4); - @memcpy(buf[0..link.name.len], link.name); - @memset(buf[link.name.len..crc_offset], 0); - @memcpy(buf[crc_offset..], std.mem.asBytes(&link.crc32)); - break :payload buf; - }; - - dest_sections[dest_section_idx] = Elf_Shdr{ - .sh_name = debuglink_name, - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = eof_offset, - .sh_size = @intCast(payload.len), - .sh_link = elf.SHN_UNDEF, - .sh_info = elf.SHN_UNDEF, - .sh_addralign = 4, - .sh_entsize = 0, - }; - dest_section_idx += 1; - - cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } }); - eof_offset += @as(Elf_OffSize, @intCast(payload.len)); - } - - // --add-section - if (options.add_section) |add_section| { - var section_file = fs.cwd().openFile(add_section.file_path, .{}) catch |err| - fatal("unable to open '{s}': {s}", .{ add_section.file_path, @errorName(err) }); - defer section_file.close(); - - const payload = try section_file.readToEndAlloc(arena.allocator(), std.math.maxInt(usize)); - - dest_sections[dest_section_idx] = Elf_Shdr{ - .sh_name = user_section_name, - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = eof_offset, - .sh_size = @intCast(payload.len), - .sh_link = elf.SHN_UNDEF, - .sh_info = elf.SHN_UNDEF, - .sh_addralign = 4, - .sh_entsize = 0, - }; - dest_section_idx += 1; - - cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } }); - eof_offset += @as(Elf_OffSize, @intCast(payload.len)); - } - - assert(dest_section_idx == new_shnum); - break :blk dest_sections; - }; - - // --set-section-alignment: overwrite alignment - if (options.set_section_alignment) |set_align| { - if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF) - fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed? - - const strtab = §ions_update[self.raw_elf_header.e_shstrndx]; - for (updated_section_header) |*section| { - const section_name = std.mem.span(@as([*:0]const u8, @ptrCast(&strtab.payload.?[section.sh_name]))); - if (std.mem.eql(u8, section_name, set_align.section_name)) { - section.sh_addralign = set_align.alignment; - break; - } - } else std.log.warn("Skipping --set-section-alignment. Section '{s}' not found", .{set_align.section_name}); - } - - // --set-section-flags: overwrite flags - if (options.set_section_flags) |set_flags| { - if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF) - fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed? - - const strtab = §ions_update[self.raw_elf_header.e_shstrndx]; - for (updated_section_header) |*section| { - const section_name = std.mem.span(@as([*:0]const u8, @ptrCast(&strtab.payload.?[section.sh_name]))); - if (std.mem.eql(u8, section_name, set_flags.section_name)) { - section.sh_flags = std.elf.SHF_WRITE; // default is writable cleared by "readonly" - const f = set_flags.flags; - - // Supporting a subset of GNU and LLVM objcopy for ELF only - // GNU: - // alloc: add SHF_ALLOC - // contents: if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing - // load: if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents) - // noload: not ELF relevant - // readonly: clear default SHF_WRITE flag - // code: add SHF_EXECINSTR - // data: not ELF relevant - // rom: ignored - // exclude: add SHF_EXCLUDE - // share: not ELF relevant - // debug: not ELF relevant - // large: add SHF_X86_64_LARGE. Fatal error if target is not x86_64 - if (f.alloc) section.sh_flags |= std.elf.SHF_ALLOC; - if (f.contents or f.load) { - if (section.sh_type == std.elf.SHT_NOBITS) section.sh_type = std.elf.SHT_PROGBITS; - } - if (f.readonly) section.sh_flags &= ~@as(@TypeOf(section.sh_type), std.elf.SHF_WRITE); - if (f.code) section.sh_flags |= std.elf.SHF_EXECINSTR; - if (f.exclude) section.sh_flags |= std.elf.SHF_EXCLUDE; - if (f.large) { - if (updated_elf_header.e_machine != std.elf.EM.X86_64) - fatal("zig objcopy: 'large' section flag is only supported on x86_64 targets", .{}); - section.sh_flags |= std.elf.SHF_X86_64_LARGE; - } - - // LLVM: - // merge: add SHF_MERGE - // strings: add SHF_STRINGS - if (f.merge) section.sh_flags |= std.elf.SHF_MERGE; - if (f.strings) section.sh_flags |= std.elf.SHF_STRINGS; - break; - } - } else std.log.warn("Skipping --set-section-flags. Section '{s}' not found", .{set_flags.section_name}); - } - - // write the section header at the tail - { - const offset = std.mem.alignForward(Elf_OffSize, eof_offset, @alignOf(Elf_Shdr)); - - const data = std.mem.sliceAsBytes(updated_section_header); - assert(data.len == @as(usize, updated_elf_header.e_shentsize) * new_shnum); - updated_elf_header.e_shoff = offset; - updated_elf_header.e_shnum = new_shnum; - - cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_shoff } }); - } - - try ElfFileHelper.write(allocator, out_file, in_file, cmdbuf.items); - } - - fn sectionWithinSegment(section: Elf_Shdr, segment: Elf_Phdr) bool { - const file_size = if (section.sh_type == elf.SHT_NOBITS) 0 else section.sh_size; - return segment.p_offset <= section.sh_offset and (segment.p_offset + segment.p_filesz) >= (section.sh_offset + file_size); - } - }; -} - -const ElfFileHelper = struct { - const DebugLink = struct { name: []const u8, crc32: u32 }; - const Filter = enum { all, program, debug, program_and_symbols, debug_and_symbols }; - - const SectionCategory = enum { common, exe, debug, symbols, none }; - fn propagateCategory(cur: *SectionCategory, new: SectionCategory) u1 { - const cat: SectionCategory = switch (cur.*) { - .none => new, - .common => .common, - .debug => switch (new) { - .none, .debug => .debug, - else => new, - }, - .exe => switch (new) { - .common => .common, - .none, .debug, .exe => .exe, - .symbols => .exe, - }, - .symbols => switch (new) { - .none, .common, .debug, .exe => unreachable, - .symbols => .symbols, - }, - }; - - if (cur.* != cat) { - cur.* = cat; - return 1; - } else { - return 0; - } - } - - const Action = enum { keep, strip, empty }; - fn selectAction(category: SectionCategory, filter: Filter) Action { - if (category == .none) return .strip; - return switch (filter) { - .all => switch (category) { - .none => .strip, - else => .keep, - }, - .program => switch (category) { - .common, .exe => .keep, - else => .strip, - }, - .program_and_symbols => switch (category) { - .common, .exe, .symbols => .keep, - else => .strip, - }, - .debug => switch (category) { - .exe, .symbols => .empty, - .none => .strip, - else => .keep, - }, - .debug_and_symbols => switch (category) { - .exe => .empty, - .none => .strip, - else => .keep, - }, - }; - } - - const WriteCmd = union(enum) { - copy_range: struct { in_offset: u64, len: u64, out_offset: u64 }, - write_data: struct { data: []const u8, out_offset: u64 }, - }; - fn write(allocator: Allocator, out_file: File, in_file: File, cmds: []const WriteCmd) !void { - // consolidate holes between writes: - // by coping original padding data from in_file (by fusing contiguous ranges) - // by writing zeroes otherwise - const zeroes = [1]u8{0} ** 4096; - var consolidated = std.ArrayList(WriteCmd).init(allocator); - defer consolidated.deinit(); - try consolidated.ensureUnusedCapacity(cmds.len * 2); - var offset: u64 = 0; - var fused_cmd: ?WriteCmd = null; - for (cmds) |cmd| { - switch (cmd) { - .write_data => |data| { - assert(data.out_offset >= offset); - if (fused_cmd) |prev| { - consolidated.appendAssumeCapacity(prev); - fused_cmd = null; - } - if (data.out_offset > offset) { - consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(data.out_offset - offset)], .out_offset = offset } }); - } - consolidated.appendAssumeCapacity(cmd); - offset = data.out_offset + data.data.len; - }, - .copy_range => |range| { - assert(range.out_offset >= offset); - if (fused_cmd) |prev| { - if (range.in_offset >= prev.copy_range.in_offset + prev.copy_range.len and (range.out_offset - prev.copy_range.out_offset == range.in_offset - prev.copy_range.in_offset)) { - fused_cmd = .{ .copy_range = .{ - .in_offset = prev.copy_range.in_offset, - .out_offset = prev.copy_range.out_offset, - .len = (range.out_offset + range.len) - prev.copy_range.out_offset, - } }; - } else { - consolidated.appendAssumeCapacity(prev); - if (range.out_offset > offset) { - consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(range.out_offset - offset)], .out_offset = offset } }); - } - fused_cmd = cmd; - } - } else { - fused_cmd = cmd; - } - offset = range.out_offset + range.len; - }, - } - } - if (fused_cmd) |cmd| { - consolidated.appendAssumeCapacity(cmd); - } - - // write the output file - for (consolidated.items) |cmd| { - switch (cmd) { - .write_data => |data| { - var iovec = [_]std.posix.iovec_const{.{ .base = data.data.ptr, .len = data.data.len }}; - try out_file.pwritevAll(&iovec, data.out_offset); - }, - .copy_range => |range| { - const copied_bytes = try in_file.copyRangeAll(range.in_offset, out_file, range.out_offset, range.len); - if (copied_bytes < range.len) return error.TRUNCATED_ELF; - }, - } - } - } - - fn tryCompressSection(allocator: Allocator, in_file: File, offset: u64, size: u64, prefix: []const u8) !?[]align(8) const u8 { - if (size < prefix.len) return null; - - try in_file.seekTo(offset); - var section_reader = std.io.limitedReader(in_file.deprecatedReader(), size); - - // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed. - const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size)); - var compressed_stream = std.io.fixedBufferStream(compressed_data); - - try compressed_stream.writer().writeAll(prefix); - - { - var compressor = try std.compress.zlib.compressor(compressed_stream.writer(), .{}); - - var buf: [8000]u8 = undefined; - while (true) { - const bytes_read = try section_reader.read(&buf); - if (bytes_read == 0) break; - const bytes_written = compressor.write(buf[0..bytes_read]) catch |err| switch (err) { - error.NoSpaceLeft => { - allocator.free(compressed_data); - return null; - }, - else => return err, - }; - std.debug.assert(bytes_written == bytes_read); - } - compressor.finish() catch |err| switch (err) { - error.NoSpaceLeft => { - allocator.free(compressed_data); - return null; - }, - else => return err, - }; - } - - const compressed_len: usize = @intCast(compressed_stream.getPos() catch unreachable); - const data = allocator.realloc(compressed_data, compressed_len) catch compressed_data; - return data[0..compressed_len]; - } - - fn createDebugLink(path: []const u8) DebugLink { - const file = std.fs.cwd().openFile(path, .{}) catch |err| { - fatal("zig objcopy: could not open `{s}`: {s}\n", .{ path, @errorName(err) }); - }; - defer file.close(); - - const crc = ElfFileHelper.computeFileCrc(file) catch |err| { - fatal("zig objcopy: could not read `{s}`: {s}\n", .{ path, @errorName(err) }); - }; - return .{ - .name = std.fs.path.basename(path), - .crc32 = crc, - }; - } - - fn computeFileCrc(file: File) !u32 { - var buf: [8000]u8 = undefined; - - try file.seekTo(0); - var hasher = std.hash.Crc32.init(); - while (true) { - const bytes_read = try file.read(&buf); - if (bytes_read == 0) break; - hasher.update(buf[0..bytes_read]); - } - return hasher.final(); - } -}; - const SectionFlags = packed struct { alloc: bool = false, contents: bool = false, diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 4e15cd3a09..47b3add84f 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -482,6 +482,7 @@ pub const Header = struct { is_64: bool, endian: std.builtin.Endian, os_abi: OSABI, + /// The meaning of this value depends on `os_abi`. abi_version: u8, type: ET, machine: EM, @@ -494,205 +495,135 @@ pub const Header = struct { shnum: u16, shstrndx: u16, - pub fn program_header_iterator(self: Header, parse_source: anytype) ProgramHeaderIterator(@TypeOf(parse_source)) { - return ProgramHeaderIterator(@TypeOf(parse_source)){ - .elf_header = self, - .parse_source = parse_source, + pub fn iterateProgramHeaders(h: Header, file_reader: *std.fs.File.Reader) ProgramHeaderIterator { + return .{ + .elf_header = h, + .file_reader = file_reader, }; } - pub fn section_header_iterator(self: Header, parse_source: anytype) SectionHeaderIterator(@TypeOf(parse_source)) { - return SectionHeaderIterator(@TypeOf(parse_source)){ - .elf_header = self, - .parse_source = parse_source, + pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator { + return .{ + .elf_header = h, + .file_reader = file_reader, }; } - pub fn read(parse_source: anytype) !Header { - var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; - try parse_source.seekableStream().seekTo(0); - try parse_source.deprecatedReader().readNoEof(&hdr_buf); - return Header.parse(&hdr_buf); - } + pub const ReadError = std.Io.Reader.Error || error{ + InvalidElfMagic, + InvalidElfVersion, + InvalidElfClass, + InvalidElfEndian, + }; - pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header { - const hdr32 = @as(*const Elf32_Ehdr, @ptrCast(hdr_buf)); - const hdr64 = @as(*const Elf64_Ehdr, @ptrCast(hdr_buf)); - if (!mem.eql(u8, hdr32.e_ident[0..4], MAGIC)) return error.InvalidElfMagic; - if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion; + pub fn read(r: *std.Io.Reader) ReadError!Header { + const buf = try r.peek(@sizeOf(Elf64_Ehdr)); - const is_64 = switch (hdr32.e_ident[EI_CLASS]) { - ELFCLASS32 => false, - ELFCLASS64 => true, - else => return error.InvalidElfClass, - }; + if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic; + if (buf[EI_VERSION] != 1) return error.InvalidElfVersion; - const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { + const endian: std.builtin.Endian = switch (buf[EI_DATA]) { ELFDATA2LSB => .little, ELFDATA2MSB => .big, else => return error.InvalidElfEndian, }; - const need_bswap = endian != native_endian; + return switch (buf[EI_CLASS]) { + ELFCLASS32 => .init(try r.takeStruct(Elf32_Ehdr, endian), endian), + ELFCLASS64 => .init(try r.takeStruct(Elf64_Ehdr, endian), endian), + else => return error.InvalidElfClass, + }; + } + + pub fn init(hdr: anytype, endian: std.builtin.Endian) Header { // Converting integers to exhaustive enums using `@enumFromInt` could cause a panic. comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive); - const os_abi: OSABI = @enumFromInt(hdr32.e_ident[EI_OSABI]); - - // The meaning of this value depends on `os_abi` so just make it available as `u8`. - const abi_version = hdr32.e_ident[EI_ABIVERSION]; - - const @"type" = if (need_bswap) blk: { - comptime assert(!@typeInfo(ET).@"enum".is_exhaustive); - const value = @intFromEnum(hdr32.e_type); - break :blk @as(ET, @enumFromInt(@byteSwap(value))); - } else hdr32.e_type; - - const machine = if (need_bswap) blk: { - comptime assert(!@typeInfo(EM).@"enum".is_exhaustive); - const value = @intFromEnum(hdr32.e_machine); - break :blk @as(EM, @enumFromInt(@byteSwap(value))); - } else hdr32.e_machine; - - return @as(Header, .{ - .is_64 = is_64, + return .{ + .is_64 = switch (@TypeOf(hdr)) { + Elf32_Ehdr => false, + Elf64_Ehdr => true, + else => @compileError("bad type"), + }, .endian = endian, - .os_abi = os_abi, - .abi_version = abi_version, - .type = @"type", - .machine = machine, - .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry), - .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff), - .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff), - .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize), - .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum), - .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize), - .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum), - .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx), - }); + .os_abi = @enumFromInt(hdr.e_ident[EI_OSABI]), + .abi_version = hdr.e_ident[EI_ABIVERSION], + .type = hdr.e_type, + .machine = hdr.e_machine, + .entry = hdr.e_entry, + .phoff = hdr.e_phoff, + .shoff = hdr.e_shoff, + .phentsize = hdr.e_phentsize, + .phnum = hdr.e_phnum, + .shentsize = hdr.e_shentsize, + .shnum = hdr.e_shnum, + .shstrndx = hdr.e_shstrndx, + }; } }; -pub fn ProgramHeaderIterator(comptime ParseSource: anytype) type { - return struct { - elf_header: Header, - parse_source: ParseSource, - index: usize = 0, +pub const ProgramHeaderIterator = struct { + elf_header: Header, + file_reader: *std.fs.File.Reader, + index: usize = 0, - pub fn next(self: *@This()) !?Elf64_Phdr { - if (self.index >= self.elf_header.phnum) return null; - defer self.index += 1; + pub fn next(it: *ProgramHeaderIterator) !?Elf64_Phdr { + if (it.index >= it.elf_header.phnum) return null; + defer it.index += 1; - if (self.elf_header.is_64) { - var phdr: Elf64_Phdr = undefined; - const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; - try self.parse_source.seekableStream().seekTo(offset); - try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr)); - - // ELF endianness matches native endianness. - if (self.elf_header.endian == native_endian) return phdr; - - // Convert fields to native endianness. - mem.byteSwapAllFields(Elf64_Phdr, &phdr); - return phdr; - } - - var phdr: Elf32_Phdr = undefined; - const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; - try self.parse_source.seekableStream().seekTo(offset); - try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr)); - - // ELF endianness does NOT match native endianness. - if (self.elf_header.endian != native_endian) { - // Convert fields to native endianness. - mem.byteSwapAllFields(Elf32_Phdr, &phdr); - } - - // Convert 32-bit header to 64-bit. - return Elf64_Phdr{ - .p_type = phdr.p_type, - .p_offset = phdr.p_offset, - .p_vaddr = phdr.p_vaddr, - .p_paddr = phdr.p_paddr, - .p_filesz = phdr.p_filesz, - .p_memsz = phdr.p_memsz, - .p_flags = phdr.p_flags, - .p_align = phdr.p_align, - }; + if (it.elf_header.is_64) { + const offset = it.elf_header.phoff + @sizeOf(Elf64_Phdr) * it.index; + try it.file_reader.seekTo(offset); + const phdr = try it.file_reader.interface.takeStruct(Elf64_Phdr, it.elf_header.endian); + return phdr; } - }; -} -pub fn SectionHeaderIterator(comptime ParseSource: anytype) type { - return struct { - elf_header: Header, - parse_source: ParseSource, - index: usize = 0, - - pub fn next(self: *@This()) !?Elf64_Shdr { - if (self.index >= self.elf_header.shnum) return null; - defer self.index += 1; - - if (self.elf_header.is_64) { - var shdr: Elf64_Shdr = undefined; - const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; - try self.parse_source.seekableStream().seekTo(offset); - try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr)); - - // ELF endianness matches native endianness. - if (self.elf_header.endian == native_endian) return shdr; - - // Convert fields to native endianness. - mem.byteSwapAllFields(Elf64_Shdr, &shdr); - return shdr; - } - - var shdr: Elf32_Shdr = undefined; - const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; - try self.parse_source.seekableStream().seekTo(offset); - try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr)); - - // ELF endianness does NOT match native endianness. - if (self.elf_header.endian != native_endian) { - // Convert fields to native endianness. - mem.byteSwapAllFields(Elf32_Shdr, &shdr); - } - - // Convert 32-bit header to 64-bit. - return Elf64_Shdr{ - .sh_name = shdr.sh_name, - .sh_type = shdr.sh_type, - .sh_flags = shdr.sh_flags, - .sh_addr = shdr.sh_addr, - .sh_offset = shdr.sh_offset, - .sh_size = shdr.sh_size, - .sh_link = shdr.sh_link, - .sh_info = shdr.sh_info, - .sh_addralign = shdr.sh_addralign, - .sh_entsize = shdr.sh_entsize, - }; - } - }; -} - -fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) { - if (is_64) { - if (need_bswap) { - return @byteSwap(int_64); - } else { - return int_64; - } - } else { - return int32(need_bswap, int_32, @TypeOf(int_64)); + const offset = it.elf_header.phoff + @sizeOf(Elf32_Phdr) * it.index; + try it.file_reader.seekTo(offset); + const phdr = try it.file_reader.interface.takeStruct(Elf32_Phdr, it.elf_header.endian); + return .{ + .p_type = phdr.p_type, + .p_offset = phdr.p_offset, + .p_vaddr = phdr.p_vaddr, + .p_paddr = phdr.p_paddr, + .p_filesz = phdr.p_filesz, + .p_memsz = phdr.p_memsz, + .p_flags = phdr.p_flags, + .p_align = phdr.p_align, + }; } -} +}; -fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 { - if (need_bswap) { - return @byteSwap(int_32); - } else { - return int_32; +pub const SectionHeaderIterator = struct { + elf_header: Header, + file_reader: *std.fs.File.Reader, + index: usize = 0, + + pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr { + if (it.index >= it.elf_header.shnum) return null; + defer it.index += 1; + + if (it.elf_header.is_64) { + try it.file_reader.seekTo(it.elf_header.shoff + @sizeOf(Elf64_Shdr) * it.index); + const shdr = try it.file_reader.interface.takeStruct(Elf64_Shdr, it.elf_header.endian); + return shdr; + } + + try it.file_reader.seekTo(it.elf_header.shoff + @sizeOf(Elf32_Shdr) * it.index); + const shdr = try it.file_reader.interface.takeStruct(Elf32_Shdr, it.elf_header.endian); + return .{ + .sh_name = shdr.sh_name, + .sh_type = shdr.sh_type, + .sh_flags = shdr.sh_flags, + .sh_addr = shdr.sh_addr, + .sh_offset = shdr.sh_offset, + .sh_size = shdr.sh_size, + .sh_link = shdr.sh_link, + .sh_info = shdr.sh_info, + .sh_addralign = shdr.sh_addralign, + .sh_entsize = shdr.sh_entsize, + }; } -} +}; pub const ELFCLASSNONE = 0; pub const ELFCLASS32 = 1; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 50f2a30876..85264dd4db 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1228,14 +1228,12 @@ pub const Reader = struct { pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void { switch (r.mode) { .positional, .positional_reading => { - // TODO: make += operator allow any integer types - r.pos = @intCast(@as(i64, @intCast(r.pos)) + offset); + setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); }, .streaming, .streaming_reading => { const seek_err = r.seek_err orelse e: { if (posix.lseek_CUR(r.file.handle, offset)) |_| { - // TODO: make += operator allow any integer types - r.pos = @intCast(@as(i64, @intCast(r.pos)) + offset); + setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); return; } else |err| { r.seek_err = err; @@ -1251,6 +1249,8 @@ pub const Reader = struct { r.pos += n; remaining -= n; } + r.interface.seek = 0; + r.interface.end = 0; }, .failure => return r.seek_err.?, } @@ -1259,7 +1259,7 @@ pub const Reader = struct { pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void { switch (r.mode) { .positional, .positional_reading => { - r.pos = offset; + setPosAdjustingBuffer(r, offset); }, .streaming, .streaming_reading => { if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos)); @@ -1268,12 +1268,22 @@ pub const Reader = struct { r.seek_err = err; return err; }; - r.pos = offset; + setPosAdjustingBuffer(r, offset); }, .failure => return r.seek_err.?, } } + fn setPosAdjustingBuffer(r: *Reader, offset: u64) void { + if (offset < r.pos or offset >= r.pos + r.interface.bufferedLen()) { + r.interface.seek = 0; + r.interface.end = 0; + } else { + r.interface.seek += @intCast(offset - r.pos); + } + r.pos = offset; + } + /// Number of slices to store on the stack, when trying to send as many byte /// vectors through the underlying read calls as possible. const max_buffers_len = 16; diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index a036a64ab7..878859312b 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -65,69 +65,70 @@ pub fn build(b: *std.Build) void { test_step.dependOn(&run_cmd.step); } - // Unwinding through a C shared library without a frame pointer (libc) - // - // getcontext version: libc - // - // Unwind info type: - // - ELF: DWARF .eh_frame + .debug_frame - // - MachO: __unwind_info encodings: - // - x86_64: STACK_IMMD, STACK_IND - // - aarch64: FRAMELESS, DWARF - { - const c_shared_lib = b.addLibrary(.{ - .linkage = .dynamic, - .name = "c_shared_lib", - .root_module = b.createModule(.{ - .root_source_file = null, - .target = target, - .optimize = optimize, - .link_libc = true, - .strip = false, - }), - }); + // https://github.com/ziglang/zig/issues/24522 + //// Unwinding through a C shared library without a frame pointer (libc) + //// + //// getcontext version: libc + //// + //// Unwind info type: + //// - ELF: DWARF .eh_frame + .debug_frame + //// - MachO: __unwind_info encodings: + //// - x86_64: STACK_IMMD, STACK_IND + //// - aarch64: FRAMELESS, DWARF + //{ + // const c_shared_lib = b.addLibrary(.{ + // .linkage = .dynamic, + // .name = "c_shared_lib", + // .root_module = b.createModule(.{ + // .root_source_file = null, + // .target = target, + // .optimize = optimize, + // .link_libc = true, + // .strip = false, + // }), + // }); - if (target.result.os.tag == .windows) - c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)"); + // if (target.result.os.tag == .windows) + // c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)"); - c_shared_lib.root_module.addCSourceFile(.{ - .file = b.path("shared_lib.c"), - .flags = &.{"-fomit-frame-pointer"}, - }); + // c_shared_lib.root_module.addCSourceFile(.{ + // .file = b.path("shared_lib.c"), + // .flags = &.{"-fomit-frame-pointer"}, + // }); - const exe = b.addExecutable(.{ - .name = "shared_lib_unwind", - .root_module = b.createModule(.{ - .root_source_file = b.path("shared_lib_unwind.zig"), - .target = target, - .optimize = optimize, - .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null, - .omit_frame_pointer = true, - }), - // zig objcopy doesn't support incremental binaries - .use_llvm = true, - }); + // const exe = b.addExecutable(.{ + // .name = "shared_lib_unwind", + // .root_module = b.createModule(.{ + // .root_source_file = b.path("shared_lib_unwind.zig"), + // .target = target, + // .optimize = optimize, + // .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null, + // .omit_frame_pointer = true, + // }), + // // zig objcopy doesn't support incremental binaries + // .use_llvm = true, + // }); - exe.linkLibrary(c_shared_lib); + // exe.linkLibrary(c_shared_lib); - const run_cmd = b.addRunArtifact(exe); - test_step.dependOn(&run_cmd.step); + // const run_cmd = b.addRunArtifact(exe); + // test_step.dependOn(&run_cmd.step); - // Separate debug info ELF file - if (target.result.ofmt == .elf) { - const filename = b.fmt("{s}_stripped", .{exe.out_filename}); - const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{ - .basename = filename, // set the name for the debuglink - .compress_debug = true, - .strip = .debug, - .extract_to_separate_file = true, - }); + // // Separate debug info ELF file + // if (target.result.ofmt == .elf) { + // const filename = b.fmt("{s}_stripped", .{exe.out_filename}); + // const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{ + // .basename = filename, // set the name for the debuglink + // .compress_debug = true, + // .strip = .debug, + // .extract_to_separate_file = true, + // }); - const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename})); - run_stripped.addFileArg(stripped_exe.getOutput()); - test_step.dependOn(&run_stripped.step); - } - } + // const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename})); + // run_stripped.addFileArg(stripped_exe.getOutput()); + // test_step.dependOn(&run_stripped.step); + // } + //} // Unwinding without libc/posix // diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig index ed60d7e67d..9fdd63eda1 100644 --- a/tools/gen_stubs.zig +++ b/tools/gen_stubs.zig @@ -310,7 +310,8 @@ pub fn main() !void { build_all_path, libc_so_path, @errorName(err), }); }; - const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]); + var stream: std.Io.Reader = .fixed(elf_bytes); + const header = try elf.Header.read(&stream); const parse: Parse = .{ .arena = arena, From 38559e282b137ea365ab074e3d78cf3312dd5702 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 20:36:26 -0700 Subject: [PATCH 13/76] disable failing test tracked by #24524 --- test/incremental/fix_many_errors | 71 -------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 test/incremental/fix_many_errors diff --git a/test/incremental/fix_many_errors b/test/incremental/fix_many_errors deleted file mode 100644 index 1d9446022c..0000000000 --- a/test/incremental/fix_many_errors +++ /dev/null @@ -1,71 +0,0 @@ -#target=x86_64-linux-selfhosted -#target=x86_64-linux-cbe -#target=x86_64-windows-cbe -#update=initial version -#file=main.zig -pub fn main() !void {} -comptime { @compileError("c0"); } -comptime { @compileError("c1"); } -comptime { @compileError("c2"); } -comptime { @compileError("c3"); } -comptime { @compileError("c4"); } -comptime { @compileError("c5"); } -comptime { @compileError("c6"); } -comptime { @compileError("c7"); } -comptime { @compileError("c8"); } -comptime { @compileError("c9"); } -export fn f0() void { @compileError("f0"); } -export fn f1() void { @compileError("f1"); } -export fn f2() void { @compileError("f2"); } -export fn f3() void { @compileError("f3"); } -export fn f4() void { @compileError("f4"); } -export fn f5() void { @compileError("f5"); } -export fn f6() void { @compileError("f6"); } -export fn f7() void { @compileError("f7"); } -export fn f8() void { @compileError("f8"); } -export fn f9() void { @compileError("f9"); } -#expect_error=main.zig:2:12: error: c0 -#expect_error=main.zig:3:12: error: c1 -#expect_error=main.zig:4:12: error: c2 -#expect_error=main.zig:5:12: error: c3 -#expect_error=main.zig:6:12: error: c4 -#expect_error=main.zig:7:12: error: c5 -#expect_error=main.zig:8:12: error: c6 -#expect_error=main.zig:9:12: error: c7 -#expect_error=main.zig:10:12: error: c8 -#expect_error=main.zig:11:12: error: c9 -#expect_error=main.zig:12:23: error: f0 -#expect_error=main.zig:13:23: error: f1 -#expect_error=main.zig:14:23: error: f2 -#expect_error=main.zig:15:23: error: f3 -#expect_error=main.zig:16:23: error: f4 -#expect_error=main.zig:17:23: error: f5 -#expect_error=main.zig:18:23: error: f6 -#expect_error=main.zig:19:23: error: f7 -#expect_error=main.zig:20:23: error: f8 -#expect_error=main.zig:21:23: error: f9 -#update=fix all the errors -#file=main.zig -pub fn main() !void {} -comptime {} -comptime {} -comptime {} -comptime {} -comptime {} -comptime {} -comptime {} -comptime {} -comptime {} -comptime {} -export fn f0() void {} -export fn f1() void {} -export fn f2() void {} -export fn f3() void {} -export fn f4() void {} -export fn f5() void {} -export fn f6() void {} -export fn f7() void {} -export fn f8() void {} -export fn f9() void {} -const std = @import("std"); -#expect_stdout="" From fe10c66d664ae7b1acb2bcf01600eeacc2958fc7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 20:46:07 -0700 Subject: [PATCH 14/76] std.fs.File.Reader: only fcopyfile if size available --- lib/std/fs/File.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 85264dd4db..04b2f1dd94 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1825,6 +1825,7 @@ pub const Writer = struct { if (file_reader.pos != 0) break :fcf; if (w.pos != 0) break :fcf; if (limit != .unlimited) break :fcf; + const size = file_reader.getSize() catch break :fcf; const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true }); switch (posix.errno(rc)) { .SUCCESS => {}, @@ -1845,10 +1846,9 @@ pub const Writer = struct { return 0; }, } - const n = if (file_reader.size) |size| size else @panic("TODO figure out how much copied"); - file_reader.pos = n; - w.pos = n; - return n; + file_reader.pos = size; + w.pos = size; + return size; } return error.Unimplemented; From b35c55e2373ace674cd1eec7f5086b805d1c8256 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Jul 2025 18:13:45 -0700 Subject: [PATCH 15/76] std.fs.File.Reader: fix seek position logic --- lib/std/fs/File.zig | 14 ++++++++++---- lib/std/fs/test.zig | 47 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 04b2f1dd94..a95bd1401a 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1135,7 +1135,7 @@ pub const Reader = struct { err: ?ReadError = null, mode: Reader.Mode = .positional, /// Tracks the true seek position in the file. To obtain the logical - /// position, subtract the buffer size from this value. + /// position, use `logicalPos`. pos: u64 = 0, size: ?u64 = null, size_err: ?GetEndPosError = null, @@ -1274,14 +1274,20 @@ pub const Reader = struct { } } + pub fn logicalPos(r: *const Reader) u64 { + return r.pos - r.interface.bufferedLen(); + } + fn setPosAdjustingBuffer(r: *Reader, offset: u64) void { - if (offset < r.pos or offset >= r.pos + r.interface.bufferedLen()) { + const logical_pos = logicalPos(r); + if (offset < logical_pos or offset >= r.pos) { r.interface.seek = 0; r.interface.end = 0; + r.pos = offset; } else { - r.interface.seek += @intCast(offset - r.pos); + const logical_delta: usize = @intCast(offset - logical_pos); + r.interface.seek += logical_delta; } - r.pos = offset; } /// Number of slices to store on the stack, when trying to send as many byte diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 9fe2551738..4b63873af5 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -2060,7 +2060,7 @@ test "invalid UTF-8/WTF-8 paths" { } test "read file non vectored" { - var tmp_dir = std.testing.tmpDir(.{}); + var tmp_dir = testing.tmpDir(.{}); defer tmp_dir.cleanup(); const contents = "hello, world!\n"; @@ -2085,6 +2085,47 @@ test "read file non vectored" { else => |e| return e, }; } - try std.testing.expectEqualStrings(contents, w.buffered()); - try std.testing.expectEqual(contents.len, i); + try testing.expectEqualStrings(contents, w.buffered()); + try testing.expectEqual(contents.len, i); +} + +test "seek keeping partial buffer" { + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + const contents = "0123456789"; + + const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + defer file.close(); + { + var file_writer: std.fs.File.Writer = .init(file, &.{}); + try file_writer.interface.writeAll(contents); + try file_writer.interface.flush(); + } + + var read_buffer: [3]u8 = undefined; + var file_reader: std.fs.File.Reader = .init(file, &read_buffer); + + try testing.expectEqual(0, file_reader.logicalPos()); + + var buf: [4]u8 = undefined; + try file_reader.interface.readSliceAll(&buf); + + if (file_reader.interface.bufferedLen() != 3) { + // Pass the test if the OS doesn't give us vectored reads. + return; + } + + try testing.expectEqual(4, file_reader.logicalPos()); + try testing.expectEqual(7, file_reader.pos); + try file_reader.seekTo(6); + try testing.expectEqual(6, file_reader.logicalPos()); + try testing.expectEqual(7, file_reader.pos); + + try testing.expectEqualStrings("0123", &buf); + + const n = try file_reader.interface.readSliceShort(&buf); + try testing.expectEqual(4, n); + + try testing.expectEqualStrings("6789", &buf); } From 96cbdd145d6ec5bf2e3ed80743c99ab4b2cdff68 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Jul 2025 20:00:45 -0700 Subject: [PATCH 16/76] std.fs.File.Reader: fix sendFile logic it wasn't accounting for both writer and reader buffering --- lib/std/c.zig | 4 +- lib/std/fs/File.zig | 134 ++++++++++++++++++-- lib/std/posix.zig | 289 -------------------------------------------- 3 files changed, 128 insertions(+), 299 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 2880e3850a..74bc76d67f 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10497,9 +10497,9 @@ pub const sysconf = switch (native_os) { pub const sf_hdtr = switch (native_os) { .freebsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct { - headers: [*]const iovec_const, + headers: ?[*]const iovec_const, hdr_cnt: c_int, - trailers: [*]const iovec_const, + trailers: ?[*]const iovec_const, trl_cnt: c_int, }, else => void, diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index a95bd1401a..2091b27f60 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1435,7 +1435,7 @@ pub const Reader = struct { } return 0; }; - const n = @min(size - pos, std.math.maxInt(i64), @intFromEnum(limit)); + const n = @min(size - pos, maxInt(i64), @intFromEnum(limit)); file.seekBy(n) catch |err| { r.seek_err = err; return 0; @@ -1726,18 +1726,123 @@ pub const Writer = struct { file_reader: *Reader, 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); + const writer_buffered = io_w.buffered(); + const file_limit = @intFromEnum(limit) - reader_buffered.len; const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); const out_fd = w.file.handle; const in_fd = file_reader.file.handle; - // TODO try using copy_file_range on FreeBSD - // TODO try using sendfile on macOS - // TODO try using sendfile on FreeBSD + + if (native_os == .freebsd and w.mode == .streaming) sf: { + // Try using sendfile on FreeBSD. + if (w.sendfile_err != null) break :sf; + const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf; + var hdtr_data: std.c.sf_hdtr = undefined; + var headers: [2]posix.iovec_const = undefined; + var headers_i: u8 = 0; + if (writer_buffered.len != 0) { + headers[headers_i] = .{ .base = writer_buffered.ptr, .len = writer_buffered.len }; + headers_i += 1; + } + if (reader_buffered.len != 0) { + headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len }; + headers_i += 1; + } + const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: { + hdtr_data = .{ + .headers = &headers, + .hdr_cnt = headers_i, + .trailers = null, + .trl_cnt = 0, + }; + break :b &hdtr_data; + }; + var sbytes: std.c.off_t = undefined; + const nbytes: usize = @min(file_limit, maxInt(usize)); + const flags = 0; + switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) { + .SUCCESS, .INTR => {}, + .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => w.sendfile_err = error.UnsupportedOperation, + .BADF => if (builtin.mode == .Debug) @panic("race condition") else { + w.sendfile_err = error.Unexpected; + }, + .FAULT => if (builtin.mode == .Debug) @panic("segmentation fault") else { + w.sendfile_err = error.Unexpected; + }, + .NOTCONN => w.sendfile_err = error.BrokenPipe, + .AGAIN, .BUSY => if (sbytes == 0) { + w.sendfile_err = error.WouldBlock; + }, + .IO => w.sendfile_err = error.InputOutput, + .PIPE => w.sendfile_err = error.BrokenPipe, + .NOBUFS => w.sendfile_err = error.SystemResources, + else => |err| w.sendfile_err = posix.unexpectedErrno(err), + } + const consumed = io_w.consume(@bitCast(sbytes)); + file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; + return consumed; + } + + if (native_os.isDarwin() and w.mode == .streaming) sf: { + // Try using sendfile on macOS. + if (w.sendfile_err != null) break :sf; + const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf; + var hdtr_data: std.c.sf_hdtr = undefined; + var headers: [2]posix.iovec_const = undefined; + var headers_i: u8 = 0; + if (writer_buffered.len != 0) { + headers[headers_i] = .{ .base = writer_buffered.ptr, .len = writer_buffered.len }; + headers_i += 1; + } + if (reader_buffered.len != 0) { + headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len }; + headers_i += 1; + } + const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: { + hdtr_data = .{ + .headers = &headers, + .hdr_cnt = headers_i, + .trailers = null, + .trl_cnt = 0, + }; + break :b &hdtr_data; + }; + const max_count = maxInt(i32); // Avoid EINVAL. + var sbytes: std.c.off_t = @min(file_limit, max_count); + const flags = 0; + switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &sbytes, hdtr, flags))) { + .SUCCESS, .INTR => {}, + .OPNOTSUPP, .NOTSOCK, .NOSYS => w.sendfile_err = error.UnsupportedOperation, + .BADF => if (builtin.mode == .Debug) @panic("race condition") else { + w.sendfile_err = error.Unexpected; + }, + .FAULT => if (builtin.mode == .Debug) @panic("segmentation fault") else { + w.sendfile_err = error.Unexpected; + }, + .INVAL => if (builtin.mode == .Debug) @panic("invalid API usage") else { + w.sendfile_err = error.Unexpected; + }, + .NOTCONN => w.sendfile_err = error.BrokenPipe, + .AGAIN => if (sbytes == 0) { + w.sendfile_err = error.WouldBlock; + }, + .IO => w.sendfile_err = error.InputOutput, + .PIPE => w.sendfile_err = error.BrokenPipe, + else => |err| w.sendfile_err = posix.unexpectedErrno(err), + } + const consumed = io_w.consume(@bitCast(sbytes)); + file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; + return consumed; + } + if (native_os == .linux and w.mode == .streaming) sf: { // Try using sendfile on Linux. if (w.sendfile_err != null) break :sf; // Linux sendfile does not support headers. - const buffered = limit.slice(file_reader.interface.buffer); - if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1); + if (writer_buffered.len != 0 or reader_buffered.len != 0) + return sendFileBuffered(io_w, file_reader, reader_buffered); const max_count = 0x7ffff000; // Avoid EINVAL. var off: std.os.linux.off_t = undefined; const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) { @@ -1784,6 +1889,7 @@ pub const Writer = struct { w.pos += n; return n; } + const copy_file_range = switch (native_os) { .freebsd => std.os.freebsd.copy_file_range, .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {}, @@ -1791,8 +1897,8 @@ pub const Writer = struct { }; if (@TypeOf(copy_file_range) != void) cfr: { if (w.copy_file_range_err != null) break :cfr; - const buffered = limit.slice(file_reader.interface.buffer); - if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1); + if (writer_buffered.len != 0 or reader_buffered.len != 0) + return sendFileBuffered(io_w, file_reader, reader_buffered); var off_in: i64 = undefined; var off_out: i64 = undefined; const off_in_ptr: ?*i64 = switch (file_reader.mode) { @@ -1832,6 +1938,8 @@ pub const Writer = struct { if (w.pos != 0) break :fcf; if (limit != .unlimited) break :fcf; const size = file_reader.getSize() catch break :fcf; + if (writer_buffered.len != 0 or reader_buffered.len != 0) + return sendFileBuffered(io_w, file_reader, reader_buffered); const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true }); switch (posix.errno(rc)) { .SUCCESS => {}, @@ -1860,6 +1968,16 @@ pub const Writer = struct { return error.Unimplemented; } + fn sendFileBuffered( + io_w: *std.io.Writer, + file_reader: *Reader, + reader_buffered: []const u8, + ) 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; + } + pub fn seekTo(w: *Writer, offset: u64) SeekError!void { switch (w.mode) { .positional, .positional_reading => { diff --git a/lib/std/posix.zig b/lib/std/posix.zig index e3e1657705..3f654422b1 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -6326,295 +6326,6 @@ pub fn send( }; } -pub const SendFileError = PReadError || WriteError || SendError; - -/// Transfer data between file descriptors, with optional headers and trailers. -/// -/// Returns the number of bytes written, which can be zero. -/// -/// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible, -/// this is done within the operating system kernel, which can provide better performance -/// characteristics than transferring data from kernel to user space and back, such as with -/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been -/// reached. Note, however, that partial writes are still possible in this case. -/// -/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor -/// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular -/// file system file, it may cause this function to fall back to calling `read` and `write`, in which case -/// atomicity guarantees no longer apply. -/// -/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated. -/// If the output file descriptor has a seek position, it is updated as bytes are written. When -/// `in_offset` is past the end of the input file, it successfully reads 0 bytes. -/// -/// `flags` has different meanings per operating system; refer to the respective man pages. -/// -/// These systems support atomically sending everything, including headers and trailers: -/// * macOS -/// * FreeBSD -/// -/// These systems support in-kernel data copying, but headers and trailers are not sent atomically: -/// * Linux -/// -/// Other systems fall back to calling `read` / `write`. -/// -/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000` -/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as -/// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page. -/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL. -/// The corresponding POSIX limit on this is `maxInt(isize)`. -pub fn sendfile( - out_fd: fd_t, - in_fd: fd_t, - in_offset: u64, - in_len: u64, - headers: []const iovec_const, - trailers: []const iovec_const, - flags: u32, -) SendFileError!usize { - var header_done = false; - var total_written: usize = 0; - - // Prevents EOVERFLOW. - const size_t = std.meta.Int(.unsigned, @typeInfo(usize).int.bits - 1); - const max_count = switch (native_os) { - .linux => 0x7ffff000, - .macos, .ios, .watchos, .tvos, .visionos => maxInt(i32), - else => maxInt(size_t), - }; - - switch (native_os) { - .linux => sf: { - if (headers.len != 0) { - const amt = try writev(out_fd, headers); - total_written += amt; - if (amt < count_iovec_bytes(headers)) return total_written; - header_done = true; - } - - // Here we match BSD behavior, making a zero count value send as many bytes as possible. - const adjusted_count = if (in_len == 0) max_count else @min(in_len, max_count); - - const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile; - while (true) { - var offset: off_t = @bitCast(in_offset); - const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count); - switch (errno(rc)) { - .SUCCESS => { - const amt: usize = @bitCast(rc); - total_written += amt; - if (in_len == 0 and amt == 0) { - // We have detected EOF from `in_fd`. - break; - } else if (amt < in_len) { - return total_written; - } else { - break; - } - }, - - .BADF => unreachable, // Always a race condition. - .FAULT => unreachable, // Segmentation fault. - .OVERFLOW => unreachable, // We avoid passing too large of a `count`. - .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket - - .INVAL => { - // EINVAL could be any of the following situations: - // * Descriptor is not valid or locked - // * an mmap(2)-like operation is not available for in_fd - // * count is negative - // * out_fd has the APPEND flag set - // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write - // manually. - break :sf; - }, - .AGAIN => return error.WouldBlock, - .IO => return error.InputOutput, - .PIPE => return error.BrokenPipe, - .NOMEM => return error.SystemResources, - .NXIO => return error.Unseekable, - .SPIPE => return error.Unseekable, - else => |err| { - unexpectedErrno(err) catch {}; - break :sf; - }, - } - } - - if (trailers.len != 0) { - total_written += try writev(out_fd, trailers); - } - - return total_written; - }, - .freebsd => sf: { - var hdtr_data: std.c.sf_hdtr = undefined; - var hdtr: ?*std.c.sf_hdtr = null; - if (headers.len != 0 or trailers.len != 0) { - // Here we carefully avoid `@intCast` by returning partial writes when - // too many io vectors are provided. - const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31); - if (headers.len > hdr_cnt) return writev(out_fd, headers); - - const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31); - - hdtr_data = std.c.sf_hdtr{ - .headers = headers.ptr, - .hdr_cnt = hdr_cnt, - .trailers = trailers.ptr, - .trl_cnt = trl_cnt, - }; - hdtr = &hdtr_data; - } - - while (true) { - var sbytes: off_t = undefined; - const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), @min(in_len, max_count), hdtr, &sbytes, flags)); - const amt: usize = @bitCast(sbytes); - switch (err) { - .SUCCESS => return amt, - - .BADF => unreachable, // Always a race condition. - .FAULT => unreachable, // Segmentation fault. - .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket - - .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => { - // EINVAL could be any of the following situations: - // * The fd argument is not a regular file. - // * The s argument is not a SOCK.STREAM type socket. - // * The offset argument is negative. - // Because of some of these possibilities, we fall back to doing read/write - // manually, the same as ENOSYS. - break :sf; - }, - - .INTR => if (amt != 0) return amt else continue, - - .AGAIN => if (amt != 0) { - return amt; - } else { - return error.WouldBlock; - }, - - .BUSY => if (amt != 0) { - return amt; - } else { - return error.WouldBlock; - }, - - .IO => return error.InputOutput, - .NOBUFS => return error.SystemResources, - .PIPE => return error.BrokenPipe, - - else => { - unexpectedErrno(err) catch {}; - if (amt != 0) { - return amt; - } else { - break :sf; - } - }, - } - } - }, - .macos, .ios, .tvos, .watchos, .visionos => sf: { - var hdtr_data: std.c.sf_hdtr = undefined; - var hdtr: ?*std.c.sf_hdtr = null; - if (headers.len != 0 or trailers.len != 0) { - // Here we carefully avoid `@intCast` by returning partial writes when - // too many io vectors are provided. - const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31); - if (headers.len > hdr_cnt) return writev(out_fd, headers); - - const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31); - - hdtr_data = std.c.sf_hdtr{ - .headers = headers.ptr, - .hdr_cnt = hdr_cnt, - .trailers = trailers.ptr, - .trl_cnt = trl_cnt, - }; - hdtr = &hdtr_data; - } - - while (true) { - var sbytes: off_t = @min(in_len, max_count); - const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), &sbytes, hdtr, flags)); - const amt: usize = @bitCast(sbytes); - switch (err) { - .SUCCESS => return amt, - - .BADF => unreachable, // Always a race condition. - .FAULT => unreachable, // Segmentation fault. - .INVAL => unreachable, - .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket - - .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf, - - .INTR => if (amt != 0) return amt else continue, - - .AGAIN => if (amt != 0) { - return amt; - } else { - return error.WouldBlock; - }, - - .IO => return error.InputOutput, - .PIPE => return error.BrokenPipe, - - else => { - unexpectedErrno(err) catch {}; - if (amt != 0) { - return amt; - } else { - break :sf; - } - }, - } - } - }, - else => {}, // fall back to read/write - } - - if (headers.len != 0 and !header_done) { - const amt = try writev(out_fd, headers); - total_written += amt; - if (amt < count_iovec_bytes(headers)) return total_written; - } - - rw: { - var buf: [8 * 4096]u8 = undefined; - // Here we match BSD behavior, making a zero count value send as many bytes as possible. - const adjusted_count = if (in_len == 0) buf.len else @min(buf.len, in_len); - const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset); - if (amt_read == 0) { - if (in_len == 0) { - // We have detected EOF from `in_fd`. - break :rw; - } else { - return total_written; - } - } - const amt_written = try write(out_fd, buf[0..amt_read]); - total_written += amt_written; - if (amt_written < in_len or in_len == 0) return total_written; - } - - if (trailers.len != 0) { - total_written += try writev(out_fd, trailers); - } - - return total_written; -} - -fn count_iovec_bytes(iovs: []const iovec_const) usize { - var count: usize = 0; - for (iovs) |iov| { - count += iov.len; - } - return count; -} - pub const CopyFileRangeError = error{ FileTooBig, InputOutput, From 76fe518d498116763dc1e3a7669619a3a9413715 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Jul 2025 23:26:18 -0700 Subject: [PATCH 17/76] std.fs.File.Reader.sendFile: fix EndOfStream detection --- lib/std/fs/File.zig | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 2091b27f60..be993895bc 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1624,7 +1624,6 @@ pub const Writer = struct { const pattern = data[data.len - 1]; if (pattern.len == 0 or splat == 0) return 0; const n = windows.WriteFile(handle, pattern, null) catch |err| { - std.debug.print("windows write file failed3: {t}\n", .{err}); w.err = err; return error.WriteFailed; }; @@ -1735,6 +1734,16 @@ pub const Writer = struct { const out_fd = w.file.handle; const in_fd = file_reader.file.handle; + if (file_reader.size) |size| { + if (size - file_reader.pos == 0) { + if (reader_buffered.len != 0) { + return sendFileBuffered(io_w, file_reader, reader_buffered); + } else { + return error.EndOfStream; + } + } + } + if (native_os == .freebsd and w.mode == .streaming) sf: { // Try using sendfile on FreeBSD. if (w.sendfile_err != null) break :sf; @@ -1780,6 +1789,10 @@ pub const Writer = struct { .NOBUFS => w.sendfile_err = error.SystemResources, else => |err| w.sendfile_err = posix.unexpectedErrno(err), } + if (sbytes == 0) { + file_reader.size = file_reader.pos; + return error.EndOfStream; + } const consumed = io_w.consume(@bitCast(sbytes)); file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; return consumed; @@ -1810,9 +1823,9 @@ pub const Writer = struct { break :b &hdtr_data; }; const max_count = maxInt(i32); // Avoid EINVAL. - var sbytes: std.c.off_t = @min(file_limit, max_count); + var len: std.c.off_t = @min(file_limit, max_count); const flags = 0; - switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &sbytes, hdtr, flags))) { + switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) { .SUCCESS, .INTR => {}, .OPNOTSUPP, .NOTSOCK, .NOSYS => w.sendfile_err = error.UnsupportedOperation, .BADF => if (builtin.mode == .Debug) @panic("race condition") else { @@ -1825,14 +1838,18 @@ pub const Writer = struct { w.sendfile_err = error.Unexpected; }, .NOTCONN => w.sendfile_err = error.BrokenPipe, - .AGAIN => if (sbytes == 0) { + .AGAIN => if (len == 0) { w.sendfile_err = error.WouldBlock; }, .IO => w.sendfile_err = error.InputOutput, .PIPE => w.sendfile_err = error.BrokenPipe, else => |err| w.sendfile_err = posix.unexpectedErrno(err), } - const consumed = io_w.consume(@bitCast(sbytes)); + if (len == 0) { + file_reader.size = file_reader.pos; + return error.EndOfStream; + } + const consumed = io_w.consume(@bitCast(len)); file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; return consumed; } From 34d2778239b7eea854385354fd956358ae7cf5a0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jul 2025 09:39:24 -0700 Subject: [PATCH 18/76] std.fs.File.Reader.sendFile: fix 32-bit freebsd --- lib/std/fs/File.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index be993895bc..138807972e 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1793,7 +1793,7 @@ pub const Writer = struct { file_reader.size = file_reader.pos; return error.EndOfStream; } - const consumed = io_w.consume(@bitCast(sbytes)); + const consumed = io_w.consume(@intCast(sbytes)); file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; return consumed; } From d509bc933fe749cad270e11a7f3be88e1ec9a1a7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:02:01 -0700 Subject: [PATCH 19/76] std.Io: delete CWriter it shan't be missed --- lib/std/Io.zig | 4 ---- lib/std/Io/c_writer.zig | 44 ----------------------------------------- 2 files changed, 48 deletions(-) delete mode 100644 lib/std/Io/c_writer.zig diff --git a/lib/std/Io.zig b/lib/std/Io.zig index ff6966d7f7..3865c17159 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -448,9 +448,6 @@ pub const bufferedReaderSize = @import("Io/buffered_reader.zig").bufferedReaderS pub const FixedBufferStream = @import("Io/fixed_buffer_stream.zig").FixedBufferStream; pub const fixedBufferStream = @import("Io/fixed_buffer_stream.zig").fixedBufferStream; -pub const CWriter = @import("Io/c_writer.zig").CWriter; -pub const cWriter = @import("Io/c_writer.zig").cWriter; - pub const LimitedReader = @import("Io/limited_reader.zig").LimitedReader; pub const limitedReader = @import("Io/limited_reader.zig").limitedReader; @@ -903,7 +900,6 @@ test { _ = @import("Io/buffered_atomic_file.zig"); _ = @import("Io/buffered_reader.zig"); _ = @import("Io/buffered_writer.zig"); - _ = @import("Io/c_writer.zig"); _ = @import("Io/counting_writer.zig"); _ = @import("Io/counting_reader.zig"); _ = @import("Io/fixed_buffer_stream.zig"); diff --git a/lib/std/Io/c_writer.zig b/lib/std/Io/c_writer.zig deleted file mode 100644 index 30d0cabcf5..0000000000 --- a/lib/std/Io/c_writer.zig +++ /dev/null @@ -1,44 +0,0 @@ -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const io = std.io; -const testing = std.testing; - -pub const CWriter = io.GenericWriter(*std.c.FILE, std.fs.File.WriteError, cWriterWrite); - -pub fn cWriter(c_file: *std.c.FILE) CWriter { - return .{ .context = c_file }; -} - -fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { - const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); - if (amt_written >= 0) return amt_written; - switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) { - .SUCCESS => unreachable, - .INVAL => unreachable, - .FAULT => unreachable, - .AGAIN => unreachable, // this is a blocking API - .BADF => unreachable, // always a race condition - .DESTADDRREQ => unreachable, // connect was never called - .DQUOT => return error.DiskQuota, - .FBIG => return error.FileTooBig, - .IO => return error.InputOutput, - .NOSPC => return error.NoSpaceLeft, - .PERM => return error.PermissionDenied, - .PIPE => return error.BrokenPipe, - else => |err| return std.posix.unexpectedErrno(err), - } -} - -test cWriter { - if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest; - - const filename = "tmp_io_test_file.txt"; - const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; - defer { - _ = std.c.fclose(out_file); - std.fs.cwd().deleteFileZ(filename) catch {}; - } - - const writer = cWriter(out_file); - try writer.print("hi: {}\n", .{@as(i32, 123)}); -} From a3efdd7279179de9591e62cab38bafdafd7e4814 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:04:00 -0700 Subject: [PATCH 20/76] std.Io: delete StreamSource it shan't be missed --- CMakeLists.txt | 9 --- lib/std/Io.zig | 3 - lib/std/Io/stream_source.zig | 127 ----------------------------------- 3 files changed, 139 deletions(-) delete mode 100644 lib/std/Io/stream_source.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index d9824e5c12..c0a53c28e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -390,15 +390,6 @@ set(ZIG_STAGE2_SOURCES lib/std/Io.zig lib/std/Io/Reader.zig lib/std/Io/Writer.zig - lib/std/Io/buffered_atomic_file.zig - lib/std/Io/buffered_writer.zig - lib/std/Io/change_detection_stream.zig - lib/std/Io/counting_reader.zig - lib/std/Io/counting_writer.zig - lib/std/Io/find_byte_writer.zig - lib/std/Io/fixed_buffer_stream.zig - lib/std/Io/limited_reader.zig - lib/std/Io/seekable_stream.zig lib/std/Progress.zig lib/std/Random.zig lib/std/Target.zig diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 3865c17159..cc79ecade5 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -473,8 +473,6 @@ pub const findByteWriter = @import("Io/find_byte_writer.zig").findByteWriter; pub const BufferedAtomicFile = @import("Io/buffered_atomic_file.zig").BufferedAtomicFile; -pub const StreamSource = @import("Io/stream_source.zig").StreamSource; - pub const tty = @import("Io/tty.zig"); /// A Writer that doesn't write to anything. @@ -904,6 +902,5 @@ test { _ = @import("Io/counting_reader.zig"); _ = @import("Io/fixed_buffer_stream.zig"); _ = @import("Io/seekable_stream.zig"); - _ = @import("Io/stream_source.zig"); _ = @import("Io/test.zig"); } diff --git a/lib/std/Io/stream_source.zig b/lib/std/Io/stream_source.zig deleted file mode 100644 index 2a3527e479..0000000000 --- a/lib/std/Io/stream_source.zig +++ /dev/null @@ -1,127 +0,0 @@ -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const io = std.io; - -/// Provides `io.GenericReader`, `io.GenericWriter`, and `io.SeekableStream` for in-memory buffers as -/// well as files. -/// For memory sources, if the supplied byte buffer is const, then `io.GenericWriter` is not available. -/// The error set of the stream functions is the error set of the corresponding file functions. -pub const StreamSource = union(enum) { - // TODO: expose UEFI files to std.os in a way that allows this to be true - const has_file = (builtin.os.tag != .freestanding and builtin.os.tag != .uefi); - - /// The stream access is redirected to this buffer. - buffer: io.FixedBufferStream([]u8), - - /// The stream access is redirected to this buffer. - /// Writing to the source will always yield `error.AccessDenied`. - const_buffer: io.FixedBufferStream([]const u8), - - /// The stream access is redirected to this file. - /// On freestanding, this must never be initialized! - file: if (has_file) std.fs.File else void, - - pub const ReadError = io.FixedBufferStream([]u8).ReadError || (if (has_file) std.fs.File.ReadError else error{}); - pub const WriteError = error{AccessDenied} || io.FixedBufferStream([]u8).WriteError || (if (has_file) std.fs.File.WriteError else error{}); - pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{}); - pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{}); - - pub const Reader = io.GenericReader(*StreamSource, ReadError, read); - pub const Writer = io.GenericWriter(*StreamSource, WriteError, write); - pub const SeekableStream = io.SeekableStream( - *StreamSource, - SeekError, - GetSeekPosError, - seekTo, - seekBy, - getPos, - getEndPos, - ); - - pub fn read(self: *StreamSource, dest: []u8) ReadError!usize { - switch (self.*) { - .buffer => |*x| return x.read(dest), - .const_buffer => |*x| return x.read(dest), - .file => |x| if (!has_file) unreachable else return x.read(dest), - } - } - - pub fn write(self: *StreamSource, bytes: []const u8) WriteError!usize { - switch (self.*) { - .buffer => |*x| return x.write(bytes), - .const_buffer => return error.AccessDenied, - .file => |x| if (!has_file) unreachable else return x.write(bytes), - } - } - - pub fn seekTo(self: *StreamSource, pos: u64) SeekError!void { - switch (self.*) { - .buffer => |*x| return x.seekTo(pos), - .const_buffer => |*x| return x.seekTo(pos), - .file => |x| if (!has_file) unreachable else return x.seekTo(pos), - } - } - - pub fn seekBy(self: *StreamSource, amt: i64) SeekError!void { - switch (self.*) { - .buffer => |*x| return x.seekBy(amt), - .const_buffer => |*x| return x.seekBy(amt), - .file => |x| if (!has_file) unreachable else return x.seekBy(amt), - } - } - - pub fn getEndPos(self: *StreamSource) GetSeekPosError!u64 { - switch (self.*) { - .buffer => |*x| return x.getEndPos(), - .const_buffer => |*x| return x.getEndPos(), - .file => |x| if (!has_file) unreachable else return x.getEndPos(), - } - } - - pub fn getPos(self: *StreamSource) GetSeekPosError!u64 { - switch (self.*) { - .buffer => |*x| return x.getPos(), - .const_buffer => |*x| return x.getPos(), - .file => |x| if (!has_file) unreachable else return x.getPos(), - } - } - - pub fn reader(self: *StreamSource) Reader { - return .{ .context = self }; - } - - pub fn writer(self: *StreamSource) Writer { - return .{ .context = self }; - } - - pub fn seekableStream(self: *StreamSource) SeekableStream { - return .{ .context = self }; - } -}; - -test "refs" { - std.testing.refAllDecls(StreamSource); -} - -test "mutable buffer" { - var buffer: [64]u8 = undefined; - var source = StreamSource{ .buffer = std.io.fixedBufferStream(&buffer) }; - - var writer = source.writer(); - - try writer.writeAll("Hello, World!"); - - try std.testing.expectEqualStrings("Hello, World!", source.buffer.getWritten()); -} - -test "const buffer" { - const buffer: [64]u8 = "Hello, World!".* ++ ([1]u8{0xAA} ** 51); - var source = StreamSource{ .const_buffer = std.io.fixedBufferStream(&buffer) }; - - var reader = source.reader(); - - var dst_buffer: [13]u8 = undefined; - try reader.readNoEof(&dst_buffer); - - try std.testing.expectEqualStrings("Hello, World!", &dst_buffer); -} From abed0f5129c5e03fce61c5c48cdc4bb396805ce2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:05:57 -0700 Subject: [PATCH 21/76] std.Io: delete BufferedAtomicFile this is now redundant --- lib/std/Io.zig | 3 -- lib/std/Io/buffered_atomic_file.zig | 55 ----------------------------- 2 files changed, 58 deletions(-) delete mode 100644 lib/std/Io/buffered_atomic_file.zig diff --git a/lib/std/Io.zig b/lib/std/Io.zig index cc79ecade5..26838517cd 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -471,8 +471,6 @@ pub const changeDetectionStream = @import("Io/change_detection_stream.zig").chan pub const FindByteWriter = @import("Io/find_byte_writer.zig").FindByteWriter; pub const findByteWriter = @import("Io/find_byte_writer.zig").findByteWriter; -pub const BufferedAtomicFile = @import("Io/buffered_atomic_file.zig").BufferedAtomicFile; - pub const tty = @import("Io/tty.zig"); /// A Writer that doesn't write to anything. @@ -895,7 +893,6 @@ test { _ = Writer; _ = @import("Io/bit_reader.zig"); _ = @import("Io/bit_writer.zig"); - _ = @import("Io/buffered_atomic_file.zig"); _ = @import("Io/buffered_reader.zig"); _ = @import("Io/buffered_writer.zig"); _ = @import("Io/counting_writer.zig"); diff --git a/lib/std/Io/buffered_atomic_file.zig b/lib/std/Io/buffered_atomic_file.zig deleted file mode 100644 index 48510bde52..0000000000 --- a/lib/std/Io/buffered_atomic_file.zig +++ /dev/null @@ -1,55 +0,0 @@ -const std = @import("../std.zig"); -const mem = std.mem; -const fs = std.fs; -const File = std.fs.File; - -pub const BufferedAtomicFile = struct { - atomic_file: fs.AtomicFile, - file_writer: File.Writer, - buffered_writer: BufferedWriter, - allocator: mem.Allocator, - - pub const buffer_size = 4096; - pub const BufferedWriter = std.io.BufferedWriter(buffer_size, File.Writer); - pub const Writer = std.io.GenericWriter(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write); - - /// TODO when https://github.com/ziglang/zig/issues/2761 is solved - /// this API will not need an allocator - pub fn create( - allocator: mem.Allocator, - dir: fs.Dir, - dest_path: []const u8, - atomic_file_options: fs.Dir.AtomicFileOptions, - ) !*BufferedAtomicFile { - var self = try allocator.create(BufferedAtomicFile); - self.* = BufferedAtomicFile{ - .atomic_file = undefined, - .file_writer = undefined, - .buffered_writer = undefined, - .allocator = allocator, - }; - errdefer allocator.destroy(self); - - self.atomic_file = try dir.atomicFile(dest_path, atomic_file_options); - errdefer self.atomic_file.deinit(); - - self.file_writer = self.atomic_file.file.deprecatedWriter(); - self.buffered_writer = .{ .unbuffered_writer = self.file_writer }; - return self; - } - - /// always call destroy, even after successful finish() - pub fn destroy(self: *BufferedAtomicFile) void { - self.atomic_file.deinit(); - self.allocator.destroy(self); - } - - pub fn finish(self: *BufferedAtomicFile) !void { - try self.buffered_writer.flush(); - try self.atomic_file.finish(); - } - - pub fn writer(self: *BufferedAtomicFile) Writer { - return .{ .context = &self.buffered_writer }; - } -}; From d9a5a3e8c55b501afe0d9df232d613f6b445f14f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:09:15 -0700 Subject: [PATCH 22/76] std.Io: delete MultiWriter nah --- lib/std/Io.zig | 3 --- lib/std/Io/multi_writer.zig | 53 ------------------------------------- 2 files changed, 56 deletions(-) delete mode 100644 lib/std/Io/multi_writer.zig diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 26838517cd..df5ef7aef8 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -456,9 +456,6 @@ pub const countingWriter = @import("Io/counting_writer.zig").countingWriter; pub const CountingReader = @import("Io/counting_reader.zig").CountingReader; pub const countingReader = @import("Io/counting_reader.zig").countingReader; -pub const MultiWriter = @import("Io/multi_writer.zig").MultiWriter; -pub const multiWriter = @import("Io/multi_writer.zig").multiWriter; - pub const BitReader = @import("Io/bit_reader.zig").BitReader; pub const bitReader = @import("Io/bit_reader.zig").bitReader; diff --git a/lib/std/Io/multi_writer.zig b/lib/std/Io/multi_writer.zig deleted file mode 100644 index 20e9e782de..0000000000 --- a/lib/std/Io/multi_writer.zig +++ /dev/null @@ -1,53 +0,0 @@ -const std = @import("../std.zig"); -const io = std.io; - -/// Takes a tuple of streams, and constructs a new stream that writes to all of them -pub fn MultiWriter(comptime Writers: type) type { - comptime var ErrSet = error{}; - inline for (@typeInfo(Writers).@"struct".fields) |field| { - const StreamType = field.type; - ErrSet = ErrSet || StreamType.Error; - } - - return struct { - const Self = @This(); - - streams: Writers, - - pub const Error = ErrSet; - pub const Writer = io.GenericWriter(*Self, Error, write); - - pub fn writer(self: *Self) Writer { - return .{ .context = self }; - } - - pub fn write(self: *Self, bytes: []const u8) Error!usize { - inline for (self.streams) |stream| - try stream.writeAll(bytes); - return bytes.len; - } - }; -} - -pub fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) { - return .{ .streams = streams }; -} - -const testing = std.testing; - -test "MultiWriter" { - var tmp = testing.tmpDir(.{}); - defer tmp.cleanup(); - var f = try tmp.dir.createFile("t.txt", .{}); - - var buf1: [255]u8 = undefined; - var fbs1 = io.fixedBufferStream(&buf1); - var buf2: [255]u8 = undefined; - var stream = multiWriter(.{ fbs1.writer(), f.writer() }); - - try stream.writer().print("HI", .{}); - f.close(); - - try testing.expectEqualSlices(u8, "HI", fbs1.getWritten()); - try testing.expectEqualSlices(u8, "HI", try tmp.dir.readFile("t.txt", &buf2)); -} From 03a6892189891f1566b9e63780129834bea7eb1e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:13:23 -0700 Subject: [PATCH 23/76] std.Io: delete ChangeDetectionStream dead code --- lib/std/Io.zig | 3 -- lib/std/Io/change_detection_stream.zig | 55 -------------------------- 2 files changed, 58 deletions(-) delete mode 100644 lib/std/Io/change_detection_stream.zig diff --git a/lib/std/Io.zig b/lib/std/Io.zig index df5ef7aef8..1ccc9a4edc 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -462,9 +462,6 @@ pub const bitReader = @import("Io/bit_reader.zig").bitReader; pub const BitWriter = @import("Io/bit_writer.zig").BitWriter; pub const bitWriter = @import("Io/bit_writer.zig").bitWriter; -pub const ChangeDetectionStream = @import("Io/change_detection_stream.zig").ChangeDetectionStream; -pub const changeDetectionStream = @import("Io/change_detection_stream.zig").changeDetectionStream; - pub const FindByteWriter = @import("Io/find_byte_writer.zig").FindByteWriter; pub const findByteWriter = @import("Io/find_byte_writer.zig").findByteWriter; diff --git a/lib/std/Io/change_detection_stream.zig b/lib/std/Io/change_detection_stream.zig deleted file mode 100644 index d9da1c4a0e..0000000000 --- a/lib/std/Io/change_detection_stream.zig +++ /dev/null @@ -1,55 +0,0 @@ -const std = @import("../std.zig"); -const io = std.io; -const mem = std.mem; -const assert = std.debug.assert; - -/// Used to detect if the data written to a stream differs from a source buffer -pub fn ChangeDetectionStream(comptime WriterType: type) type { - return struct { - const Self = @This(); - pub const Error = WriterType.Error; - pub const Writer = io.GenericWriter(*Self, Error, write); - - anything_changed: bool, - underlying_writer: WriterType, - source_index: usize, - source: []const u8, - - pub fn writer(self: *Self) Writer { - return .{ .context = self }; - } - - fn write(self: *Self, bytes: []const u8) Error!usize { - if (!self.anything_changed) { - const end = self.source_index + bytes.len; - if (end > self.source.len) { - self.anything_changed = true; - } else { - const src_slice = self.source[self.source_index..end]; - self.source_index += bytes.len; - if (!mem.eql(u8, bytes, src_slice)) { - self.anything_changed = true; - } - } - } - - return self.underlying_writer.write(bytes); - } - - pub fn changeDetected(self: *Self) bool { - return self.anything_changed or (self.source_index != self.source.len); - } - }; -} - -pub fn changeDetectionStream( - source: []const u8, - underlying_writer: anytype, -) ChangeDetectionStream(@TypeOf(underlying_writer)) { - return ChangeDetectionStream(@TypeOf(underlying_writer)){ - .anything_changed = false, - .underlying_writer = underlying_writer, - .source_index = 0, - .source = source, - }; -} From af0a02a2de8fad56029cccbfeb6254a2c2169b51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:15:09 -0700 Subject: [PATCH 24/76] std.Io: delete FindByteWriter dead --- lib/std/Io.zig | 3 --- lib/std/Io/find_byte_writer.zig | 40 --------------------------------- 2 files changed, 43 deletions(-) delete mode 100644 lib/std/Io/find_byte_writer.zig diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 1ccc9a4edc..9228a61287 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -462,9 +462,6 @@ pub const bitReader = @import("Io/bit_reader.zig").bitReader; pub const BitWriter = @import("Io/bit_writer.zig").BitWriter; pub const bitWriter = @import("Io/bit_writer.zig").bitWriter; -pub const FindByteWriter = @import("Io/find_byte_writer.zig").FindByteWriter; -pub const findByteWriter = @import("Io/find_byte_writer.zig").findByteWriter; - pub const tty = @import("Io/tty.zig"); /// A Writer that doesn't write to anything. diff --git a/lib/std/Io/find_byte_writer.zig b/lib/std/Io/find_byte_writer.zig deleted file mode 100644 index fe6836f603..0000000000 --- a/lib/std/Io/find_byte_writer.zig +++ /dev/null @@ -1,40 +0,0 @@ -const std = @import("../std.zig"); -const io = std.io; -const assert = std.debug.assert; - -/// A Writer that returns whether the given character has been written to it. -/// The contents are not written to anything. -pub fn FindByteWriter(comptime UnderlyingWriter: type) type { - return struct { - const Self = @This(); - pub const Error = UnderlyingWriter.Error; - pub const Writer = io.GenericWriter(*Self, Error, write); - - underlying_writer: UnderlyingWriter, - byte_found: bool, - byte: u8, - - pub fn writer(self: *Self) Writer { - return .{ .context = self }; - } - - fn write(self: *Self, bytes: []const u8) Error!usize { - if (!self.byte_found) { - self.byte_found = blk: { - for (bytes) |b| - if (b == self.byte) break :blk true; - break :blk false; - }; - } - return self.underlying_writer.write(bytes); - } - }; -} - -pub fn findByteWriter(byte: u8, underlying_writer: anytype) FindByteWriter(@TypeOf(underlying_writer)) { - return FindByteWriter(@TypeOf(underlying_writer)){ - .underlying_writer = underlying_writer, - .byte = byte, - .byte_found = false, - }; -} From 2ac81c76e3d176d4ac0c568d195d5b979078a938 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 17:21:33 -0700 Subject: [PATCH 25/76] std.Io: add deprecation warnings --- lib/std/Io.zig | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 9228a61287..2380b3abbf 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -435,25 +435,33 @@ pub fn GenericWriter( pub const AnyReader = @import("Io/DeprecatedReader.zig"); /// Deprecated in favor of `Writer`. pub const AnyWriter = @import("Io/DeprecatedWriter.zig"); - +/// Deprecated in favor of `File.Reader` and `File.Writer`. pub const SeekableStream = @import("Io/seekable_stream.zig").SeekableStream; - +/// Deprecated in favor of `Writer`. pub const BufferedWriter = @import("Io/buffered_writer.zig").BufferedWriter; +/// Deprecated in favor of `Writer`. pub const bufferedWriter = @import("Io/buffered_writer.zig").bufferedWriter; - +/// Deprecated in favor of `Reader`. pub const BufferedReader = @import("Io/buffered_reader.zig").BufferedReader; +/// Deprecated in favor of `Reader`. pub const bufferedReader = @import("Io/buffered_reader.zig").bufferedReader; +/// Deprecated in favor of `Reader`. pub const bufferedReaderSize = @import("Io/buffered_reader.zig").bufferedReaderSize; - +/// Deprecated in favor of `Reader`. pub const FixedBufferStream = @import("Io/fixed_buffer_stream.zig").FixedBufferStream; +/// Deprecated in favor of `Reader`. pub const fixedBufferStream = @import("Io/fixed_buffer_stream.zig").fixedBufferStream; - +/// Deprecated in favor of `Reader.Limited`. pub const LimitedReader = @import("Io/limited_reader.zig").LimitedReader; +/// Deprecated in favor of `Reader.Limited`. pub const limitedReader = @import("Io/limited_reader.zig").limitedReader; - +/// Deprecated with no replacement; inefficient pattern pub const CountingWriter = @import("Io/counting_writer.zig").CountingWriter; +/// Deprecated with no replacement; inefficient pattern pub const countingWriter = @import("Io/counting_writer.zig").countingWriter; +/// Deprecated with no replacement; inefficient pattern pub const CountingReader = @import("Io/counting_reader.zig").CountingReader; +/// Deprecated with no replacement; inefficient pattern pub const countingReader = @import("Io/counting_reader.zig").countingReader; pub const BitReader = @import("Io/bit_reader.zig").BitReader; @@ -464,9 +472,9 @@ pub const bitWriter = @import("Io/bit_writer.zig").bitWriter; pub const tty = @import("Io/tty.zig"); -/// A Writer that doesn't write to anything. +/// Deprecated in favor of `Writer.Discarding`. pub const null_writer: NullWriter = .{ .context = {} }; - +/// Deprecated in favor of `Writer.Discarding`. pub const NullWriter = GenericWriter(void, error{}, dummyWrite); fn dummyWrite(context: void, data: []const u8) error{}!usize { _ = context; @@ -882,13 +890,14 @@ test { _ = Reader; _ = Reader.Limited; _ = Writer; - _ = @import("Io/bit_reader.zig"); - _ = @import("Io/bit_writer.zig"); - _ = @import("Io/buffered_reader.zig"); - _ = @import("Io/buffered_writer.zig"); - _ = @import("Io/counting_writer.zig"); - _ = @import("Io/counting_reader.zig"); - _ = @import("Io/fixed_buffer_stream.zig"); - _ = @import("Io/seekable_stream.zig"); + _ = BitReader; + _ = BitWriter; + _ = BufferedReader; + _ = BufferedWriter; + _ = CountingWriter; + _ = CountingReader; + _ = FixedBufferStream; + _ = SeekableStream; + _ = tty; _ = @import("Io/test.zig"); } From 1dcea220a4daf537c1a75835de45076fac429895 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 20:14:28 -0700 Subject: [PATCH 26/76] std.tar: update to new I/O API --- lib/std/Build/Fuzz/WebServer.zig | 13 +- lib/std/Io/Reader.zig | 6 + lib/std/crypto/md5.zig | 12 +- lib/std/tar.zig | 636 ++++++++++++++----------------- lib/std/tar/Writer.zig | 498 ++++++++++++++++++++++++ lib/std/tar/test.zig | 350 +++++++++-------- lib/std/tar/writer.zig | 497 ------------------------ src/Compilation.zig | 37 +- src/Package/Fetch.zig | 24 +- 9 files changed, 1028 insertions(+), 1045 deletions(-) create mode 100644 lib/std/tar/Writer.zig delete mode 100644 lib/std/tar/writer.zig diff --git a/lib/std/Build/Fuzz/WebServer.zig b/lib/std/Build/Fuzz/WebServer.zig index b28a6e185c..0df43408f1 100644 --- a/lib/std/Build/Fuzz/WebServer.zig +++ b/lib/std/Build/Fuzz/WebServer.zig @@ -522,7 +522,9 @@ fn serveSourcesTar(ws: *WebServer, request: *std.http.Server.Request) !void { var cwd_cache: ?[]const u8 = null; - var archiver = std.tar.writer(response.writer()); + var adapter = response.writer().adaptToNewApi(); + var archiver: std.tar.Writer = .{ .underlying_writer = &adapter.new_interface }; + var read_buffer: [1024]u8 = undefined; for (deduped_paths) |joined_path| { var file = joined_path.root_dir.handle.openFile(joined_path.sub_path, .{}) catch |err| { @@ -530,13 +532,14 @@ fn serveSourcesTar(ws: *WebServer, request: *std.http.Server.Request) !void { continue; }; defer file.close(); - + const stat = try file.stat(); + var file_reader: std.fs.File.Reader = .initSize(file, &read_buffer, stat.size); archiver.prefix = joined_path.root_dir.path orelse try memoizedCwd(arena, &cwd_cache); - try archiver.writeFile(joined_path.sub_path, file); + try archiver.writeFile(joined_path.sub_path, &file_reader, stat.mtime); } - // intentionally omitting the pointless trailer - //try archiver.finish(); + // intentionally not calling `archiver.finishPedantically` + try adapter.new_interface.flush(); try response.end(); } diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index f25e113522..5c7fdafe76 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -179,6 +179,12 @@ pub fn streamExact(r: *Reader, w: *Writer, n: usize) StreamError!void { while (remaining != 0) remaining -= try r.stream(w, .limited(remaining)); } +/// "Pump" exactly `n` bytes from the reader to the writer. +pub fn streamExact64(r: *Reader, w: *Writer, n: u64) StreamError!void { + var remaining = n; + while (remaining != 0) remaining -= try r.stream(w, .limited64(remaining)); +} + /// "Pump" data from the reader to the writer, handling `error.EndOfStream` as /// a success case. /// diff --git a/lib/std/crypto/md5.zig b/lib/std/crypto/md5.zig index 92c8dac796..a580f826f3 100644 --- a/lib/std/crypto/md5.zig +++ b/lib/std/crypto/md5.zig @@ -54,12 +54,20 @@ pub const Md5 = struct { }; } - pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { + pub fn hash(data: []const u8, out: *[digest_length]u8, options: Options) void { var d = Md5.init(options); - d.update(b); + d.update(data); d.final(out); } + pub fn hashResult(data: []const u8) [digest_length]u8 { + var out: [digest_length]u8 = undefined; + var d = Md5.init(.{}); + d.update(data); + d.final(&out); + return out; + } + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 729a07db0a..e397677cf3 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -19,7 +19,7 @@ const std = @import("std"); const assert = std.debug.assert; const testing = std.testing; -pub const writer = @import("tar/writer.zig").writer; +pub const Writer = @import("tar/Writer.zig"); /// Provide this to receive detailed error messages. /// When this is provided, some errors which would otherwise be returned @@ -293,28 +293,6 @@ fn nullStr(str: []const u8) []const u8 { return str; } -/// Options for iterator. -/// Buffers should be provided by the caller. -pub const IteratorOptions = struct { - /// Use a buffer with length `std.fs.max_path_bytes` to match file system capabilities. - file_name_buffer: []u8, - /// Use a buffer with length `std.fs.max_path_bytes` to match file system capabilities. - link_name_buffer: []u8, - /// Collects error messages during unpacking - diagnostics: ?*Diagnostics = null, -}; - -/// Iterates over files in tar archive. -/// `next` returns each file in tar archive. -pub fn iterator(reader: anytype, options: IteratorOptions) Iterator(@TypeOf(reader)) { - return .{ - .reader = reader, - .diagnostics = options.diagnostics, - .file_name_buffer = options.file_name_buffer, - .link_name_buffer = options.link_name_buffer, - }; -} - /// Type of the file returned by iterator `next` method. pub const FileKind = enum { directory, @@ -323,206 +301,192 @@ pub const FileKind = enum { }; /// Iterator over entries in the tar file represented by reader. -pub fn Iterator(comptime ReaderType: type) type { - return struct { - reader: ReaderType, - diagnostics: ?*Diagnostics = null, +pub const Iterator = struct { + reader: *std.Io.Reader, + diagnostics: ?*Diagnostics = null, - // buffers for heeader and file attributes - header_buffer: [Header.SIZE]u8 = undefined, + // buffers for heeader and file attributes + header_buffer: [Header.SIZE]u8 = undefined, + file_name_buffer: []u8, + link_name_buffer: []u8, + + // bytes of padding to the end of the block + padding: usize = 0, + // not consumed bytes of file from last next iteration + unread_file_bytes: u64 = 0, + + /// Options for iterator. + /// Buffers should be provided by the caller. + pub const Options = struct { + /// Use a buffer with length `std.fs.max_path_bytes` to match file system capabilities. file_name_buffer: []u8, + /// Use a buffer with length `std.fs.max_path_bytes` to match file system capabilities. link_name_buffer: []u8, + /// Collects error messages during unpacking + diagnostics: ?*Diagnostics = null, + }; - // bytes of padding to the end of the block - padding: usize = 0, - // not consumed bytes of file from last next iteration - unread_file_bytes: u64 = 0, - - pub const File = struct { - name: []const u8, // name of file, symlink or directory - link_name: []const u8, // target name of symlink - size: u64 = 0, // size of the file in bytes - mode: u32 = 0, - kind: FileKind = .file, - - unread_bytes: *u64, - parent_reader: ReaderType, - - pub const Reader = std.io.GenericReader(File, ReaderType.Error, File.read); - - pub fn reader(self: File) Reader { - return .{ .context = self }; - } - - pub fn read(self: File, dest: []u8) ReaderType.Error!usize { - const buf = dest[0..@min(dest.len, self.unread_bytes.*)]; - const n = try self.parent_reader.read(buf); - self.unread_bytes.* -= n; - return n; - } - - // Writes file content to writer. - pub fn writeAll(self: File, out_writer: anytype) !void { - var buffer: [4096]u8 = undefined; - - while (self.unread_bytes.* > 0) { - const buf = buffer[0..@min(buffer.len, self.unread_bytes.*)]; - try self.parent_reader.readNoEof(buf); - try out_writer.writeAll(buf); - self.unread_bytes.* -= buf.len; - } - } + /// Iterates over files in tar archive. + /// `next` returns each file in tar archive. + pub fn init(reader: *std.Io.Reader, options: Options) Iterator { + return .{ + .reader = reader, + .diagnostics = options.diagnostics, + .file_name_buffer = options.file_name_buffer, + .link_name_buffer = options.link_name_buffer, }; + } - const Self = @This(); - - fn readHeader(self: *Self) !?Header { - if (self.padding > 0) { - try self.reader.skipBytes(self.padding, .{}); - } - const n = try self.reader.readAll(&self.header_buffer); - if (n == 0) return null; - if (n < Header.SIZE) return error.UnexpectedEndOfStream; - const header = Header{ .bytes = self.header_buffer[0..Header.SIZE] }; - if (try header.checkChksum() == 0) return null; - return header; - } - - fn readString(self: *Self, size: usize, buffer: []u8) ![]const u8 { - if (size > buffer.len) return error.TarInsufficientBuffer; - const buf = buffer[0..size]; - try self.reader.readNoEof(buf); - return nullStr(buf); - } - - fn newFile(self: *Self) File { - return .{ - .name = self.file_name_buffer[0..0], - .link_name = self.link_name_buffer[0..0], - .parent_reader = self.reader, - .unread_bytes = &self.unread_file_bytes, - }; - } - - // Number of padding bytes in the last file block. - fn blockPadding(size: u64) usize { - const block_rounded = std.mem.alignForward(u64, size, Header.SIZE); // size rounded to te block boundary - return @intCast(block_rounded - size); - } - - /// Iterates through the tar archive as if it is a series of files. - /// Internally, the tar format often uses entries (header with optional - /// content) to add meta data that describes the next file. These - /// entries should not normally be visible to the outside. As such, this - /// loop iterates through one or more entries until it collects a all - /// file attributes. - pub fn next(self: *Self) !?File { - if (self.unread_file_bytes > 0) { - // If file content was not consumed by caller - try self.reader.skipBytes(self.unread_file_bytes, .{}); - self.unread_file_bytes = 0; - } - var file: File = self.newFile(); - - while (try self.readHeader()) |header| { - const kind = header.kind(); - const size: u64 = try header.size(); - self.padding = blockPadding(size); - - switch (kind) { - // File types to return upstream - .directory, .normal, .symbolic_link => { - file.kind = switch (kind) { - .directory => .directory, - .normal => .file, - .symbolic_link => .sym_link, - else => unreachable, - }; - file.mode = try header.mode(); - - // set file attributes if not already set by prefix/extended headers - if (file.size == 0) { - file.size = size; - } - if (file.link_name.len == 0) { - file.link_name = try header.linkName(self.link_name_buffer); - } - if (file.name.len == 0) { - file.name = try header.fullName(self.file_name_buffer); - } - - self.padding = blockPadding(file.size); - self.unread_file_bytes = file.size; - return file; - }, - // Prefix header types - .gnu_long_name => { - file.name = try self.readString(@intCast(size), self.file_name_buffer); - }, - .gnu_long_link => { - file.link_name = try self.readString(@intCast(size), self.link_name_buffer); - }, - .extended_header => { - // Use just attributes from last extended header. - file = self.newFile(); - - var rdr = paxIterator(self.reader, @intCast(size)); - while (try rdr.next()) |attr| { - switch (attr.kind) { - .path => { - file.name = try attr.value(self.file_name_buffer); - }, - .linkpath => { - file.link_name = try attr.value(self.link_name_buffer); - }, - .size => { - var buf: [pax_max_size_attr_len]u8 = undefined; - file.size = try std.fmt.parseInt(u64, try attr.value(&buf), 10); - }, - } - } - }, - // Ignored header type - .global_extended_header => { - self.reader.skipBytes(size, .{}) catch return error.TarHeadersTooBig; - }, - // All other are unsupported header types - else => { - const d = self.diagnostics orelse return error.TarUnsupportedHeader; - try d.errors.append(d.allocator, .{ .unsupported_file_type = .{ - .file_name = try d.allocator.dupe(u8, header.name()), - .file_type = kind, - } }); - if (kind == .gnu_sparse) { - try self.skipGnuSparseExtendedHeaders(header); - } - self.reader.skipBytes(size, .{}) catch return error.TarHeadersTooBig; - }, - } - } - return null; - } - - fn skipGnuSparseExtendedHeaders(self: *Self, header: Header) !void { - var is_extended = header.bytes[482] > 0; - while (is_extended) { - var buf: [Header.SIZE]u8 = undefined; - const n = try self.reader.readAll(&buf); - if (n < Header.SIZE) return error.UnexpectedEndOfStream; - is_extended = buf[504] > 0; - } - } + pub const File = struct { + name: []const u8, // name of file, symlink or directory + link_name: []const u8, // target name of symlink + size: u64 = 0, // size of the file in bytes + mode: u32 = 0, + kind: FileKind = .file, }; -} -/// Pax attributes iterator. -/// Size is length of pax extended header in reader. -fn paxIterator(reader: anytype, size: usize) PaxIterator(@TypeOf(reader)) { - return PaxIterator(@TypeOf(reader)){ - .reader = reader, - .size = size, - }; -} + fn readHeader(self: *Iterator) !?Header { + if (self.padding > 0) { + try self.reader.discardAll(self.padding); + } + const n = try self.reader.readSliceShort(&self.header_buffer); + if (n == 0) return null; + if (n < Header.SIZE) return error.UnexpectedEndOfStream; + const header = Header{ .bytes = self.header_buffer[0..Header.SIZE] }; + if (try header.checkChksum() == 0) return null; + return header; + } + + fn readString(self: *Iterator, size: usize, buffer: []u8) ![]const u8 { + if (size > buffer.len) return error.TarInsufficientBuffer; + const buf = buffer[0..size]; + try self.reader.readSliceAll(buf); + return nullStr(buf); + } + + fn newFile(self: *Iterator) File { + return .{ + .name = self.file_name_buffer[0..0], + .link_name = self.link_name_buffer[0..0], + }; + } + + // Number of padding bytes in the last file block. + fn blockPadding(size: u64) usize { + const block_rounded = std.mem.alignForward(u64, size, Header.SIZE); // size rounded to te block boundary + return @intCast(block_rounded - size); + } + + /// Iterates through the tar archive as if it is a series of files. + /// Internally, the tar format often uses entries (header with optional + /// content) to add meta data that describes the next file. These + /// entries should not normally be visible to the outside. As such, this + /// loop iterates through one or more entries until it collects a all + /// file attributes. + pub fn next(self: *Iterator) !?File { + if (self.unread_file_bytes > 0) { + // If file content was not consumed by caller + try self.reader.discardAll64(self.unread_file_bytes); + self.unread_file_bytes = 0; + } + var file: File = self.newFile(); + + while (try self.readHeader()) |header| { + const kind = header.kind(); + const size: u64 = try header.size(); + self.padding = blockPadding(size); + + switch (kind) { + // File types to return upstream + .directory, .normal, .symbolic_link => { + file.kind = switch (kind) { + .directory => .directory, + .normal => .file, + .symbolic_link => .sym_link, + else => unreachable, + }; + file.mode = try header.mode(); + + // set file attributes if not already set by prefix/extended headers + if (file.size == 0) { + file.size = size; + } + if (file.link_name.len == 0) { + file.link_name = try header.linkName(self.link_name_buffer); + } + if (file.name.len == 0) { + file.name = try header.fullName(self.file_name_buffer); + } + + self.padding = blockPadding(file.size); + self.unread_file_bytes = file.size; + return file; + }, + // Prefix header types + .gnu_long_name => { + file.name = try self.readString(@intCast(size), self.file_name_buffer); + }, + .gnu_long_link => { + file.link_name = try self.readString(@intCast(size), self.link_name_buffer); + }, + .extended_header => { + // Use just attributes from last extended header. + file = self.newFile(); + + var rdr: PaxIterator = .{ + .reader = self.reader, + .size = @intCast(size), + }; + while (try rdr.next()) |attr| { + switch (attr.kind) { + .path => { + file.name = try attr.value(self.file_name_buffer); + }, + .linkpath => { + file.link_name = try attr.value(self.link_name_buffer); + }, + .size => { + var buf: [pax_max_size_attr_len]u8 = undefined; + file.size = try std.fmt.parseInt(u64, try attr.value(&buf), 10); + }, + } + } + }, + // Ignored header type + .global_extended_header => { + self.reader.discardAll64(size) catch return error.TarHeadersTooBig; + }, + // All other are unsupported header types + else => { + const d = self.diagnostics orelse return error.TarUnsupportedHeader; + try d.errors.append(d.allocator, .{ .unsupported_file_type = .{ + .file_name = try d.allocator.dupe(u8, header.name()), + .file_type = kind, + } }); + if (kind == .gnu_sparse) { + try self.skipGnuSparseExtendedHeaders(header); + } + self.reader.discardAll64(size) catch return error.TarHeadersTooBig; + }, + } + } + return null; + } + + pub fn streamRemaining(it: *Iterator, file: File, w: *std.Io.Writer) std.Io.Reader.StreamError!void { + try it.reader.streamExact64(w, file.size); + it.unread_file_bytes = 0; + } + + fn skipGnuSparseExtendedHeaders(self: *Iterator, header: Header) !void { + var is_extended = header.bytes[482] > 0; + while (is_extended) { + var buf: [Header.SIZE]u8 = undefined; + try self.reader.readSliceAll(&buf); + is_extended = buf[504] > 0; + } + } +}; const PaxAttributeKind = enum { path, @@ -533,108 +497,99 @@ const PaxAttributeKind = enum { // maxInt(u64) has 20 chars, base 10 in practice we got 24 chars const pax_max_size_attr_len = 64; -fn PaxIterator(comptime ReaderType: type) type { - return struct { - size: usize, // cumulative size of all pax attributes - reader: ReaderType, - // scratch buffer used for reading attribute length and keyword - scratch: [128]u8 = undefined, +pub const PaxIterator = struct { + size: usize, // cumulative size of all pax attributes + reader: *std.Io.Reader, - const Self = @This(); + const Self = @This(); - const Attribute = struct { - kind: PaxAttributeKind, - len: usize, // length of the attribute value - reader: ReaderType, // reader positioned at value start + const Attribute = struct { + kind: PaxAttributeKind, + len: usize, // length of the attribute value + reader: *std.Io.Reader, // reader positioned at value start - // Copies pax attribute value into destination buffer. - // Must be called with destination buffer of size at least Attribute.len. - pub fn value(self: Attribute, dst: []u8) ![]const u8 { - if (self.len > dst.len) return error.TarInsufficientBuffer; - // assert(self.len <= dst.len); - const buf = dst[0..self.len]; - const n = try self.reader.readAll(buf); - if (n < self.len) return error.UnexpectedEndOfStream; - try validateAttributeEnding(self.reader); - if (hasNull(buf)) return error.PaxNullInValue; - return buf; - } - }; - - // Iterates over pax attributes. Returns known only known attributes. - // Caller has to call value in Attribute, to advance reader across value. - pub fn next(self: *Self) !?Attribute { - // Pax extended header consists of one or more attributes, each constructed as follows: - // "%d %s=%s\n", , , - while (self.size > 0) { - const length_buf = try self.readUntil(' '); - const length = try std.fmt.parseInt(usize, length_buf, 10); // record length in bytes - - const keyword = try self.readUntil('='); - if (hasNull(keyword)) return error.PaxNullInKeyword; - - // calculate value_len - const value_start = length_buf.len + keyword.len + 2; // 2 separators - if (length < value_start + 1 or self.size < length) return error.UnexpectedEndOfStream; - const value_len = length - value_start - 1; // \n separator at end - self.size -= length; - - const kind: PaxAttributeKind = if (eql(keyword, "path")) - .path - else if (eql(keyword, "linkpath")) - .linkpath - else if (eql(keyword, "size")) - .size - else { - try self.reader.skipBytes(value_len, .{}); - try validateAttributeEnding(self.reader); - continue; - }; - if (kind == .size and value_len > pax_max_size_attr_len) { - return error.PaxSizeAttrOverflow; - } - return Attribute{ - .kind = kind, - .len = value_len, - .reader = self.reader, - }; - } - - return null; - } - - fn readUntil(self: *Self, delimiter: u8) ![]const u8 { - var fbs = std.io.fixedBufferStream(&self.scratch); - try self.reader.streamUntilDelimiter(fbs.writer(), delimiter, null); - return fbs.getWritten(); - } - - fn eql(a: []const u8, b: []const u8) bool { - return std.mem.eql(u8, a, b); - } - - fn hasNull(str: []const u8) bool { - return (std.mem.indexOfScalar(u8, str, 0)) != null; - } - - // Checks that each record ends with new line. - fn validateAttributeEnding(reader: ReaderType) !void { - if (try reader.readByte() != '\n') return error.PaxInvalidAttributeEnd; + // Copies pax attribute value into destination buffer. + // Must be called with destination buffer of size at least Attribute.len. + pub fn value(self: Attribute, dst: []u8) ![]const u8 { + if (self.len > dst.len) return error.TarInsufficientBuffer; + // assert(self.len <= dst.len); + const buf = dst[0..self.len]; + const n = try self.reader.readSliceShort(buf); + if (n < self.len) return error.UnexpectedEndOfStream; + try validateAttributeEnding(self.reader); + if (hasNull(buf)) return error.PaxNullInValue; + return buf; } }; -} + + // Iterates over pax attributes. Returns known only known attributes. + // Caller has to call value in Attribute, to advance reader across value. + pub fn next(self: *Self) !?Attribute { + // Pax extended header consists of one or more attributes, each constructed as follows: + // "%d %s=%s\n", , , + while (self.size > 0) { + const length_buf = try self.reader.takeSentinel(' '); + const length = try std.fmt.parseInt(usize, length_buf, 10); // record length in bytes + + const keyword = try self.reader.takeSentinel('='); + if (hasNull(keyword)) return error.PaxNullInKeyword; + + // calculate value_len + const value_start = length_buf.len + keyword.len + 2; // 2 separators + if (length < value_start + 1 or self.size < length) return error.UnexpectedEndOfStream; + const value_len = length - value_start - 1; // \n separator at end + self.size -= length; + + const kind: PaxAttributeKind = if (eql(keyword, "path")) + .path + else if (eql(keyword, "linkpath")) + .linkpath + else if (eql(keyword, "size")) + .size + else { + try self.reader.discardAll(value_len); + try validateAttributeEnding(self.reader); + continue; + }; + if (kind == .size and value_len > pax_max_size_attr_len) { + return error.PaxSizeAttrOverflow; + } + return .{ + .kind = kind, + .len = value_len, + .reader = self.reader, + }; + } + + return null; + } + + fn eql(a: []const u8, b: []const u8) bool { + return std.mem.eql(u8, a, b); + } + + fn hasNull(str: []const u8) bool { + return (std.mem.indexOfScalar(u8, str, 0)) != null; + } + + // Checks that each record ends with new line. + fn validateAttributeEnding(reader: *std.Io.Reader) !void { + if (try reader.takeByte() != '\n') return error.PaxInvalidAttributeEnd; + } +}; /// Saves tar file content to the file systems. -pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions) !void { +pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOptions) !void { var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; - var iter = iterator(reader, .{ + var file_contents_buffer: [1024]u8 = undefined; + var it: Iterator = .init(reader, .{ .file_name_buffer = &file_name_buffer, .link_name_buffer = &link_name_buffer, .diagnostics = options.diagnostics, }); - while (try iter.next()) |file| { + while (try it.next()) |file| { const file_name = stripComponents(file.name, options.strip_components); if (file_name.len == 0 and file.kind != .directory) { const d = options.diagnostics orelse return error.TarComponentsOutsideStrippedPrefix; @@ -656,7 +611,9 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions) .file => { if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| { defer fs_file.close(); - try file.writeAll(fs_file); + var file_writer = fs_file.writer(&file_contents_buffer); + try it.streamRemaining(file, &file_writer.interface); + try file_writer.interface.flush(); } else |err| { const d = options.diagnostics orelse return err; try d.errors.append(d.allocator, .{ .unable_to_create_file = .{ @@ -826,11 +783,14 @@ test PaxIterator { var buffer: [1024]u8 = undefined; outer: for (cases) |case| { - var stream = std.io.fixedBufferStream(case.data); - var iter = paxIterator(stream.reader(), case.data.len); + var reader: std.Io.Reader = .fixed(case.data); + var it: PaxIterator = .{ + .size = case.data.len, + .reader = &reader, + }; var i: usize = 0; - while (iter.next() catch |err| { + while (it.next() catch |err| { if (case.err) |e| { try testing.expectEqual(e, err); continue; @@ -853,12 +813,6 @@ test PaxIterator { } } -test { - _ = @import("tar/test.zig"); - _ = @import("tar/writer.zig"); - _ = Diagnostics; -} - test "header parse size" { const cases = [_]struct { in: []const u8, @@ -941,7 +895,7 @@ test "create file and symlink" { file.close(); } -test iterator { +test Iterator { // Example tar file is created from this tree structure: // $ tree example // example @@ -962,19 +916,19 @@ test iterator { // example/empty/ const data = @embedFile("tar/testdata/example.tar"); - var fbs = std.io.fixedBufferStream(data); + var reader: std.Io.Reader = .fixed(data); // User provided buffers to the iterator var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; // Create iterator - var iter = iterator(fbs.reader(), .{ + var it: Iterator = .init(&reader, .{ .file_name_buffer = &file_name_buffer, .link_name_buffer = &link_name_buffer, }); // Iterate over files in example.tar var file_no: usize = 0; - while (try iter.next()) |file| : (file_no += 1) { + while (try it.next()) |file| : (file_no += 1) { switch (file.kind) { .directory => { switch (file_no) { @@ -987,10 +941,10 @@ test iterator { }, .file => { try testing.expectEqualStrings("example/a/file", file.name); - // Read file content var buf: [16]u8 = undefined; - const n = try file.reader().readAll(&buf); - try testing.expectEqualStrings("content\n", buf[0..n]); + var w: std.Io.Writer = .fixed(&buf); + try it.streamRemaining(file, &w); + try testing.expectEqualStrings("content\n", w.buffered()); }, .sym_link => { try testing.expectEqualStrings("example/b/symlink", file.name); @@ -1021,15 +975,14 @@ test pipeToFileSystem { // example/empty/ const data = @embedFile("tar/testdata/example.tar"); - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .no_follow = true }); defer tmp.cleanup(); const dir = tmp.dir; - // Save tar from `reader` to the file system `dir` - pipeToFileSystem(dir, reader, .{ + // Save tar from reader to the file system `dir` + pipeToFileSystem(dir, &reader, .{ .mode_mode = .ignore, .strip_components = 1, .exclude_empty_directories = true, @@ -1053,8 +1006,7 @@ test pipeToFileSystem { test "pipeToFileSystem root_dir" { const data = @embedFile("tar/testdata/example.tar"); - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); // with strip_components = 1 { @@ -1063,7 +1015,7 @@ test "pipeToFileSystem root_dir" { var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, reader, .{ + pipeToFileSystem(tmp.dir, &reader, .{ .strip_components = 1, .diagnostics = &diagnostics, }) catch |err| { @@ -1079,13 +1031,13 @@ test "pipeToFileSystem root_dir" { // with strip_components = 0 { - fbs.reset(); + reader = .fixed(data); var tmp = testing.tmpDir(.{ .no_follow = true }); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, reader, .{ + pipeToFileSystem(tmp.dir, &reader, .{ .strip_components = 0, .diagnostics = &diagnostics, }) catch |err| { @@ -1102,45 +1054,42 @@ test "pipeToFileSystem root_dir" { test "findRoot with single file archive" { const data = @embedFile("tar/testdata/22752.tar"); - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try pipeToFileSystem(tmp.dir, reader, .{ .diagnostics = &diagnostics }); + try pipeToFileSystem(tmp.dir, &reader, .{ .diagnostics = &diagnostics }); try testing.expectEqualStrings("", diagnostics.root_dir); } test "findRoot without explicit root dir" { const data = @embedFile("tar/testdata/19820.tar"); - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try pipeToFileSystem(tmp.dir, reader, .{ .diagnostics = &diagnostics }); + try pipeToFileSystem(tmp.dir, &reader, .{ .diagnostics = &diagnostics }); try testing.expectEqualStrings("root", diagnostics.root_dir); } test "pipeToFileSystem strip_components" { const data = @embedFile("tar/testdata/example.tar"); - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .no_follow = true }); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, reader, .{ + pipeToFileSystem(tmp.dir, &reader, .{ .strip_components = 3, .diagnostics = &diagnostics, }) catch |err| { @@ -1194,13 +1143,12 @@ test "executable bit" { const data = @embedFile("tar/testdata/example.tar"); for ([_]PipeOptions.ModeMode{ .ignore, .executable_bit_only }) |opt| { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .no_follow = true }); //defer tmp.cleanup(); - pipeToFileSystem(tmp.dir, reader, .{ + pipeToFileSystem(tmp.dir, &reader, .{ .strip_components = 1, .exclude_empty_directories = true, .mode_mode = opt, @@ -1226,3 +1174,9 @@ test "executable bit" { } } } + +test { + _ = @import("tar/test.zig"); + _ = Writer; + _ = Diagnostics; +} diff --git a/lib/std/tar/Writer.zig b/lib/std/tar/Writer.zig new file mode 100644 index 0000000000..78baa69d84 --- /dev/null +++ b/lib/std/tar/Writer.zig @@ -0,0 +1,498 @@ +const std = @import("std"); +const assert = std.debug.assert; +const testing = std.testing; +const Writer = @This(); + +const block_size = @sizeOf(Header); + +/// Options for writing file/dir/link. If left empty 0o664 is used for +/// file mode and current time for mtime. +pub const Options = struct { + /// File system permission mode. + mode: u32 = 0, + /// File system modification time. + mtime: u64 = 0, +}; + +underlying_writer: *std.Io.Writer, +prefix: []const u8 = "", +mtime_now: u64 = 0, + +const Error = error{ + WriteFailed, + OctalOverflow, + NameTooLong, +}; + +/// Sets prefix for all other write* method paths. +pub fn setRoot(w: *Writer, root: []const u8) Error!void { + if (root.len > 0) + try w.writeDir(root, .{}); + + w.prefix = root; +} + +pub fn writeDir(w: *Writer, sub_path: []const u8, options: Options) Error!void { + try w.writeHeader(.directory, sub_path, "", 0, options); +} + +pub const WriteFileError = std.Io.Writer.FileError || Error || std.fs.File.GetEndPosError; + +pub fn writeFile( + w: *Writer, + sub_path: []const u8, + file_reader: *std.fs.File.Reader, + stat_mtime: i128, +) WriteFileError!void { + const size = try file_reader.getSize(); + const mtime: u64 = @intCast(@divFloor(stat_mtime, std.time.ns_per_s)); + + var header: Header = .{}; + try w.setPath(&header, sub_path); + try header.setSize(size); + try header.setMtime(mtime); + try header.updateChecksum(); + + try w.underlying_writer.writeAll(@ptrCast((&header)[0..1])); + _ = try w.underlying_writer.sendFileAll(file_reader, .unlimited); + try w.writePadding(size); +} + +pub const WriteFileStreamError = Error || std.Io.Reader.StreamError; + +/// Writes file reading file content from `reader`. Reads exactly `size` bytes +/// from `reader`, or returns `error.EndOfStream`. +pub fn writeFileStream( + w: *Writer, + sub_path: []const u8, + size: u64, + reader: *std.Io.Reader, + options: Options, +) WriteFileStreamError!void { + try w.writeHeader(.regular, sub_path, "", size, options); + try reader.streamExact64(w.underlying_writer, size); + try w.writePadding(size); +} + +/// Writes file using bytes buffer `content` for size and file content. +pub fn writeFileBytes(w: *Writer, sub_path: []const u8, content: []const u8, options: Options) Error!void { + try w.writeHeader(.regular, sub_path, "", content.len, options); + try w.underlying_writer.writeAll(content); + try w.writePadding(content.len); +} + +pub fn writeLink(w: *Writer, sub_path: []const u8, link_name: []const u8, options: Options) Error!void { + try w.writeHeader(.symbolic_link, sub_path, link_name, 0, options); +} + +/// Writes fs.Dir.WalkerEntry. Uses `mtime` from file system entry and +/// default for entry mode . +pub fn writeEntry(w: *Writer, entry: std.fs.Dir.Walker.Entry) Error!void { + switch (entry.kind) { + .directory => { + try w.writeDir(entry.path, .{ .mtime = try entryMtime(entry) }); + }, + .file => { + var file = try entry.dir.openFile(entry.basename, .{}); + defer file.close(); + const stat = try file.stat(); + try w.writeFile(entry.path, file, stat); + }, + .sym_link => { + var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; + const link_name = try entry.dir.readLink(entry.basename, &link_name_buffer); + try w.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) }); + }, + else => { + return error.UnsupportedWalkerEntryKind; + }, + } +} + +fn writeHeader( + w: *Writer, + typeflag: Header.FileType, + sub_path: []const u8, + link_name: []const u8, + size: u64, + options: Options, +) Error!void { + var header = Header.init(typeflag); + try w.setPath(&header, sub_path); + try header.setSize(size); + try header.setMtime(if (options.mtime != 0) options.mtime else w.mtimeNow()); + if (options.mode != 0) + try header.setMode(options.mode); + if (typeflag == .symbolic_link) + header.setLinkname(link_name) catch |err| switch (err) { + error.NameTooLong => try w.writeExtendedHeader(.gnu_long_link, &.{link_name}), + else => return err, + }; + try header.write(w.underlying_writer); +} + +fn mtimeNow(w: *Writer) u64 { + if (w.mtime_now == 0) + w.mtime_now = @intCast(std.time.timestamp()); + return w.mtime_now; +} + +fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 { + const stat = try entry.dir.statFile(entry.basename); + return @intCast(@divFloor(stat.mtime, std.time.ns_per_s)); +} + +/// Writes path in posix header, if don't fit (in name+prefix; 100+155 +/// bytes) writes it in gnu extended header. +fn setPath(w: *Writer, header: *Header, sub_path: []const u8) Error!void { + header.setPath(w.prefix, sub_path) catch |err| switch (err) { + error.NameTooLong => { + // write extended header + const buffers: []const []const u8 = if (w.prefix.len == 0) + &.{sub_path} + else + &.{ w.prefix, "/", sub_path }; + try w.writeExtendedHeader(.gnu_long_name, buffers); + }, + else => return err, + }; +} + +/// Writes gnu extended header: gnu_long_name or gnu_long_link. +fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const []const u8) Error!void { + var len: usize = 0; + for (buffers) |buf| len += buf.len; + + var header: Header = .init(typeflag); + try header.setSize(len); + try header.write(w.underlying_writer); + for (buffers) |buf| + try w.underlying_writer.writeAll(buf); + try w.writePadding(len); +} + +fn writePadding(w: *Writer, bytes: usize) std.Io.Writer.Error!void { + const pos = bytes % block_size; + if (pos == 0) return; + try w.underlying_writer.splatByteAll(0, block_size - pos); +} + +/// According to the specification, tar should finish with two zero blocks, but +/// "reasonable system must not assume that such a block exists when reading an +/// archive". Therefore, the Zig standard library recommends to not call this +/// function. +pub fn finishPedantically(w: *Writer) std.Io.Writer.Error!void { + try w.underlying_writer.splatByteAll(0, block_size * 2); +} + +/// A struct that is exactly 512 bytes and matches tar file format. This is +/// intended to be used for outputting tar files; for parsing there is +/// `std.tar.Header`. +pub const Header = extern struct { + // This struct was originally copied from + // https://github.com/mattnite/tar/blob/main/src/main.zig which is MIT + // licensed. + // + // The name, linkname, magic, uname, and gname are null-terminated character + // strings. All other fields are zero-filled octal numbers in ASCII. Each + // numeric field of width w contains w minus 1 digits, and a null. + // Reference: https://www.gnu.org/software/tar/manual/html_node/Standard.html + // POSIX header: byte offset + name: [100]u8 = [_]u8{0} ** 100, // 0 + mode: [7:0]u8 = default_mode.file, // 100 + uid: [7:0]u8 = [_:0]u8{0} ** 7, // unused 108 + gid: [7:0]u8 = [_:0]u8{0} ** 7, // unused 116 + size: [11:0]u8 = [_:0]u8{'0'} ** 11, // 124 + mtime: [11:0]u8 = [_:0]u8{'0'} ** 11, // 136 + checksum: [7:0]u8 = [_:0]u8{' '} ** 7, // 148 + typeflag: FileType = .regular, // 156 + linkname: [100]u8 = [_]u8{0} ** 100, // 157 + magic: [6]u8 = [_]u8{ 'u', 's', 't', 'a', 'r', 0 }, // 257 + version: [2]u8 = [_]u8{ '0', '0' }, // 263 + uname: [32]u8 = [_]u8{0} ** 32, // unused 265 + gname: [32]u8 = [_]u8{0} ** 32, // unused 297 + devmajor: [7:0]u8 = [_:0]u8{0} ** 7, // unused 329 + devminor: [7:0]u8 = [_:0]u8{0} ** 7, // unused 337 + prefix: [155]u8 = [_]u8{0} ** 155, // 345 + pad: [12]u8 = [_]u8{0} ** 12, // unused 500 + + pub const FileType = enum(u8) { + regular = '0', + symbolic_link = '2', + directory = '5', + gnu_long_name = 'L', + gnu_long_link = 'K', + }; + + const default_mode = struct { + const file = [_:0]u8{ '0', '0', '0', '0', '6', '6', '4' }; // 0o664 + const dir = [_:0]u8{ '0', '0', '0', '0', '7', '7', '5' }; // 0o775 + const sym_link = [_:0]u8{ '0', '0', '0', '0', '7', '7', '7' }; // 0o777 + const other = [_:0]u8{ '0', '0', '0', '0', '0', '0', '0' }; // 0o000 + }; + + pub fn init(typeflag: FileType) Header { + return .{ + .typeflag = typeflag, + .mode = switch (typeflag) { + .directory => default_mode.dir, + .symbolic_link => default_mode.sym_link, + .regular => default_mode.file, + else => default_mode.other, + }, + }; + } + + pub fn setSize(w: *Header, size: u64) error{OctalOverflow}!void { + try octal(&w.size, size); + } + + fn octal(buf: []u8, value: u64) error{OctalOverflow}!void { + var remainder: u64 = value; + var pos: usize = buf.len; + while (remainder > 0 and pos > 0) { + pos -= 1; + const c: u8 = @as(u8, @intCast(remainder % 8)) + '0'; + buf[pos] = c; + remainder /= 8; + if (pos == 0 and remainder > 0) return error.OctalOverflow; + } + } + + pub fn setMode(w: *Header, mode: u32) error{OctalOverflow}!void { + try octal(&w.mode, mode); + } + + // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time. + // mtime == 0 will use current time + pub fn setMtime(w: *Header, mtime: u64) error{OctalOverflow}!void { + try octal(&w.mtime, mtime); + } + + pub fn updateChecksum(w: *Header) !void { + var checksum: usize = ' '; // other 7 w.checksum bytes are initialized to ' ' + for (std.mem.asBytes(w)) |val| + checksum += val; + try octal(&w.checksum, checksum); + } + + pub fn write(h: *Header, bw: *std.Io.Writer) error{ OctalOverflow, WriteFailed }!void { + try h.updateChecksum(); + try bw.writeAll(std.mem.asBytes(h)); + } + + pub fn setLinkname(w: *Header, link: []const u8) !void { + if (link.len > w.linkname.len) return error.NameTooLong; + @memcpy(w.linkname[0..link.len], link); + } + + pub fn setPath(w: *Header, prefix: []const u8, sub_path: []const u8) !void { + const max_prefix = w.prefix.len; + const max_name = w.name.len; + const sep = std.fs.path.sep_posix; + + if (prefix.len + sub_path.len > max_name + max_prefix or prefix.len > max_prefix) + return error.NameTooLong; + + // both fit into name + if (prefix.len > 0 and prefix.len + sub_path.len < max_name) { + @memcpy(w.name[0..prefix.len], prefix); + w.name[prefix.len] = sep; + @memcpy(w.name[prefix.len + 1 ..][0..sub_path.len], sub_path); + return; + } + + // sub_path fits into name + // there is no prefix or prefix fits into prefix + if (sub_path.len <= max_name) { + @memcpy(w.name[0..sub_path.len], sub_path); + @memcpy(w.prefix[0..prefix.len], prefix); + return; + } + + if (prefix.len > 0) { + @memcpy(w.prefix[0..prefix.len], prefix); + w.prefix[prefix.len] = sep; + } + const prefix_pos = if (prefix.len > 0) prefix.len + 1 else 0; + + // add as much to prefix as you can, must split at / + const prefix_remaining = max_prefix - prefix_pos; + if (std.mem.lastIndexOf(u8, sub_path[0..@min(prefix_remaining, sub_path.len)], &.{'/'})) |sep_pos| { + @memcpy(w.prefix[prefix_pos..][0..sep_pos], sub_path[0..sep_pos]); + if ((sub_path.len - sep_pos - 1) > max_name) return error.NameTooLong; + @memcpy(w.name[0..][0 .. sub_path.len - sep_pos - 1], sub_path[sep_pos + 1 ..]); + return; + } + + return error.NameTooLong; + } + + comptime { + assert(@sizeOf(Header) == 512); + } + + test "setPath" { + const cases = [_]struct { + in: []const []const u8, + out: []const []const u8, + }{ + .{ + .in = &.{ "", "123456789" }, + .out = &.{ "", "123456789" }, + }, + // can fit into name + .{ + .in = &.{ "prefix", "sub_path" }, + .out = &.{ "", "prefix/sub_path" }, + }, + // no more both fits into name + .{ + .in = &.{ "prefix", "0123456789/" ** 8 ++ "basename" }, + .out = &.{ "prefix", "0123456789/" ** 8 ++ "basename" }, + }, + // put as much as you can into prefix the rest goes into name + .{ + .in = &.{ "prefix", "0123456789/" ** 10 ++ "basename" }, + .out = &.{ "prefix/" ++ "0123456789/" ** 9 ++ "0123456789", "basename" }, + }, + + .{ + .in = &.{ "prefix", "0123456789/" ** 15 ++ "basename" }, + .out = &.{ "prefix/" ++ "0123456789/" ** 12 ++ "0123456789", "0123456789/0123456789/basename" }, + }, + .{ + .in = &.{ "prefix", "0123456789/" ** 21 ++ "basename" }, + .out = &.{ "prefix/" ++ "0123456789/" ** 12 ++ "0123456789", "0123456789/" ** 8 ++ "basename" }, + }, + .{ + .in = &.{ "", "012345678/" ** 10 ++ "foo" }, + .out = &.{ "012345678/" ** 9 ++ "012345678", "foo" }, + }, + }; + + for (cases) |case| { + var header = Header.init(.regular); + try header.setPath(case.in[0], case.in[1]); + try testing.expectEqualStrings(case.out[0], str(&header.prefix)); + try testing.expectEqualStrings(case.out[1], str(&header.name)); + } + + const error_cases = [_]struct { + in: []const []const u8, + }{ + // basename can't fit into name (106 characters) + .{ .in = &.{ "zig", "test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig" } }, + // cant fit into 255 + sep + .{ .in = &.{ "prefix", "0123456789/" ** 22 ++ "basename" } }, + // can fit but sub_path can't be split (there is no separator) + .{ .in = &.{ "prefix", "0123456789" ** 10 ++ "a" } }, + .{ .in = &.{ "prefix", "0123456789" ** 14 ++ "basename" } }, + }; + + for (error_cases) |case| { + var header = Header.init(.regular); + try testing.expectError( + error.NameTooLong, + header.setPath(case.in[0], case.in[1]), + ); + } + } + + // Breaks string on first null character. + fn str(s: []const u8) []const u8 { + for (s, 0..) |c, i| { + if (c == 0) return s[0..i]; + } + return s; + } +}; + +test { + _ = Header; +} + +test "write files" { + const files = [_]struct { + path: []const u8, + content: []const u8, + }{ + .{ .path = "foo", .content = "bar" }, + .{ .path = "a12345678/" ** 10 ++ "foo", .content = "a" ** 511 }, + .{ .path = "b12345678/" ** 24 ++ "foo", .content = "b" ** 512 }, + .{ .path = "c12345678/" ** 25 ++ "foo", .content = "c" ** 513 }, + .{ .path = "d12345678/" ** 51 ++ "foo", .content = "d" ** 1025 }, + .{ .path = "e123456789" ** 11, .content = "e" }, + }; + + var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; + var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; + + // with root + { + const root = "root"; + + var output: std.Io.Writer.Allocating = .init(testing.allocator); + var w: Writer = .{ .underlying_writer = &output.writer }; + defer output.deinit(); + try w.setRoot(root); + for (files) |file| + try w.writeFileBytes(file.path, file.content, .{}); + + var input: std.Io.Reader = .fixed(output.getWritten()); + var it: std.tar.Iterator = .init(&input, .{ + .file_name_buffer = &file_name_buffer, + .link_name_buffer = &link_name_buffer, + }); + + // first entry is directory with prefix + { + const actual = (try it.next()).?; + try testing.expectEqualStrings(root, actual.name); + try testing.expectEqual(std.tar.FileKind.directory, actual.kind); + } + + var i: usize = 0; + while (try it.next()) |actual| { + defer i += 1; + const expected = files[i]; + try testing.expectEqualStrings(root, actual.name[0..root.len]); + try testing.expectEqual('/', actual.name[root.len..][0]); + try testing.expectEqualStrings(expected.path, actual.name[root.len + 1 ..]); + + var content: std.Io.Writer.Allocating = .init(testing.allocator); + defer content.deinit(); + try it.streamRemaining(actual, &content.writer); + try testing.expectEqualSlices(u8, expected.content, content.getWritten()); + } + } + // without root + { + var output: std.Io.Writer.Allocating = .init(testing.allocator); + var w: Writer = .{ .underlying_writer = &output.writer }; + defer output.deinit(); + for (files) |file| { + var content: std.Io.Reader = .fixed(file.content); + try w.writeFileStream(file.path, file.content.len, &content, .{}); + } + + var input: std.Io.Reader = .fixed(output.getWritten()); + var it: std.tar.Iterator = .init(&input, .{ + .file_name_buffer = &file_name_buffer, + .link_name_buffer = &link_name_buffer, + }); + + var i: usize = 0; + while (try it.next()) |actual| { + defer i += 1; + const expected = files[i]; + try testing.expectEqualStrings(expected.path, actual.name); + + var content: std.Io.Writer.Allocating = .init(testing.allocator); + defer content.deinit(); + try it.streamRemaining(actual, &content.writer); + try testing.expectEqualSlices(u8, expected.content, content.getWritten()); + } + try w.finishPedantically(); + } +} diff --git a/lib/std/tar/test.zig b/lib/std/tar/test.zig index 3bcb5af90c..3356baacb5 100644 --- a/lib/std/tar/test.zig +++ b/lib/std/tar/test.zig @@ -18,31 +18,72 @@ const Case = struct { err: ?anyerror = null, // parsing should fail with this error }; -const cases = [_]Case{ - .{ - .data = @embedFile("testdata/gnu.tar"), - .files = &[_]Case.File{ - .{ - .name = "small.txt", - .size = 5, - .mode = 0o640, - }, - .{ - .name = "small2.txt", - .size = 11, - .mode = 0o640, - }, +const gnu_case: Case = .{ + .data = @embedFile("testdata/gnu.tar"), + .files = &[_]Case.File{ + .{ + .name = "small.txt", + .size = 5, + .mode = 0o640, }, - .chksums = &[_][]const u8{ - "e38b27eaccb4391bdec553a7f3ae6b2f", - "c65bd2e50a56a2138bf1716f2fd56fe9", + .{ + .name = "small2.txt", + .size = 11, + .mode = 0o640, }, }, - .{ + .chksums = &[_][]const u8{ + "e38b27eaccb4391bdec553a7f3ae6b2f", + "c65bd2e50a56a2138bf1716f2fd56fe9", + }, +}; + +const gnu_multi_headers_case: Case = .{ + .data = @embedFile("testdata/gnu-multi-hdrs.tar"), + .files = &[_]Case.File{ + .{ + .name = "GNU2/GNU2/long-path-name", + .link_name = "GNU4/GNU4/long-linkpath-name", + .kind = .sym_link, + }, + }, +}; + +const trailing_slash_case: Case = .{ + .data = @embedFile("testdata/trailing-slash.tar"), + .files = &[_]Case.File{ + .{ + .name = "123456789/" ** 30, + .kind = .directory, + }, + }, +}; + +const writer_big_long_case: Case = .{ + // Size in gnu extended format, and name in pax attribute. + .data = @embedFile("testdata/writer-big-long.tar"), + .files = &[_]Case.File{ + .{ + .name = "longname/" ** 15 ++ "16gig.txt", + .size = 16 * 1024 * 1024 * 1024, + .mode = 0o644, + .truncated = true, + }, + }, +}; + +const fuzz1_case: Case = .{ + .data = @embedFile("testdata/fuzz1.tar"), + .err = error.TarInsufficientBuffer, +}; + +test "run test cases" { + try testCase(gnu_case); + try testCase(.{ .data = @embedFile("testdata/sparse-formats.tar"), .err = error.TarUnsupportedHeader, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/star.tar"), .files = &[_]Case.File{ .{ @@ -60,8 +101,8 @@ const cases = [_]Case{ "e38b27eaccb4391bdec553a7f3ae6b2f", "c65bd2e50a56a2138bf1716f2fd56fe9", }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/v7.tar"), .files = &[_]Case.File{ .{ @@ -79,8 +120,8 @@ const cases = [_]Case{ "e38b27eaccb4391bdec553a7f3ae6b2f", "c65bd2e50a56a2138bf1716f2fd56fe9", }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/pax.tar"), .files = &[_]Case.File{ .{ @@ -99,13 +140,13 @@ const cases = [_]Case{ .chksums = &[_][]const u8{ "3c382e8f5b6631aa2db52643912ffd4a", }, - }, - .{ + }); + try testCase(.{ // pax attribute don't end with \n .data = @embedFile("testdata/pax-bad-hdr-file.tar"), .err = error.PaxInvalidAttributeEnd, - }, - .{ + }); + try testCase(.{ // size is in pax attribute .data = @embedFile("testdata/pax-pos-size-file.tar"), .files = &[_]Case.File{ @@ -119,8 +160,8 @@ const cases = [_]Case{ .chksums = &[_][]const u8{ "0afb597b283fe61b5d4879669a350556", }, - }, - .{ + }); + try testCase(.{ // has pax records which we are not interested in .data = @embedFile("testdata/pax-records.tar"), .files = &[_]Case.File{ @@ -128,8 +169,8 @@ const cases = [_]Case{ .name = "file", }, }, - }, - .{ + }); + try testCase(.{ // has global records which we are ignoring .data = @embedFile("testdata/pax-global-records.tar"), .files = &[_]Case.File{ @@ -146,8 +187,8 @@ const cases = [_]Case{ .name = "file4", }, }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/nil-uid.tar"), .files = &[_]Case.File{ .{ @@ -160,8 +201,8 @@ const cases = [_]Case{ .chksums = &[_][]const u8{ "08d504674115e77a67244beac19668f5", }, - }, - .{ + }); + try testCase(.{ // has xattrs and pax records which we are ignoring .data = @embedFile("testdata/xattrs.tar"), .files = &[_]Case.File{ @@ -182,23 +223,14 @@ const cases = [_]Case{ "e38b27eaccb4391bdec553a7f3ae6b2f", "c65bd2e50a56a2138bf1716f2fd56fe9", }, - }, - .{ - .data = @embedFile("testdata/gnu-multi-hdrs.tar"), - .files = &[_]Case.File{ - .{ - .name = "GNU2/GNU2/long-path-name", - .link_name = "GNU4/GNU4/long-linkpath-name", - .kind = .sym_link, - }, - }, - }, - .{ + }); + try testCase(gnu_multi_headers_case); + try testCase(.{ // has gnu type D (directory) and S (sparse) blocks .data = @embedFile("testdata/gnu-incremental.tar"), .err = error.TarUnsupportedHeader, - }, - .{ + }); + try testCase(.{ // should use values only from last pax header .data = @embedFile("testdata/pax-multi-hdrs.tar"), .files = &[_]Case.File{ @@ -208,8 +240,8 @@ const cases = [_]Case{ .kind = .sym_link, }, }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/gnu-long-nul.tar"), .files = &[_]Case.File{ .{ @@ -217,8 +249,8 @@ const cases = [_]Case{ .mode = 0o644, }, }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/gnu-utf8.tar"), .files = &[_]Case.File{ .{ @@ -226,8 +258,8 @@ const cases = [_]Case{ .mode = 0o644, }, }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/gnu-not-utf8.tar"), .files = &[_]Case.File{ .{ @@ -235,33 +267,33 @@ const cases = [_]Case{ .mode = 0o644, }, }, - }, - .{ + }); + try testCase(.{ // null in pax key .data = @embedFile("testdata/pax-nul-xattrs.tar"), .err = error.PaxNullInKeyword, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/pax-nul-path.tar"), .err = error.PaxNullInValue, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/neg-size.tar"), .err = error.TarHeader, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/issue10968.tar"), .err = error.TarHeader, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/issue11169.tar"), .err = error.TarHeader, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/issue12435.tar"), .err = error.TarHeaderChksum, - }, - .{ + }); + try testCase(.{ // has magic with space at end instead of null .data = @embedFile("testdata/invalid-go17.tar"), .files = &[_]Case.File{ @@ -269,8 +301,8 @@ const cases = [_]Case{ .name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo", }, }, - }, - .{ + }); + try testCase(.{ .data = @embedFile("testdata/ustar-file-devs.tar"), .files = &[_]Case.File{ .{ @@ -278,17 +310,9 @@ const cases = [_]Case{ .mode = 0o644, }, }, - }, - .{ - .data = @embedFile("testdata/trailing-slash.tar"), - .files = &[_]Case.File{ - .{ - .name = "123456789/" ** 30, - .kind = .directory, - }, - }, - }, - .{ + }); + try testCase(trailing_slash_case); + try testCase(.{ // Has size in gnu extended format. To represent size bigger than 8 GB. .data = @embedFile("testdata/writer-big.tar"), .files = &[_]Case.File{ @@ -299,120 +323,92 @@ const cases = [_]Case{ .mode = 0o640, }, }, - }, - .{ - // Size in gnu extended format, and name in pax attribute. - .data = @embedFile("testdata/writer-big-long.tar"), - .files = &[_]Case.File{ - .{ - .name = "longname/" ** 15 ++ "16gig.txt", - .size = 16 * 1024 * 1024 * 1024, - .mode = 0o644, - .truncated = true, - }, - }, - }, - .{ - .data = @embedFile("testdata/fuzz1.tar"), - .err = error.TarInsufficientBuffer, - }, - .{ + }); + try testCase(writer_big_long_case); + try testCase(fuzz1_case); + try testCase(.{ .data = @embedFile("testdata/fuzz2.tar"), .err = error.PaxSizeAttrOverflow, - }, -}; + }); +} -// used in test to calculate file chksum -const Md5Writer = struct { - h: std.crypto.hash.Md5 = std.crypto.hash.Md5.init(.{}), - - pub fn writeAll(self: *Md5Writer, buf: []const u8) !void { - self.h.update(buf); - } - - pub fn writeByte(self: *Md5Writer, byte: u8) !void { - self.h.update(&[_]u8{byte}); - } - - pub fn chksum(self: *Md5Writer) [32]u8 { - var s = [_]u8{0} ** 16; - self.h.final(&s); - return std.fmt.bytesToHex(s, .lower); - } -}; - -test "run test cases" { +fn testCase(case: Case) !void { var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; - for (cases) |case| { - var fsb = std.io.fixedBufferStream(case.data); - var iter = tar.iterator(fsb.reader(), .{ - .file_name_buffer = &file_name_buffer, - .link_name_buffer = &link_name_buffer, - }); - var i: usize = 0; - while (iter.next() catch |err| { - if (case.err) |e| { - try testing.expectEqual(e, err); - continue; - } else { - return err; - } - }) |actual| : (i += 1) { - const expected = case.files[i]; - try testing.expectEqualStrings(expected.name, actual.name); - try testing.expectEqual(expected.size, actual.size); - try testing.expectEqual(expected.kind, actual.kind); - try testing.expectEqual(expected.mode, actual.mode); - try testing.expectEqualStrings(expected.link_name, actual.link_name); + var br: std.io.Reader = .fixed(case.data); + var it: tar.Iterator = .init(&br, .{ + .file_name_buffer = &file_name_buffer, + .link_name_buffer = &link_name_buffer, + }); + var i: usize = 0; + while (it.next() catch |err| { + if (case.err) |e| { + try testing.expectEqual(e, err); + return; + } else { + return err; + } + }) |actual| : (i += 1) { + const expected = case.files[i]; + try testing.expectEqualStrings(expected.name, actual.name); + try testing.expectEqual(expected.size, actual.size); + try testing.expectEqual(expected.kind, actual.kind); + try testing.expectEqual(expected.mode, actual.mode); + try testing.expectEqualStrings(expected.link_name, actual.link_name); - if (case.chksums.len > i) { - var md5writer = Md5Writer{}; - try actual.writeAll(&md5writer); - const chksum = md5writer.chksum(); - try testing.expectEqualStrings(case.chksums[i], &chksum); - } else { - if (expected.truncated) { - iter.unread_file_bytes = 0; - } + if (case.chksums.len > i) { + var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); + try it.streamRemaining(actual, &aw.writer); + const chksum = std.fmt.bytesToHex(std.crypto.hash.Md5.hashResult(aw.getWritten()), .lower); + try testing.expectEqualStrings(case.chksums[i], &chksum); + } else { + if (expected.truncated) { + it.unread_file_bytes = 0; } } - try testing.expectEqual(case.files.len, i); } + try testing.expectEqual(case.files.len, i); } test "pax/gnu long names with small buffer" { + try testLongNameCase(gnu_multi_headers_case); + try testLongNameCase(trailing_slash_case); + try testLongNameCase(.{ + .data = @embedFile("testdata/fuzz1.tar"), + .err = error.TarInsufficientBuffer, + }); +} + +fn testLongNameCase(case: Case) !void { // should fail with insufficient buffer error var min_file_name_buffer: [256]u8 = undefined; var min_link_name_buffer: [100]u8 = undefined; - const long_name_cases = [_]Case{ cases[11], cases[25], cases[28] }; - for (long_name_cases) |case| { - var fsb = std.io.fixedBufferStream(case.data); - var iter = tar.iterator(fsb.reader(), .{ - .file_name_buffer = &min_file_name_buffer, - .link_name_buffer = &min_link_name_buffer, - }); + var br: std.io.Reader = .fixed(case.data); + var iter: tar.Iterator = .init(&br, .{ + .file_name_buffer = &min_file_name_buffer, + .link_name_buffer = &min_link_name_buffer, + }); - var iter_err: ?anyerror = null; - while (iter.next() catch |err| brk: { - iter_err = err; - break :brk null; - }) |_| {} + var iter_err: ?anyerror = null; + while (iter.next() catch |err| brk: { + iter_err = err; + break :brk null; + }) |_| {} - try testing.expect(iter_err != null); - try testing.expectEqual(error.TarInsufficientBuffer, iter_err.?); - } + try testing.expect(iter_err != null); + try testing.expectEqual(error.TarInsufficientBuffer, iter_err.?); } test "insufficient buffer in Header name filed" { var min_file_name_buffer: [9]u8 = undefined; var min_link_name_buffer: [100]u8 = undefined; - var fsb = std.io.fixedBufferStream(cases[0].data); - var iter = tar.iterator(fsb.reader(), .{ + var br: std.io.Reader = .fixed(gnu_case.data); + var iter: tar.Iterator = .init(&br, .{ .file_name_buffer = &min_file_name_buffer, .link_name_buffer = &min_link_name_buffer, }); @@ -466,21 +462,21 @@ test "should not overwrite existing file" { // This ensures that file is not overwritten. // const data = @embedFile("testdata/overwrite_file.tar"); - var fsb = std.io.fixedBufferStream(data); + var r: std.io.Reader = .fixed(data); // Unpack with strip_components = 1 should fail var root = std.testing.tmpDir(.{}); defer root.cleanup(); try testing.expectError( error.PathAlreadyExists, - tar.pipeToFileSystem(root.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 1 }), + tar.pipeToFileSystem(root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }), ); // Unpack with strip_components = 0 should pass - fsb.reset(); + r = .fixed(data); var root2 = std.testing.tmpDir(.{}); defer root2.cleanup(); - try tar.pipeToFileSystem(root2.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 0 }); + try tar.pipeToFileSystem(root2.dir, &r, .{ .mode_mode = .ignore, .strip_components = 0 }); } test "case sensitivity" { @@ -494,12 +490,12 @@ test "case sensitivity" { // 18089/alacritty/Darkermatrix.yml // const data = @embedFile("testdata/18089.tar"); - var fsb = std.io.fixedBufferStream(data); + var r: std.io.Reader = .fixed(data); var root = std.testing.tmpDir(.{}); defer root.cleanup(); - tar.pipeToFileSystem(root.dir, fsb.reader(), .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { + tar.pipeToFileSystem(root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { // on case insensitive fs we fail on overwrite existing file try testing.expectEqual(error.PathAlreadyExists, err); return; diff --git a/lib/std/tar/writer.zig b/lib/std/tar/writer.zig deleted file mode 100644 index 4ced287eec..0000000000 --- a/lib/std/tar/writer.zig +++ /dev/null @@ -1,497 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const testing = std.testing; - -/// Creates tar Writer which will write tar content to the `underlying_writer`. -/// Use setRoot to nest all following entries under single root. If file don't -/// fit into posix header (name+prefix: 100+155 bytes) gnu extented header will -/// be used for long names. Options enables setting file premission mode and -/// mtime. Default is to use current time for mtime and 0o664 for file mode. -pub fn writer(underlying_writer: anytype) Writer(@TypeOf(underlying_writer)) { - return .{ .underlying_writer = underlying_writer }; -} - -pub fn Writer(comptime WriterType: type) type { - return struct { - const block_size = @sizeOf(Header); - const empty_block: [block_size]u8 = [_]u8{0} ** block_size; - - /// Options for writing file/dir/link. If left empty 0o664 is used for - /// file mode and current time for mtime. - pub const Options = struct { - /// File system permission mode. - mode: u32 = 0, - /// File system modification time. - mtime: u64 = 0, - }; - const Self = @This(); - - underlying_writer: WriterType, - prefix: []const u8 = "", - mtime_now: u64 = 0, - - /// Sets prefix for all other write* method paths. - pub fn setRoot(self: *Self, root: []const u8) !void { - if (root.len > 0) - try self.writeDir(root, .{}); - - self.prefix = root; - } - - /// Writes directory. - pub fn writeDir(self: *Self, sub_path: []const u8, opt: Options) !void { - try self.writeHeader(.directory, sub_path, "", 0, opt); - } - - /// Writes file system file. - pub fn writeFile(self: *Self, sub_path: []const u8, file: std.fs.File) !void { - const stat = try file.stat(); - const mtime: u64 = @intCast(@divFloor(stat.mtime, std.time.ns_per_s)); - - var header = Header{}; - try self.setPath(&header, sub_path); - try header.setSize(stat.size); - try header.setMtime(mtime); - try header.write(self.underlying_writer); - - try self.underlying_writer.writeFile(file); - try self.writePadding(stat.size); - } - - /// Writes file reading file content from `reader`. Number of bytes in - /// reader must be equal to `size`. - pub fn writeFileStream(self: *Self, sub_path: []const u8, size: usize, reader: anytype, opt: Options) !void { - try self.writeHeader(.regular, sub_path, "", @intCast(size), opt); - - var counting_reader = std.io.countingReader(reader); - var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); - try fifo.pump(counting_reader.reader(), self.underlying_writer); - if (counting_reader.bytes_read != size) return error.WrongReaderSize; - try self.writePadding(size); - } - - /// Writes file using bytes buffer `content` for size and file content. - pub fn writeFileBytes(self: *Self, sub_path: []const u8, content: []const u8, opt: Options) !void { - try self.writeHeader(.regular, sub_path, "", @intCast(content.len), opt); - try self.underlying_writer.writeAll(content); - try self.writePadding(content.len); - } - - /// Writes symlink. - pub fn writeLink(self: *Self, sub_path: []const u8, link_name: []const u8, opt: Options) !void { - try self.writeHeader(.symbolic_link, sub_path, link_name, 0, opt); - } - - /// Writes fs.Dir.WalkerEntry. Uses `mtime` from file system entry and - /// default for entry mode . - pub fn writeEntry(self: *Self, entry: std.fs.Dir.Walker.Entry) !void { - switch (entry.kind) { - .directory => { - try self.writeDir(entry.path, .{ .mtime = try entryMtime(entry) }); - }, - .file => { - var file = try entry.dir.openFile(entry.basename, .{}); - defer file.close(); - try self.writeFile(entry.path, file); - }, - .sym_link => { - var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; - const link_name = try entry.dir.readLink(entry.basename, &link_name_buffer); - try self.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) }); - }, - else => { - return error.UnsupportedWalkerEntryKind; - }, - } - } - - fn writeHeader( - self: *Self, - typeflag: Header.FileType, - sub_path: []const u8, - link_name: []const u8, - size: u64, - opt: Options, - ) !void { - var header = Header.init(typeflag); - try self.setPath(&header, sub_path); - try header.setSize(size); - try header.setMtime(if (opt.mtime != 0) opt.mtime else self.mtimeNow()); - if (opt.mode != 0) - try header.setMode(opt.mode); - if (typeflag == .symbolic_link) - header.setLinkname(link_name) catch |err| switch (err) { - error.NameTooLong => try self.writeExtendedHeader(.gnu_long_link, &.{link_name}), - else => return err, - }; - try header.write(self.underlying_writer); - } - - fn mtimeNow(self: *Self) u64 { - if (self.mtime_now == 0) - self.mtime_now = @intCast(std.time.timestamp()); - return self.mtime_now; - } - - fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 { - const stat = try entry.dir.statFile(entry.basename); - return @intCast(@divFloor(stat.mtime, std.time.ns_per_s)); - } - - /// Writes path in posix header, if don't fit (in name+prefix; 100+155 - /// bytes) writes it in gnu extended header. - fn setPath(self: *Self, header: *Header, sub_path: []const u8) !void { - header.setPath(self.prefix, sub_path) catch |err| switch (err) { - error.NameTooLong => { - // write extended header - const buffers: []const []const u8 = if (self.prefix.len == 0) - &.{sub_path} - else - &.{ self.prefix, "/", sub_path }; - try self.writeExtendedHeader(.gnu_long_name, buffers); - }, - else => return err, - }; - } - - /// Writes gnu extended header: gnu_long_name or gnu_long_link. - fn writeExtendedHeader(self: *Self, typeflag: Header.FileType, buffers: []const []const u8) !void { - var len: usize = 0; - for (buffers) |buf| - len += buf.len; - - var header = Header.init(typeflag); - try header.setSize(len); - try header.write(self.underlying_writer); - for (buffers) |buf| - try self.underlying_writer.writeAll(buf); - try self.writePadding(len); - } - - fn writePadding(self: *Self, bytes: u64) !void { - const pos: usize = @intCast(bytes % block_size); - if (pos == 0) return; - try self.underlying_writer.writeAll(empty_block[pos..]); - } - - /// Tar should finish with two zero blocks, but 'reasonable system must - /// not assume that such a block exists when reading an archive' (from - /// reference). In practice it is safe to skip this finish. - pub fn finish(self: *Self) !void { - try self.underlying_writer.writeAll(&empty_block); - try self.underlying_writer.writeAll(&empty_block); - } - }; -} - -/// A struct that is exactly 512 bytes and matches tar file format. This is -/// intended to be used for outputting tar files; for parsing there is -/// `std.tar.Header`. -const Header = extern struct { - // This struct was originally copied from - // https://github.com/mattnite/tar/blob/main/src/main.zig which is MIT - // licensed. - // - // The name, linkname, magic, uname, and gname are null-terminated character - // strings. All other fields are zero-filled octal numbers in ASCII. Each - // numeric field of width w contains w minus 1 digits, and a null. - // Reference: https://www.gnu.org/software/tar/manual/html_node/Standard.html - // POSIX header: byte offset - name: [100]u8 = [_]u8{0} ** 100, // 0 - mode: [7:0]u8 = default_mode.file, // 100 - uid: [7:0]u8 = [_:0]u8{0} ** 7, // unused 108 - gid: [7:0]u8 = [_:0]u8{0} ** 7, // unused 116 - size: [11:0]u8 = [_:0]u8{'0'} ** 11, // 124 - mtime: [11:0]u8 = [_:0]u8{'0'} ** 11, // 136 - checksum: [7:0]u8 = [_:0]u8{' '} ** 7, // 148 - typeflag: FileType = .regular, // 156 - linkname: [100]u8 = [_]u8{0} ** 100, // 157 - magic: [6]u8 = [_]u8{ 'u', 's', 't', 'a', 'r', 0 }, // 257 - version: [2]u8 = [_]u8{ '0', '0' }, // 263 - uname: [32]u8 = [_]u8{0} ** 32, // unused 265 - gname: [32]u8 = [_]u8{0} ** 32, // unused 297 - devmajor: [7:0]u8 = [_:0]u8{0} ** 7, // unused 329 - devminor: [7:0]u8 = [_:0]u8{0} ** 7, // unused 337 - prefix: [155]u8 = [_]u8{0} ** 155, // 345 - pad: [12]u8 = [_]u8{0} ** 12, // unused 500 - - pub const FileType = enum(u8) { - regular = '0', - symbolic_link = '2', - directory = '5', - gnu_long_name = 'L', - gnu_long_link = 'K', - }; - - const default_mode = struct { - const file = [_:0]u8{ '0', '0', '0', '0', '6', '6', '4' }; // 0o664 - const dir = [_:0]u8{ '0', '0', '0', '0', '7', '7', '5' }; // 0o775 - const sym_link = [_:0]u8{ '0', '0', '0', '0', '7', '7', '7' }; // 0o777 - const other = [_:0]u8{ '0', '0', '0', '0', '0', '0', '0' }; // 0o000 - }; - - pub fn init(typeflag: FileType) Header { - return .{ - .typeflag = typeflag, - .mode = switch (typeflag) { - .directory => default_mode.dir, - .symbolic_link => default_mode.sym_link, - .regular => default_mode.file, - else => default_mode.other, - }, - }; - } - - pub fn setSize(self: *Header, size: u64) !void { - try octal(&self.size, size); - } - - fn octal(buf: []u8, value: u64) !void { - var remainder: u64 = value; - var pos: usize = buf.len; - while (remainder > 0 and pos > 0) { - pos -= 1; - const c: u8 = @as(u8, @intCast(remainder % 8)) + '0'; - buf[pos] = c; - remainder /= 8; - if (pos == 0 and remainder > 0) return error.OctalOverflow; - } - } - - pub fn setMode(self: *Header, mode: u32) !void { - try octal(&self.mode, mode); - } - - // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time. - // mtime == 0 will use current time - pub fn setMtime(self: *Header, mtime: u64) !void { - try octal(&self.mtime, mtime); - } - - pub fn updateChecksum(self: *Header) !void { - var checksum: usize = ' '; // other 7 self.checksum bytes are initialized to ' ' - for (std.mem.asBytes(self)) |val| - checksum += val; - try octal(&self.checksum, checksum); - } - - pub fn write(self: *Header, output_writer: anytype) !void { - try self.updateChecksum(); - try output_writer.writeAll(std.mem.asBytes(self)); - } - - pub fn setLinkname(self: *Header, link: []const u8) !void { - if (link.len > self.linkname.len) return error.NameTooLong; - @memcpy(self.linkname[0..link.len], link); - } - - pub fn setPath(self: *Header, prefix: []const u8, sub_path: []const u8) !void { - const max_prefix = self.prefix.len; - const max_name = self.name.len; - const sep = std.fs.path.sep_posix; - - if (prefix.len + sub_path.len > max_name + max_prefix or prefix.len > max_prefix) - return error.NameTooLong; - - // both fit into name - if (prefix.len > 0 and prefix.len + sub_path.len < max_name) { - @memcpy(self.name[0..prefix.len], prefix); - self.name[prefix.len] = sep; - @memcpy(self.name[prefix.len + 1 ..][0..sub_path.len], sub_path); - return; - } - - // sub_path fits into name - // there is no prefix or prefix fits into prefix - if (sub_path.len <= max_name) { - @memcpy(self.name[0..sub_path.len], sub_path); - @memcpy(self.prefix[0..prefix.len], prefix); - return; - } - - if (prefix.len > 0) { - @memcpy(self.prefix[0..prefix.len], prefix); - self.prefix[prefix.len] = sep; - } - const prefix_pos = if (prefix.len > 0) prefix.len + 1 else 0; - - // add as much to prefix as you can, must split at / - const prefix_remaining = max_prefix - prefix_pos; - if (std.mem.lastIndexOf(u8, sub_path[0..@min(prefix_remaining, sub_path.len)], &.{'/'})) |sep_pos| { - @memcpy(self.prefix[prefix_pos..][0..sep_pos], sub_path[0..sep_pos]); - if ((sub_path.len - sep_pos - 1) > max_name) return error.NameTooLong; - @memcpy(self.name[0..][0 .. sub_path.len - sep_pos - 1], sub_path[sep_pos + 1 ..]); - return; - } - - return error.NameTooLong; - } - - comptime { - assert(@sizeOf(Header) == 512); - } - - test setPath { - const cases = [_]struct { - in: []const []const u8, - out: []const []const u8, - }{ - .{ - .in = &.{ "", "123456789" }, - .out = &.{ "", "123456789" }, - }, - // can fit into name - .{ - .in = &.{ "prefix", "sub_path" }, - .out = &.{ "", "prefix/sub_path" }, - }, - // no more both fits into name - .{ - .in = &.{ "prefix", "0123456789/" ** 8 ++ "basename" }, - .out = &.{ "prefix", "0123456789/" ** 8 ++ "basename" }, - }, - // put as much as you can into prefix the rest goes into name - .{ - .in = &.{ "prefix", "0123456789/" ** 10 ++ "basename" }, - .out = &.{ "prefix/" ++ "0123456789/" ** 9 ++ "0123456789", "basename" }, - }, - - .{ - .in = &.{ "prefix", "0123456789/" ** 15 ++ "basename" }, - .out = &.{ "prefix/" ++ "0123456789/" ** 12 ++ "0123456789", "0123456789/0123456789/basename" }, - }, - .{ - .in = &.{ "prefix", "0123456789/" ** 21 ++ "basename" }, - .out = &.{ "prefix/" ++ "0123456789/" ** 12 ++ "0123456789", "0123456789/" ** 8 ++ "basename" }, - }, - .{ - .in = &.{ "", "012345678/" ** 10 ++ "foo" }, - .out = &.{ "012345678/" ** 9 ++ "012345678", "foo" }, - }, - }; - - for (cases) |case| { - var header = Header.init(.regular); - try header.setPath(case.in[0], case.in[1]); - try testing.expectEqualStrings(case.out[0], str(&header.prefix)); - try testing.expectEqualStrings(case.out[1], str(&header.name)); - } - - const error_cases = [_]struct { - in: []const []const u8, - }{ - // basename can't fit into name (106 characters) - .{ .in = &.{ "zig", "test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig" } }, - // cant fit into 255 + sep - .{ .in = &.{ "prefix", "0123456789/" ** 22 ++ "basename" } }, - // can fit but sub_path can't be split (there is no separator) - .{ .in = &.{ "prefix", "0123456789" ** 10 ++ "a" } }, - .{ .in = &.{ "prefix", "0123456789" ** 14 ++ "basename" } }, - }; - - for (error_cases) |case| { - var header = Header.init(.regular); - try testing.expectError( - error.NameTooLong, - header.setPath(case.in[0], case.in[1]), - ); - } - } - - // Breaks string on first null character. - fn str(s: []const u8) []const u8 { - for (s, 0..) |c, i| { - if (c == 0) return s[0..i]; - } - return s; - } -}; - -test { - _ = Header; -} - -test "write files" { - const files = [_]struct { - path: []const u8, - content: []const u8, - }{ - .{ .path = "foo", .content = "bar" }, - .{ .path = "a12345678/" ** 10 ++ "foo", .content = "a" ** 511 }, - .{ .path = "b12345678/" ** 24 ++ "foo", .content = "b" ** 512 }, - .{ .path = "c12345678/" ** 25 ++ "foo", .content = "c" ** 513 }, - .{ .path = "d12345678/" ** 51 ++ "foo", .content = "d" ** 1025 }, - .{ .path = "e123456789" ** 11, .content = "e" }, - }; - - var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; - var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; - - // with root - { - const root = "root"; - - var output = std.ArrayList(u8).init(testing.allocator); - defer output.deinit(); - var wrt = writer(output.writer()); - try wrt.setRoot(root); - for (files) |file| - try wrt.writeFileBytes(file.path, file.content, .{}); - - var input = std.io.fixedBufferStream(output.items); - var iter = std.tar.iterator( - input.reader(), - .{ .file_name_buffer = &file_name_buffer, .link_name_buffer = &link_name_buffer }, - ); - - // first entry is directory with prefix - { - const actual = (try iter.next()).?; - try testing.expectEqualStrings(root, actual.name); - try testing.expectEqual(std.tar.FileKind.directory, actual.kind); - } - - var i: usize = 0; - while (try iter.next()) |actual| { - defer i += 1; - const expected = files[i]; - try testing.expectEqualStrings(root, actual.name[0..root.len]); - try testing.expectEqual('/', actual.name[root.len..][0]); - try testing.expectEqualStrings(expected.path, actual.name[root.len + 1 ..]); - - var content = std.ArrayList(u8).init(testing.allocator); - defer content.deinit(); - try actual.writeAll(content.writer()); - try testing.expectEqualSlices(u8, expected.content, content.items); - } - } - // without root - { - var output = std.ArrayList(u8).init(testing.allocator); - defer output.deinit(); - var wrt = writer(output.writer()); - for (files) |file| { - var content = std.io.fixedBufferStream(file.content); - try wrt.writeFileStream(file.path, file.content.len, content.reader(), .{}); - } - - var input = std.io.fixedBufferStream(output.items); - var iter = std.tar.iterator( - input.reader(), - .{ .file_name_buffer = &file_name_buffer, .link_name_buffer = &link_name_buffer }, - ); - - var i: usize = 0; - while (try iter.next()) |actual| { - defer i += 1; - const expected = files[i]; - try testing.expectEqualStrings(expected.path, actual.name); - - var content = std.ArrayList(u8).init(testing.allocator); - defer content.deinit(); - try actual.writeAll(content.writer()); - try testing.expectEqualSlices(u8, expected.content, content.items); - } - try wrt.finish(); - } -} diff --git a/src/Compilation.zig b/src/Compilation.zig index 649288dab2..dfdae8aa14 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4862,6 +4862,9 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { }; defer tar_file.close(); + var buffer: [1024]u8 = undefined; + var tar_file_writer = tar_file.writer(&buffer); + var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, []const u8) = .empty; defer seen_table.deinit(comp.gpa); @@ -4871,7 +4874,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { var i: usize = 0; while (i < seen_table.count()) : (i += 1) { const mod = seen_table.keys()[i]; - try comp.docsCopyModule(mod, seen_table.values()[i], tar_file); + try comp.docsCopyModule(mod, seen_table.values()[i], &tar_file_writer); const deps = mod.deps.values(); try seen_table.ensureUnusedCapacity(comp.gpa, deps.len); @@ -4879,24 +4882,29 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { } } -fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, tar_file: fs.File) !void { +fn docsCopyModule( + comp: *Compilation, + module: *Package.Module, + name: []const u8, + tar_file_writer: *fs.File.Writer, +) !void { const root = module.root; var mod_dir = d: { const root_dir, const sub_path = root.openInfo(comp.dirs); break :d root_dir.openDir(sub_path, .{ .iterate = true }); } catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to open directory '{f}': {s}", .{ - root.fmt(comp), @errorName(err), - }); + return comp.lockAndSetMiscFailure(.docs_copy, "unable to open directory '{f}': {t}", .{ root.fmt(comp), err }); }; defer mod_dir.close(); var walker = try mod_dir.walk(comp.gpa); defer walker.deinit(); - var archiver = std.tar.writer(tar_file.deprecatedWriter().any()); + var archiver: std.tar.Writer = .{ .underlying_writer = &tar_file_writer.interface }; archiver.prefix = name; + var buffer: [1024]u8 = undefined; + while (try walker.next()) |entry| { switch (entry.kind) { .file => { @@ -4907,14 +4915,17 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, else => continue, } var file = mod_dir.openFile(entry.path, .{}) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to open '{f}{s}': {s}", .{ - root.fmt(comp), entry.path, @errorName(err), + return comp.lockAndSetMiscFailure(.docs_copy, "unable to open {f}{s}: {t}", .{ + root.fmt(comp), entry.path, err, }); }; defer file.close(); - archiver.writeFile(entry.path, file) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive '{f}{s}': {s}", .{ - root.fmt(comp), entry.path, @errorName(err), + const stat = try file.stat(); + var file_reader: fs.File.Reader = .initSize(file, &buffer, stat.size); + + archiver.writeFile(entry.path, &file_reader, stat.mtime) catch |err| { + return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive {f}{s}: {t}", .{ + root.fmt(comp), entry.path, err, }); }; } @@ -4926,9 +4937,7 @@ fn workerDocsWasm(comp: *Compilation, parent_prog_node: std.Progress.Node) void workerDocsWasmFallible(comp, prog_node) catch |err| switch (err) { error.SubCompilationFailed => return, // error reported already - else => comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {s}", .{ - @errorName(err), - }), + else => comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {t}", .{err}), }; } diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index a97b60a17c..69fdfb2d9e 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -1197,12 +1197,16 @@ fn unpackResource( }; switch (file_type) { - .tar => return try unpackTarball(f, tmp_directory.handle, resource.reader()), + .tar => { + var adapter = resource.reader().adaptToNewApi(); + return unpackTarball(f, tmp_directory.handle, &adapter.new_interface); + }, .@"tar.gz" => { const reader = resource.reader(); var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); var dcp = std.compress.gzip.decompressor(br.reader()); - return try unpackTarball(f, tmp_directory.handle, dcp.reader()); + var adapter = dcp.reader().adaptToNewApi(); + return try unpackTarball(f, tmp_directory.handle, &adapter.new_interface); }, .@"tar.xz" => { const gpa = f.arena.child_allocator; @@ -1215,7 +1219,8 @@ fn unpackResource( )); }; defer dcp.deinit(); - return try unpackTarball(f, tmp_directory.handle, dcp.reader()); + var adapter = dcp.reader().adaptToNewApi(); + return try unpackTarball(f, tmp_directory.handle, &adapter.new_interface); }, .@"tar.zst" => { const window_size = std.compress.zstd.DecompressorOptions.default_window_buffer_len; @@ -1225,7 +1230,8 @@ fn unpackResource( var dcp = std.compress.zstd.decompressor(br.reader(), .{ .window_buffer = window_buffer, }); - return try unpackTarball(f, tmp_directory.handle, dcp.reader()); + var adapter = dcp.reader().adaptToNewApi(); + return try unpackTarball(f, tmp_directory.handle, &adapter.new_interface); }, .git_pack => return unpackGitPack(f, tmp_directory.handle, &resource.git) catch |err| switch (err) { error.FetchFailed => return error.FetchFailed, @@ -1239,7 +1245,7 @@ fn unpackResource( } } -fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { +fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: *std.Io.Reader) RunError!UnpackResult { const eb = &f.error_bundle; const arena = f.arena.allocator(); @@ -1250,10 +1256,10 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes .strip_components = 0, .mode_mode = .ignore, .exclude_empty_directories = true, - }) catch |err| return f.fail(f.location_tok, try eb.printString( - "unable to unpack tarball to temporary directory: {s}", - .{@errorName(err)}, - )); + }) catch |err| return f.fail( + f.location_tok, + try eb.printString("unable to unpack tarball to temporary directory: {t}", .{err}), + ); var res: UnpackResult = .{ .root_dir = diagnostics.root_dir }; if (diagnostics.errors.items.len > 0) { From cc334b4ee2c8dcceba4f8c6739207f5ddfa6a358 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 20:54:15 -0700 Subject: [PATCH 27/76] std.tar.Writer: fix 32-bit --- lib/std/tar/Writer.zig | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/std/tar/Writer.zig b/lib/std/tar/Writer.zig index 78baa69d84..4c41251d05 100644 --- a/lib/std/tar/Writer.zig +++ b/lib/std/tar/Writer.zig @@ -55,7 +55,7 @@ pub fn writeFile( try w.underlying_writer.writeAll(@ptrCast((&header)[0..1])); _ = try w.underlying_writer.sendFileAll(file_reader, .unlimited); - try w.writePadding(size); + try w.writePadding64(size); } pub const WriteFileStreamError = Error || std.Io.Reader.StreamError; @@ -71,7 +71,7 @@ pub fn writeFileStream( ) WriteFileStreamError!void { try w.writeHeader(.regular, sub_path, "", size, options); try reader.streamExact64(w.underlying_writer, size); - try w.writePadding(size); + try w.writePadding64(size); } /// Writes file using bytes buffer `content` for size and file content. @@ -172,7 +172,14 @@ fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const [ } fn writePadding(w: *Writer, bytes: usize) std.Io.Writer.Error!void { - const pos = bytes % block_size; + return writePaddingPos(w, bytes % block_size); +} + +fn writePadding64(w: *Writer, bytes: u64) std.Io.Writer.Error!void { + return writePaddingPos(w, @intCast(bytes % block_size)); +} + +fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void { if (pos == 0) return; try w.underlying_writer.splatByteAll(0, block_size - pos); } From 6ae1bcd8bdf284b28bc708ab6bdafd4e0a5f7ac5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 20:56:03 -0700 Subject: [PATCH 28/76] fix docs wasm std.tar API usage --- lib/docs/wasm/main.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/docs/wasm/main.zig b/lib/docs/wasm/main.zig index 7e9ffa5e4c..d3043cd917 100644 --- a/lib/docs/wasm/main.zig +++ b/lib/docs/wasm/main.zig @@ -772,10 +772,10 @@ export fn decl_type_html(decl_index: Decl.Index) String { const Oom = error{OutOfMemory}; fn unpackInner(tar_bytes: []u8) !void { - var fbs = std.io.fixedBufferStream(tar_bytes); + var reader: std.Io.Reader = .fixed(tar_bytes); var file_name_buffer: [1024]u8 = undefined; var link_name_buffer: [1024]u8 = undefined; - var it = std.tar.iterator(fbs.reader(), .{ + var it: std.tar.Iterator = .init(&reader, .{ .file_name_buffer = &file_name_buffer, .link_name_buffer = &link_name_buffer, }); @@ -796,7 +796,7 @@ fn unpackInner(tar_bytes: []u8) !void { { gop.value_ptr.* = file; } - const file_bytes = tar_bytes[fbs.pos..][0..@intCast(tar_file.size)]; + const file_bytes = tar_bytes[reader.seek..][0..@intCast(tar_file.size)]; assert(file == try Walk.add_file(file_name, file_bytes)); } } else { From 91640f5f819de5bb6211d6bfa3c778737fa822aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jul 2025 21:08:40 -0700 Subject: [PATCH 29/76] give the Reader API adapter a buffer it needs one or else it always asserts --- lib/std/Io.zig | 4 ++-- src/Package/Fetch.zig | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 2380b3abbf..b27217ece0 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -314,11 +314,11 @@ pub fn GenericReader( } /// Helper for bridging to the new `Reader` API while upgrading. - pub fn adaptToNewApi(self: *const Self) Adapter { + pub fn adaptToNewApi(self: *const Self, buffer: []u8) Adapter { return .{ .derp_reader = self.*, .new_interface = .{ - .buffer = &.{}, + .buffer = buffer, .vtable = &.{ .stream = Adapter.stream }, .seek = 0, .end = 0, diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 69fdfb2d9e..5d6819149f 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -1205,7 +1205,8 @@ fn unpackResource( const reader = resource.reader(); var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); var dcp = std.compress.gzip.decompressor(br.reader()); - var adapter = dcp.reader().adaptToNewApi(); + var adapter_buffer: [1024]u8 = undefined; + var adapter = dcp.reader().adaptToNewApi(&adapter_buffer); return try unpackTarball(f, tmp_directory.handle, &adapter.new_interface); }, .@"tar.xz" => { @@ -1219,7 +1220,8 @@ fn unpackResource( )); }; defer dcp.deinit(); - var adapter = dcp.reader().adaptToNewApi(); + var adapter_buffer: [1024]u8 = undefined; + var adapter = dcp.reader().adaptToNewApi(&adapter_buffer); return try unpackTarball(f, tmp_directory.handle, &adapter.new_interface); }, .@"tar.zst" => { @@ -1230,7 +1232,8 @@ fn unpackResource( var dcp = std.compress.zstd.decompressor(br.reader(), .{ .window_buffer = window_buffer, }); - var adapter = dcp.reader().adaptToNewApi(); + var adapter_buffer: [1024]u8 = undefined; + var adapter = dcp.reader().adaptToNewApi(&adapter_buffer); return try unpackTarball(f, tmp_directory.handle, &adapter.new_interface); }, .git_pack => return unpackGitPack(f, tmp_directory.handle, &resource.git) catch |err| switch (err) { From 687370237fdd80bf0693679cbc11598f14151f0a Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Tue, 22 Jul 2025 04:50:34 -0400 Subject: [PATCH 30/76] llvm: fix switch loop on larger than pointer integer --- src/codegen/llvm.zig | 6 +++--- test/behavior/switch_loop.zig | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a570dd5ec0..c60b857fd9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6050,10 +6050,10 @@ pub const FuncGen = struct { const target_blocks = dispatch_info.case_blocks[0..target_blocks_len]; // Make sure to cast the index to a usize so it's not treated as negative! - const table_index = try self.wip.cast( - .zext, + const table_index = try self.wip.conv( + .unsigned, try self.wip.bin(.@"sub nuw", cond, jmp_table.min.toValue(), ""), - try o.lowerType(pt, Type.usize), + try o.lowerType(pt, .usize), "", ); const target_ptr_ptr = try self.wip.gep( diff --git a/test/behavior/switch_loop.zig b/test/behavior/switch_loop.zig index 98605692be..35cc857b62 100644 --- a/test/behavior/switch_loop.zig +++ b/test/behavior/switch_loop.zig @@ -226,3 +226,28 @@ test "unanalyzed continue with operand" { true => {}, } } + +test "switch loop on larger than pointer integer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + + var entry: @Type(.{ .int = .{ + .signedness = .unsigned, + .bits = @bitSizeOf(usize) + 1, + } }) = undefined; + entry = 0; + loop: switch (entry) { + 0 => { + entry += 1; + continue :loop 1; + }, + 1 => |x| { + entry += 1; + continue :loop x + 1; + }, + 2 => entry += 1, + else => unreachable, + } + try expect(entry == 3); +} From ec5cdb2fe394b6a8ca3d30b7652049f39c1aecdb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jul 2025 17:06:25 -0700 Subject: [PATCH 31/76] std: fix deprecated writer not handling the buffer --- lib/std/Io.zig | 7 ++++++- lib/std/Io/DeprecatedWriter.zig | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/std/Io.zig b/lib/std/Io.zig index b27217ece0..3f98ec3043 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -419,9 +419,14 @@ pub fn GenericWriter( new_interface: Writer, err: ?Error = null, - fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { + fn drain(w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { _ = splat; const a: *@This() = @alignCast(@fieldParentPtr("new_interface", w)); + const buffered = w.buffered(); + if (buffered.len != 0) return w.consume(a.derp_writer.write(buffered) catch |err| { + a.err = err; + return error.WriteFailed; + }); return a.derp_writer.write(data[0]) catch |err| { a.err = err; return error.WriteFailed; diff --git a/lib/std/Io/DeprecatedWriter.zig b/lib/std/Io/DeprecatedWriter.zig index 391b985357..81774b357c 100644 --- a/lib/std/Io/DeprecatedWriter.zig +++ b/lib/std/Io/DeprecatedWriter.zig @@ -100,7 +100,12 @@ pub const Adapter = struct { fn drain(w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { _ = splat; - const a: *@This() = @fieldParentPtr("new_interface", w); + const a: *@This() = @alignCast(@fieldParentPtr("new_interface", w)); + const buffered = w.buffered(); + if (buffered.len != 0) return w.consume(a.derp_writer.write(buffered) catch |err| { + a.err = err; + return error.WriteFailed; + }); return a.derp_writer.write(data[0]) catch |err| { a.err = err; return error.WriteFailed; From 11a81bc659aa44caf084046e3e1048a4ca0957a6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jul 2025 17:07:03 -0700 Subject: [PATCH 32/76] std.tar.Writer: delete ill-advised API dependency on time is sus --- lib/std/tar/Writer.zig | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/lib/std/tar/Writer.zig b/lib/std/tar/Writer.zig index 4c41251d05..f5ebd8f803 100644 --- a/lib/std/tar/Writer.zig +++ b/lib/std/tar/Writer.zig @@ -85,30 +85,6 @@ pub fn writeLink(w: *Writer, sub_path: []const u8, link_name: []const u8, option try w.writeHeader(.symbolic_link, sub_path, link_name, 0, options); } -/// Writes fs.Dir.WalkerEntry. Uses `mtime` from file system entry and -/// default for entry mode . -pub fn writeEntry(w: *Writer, entry: std.fs.Dir.Walker.Entry) Error!void { - switch (entry.kind) { - .directory => { - try w.writeDir(entry.path, .{ .mtime = try entryMtime(entry) }); - }, - .file => { - var file = try entry.dir.openFile(entry.basename, .{}); - defer file.close(); - const stat = try file.stat(); - try w.writeFile(entry.path, file, stat); - }, - .sym_link => { - var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; - const link_name = try entry.dir.readLink(entry.basename, &link_name_buffer); - try w.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) }); - }, - else => { - return error.UnsupportedWalkerEntryKind; - }, - } -} - fn writeHeader( w: *Writer, typeflag: Header.FileType, @@ -120,7 +96,7 @@ fn writeHeader( var header = Header.init(typeflag); try w.setPath(&header, sub_path); try header.setSize(size); - try header.setMtime(if (options.mtime != 0) options.mtime else w.mtimeNow()); + try header.setMtime(options.mtime); if (options.mode != 0) try header.setMode(options.mode); if (typeflag == .symbolic_link) @@ -131,17 +107,6 @@ fn writeHeader( try header.write(w.underlying_writer); } -fn mtimeNow(w: *Writer) u64 { - if (w.mtime_now == 0) - w.mtime_now = @intCast(std.time.timestamp()); - return w.mtime_now; -} - -fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 { - const stat = try entry.dir.statFile(entry.basename); - return @intCast(@divFloor(stat.mtime, std.time.ns_per_s)); -} - /// Writes path in posix header, if don't fit (in name+prefix; 100+155 /// bytes) writes it in gnu extended header. fn setPath(w: *Writer, header: *Header, sub_path: []const u8) Error!void { From 2e8dbcac9ab9f09e0890507c32ea2105c55cbf09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jul 2025 17:07:19 -0700 Subject: [PATCH 33/76] zig std: update for new tar I/O API --- lib/compiler/std-docs.zig | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index b5bc742717..33304b8c5d 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -60,7 +60,9 @@ pub fn main() !void { const should_open_browser = force_open_browser orelse (listen_port == 0); const address = std.net.Address.parseIp("127.0.0.1", listen_port) catch unreachable; - var http_server = try address.listen(.{}); + var http_server = try address.listen(.{ + .reuse_address = true, + }); const port = http_server.listen_address.in.getPort(); const url_with_newline = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port}); std.fs.File.stdout().writeAll(url_with_newline) catch {}; @@ -189,7 +191,11 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { var walker = try std_dir.walk(gpa); defer walker.deinit(); - var archiver = std.tar.writer(response.writer()); + var adapter_buffer: [500]u8 = undefined; + var response_writer = response.writer().adaptToNewApi(); + response_writer.new_interface.buffer = &adapter_buffer; + + var archiver: std.tar.Writer = .{ .underlying_writer = &response_writer.new_interface }; archiver.prefix = "std"; while (try walker.next()) |entry| { @@ -204,7 +210,13 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { } var file = try entry.dir.openFile(entry.basename, .{}); defer file.close(); - try archiver.writeFile(entry.path, file); + const stat = try file.stat(); + var file_reader: std.fs.File.Reader = .{ + .file = file, + .interface = std.fs.File.Reader.initInterface(&.{}), + .size = stat.size, + }; + try archiver.writeFile(entry.path, &file_reader, stat.mtime); } { From 6038192fadbd785894368d483e98b9713b746135 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jul 2025 17:40:12 -0700 Subject: [PATCH 34/76] std.tar: delete function redundant with std.mem --- lib/std/tar/Writer.zig | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/std/tar/Writer.zig b/lib/std/tar/Writer.zig index f5ebd8f803..61ae00b24e 100644 --- a/lib/std/tar/Writer.zig +++ b/lib/std/tar/Writer.zig @@ -346,8 +346,8 @@ pub const Header = extern struct { for (cases) |case| { var header = Header.init(.regular); try header.setPath(case.in[0], case.in[1]); - try testing.expectEqualStrings(case.out[0], str(&header.prefix)); - try testing.expectEqualStrings(case.out[1], str(&header.name)); + try testing.expectEqualStrings(case.out[0], std.mem.sliceTo(&header.prefix, 0)); + try testing.expectEqualStrings(case.out[1], std.mem.sliceTo(&header.name, 0)); } const error_cases = [_]struct { @@ -370,14 +370,6 @@ pub const Header = extern struct { ); } } - - // Breaks string on first null character. - fn str(s: []const u8) []const u8 { - for (s, 0..) |c, i| { - if (c == 0) return s[0..i]; - } - return s; - } }; test { From 4fcb479de95775d056f378fdf8a0b9570bc315b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jul 2025 18:42:54 -0700 Subject: [PATCH 35/76] don't forget to advance in the deprecated adapter --- lib/std/Io.zig | 4 +++- lib/std/Io/DeprecatedReader.zig | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 3f98ec3043..a93c31954b 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -334,10 +334,12 @@ pub fn GenericReader( fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { const a: *@This() = @alignCast(@fieldParentPtr("new_interface", r)); const buf = limit.slice(try w.writableSliceGreedy(1)); - return a.derp_reader.read(buf) catch |err| { + const n = a.derp_reader.read(buf) catch |err| { a.err = err; return error.ReadFailed; }; + w.advance(n); + return n; } }; }; diff --git a/lib/std/Io/DeprecatedReader.zig b/lib/std/Io/DeprecatedReader.zig index f6cb9f61d5..4d51b05148 100644 --- a/lib/std/Io/DeprecatedReader.zig +++ b/lib/std/Io/DeprecatedReader.zig @@ -393,10 +393,12 @@ pub const Adapter = struct { fn stream(r: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize { const a: *@This() = @alignCast(@fieldParentPtr("new_interface", r)); const buf = limit.slice(try w.writableSliceGreedy(1)); - return a.derp_reader.read(buf) catch |err| { + const n = a.derp_reader.read(buf) catch |err| { a.err = err; return error.ReadFailed; }; + w.advance(n); + return n; } }; From a023b9b22b6593ebd5a86736a4a9955840d1bfa1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 22 Jul 2025 05:29:19 -0400 Subject: [PATCH 36/76] stage1: update zig1.wasm Compiler needs cbe packed union fix. --- stage1/zig1.wasm | Bin 2889638 -> 2897241 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index b6d4e8ba0024c9817a98487ca46c4f91fff58863..0cc1a56393d2370a2043b0076b3eb90cac9e8e77 100644 GIT binary patch delta 1011042 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

u6VQ^1;tyEqPn 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%0DUr2I3u(i>)g`5jl!&(H#pdDG?@P_?R6^$bQ~ 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 G?se(@ar{rg@;Ic$ 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"bD@k?d{kz-Z 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
r2v>R>+?B&?N(iX2;|qD#JzQn*f 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 ip$ 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%0D=!@scG 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
7N)|kT!)%L_v2&}x*Z~n@6^rc6C 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

!Xx7tDD8-if?gzT~f`;D@ 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 Ve{7yHoDd|E*_ImlE|SB+{{zQ&KmY&$ From 5060ab99c94dd8afc8b84e74fe4d050c88cdfc0a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 18 Jun 2025 10:59:09 -0400 Subject: [PATCH 37/76] aarch64: add new from scratch self-hosted backend --- CMakeLists.txt | 8 + lib/compiler/test_runner.zig | 39 +- lib/compiler_rt.zig | 6 +- lib/compiler_rt/addo.zig | 4 +- lib/compiler_rt/addoti4_test.zig | 3 + lib/compiler_rt/clear_cache.zig | 14 +- lib/compiler_rt/cmp.zig | 1 - lib/compiler_rt/common.zig | 7 +- lib/compiler_rt/comparedf2_test.zig | 1 - lib/compiler_rt/comparesf2_test.zig | 1 - lib/compiler_rt/count0bits.zig | 1 - lib/compiler_rt/divdf3.zig | 1 - lib/compiler_rt/divmodei4.zig | 4 +- lib/compiler_rt/fixint_test.zig | 1 - lib/compiler_rt/int.zig | 1 - lib/compiler_rt/memcpy.zig | 4 +- lib/compiler_rt/memmove.zig | 16 +- lib/compiler_rt/mulf3.zig | 4 +- lib/compiler_rt/rem_pio2_large.zig | 2 +- lib/compiler_rt/stack_probe.zig | 1 - lib/compiler_rt/suboti4_test.zig | 3 + lib/compiler_rt/udivmod.zig | 10 +- lib/compiler_rt/udivmodei4.zig | 5 +- lib/std/Io/Writer.zig | 4 + lib/std/Progress.zig | 5 +- lib/std/builtin.zig | 7 +- lib/std/elf.zig | 2 +- lib/std/fs/File.zig | 5 +- lib/std/math.zig | 93 +- lib/std/math/big/int_test.zig | 1 - lib/std/math/float.zig | 6 +- lib/std/math/log10.zig | 1 - lib/std/mem.zig | 7 +- lib/std/os/linux.zig | 1 - lib/std/start.zig | 59 +- lib/std/testing.zig | 1 + src/Compilation.zig | 2 +- src/InternPool.zig | 14 +- src/Sema.zig | 9 +- src/Type.zig | 2 +- src/Zcu.zig | 29 +- src/Zcu/PerThread.zig | 35 +- src/arch/aarch64/bits.zig | 2063 --- src/arch/riscv64/CodeGen.zig | 10 +- src/arch/sparc64/CodeGen.zig | 4 +- src/arch/wasm/CodeGen.zig | 10 +- src/arch/x86_64/CodeGen.zig | 51 +- src/codegen.zig | 51 +- src/codegen/aarch64.zig | 194 + src/codegen/aarch64/Assemble.zig | 1653 +++ src/codegen/aarch64/Disassemble.zig | 905 ++ src/codegen/aarch64/Mir.zig | 275 + src/codegen/aarch64/Select.zig | 10981 ++++++++++++++ src/codegen/aarch64/abi.zig | 20 +- src/codegen/aarch64/encoding.zig | 11799 ++++++++++++++++ src/codegen/aarch64/instructions.zon | 1343 ++ src/codegen/c.zig | 33 +- src/codegen/llvm.zig | 11 +- src/codegen/spirv.zig | 6 +- src/dev.zig | 41 +- src/link.zig | 1 + src/link/Coff.zig | 76 +- src/link/Dwarf.zig | 8 +- src/link/Elf/Atom.zig | 70 +- src/link/Elf/Thunk.zig | 15 +- src/link/Elf/ZigObject.zig | 10 +- src/link/Elf/relocation.zig | 24 +- src/link/Elf/synthetic_sections.zig | 75 +- src/link/MachO.zig | 3 +- src/link/MachO/Atom.zig | 102 +- src/link/MachO/Thunk.zig | 10 +- src/link/MachO/ZigObject.zig | 10 +- src/link/MachO/synthetic.zig | 95 +- src/link/aarch64.zig | 64 +- src/main.zig | 1 + src/target.zig | 18 +- test/behavior.zig | 4 +- test/behavior/abs.zig | 10 +- test/behavior/align.zig | 20 +- test/behavior/array.zig | 42 +- test/behavior/asm.zig | 9 +- test/behavior/atomics.zig | 25 +- test/behavior/basic.zig | 19 +- test/behavior/bit_shifting.zig | 3 +- test/behavior/bitcast.zig | 21 +- test/behavior/bitreverse.zig | 8 +- ...n_functions_returning_void_or_noreturn.zig | 1 - test/behavior/byteswap.zig | 75 +- test/behavior/call.zig | 19 +- test/behavior/cast.zig | 119 +- test/behavior/cast_int.zig | 7 +- test/behavior/comptime_memory.zig | 2 - test/behavior/const_slice_child.zig | 1 - test/behavior/decl_literals.zig | 3 +- test/behavior/defer.zig | 8 +- test/behavior/enum.zig | 28 +- test/behavior/error.zig | 41 +- test/behavior/eval.zig | 36 +- test/behavior/export_builtin.zig | 4 - test/behavior/field_parent_ptr.zig | 5 + test/behavior/floatop.zig | 86 +- test/behavior/fn.zig | 13 - test/behavior/for.zig | 20 - test/behavior/generics.zig | 19 +- test/behavior/globals.zig | 6 +- test/behavior/if.zig | 6 +- test/behavior/import_c_keywords.zig | 1 - test/behavior/inline_switch.zig | 11 +- test/behavior/int128.zig | 5 - test/behavior/int_comparison_elision.zig | 1 - test/behavior/ir_block_deps.zig | 1 - test/behavior/lower_strlit_to_vector.zig | 1 - test/behavior/math.zig | 72 +- test/behavior/maximum_minimum.zig | 22 +- test/behavior/member_func.zig | 2 - test/behavior/memcpy.zig | 5 +- test/behavior/memmove.zig | 3 - test/behavior/memset.zig | 7 - test/behavior/muladd.zig | 14 +- ...ultiple_externs_with_conflicting_types.zig | 1 - test/behavior/nan.zig | 1 - test/behavior/null.zig | 6 +- test/behavior/optional.zig | 22 +- test/behavior/packed-struct.zig | 46 +- test/behavior/packed-union.zig | 6 +- .../packed_struct_explicit_backing_int.zig | 1 - test/behavior/pointers.zig | 13 +- test/behavior/popcount.zig | 5 +- test/behavior/ptrcast.zig | 19 - test/behavior/ptrfromint.zig | 3 - ...ef_var_in_if_after_if_2nd_switch_prong.zig | 1 - test/behavior/reflection.zig | 1 - test/behavior/return_address.zig | 2 +- test/behavior/saturating_arithmetic.zig | 17 +- test/behavior/select.zig | 5 +- test/behavior/shuffle.zig | 8 +- test/behavior/sizeof_and_typeof.zig | 3 - test/behavior/slice.zig | 24 +- test/behavior/src.zig | 1 - test/behavior/string_literals.zig | 6 +- test/behavior/struct.zig | 62 +- .../struct_contains_null_ptr_itself.zig | 1 - .../struct_contains_slice_of_itself.zig | 3 +- test/behavior/switch.zig | 33 +- test/behavior/switch_loop.zig | 16 +- test/behavior/switch_on_captured_error.zig | 2 + test/behavior/switch_prong_err_enum.zig | 2 +- test/behavior/switch_prong_implicit_cast.zig | 2 +- test/behavior/this.zig | 2 - test/behavior/threadlocal.zig | 3 - test/behavior/truncate.zig | 2 +- test/behavior/try.zig | 3 +- test/behavior/tuple.zig | 22 +- test/behavior/tuple_declarations.zig | 2 - test/behavior/type.zig | 9 +- test/behavior/type_info.zig | 6 +- test/behavior/typename.zig | 8 - test/behavior/undefined.zig | 4 - test/behavior/union.zig | 120 +- test/behavior/union_with_members.zig | 2 +- test/behavior/var_args.zig | 22 +- test/behavior/vector.zig | 98 +- test/behavior/void.zig | 1 - test/behavior/while.zig | 12 +- test/behavior/widening.zig | 5 - test/c_import/macros.zig | 12 - test/tests.zig | 38 +- 167 files changed, 28210 insertions(+), 3730 deletions(-) delete mode 100644 src/arch/aarch64/bits.zig create mode 100644 src/codegen/aarch64.zig create mode 100644 src/codegen/aarch64/Assemble.zig create mode 100644 src/codegen/aarch64/Disassemble.zig create mode 100644 src/codegen/aarch64/Mir.zig create mode 100644 src/codegen/aarch64/Select.zig create mode 100644 src/codegen/aarch64/encoding.zig create mode 100644 src/codegen/aarch64/instructions.zon diff --git a/CMakeLists.txt b/CMakeLists.txt index d9824e5c12..b78b0012f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -550,6 +550,14 @@ set(ZIG_STAGE2_SOURCES src/clang_options.zig src/clang_options_data.zig src/codegen.zig + src/codegen/aarch64.zig + src/codegen/aarch64/abi.zig + src/codegen/aarch64/Assemble.zig + src/codegen/aarch64/Disassemble.zig + src/codegen/aarch64/encoding.zig + src/codegen/aarch64/instructions.zon + src/codegen/aarch64/Mir.zig + src/codegen/aarch64/Select.zig src/codegen/c.zig src/codegen/c/Type.zig src/codegen/llvm.zig diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 8b60a75399..e618f72d2f 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -16,6 +16,7 @@ var stdin_buffer: [4096]u8 = undefined; var stdout_buffer: [4096]u8 = undefined; const crippled = switch (builtin.zig_backend) { + .stage2_aarch64, .stage2_powerpc, .stage2_riscv64, => true, @@ -287,13 +288,14 @@ pub fn log( /// work-in-progress backends can handle it. pub fn mainSimple() anyerror!void { @disableInstrumentation(); - // is the backend capable of printing to stderr? - const enable_print = switch (builtin.zig_backend) { + // is the backend capable of calling `std.fs.File.writeAll`? + const enable_write = switch (builtin.zig_backend) { + .stage2_aarch64, .stage2_riscv64 => true, else => false, }; - // is the backend capable of using std.fmt.format to print a summary at the end? - const print_summary = switch (builtin.zig_backend) { - .stage2_riscv64 => true, + // is the backend capable of calling `std.Io.Writer.print`? + const enable_print = switch (builtin.zig_backend) { + .stage2_aarch64, .stage2_riscv64 => true, else => false, }; @@ -302,34 +304,31 @@ pub fn mainSimple() anyerror!void { var failed: u64 = 0; // we don't want to bring in File and Writer if the backend doesn't support it - const stderr = if (comptime enable_print) std.fs.File.stderr() else {}; + const stdout = if (enable_write) std.fs.File.stdout() else {}; for (builtin.test_functions) |test_fn| { + if (enable_write) { + stdout.writeAll(test_fn.name) catch {}; + stdout.writeAll("... ") catch {}; + } if (test_fn.func()) |_| { - if (enable_print) { - stderr.writeAll(test_fn.name) catch {}; - stderr.writeAll("... ") catch {}; - stderr.writeAll("PASS\n") catch {}; - } + if (enable_write) stdout.writeAll("PASS\n") catch {}; } else |err| { - if (enable_print) { - stderr.writeAll(test_fn.name) catch {}; - stderr.writeAll("... ") catch {}; - } if (err != error.SkipZigTest) { - if (enable_print) stderr.writeAll("FAIL\n") catch {}; + if (enable_write) stdout.writeAll("FAIL\n") catch {}; failed += 1; - if (!enable_print) return err; + if (!enable_write) return err; continue; } - if (enable_print) stderr.writeAll("SKIP\n") catch {}; + if (enable_write) stdout.writeAll("SKIP\n") catch {}; skipped += 1; continue; } passed += 1; } - if (enable_print and print_summary) { - stderr.deprecatedWriter().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {}; + if (enable_print) { + var stdout_writer = stdout.writer(&.{}); + stdout_writer.interface.print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {}; } if (failed != 0) std.process.exit(1); } diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 46db464fd9..17e9e04da7 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -240,7 +240,7 @@ comptime { _ = @import("compiler_rt/udivmodti4.zig"); // extra - _ = @import("compiler_rt/os_version_check.zig"); + if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/os_version_check.zig"); _ = @import("compiler_rt/emutls.zig"); _ = @import("compiler_rt/arm.zig"); _ = @import("compiler_rt/aulldiv.zig"); @@ -249,12 +249,12 @@ comptime { _ = @import("compiler_rt/hexagon.zig"); if (@import("builtin").object_format != .c) { - _ = @import("compiler_rt/atomics.zig"); + if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/atomics.zig"); _ = @import("compiler_rt/stack_probe.zig"); // macOS has these functions inside libSystem. if (builtin.cpu.arch.isAARCH64() and !builtin.os.tag.isDarwin()) { - _ = @import("compiler_rt/aarch64_outline_atomics.zig"); + if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/aarch64_outline_atomics.zig"); } _ = @import("compiler_rt/memcpy.zig"); diff --git a/lib/compiler_rt/addo.zig b/lib/compiler_rt/addo.zig index beb6249223..610d620690 100644 --- a/lib/compiler_rt/addo.zig +++ b/lib/compiler_rt/addo.zig @@ -1,6 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); -const is_test = builtin.is_test; const common = @import("./common.zig"); pub const panic = @import("common.zig").panic; @@ -16,7 +14,7 @@ comptime { // - addoXi4_generic as default inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); overflow.* = 0; const sum: ST = a +% b; // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract diff --git a/lib/compiler_rt/addoti4_test.zig b/lib/compiler_rt/addoti4_test.zig index dc85830df9..d031d1d428 100644 --- a/lib/compiler_rt/addoti4_test.zig +++ b/lib/compiler_rt/addoti4_test.zig @@ -1,4 +1,5 @@ const addv = @import("addo.zig"); +const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const math = std.math; @@ -23,6 +24,8 @@ fn simple_addoti4(a: i128, b: i128, overflow: *c_int) i128 { } test "addoti4" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + const min: i128 = math.minInt(i128); const max: i128 = math.maxInt(i128); var i: i128 = 1; diff --git a/lib/compiler_rt/clear_cache.zig b/lib/compiler_rt/clear_cache.zig index e4a0a9d00d..c43d35602c 100644 --- a/lib/compiler_rt/clear_cache.zig +++ b/lib/compiler_rt/clear_cache.zig @@ -97,8 +97,7 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void { .nbytes = end - start, .whichcache = 3, // ICACHE | DCACHE }; - asm volatile ( - \\ syscall + asm volatile ("syscall" : : [_] "{$2}" (165), // nr = SYS_sysarch [_] "{$4}" (0), // op = MIPS_CACHEFLUSH @@ -116,11 +115,8 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void { } else if (arm64 and !apple) { // Get Cache Type Info. // TODO memoize this? - var ctr_el0: u64 = 0; - asm volatile ( - \\mrs %[x], ctr_el0 - \\ - : [x] "=r" (ctr_el0), + const ctr_el0 = asm volatile ("mrs %[ctr_el0], ctr_el0" + : [ctr_el0] "=r" (-> u64), ); // The DC and IC instructions must use 64-bit registers so we don't use // uintptr_t in case this runs in an IPL32 environment. @@ -187,9 +183,7 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void { exportIt(); } else if (os == .linux and loongarch) { // See: https://github.com/llvm/llvm-project/blob/cf54cae26b65fc3201eff7200ffb9b0c9e8f9a13/compiler-rt/lib/builtins/clear_cache.c#L94-L95 - asm volatile ( - \\ ibar 0 - ); + asm volatile ("ibar 0"); exportIt(); } diff --git a/lib/compiler_rt/cmp.zig b/lib/compiler_rt/cmp.zig index e1273aa622..67cb5b0938 100644 --- a/lib/compiler_rt/cmp.zig +++ b/lib/compiler_rt/cmp.zig @@ -1,6 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; const common = @import("common.zig"); pub const panic = common.panic; diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index f5423019f1..1160b1c718 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -102,9 +102,14 @@ pub const gnu_f16_abi = switch (builtin.cpu.arch) { pub const want_sparc_abi = builtin.cpu.arch.isSPARC(); +pub const test_safety = switch (builtin.zig_backend) { + .stage2_aarch64 => false, + else => builtin.is_test, +}; + // Avoid dragging in the runtime safety mechanisms into this .o file, unless // we're trying to test compiler-rt. -pub const panic = if (builtin.is_test) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.no_panic; +pub const panic = if (test_safety) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.no_panic; /// This seems to mostly correspond to `clang::TargetInfo::HasFloat16`. pub fn F16T(comptime OtherType: type) type { diff --git a/lib/compiler_rt/comparedf2_test.zig b/lib/compiler_rt/comparedf2_test.zig index 9444c6adf7..dbae6bbeec 100644 --- a/lib/compiler_rt/comparedf2_test.zig +++ b/lib/compiler_rt/comparedf2_test.zig @@ -4,7 +4,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; const __eqdf2 = @import("./cmpdf2.zig").__eqdf2; const __ledf2 = @import("./cmpdf2.zig").__ledf2; diff --git a/lib/compiler_rt/comparesf2_test.zig b/lib/compiler_rt/comparesf2_test.zig index 40b1324cfa..65e78da99e 100644 --- a/lib/compiler_rt/comparesf2_test.zig +++ b/lib/compiler_rt/comparesf2_test.zig @@ -4,7 +4,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; const __eqsf2 = @import("./cmpsf2.zig").__eqsf2; const __lesf2 = @import("./cmpsf2.zig").__lesf2; diff --git a/lib/compiler_rt/count0bits.zig b/lib/compiler_rt/count0bits.zig index c9bdfb7c23..874604eb2c 100644 --- a/lib/compiler_rt/count0bits.zig +++ b/lib/compiler_rt/count0bits.zig @@ -1,6 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; const common = @import("common.zig"); pub const panic = common.panic; diff --git a/lib/compiler_rt/divdf3.zig b/lib/compiler_rt/divdf3.zig index 0340404a69..7b47cd3a70 100644 --- a/lib/compiler_rt/divdf3.zig +++ b/lib/compiler_rt/divdf3.zig @@ -5,7 +5,6 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; -const is_test = builtin.is_test; const common = @import("common.zig"); const normalize = common.normalize; diff --git a/lib/compiler_rt/divmodei4.zig b/lib/compiler_rt/divmodei4.zig index 3f12e8697d..ab11452206 100644 --- a/lib/compiler_rt/divmodei4.zig +++ b/lib/compiler_rt/divmodei4.zig @@ -34,7 +34,7 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []u32, v: []u32) !void { } pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size])); const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size])); @@ -43,7 +43,7 @@ pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) vo } pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size])); const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size])); diff --git a/lib/compiler_rt/fixint_test.zig b/lib/compiler_rt/fixint_test.zig index 57b4093809..198167ab86 100644 --- a/lib/compiler_rt/fixint_test.zig +++ b/lib/compiler_rt/fixint_test.zig @@ -1,4 +1,3 @@ -const is_test = @import("builtin").is_test; const std = @import("std"); const math = std.math; const testing = std.testing; diff --git a/lib/compiler_rt/int.zig b/lib/compiler_rt/int.zig index 4a89d0799d..16c504ee66 100644 --- a/lib/compiler_rt/int.zig +++ b/lib/compiler_rt/int.zig @@ -6,7 +6,6 @@ const testing = std.testing; const maxInt = std.math.maxInt; const minInt = std.math.minInt; const arch = builtin.cpu.arch; -const is_test = builtin.is_test; const common = @import("common.zig"); const udivmod = @import("udivmod.zig").udivmod; const __divti3 = @import("divti3.zig").__divti3; diff --git a/lib/compiler_rt/memcpy.zig b/lib/compiler_rt/memcpy.zig index 30971677ab..424e92954d 100644 --- a/lib/compiler_rt/memcpy.zig +++ b/lib/compiler_rt/memcpy.zig @@ -11,7 +11,7 @@ comptime { .visibility = common.visibility, }; - if (builtin.mode == .ReleaseSmall) + if (builtin.mode == .ReleaseSmall or builtin.zig_backend == .stage2_aarch64) @export(&memcpySmall, export_options) else @export(&memcpyFast, export_options); @@ -195,6 +195,8 @@ inline fn copyRange4( } test "memcpy" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + const S = struct { fn testFunc(comptime copy_func: anytype) !void { const max_len = 1024; diff --git a/lib/compiler_rt/memmove.zig b/lib/compiler_rt/memmove.zig index 71289a50ae..46c5a631cb 100644 --- a/lib/compiler_rt/memmove.zig +++ b/lib/compiler_rt/memmove.zig @@ -14,7 +14,7 @@ comptime { .visibility = common.visibility, }; - if (builtin.mode == .ReleaseSmall) + if (builtin.mode == .ReleaseSmall or builtin.zig_backend == .stage2_aarch64) @export(&memmoveSmall, export_options) else @export(&memmoveFast, export_options); @@ -39,7 +39,7 @@ fn memmoveSmall(opt_dest: ?[*]u8, opt_src: ?[*]const u8, len: usize) callconv(.c } fn memmoveFast(dest: ?[*]u8, src: ?[*]u8, len: usize) callconv(.c) ?[*]u8 { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const small_limit = @max(2 * @sizeOf(Element), @sizeOf(Element)); if (copySmallLength(small_limit, dest.?, src.?, len)) return dest; @@ -79,7 +79,7 @@ inline fn copyLessThan16( src: [*]const u8, len: usize, ) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); if (len < 4) { if (len == 0) return; const b = len / 2; @@ -100,7 +100,7 @@ inline fn copy16ToSmallLimit( src: [*]const u8, len: usize, ) bool { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); inline for (2..(std.math.log2(small_limit) + 1) / 2 + 1) |p| { const limit = 1 << (2 * p); if (len < limit) { @@ -119,7 +119,7 @@ inline fn copyRange4( src: [*]const u8, len: usize, ) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); comptime assert(std.math.isPowerOfTwo(copy_len)); assert(len >= copy_len); assert(len < 4 * copy_len); @@ -147,7 +147,7 @@ inline fn copyForwards( src: [*]const u8, len: usize, ) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); assert(len >= 2 * @sizeOf(Element)); const head = src[0..@sizeOf(Element)].*; @@ -181,7 +181,7 @@ inline fn copyBlocks( src: anytype, max_bytes: usize, ) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const T = @typeInfo(@TypeOf(dest)).pointer.child; comptime assert(T == @typeInfo(@TypeOf(src)).pointer.child); @@ -217,6 +217,8 @@ inline fn copyBackwards( } test memmoveFast { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + const max_len = 1024; var buffer: [max_len + @alignOf(Element) - 1]u8 = undefined; for (&buffer, 0..) |*b, i| { diff --git a/lib/compiler_rt/mulf3.zig b/lib/compiler_rt/mulf3.zig index ad60ec41a5..34d39fb9b7 100644 --- a/lib/compiler_rt/mulf3.zig +++ b/lib/compiler_rt/mulf3.zig @@ -6,7 +6,7 @@ const common = @import("./common.zig"); /// Ported from: /// https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/fp_mul_impl.inc pub inline fn mulf3(comptime T: type, a: T, b: T) T { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const typeWidth = @typeInfo(T).float.bits; const significandBits = math.floatMantissaBits(T); const fractionalBits = math.floatFractionalBits(T); @@ -163,7 +163,7 @@ pub inline fn mulf3(comptime T: type, a: T, b: T) T { /// /// This is analogous to an shr version of `@shlWithOverflow` fn wideShrWithTruncation(comptime Z: type, hi: *Z, lo: *Z, count: u32) bool { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const typeWidth = @typeInfo(Z).int.bits; var inexact = false; if (count < typeWidth) { diff --git a/lib/compiler_rt/rem_pio2_large.zig b/lib/compiler_rt/rem_pio2_large.zig index b107a0fabb..f15e0d71f6 100644 --- a/lib/compiler_rt/rem_pio2_large.zig +++ b/lib/compiler_rt/rem_pio2_large.zig @@ -251,7 +251,7 @@ const PIo2 = [_]f64{ /// compiler will convert from decimal to binary accurately enough /// to produce the hexadecimal values shown. /// -pub fn rem_pio2_large(x: []f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 { +pub fn rem_pio2_large(x: []const f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 { var jz: i32 = undefined; var jx: i32 = undefined; var jv: i32 = undefined; diff --git a/lib/compiler_rt/stack_probe.zig b/lib/compiler_rt/stack_probe.zig index 94212b7a23..21259ec435 100644 --- a/lib/compiler_rt/stack_probe.zig +++ b/lib/compiler_rt/stack_probe.zig @@ -4,7 +4,6 @@ const common = @import("common.zig"); const os_tag = builtin.os.tag; const arch = builtin.cpu.arch; const abi = builtin.abi; -const is_test = builtin.is_test; pub const panic = common.panic; diff --git a/lib/compiler_rt/suboti4_test.zig b/lib/compiler_rt/suboti4_test.zig index 68ad0ff72f..65018bc966 100644 --- a/lib/compiler_rt/suboti4_test.zig +++ b/lib/compiler_rt/suboti4_test.zig @@ -1,4 +1,5 @@ const subo = @import("subo.zig"); +const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const math = std.math; @@ -27,6 +28,8 @@ pub fn simple_suboti4(a: i128, b: i128, overflow: *c_int) i128 { } test "suboti3" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + const min: i128 = math.minInt(i128); const max: i128 = math.maxInt(i128); var i: i128 = 1; diff --git a/lib/compiler_rt/udivmod.zig b/lib/compiler_rt/udivmod.zig index a9705f317d..bf6aaadeae 100644 --- a/lib/compiler_rt/udivmod.zig +++ b/lib/compiler_rt/udivmod.zig @@ -1,8 +1,8 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; const Log2Int = std.math.Log2Int; -const HalveInt = @import("common.zig").HalveInt; +const common = @import("common.zig"); +const HalveInt = common.HalveInt; const lo = switch (builtin.cpu.arch.endian()) { .big => 1, @@ -14,7 +14,7 @@ const hi = 1 - lo; // Returns U / v_ and sets r = U % v_. fn divwide_generic(comptime T: type, _u1: T, _u0: T, v_: T, r: *T) T { const HalfT = HalveInt(T, false).HalfT; - @setRuntimeSafety(is_test); + @setRuntimeSafety(common.test_safety); var v = v_; const b = @as(T, 1) << (@bitSizeOf(T) / 2); @@ -70,7 +70,7 @@ fn divwide_generic(comptime T: type, _u1: T, _u0: T, v_: T, r: *T) T { } fn divwide(comptime T: type, _u1: T, _u0: T, v: T, r: *T) T { - @setRuntimeSafety(is_test); + @setRuntimeSafety(common.test_safety); if (T == u64 and builtin.target.cpu.arch == .x86_64 and builtin.target.os.tag != .windows) { var rem: T = undefined; const quo = asm ( @@ -90,7 +90,7 @@ fn divwide(comptime T: type, _u1: T, _u0: T, v: T, r: *T) T { // Returns a_ / b_ and sets maybe_rem = a_ % b. pub fn udivmod(comptime T: type, a_: T, b_: T, maybe_rem: ?*T) T { - @setRuntimeSafety(is_test); + @setRuntimeSafety(common.test_safety); const HalfT = HalveInt(T, false).HalfT; const SignedT = std.meta.Int(.signed, @bitSizeOf(T)); diff --git a/lib/compiler_rt/udivmodei4.zig b/lib/compiler_rt/udivmodei4.zig index 6d6f6c1b65..0923f3f222 100644 --- a/lib/compiler_rt/udivmodei4.zig +++ b/lib/compiler_rt/udivmodei4.zig @@ -113,7 +113,7 @@ pub fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { } pub fn __udivei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size])); const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size])); @@ -122,7 +122,7 @@ pub fn __udivei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) ca } pub fn __umodei4(r_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void { - @setRuntimeSafety(builtin.is_test); + @setRuntimeSafety(common.test_safety); const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size])); const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size])); @@ -131,6 +131,7 @@ pub fn __umodei4(r_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) ca } test "__udivei4/__umodei4" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 0723073592..55672c557c 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2239,6 +2239,10 @@ pub const Discarding = struct { pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize { if (File.Handle == void) return error.Unimplemented; + switch (builtin.zig_backend) { + else => {}, + .stage2_aarch64 => return error.Unimplemented, + } const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); d.count += w.end; w.end = 0; diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 2634553d25..5ee5828970 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -408,6 +408,9 @@ pub const have_ipc = switch (builtin.os.tag) { const noop_impl = builtin.single_threaded or switch (builtin.os.tag) { .wasi, .freestanding => true, else => false, +} or switch (builtin.zig_backend) { + .stage2_aarch64 => true, + else => false, }; /// Initializes a global Progress instance. @@ -754,7 +757,7 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { } fn clearWrittenWithEscapeCodes() anyerror!void { - if (!global_progress.need_clear) return; + if (noop_impl or !global_progress.need_clear) return; global_progress.need_clear = false; try write(clear); diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 8f4aefc713..54376426e2 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -772,7 +772,7 @@ pub const Endian = enum { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. -pub const Signedness = enum { +pub const Signedness = enum(u1) { signed, unsigned, }; @@ -894,7 +894,10 @@ pub const VaList = switch (builtin.cpu.arch) { .aarch64, .aarch64_be => switch (builtin.os.tag) { .windows => *u8, .ios, .macos, .tvos, .watchos, .visionos => *u8, - else => @compileError("disabled due to miscompilations"), // VaListAarch64, + else => switch (builtin.zig_backend) { + .stage2_aarch64 => VaListAarch64, + else => @compileError("disabled due to miscompilations"), + }, }, .arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) { .ios, .macos, .tvos, .watchos, .visionos => *u8, diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 47b3add84f..2583e83d19 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -2001,7 +2001,7 @@ pub const R_AARCH64 = enum(u32) { TLSLE_LDST64_TPREL_LO12 = 558, /// Likewise; no check. TLSLE_LDST64_TPREL_LO12_NC = 559, - /// PC-rel. load immediate 20:2. + /// PC-rel. load immediate 20:2. TLSDESC_LD_PREL19 = 560, /// PC-rel. ADR immediate 20:0. TLSDESC_ADR_PREL21 = 561, diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 138807972e..39111f634d 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1554,7 +1554,10 @@ pub const Writer = struct { return .{ .vtable = &.{ .drain = drain, - .sendFile = sendFile, + .sendFile = switch (builtin.zig_backend) { + else => sendFile, + .stage2_aarch64 => std.io.Writer.unimplementedSendFile, + }, }, .buffer = buffer, }; diff --git a/lib/std/math.zig b/lib/std/math.zig index 1cd9a83a14..9f2d12a65e 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -45,6 +45,7 @@ pub const rad_per_deg = 0.017453292519943295769236907684886127134428718885417254 /// 180.0/pi pub const deg_per_rad = 57.295779513082320876798154814105170332405472466564321549160243861; +pub const Sign = enum(u1) { positive, negative }; pub const FloatRepr = float.FloatRepr; pub const floatExponentBits = float.floatExponentBits; pub const floatMantissaBits = float.floatMantissaBits; @@ -594,27 +595,30 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { /// Shifts left. Overflowed bits are truncated. /// A negative shift amount results in a right shift. pub fn shl(comptime T: type, a: T, shift_amt: anytype) T { + const is_shl = shift_amt >= 0; const abs_shift_amt = @abs(shift_amt); - - const casted_shift_amt = blk: { - if (@typeInfo(T) == .vector) { - const C = @typeInfo(T).vector.child; - const len = @typeInfo(T).vector.len; - if (abs_shift_amt >= @typeInfo(C).int.bits) return @splat(0); - break :blk @as(@Vector(len, Log2Int(C)), @splat(@as(Log2Int(C), @intCast(abs_shift_amt)))); - } else { - if (abs_shift_amt >= @typeInfo(T).int.bits) return 0; - break :blk @as(Log2Int(T), @intCast(abs_shift_amt)); - } + const casted_shift_amt = casted_shift_amt: switch (@typeInfo(T)) { + .int => |info| { + if (abs_shift_amt < info.bits) break :casted_shift_amt @as( + Log2Int(T), + @intCast(abs_shift_amt), + ); + if (info.signedness == .unsigned or is_shl) return 0; + return a >> (info.bits - 1); + }, + .vector => |info| { + const Child = info.child; + const child_info = @typeInfo(Child).int; + if (abs_shift_amt < child_info.bits) break :casted_shift_amt @as( + @Vector(info.len, Log2Int(Child)), + @splat(@as(Log2Int(Child), @intCast(abs_shift_amt))), + ); + if (child_info.signedness == .unsigned or is_shl) return @splat(0); + return a >> @splat(child_info.bits - 1); + }, + else => comptime unreachable, }; - - if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).int.signedness == .signed) { - if (shift_amt < 0) { - return a >> casted_shift_amt; - } - } - - return a << casted_shift_amt; + return if (is_shl) a << casted_shift_amt else a >> casted_shift_amt; } test shl { @@ -629,32 +633,40 @@ test shl { try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) << 1); try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) >> 1); try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0); + + try testing.expect(shl(i8, -1, -100) == -1); + try testing.expect(shl(i8, -1, 100) == 0); + try testing.expect(@reduce(.And, shl(@Vector(2, i8), .{ -1, 1 }, -100) == @Vector(2, i8){ -1, 0 })); + try testing.expect(@reduce(.And, shl(@Vector(2, i8), .{ -1, 1 }, 100) == @Vector(2, i8){ 0, 0 })); } /// Shifts right. Overflowed bits are truncated. /// A negative shift amount results in a left shift. pub fn shr(comptime T: type, a: T, shift_amt: anytype) T { + const is_shl = shift_amt < 0; const abs_shift_amt = @abs(shift_amt); - - const casted_shift_amt = blk: { - if (@typeInfo(T) == .vector) { - const C = @typeInfo(T).vector.child; - const len = @typeInfo(T).vector.len; - if (abs_shift_amt >= @typeInfo(C).int.bits) return @splat(0); - break :blk @as(@Vector(len, Log2Int(C)), @splat(@as(Log2Int(C), @intCast(abs_shift_amt)))); - } else { - if (abs_shift_amt >= @typeInfo(T).int.bits) return 0; - break :blk @as(Log2Int(T), @intCast(abs_shift_amt)); - } + const casted_shift_amt = casted_shift_amt: switch (@typeInfo(T)) { + .int => |info| { + if (abs_shift_amt < info.bits) break :casted_shift_amt @as( + Log2Int(T), + @intCast(abs_shift_amt), + ); + if (info.signedness == .unsigned or is_shl) return 0; + return a >> (info.bits - 1); + }, + .vector => |info| { + const Child = info.child; + const child_info = @typeInfo(Child).int; + if (abs_shift_amt < child_info.bits) break :casted_shift_amt @as( + @Vector(info.len, Log2Int(Child)), + @splat(@as(Log2Int(Child), @intCast(abs_shift_amt))), + ); + if (child_info.signedness == .unsigned or is_shl) return @splat(0); + return a >> @splat(child_info.bits - 1); + }, + else => comptime unreachable, }; - - if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).int.signedness == .signed) { - if (shift_amt < 0) { - return a << casted_shift_amt; - } - } - - return a >> casted_shift_amt; + return if (is_shl) a << casted_shift_amt else a >> casted_shift_amt; } test shr { @@ -669,6 +681,11 @@ test shr { try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) >> 1); try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) << 1); try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0); + + try testing.expect(shr(i8, -1, -100) == 0); + try testing.expect(shr(i8, -1, 100) == -1); + try testing.expect(@reduce(.And, shr(@Vector(2, i8), .{ -1, 1 }, -100) == @Vector(2, i8){ 0, 0 })); + try testing.expect(@reduce(.And, shr(@Vector(2, i8), .{ -1, 1 }, 100) == @Vector(2, i8){ -1, 0 })); } /// Rotates right. Only unsigned values can be rotated. Negative shift diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index f44b254cf1..bb6deeb778 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2774,7 +2774,6 @@ test "bitNotWrap more than two limbs" { // This test requires int sizes greater than 128 bits. if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO // LLVM: unexpected runtime library name: __umodei4 if (builtin.zig_backend == .stage2_llvm and comptime builtin.target.cpu.arch.isWasm()) return error.SkipZigTest; // TODO diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index df7d7fe1ab..6ffbd85bd2 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -4,8 +4,6 @@ const assert = std.debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -pub const Sign = enum(u1) { positive, negative }; - pub fn FloatRepr(comptime Float: type) type { const fractional_bits = floatFractionalBits(Float); const exponent_bits = floatExponentBits(Float); @@ -14,7 +12,7 @@ pub fn FloatRepr(comptime Float: type) type { mantissa: StoredMantissa, exponent: BiasedExponent, - sign: Sign, + sign: std.math.Sign, pub const StoredMantissa = @Type(.{ .int = .{ .signedness = .unsigned, @@ -69,7 +67,7 @@ pub fn FloatRepr(comptime Float: type) type { /// This currently truncates denormal values, which needs to be fixed before this can be used to /// produce a rounded value. - pub fn reconstruct(normalized: Normalized, sign: Sign) Float { + pub fn reconstruct(normalized: Normalized, sign: std.math.Sign) Float { if (normalized.exponent > BiasedExponent.max_normal.unbias()) return @bitCast(Repr{ .mantissa = 0, .exponent = .infinite, diff --git a/lib/std/math/log10.zig b/lib/std/math/log10.zig index 655a42215e..9ac5c6da24 100644 --- a/lib/std/math/log10.zig +++ b/lib/std/math/log10.zig @@ -132,7 +132,6 @@ inline fn less_than_5(x: u32) u32 { test log10_int { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm and comptime builtin.target.cpu.arch.isWasm()) return error.SkipZigTest; // TODO diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 1a61076f32..3b72a2b579 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -676,6 +676,7 @@ test lessThan { const eqlBytes_allowed = switch (builtin.zig_backend) { // These backends don't support vectors yet. + .stage2_aarch64, .stage2_powerpc, .stage2_riscv64, => false, @@ -4482,7 +4483,7 @@ pub fn doNotOptimizeAway(val: anytype) void { ); asm volatile ("" : - : [val2] "r" (val2), + : [_] "r" (val2), ); } else doNotOptimizeAway(&val); }, @@ -4490,7 +4491,7 @@ pub fn doNotOptimizeAway(val: anytype) void { if ((t.float.bits == 32 or t.float.bits == 64) and builtin.zig_backend != .stage2_c) { asm volatile ("" : - : [val] "rm" (val), + : [_] "rm" (val), ); } else doNotOptimizeAway(&val); }, @@ -4500,7 +4501,7 @@ pub fn doNotOptimizeAway(val: anytype) void { } else { asm volatile ("" : - : [val] "m" (val), + : [_] "m" (val), : .{ .memory = true }); } }, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 75494145b9..a02451c0fd 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -503,7 +503,6 @@ pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; /// Whether an external or internal getauxval implementation is used. const extern_getauxval = switch (builtin.zig_backend) { // Calling extern functions is not yet supported with these backends - .stage2_aarch64, .stage2_arm, .stage2_powerpc, .stage2_riscv64, diff --git a/lib/std/start.zig b/lib/std/start.zig index 22ccda1e40..43355d34f4 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -101,17 +101,11 @@ comptime { // Simplified start code for stage2 until it supports more language features /// fn main2() callconv(.c) c_int { - root.main(); - return 0; + return callMain(); } fn _start2() callconv(.withStackAlign(.c, 1)) noreturn { - callMain2(); -} - -fn callMain2() noreturn { - root.main(); - exit2(0); + std.posix.exit(callMain()); } fn spirvMain2() callconv(.kernel) void { @@ -119,51 +113,7 @@ fn spirvMain2() callconv(.kernel) void { } fn wWinMainCRTStartup2() callconv(.c) noreturn { - root.main(); - exit2(0); -} - -fn exit2(code: usize) noreturn { - switch (native_os) { - .linux => switch (builtin.cpu.arch) { - .x86_64 => { - asm volatile ("syscall" - : - : [number] "{rax}" (231), - [arg1] "{rdi}" (code), - : .{ .rcx = true, .r11 = true, .memory = true }); - }, - .arm => { - asm volatile ("svc #0" - : - : [number] "{r7}" (1), - [arg1] "{r0}" (code), - : .{ .memory = true }); - }, - .aarch64 => { - asm volatile ("svc #0" - : - : [number] "{x8}" (93), - [arg1] "{x0}" (code), - : .{ .memory = true }); - }, - .sparc64 => { - asm volatile ("ta 0x6d" - : - : [number] "{g1}" (1), - [arg1] "{o0}" (code), - : .{ .o0 = true, .o1 = true, .o2 = true, .o3 = true, .o4 = true, .o5 = true, .o6 = true, .o7 = true, .memory = true }); - }, - else => @compileError("TODO"), - }, - // exits(0) - .plan9 => std.os.plan9.exits(null), - .windows => { - std.os.windows.ntdll.RtlExitUserProcess(@truncate(code)); - }, - else => @compileError("TODO"), - } - unreachable; + std.posix.exit(callMain()); } //////////////////////////////////////////////////////////////////////////////// @@ -676,10 +626,11 @@ pub inline fn callMain() u8 { const result = root.main() catch |err| { switch (builtin.zig_backend) { + .stage2_aarch64, .stage2_powerpc, .stage2_riscv64, => { - std.debug.print("error: failed with error\n", .{}); + _ = std.posix.write(std.posix.STDERR_FILENO, "error: failed with error\n") catch {}; return 1; }, else => {}, diff --git a/lib/std/testing.zig b/lib/std/testing.zig index f9027a4f47..e80e961b13 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -33,6 +33,7 @@ pub var log_level = std.log.Level.warn; // Disable printing in tests for simple backends. pub const backend_can_print = switch (builtin.zig_backend) { + .stage2_aarch64, .stage2_powerpc, .stage2_riscv64, .stage2_spirv, diff --git a/src/Compilation.zig b/src/Compilation.zig index 649288dab2..412d6a023c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1850,7 +1850,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // approach, since the ubsan runtime uses quite a lot of the standard library // and this reduces unnecessary bloat. const ubsan_rt_strat: RtStrat = s: { - const can_build_ubsan_rt = target_util.canBuildLibUbsanRt(target); + const can_build_ubsan_rt = target_util.canBuildLibUbsanRt(target, use_llvm, build_options.have_llvm); 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; diff --git a/src/InternPool.zig b/src/InternPool.zig index c9036da45b..15d895aed0 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -7556,12 +7556,18 @@ fn extraFuncCoerced(ip: *const InternPool, extra: Local.Extra, extra_index: u32) fn indexToKeyBigInt(ip: *const InternPool, tid: Zcu.PerThread.Id, limb_index: u32, positive: bool) Key { const limbs_items = ip.getLocalShared(tid).getLimbs().view().items(.@"0"); const int: Int = @bitCast(limbs_items[limb_index..][0..Int.limbs_items_len].*); + const big_int: BigIntConst = .{ + .limbs = limbs_items[limb_index + Int.limbs_items_len ..][0..int.limbs_len], + .positive = positive, + }; return .{ .int = .{ .ty = int.ty, - .storage = .{ .big_int = .{ - .limbs = limbs_items[limb_index + Int.limbs_items_len ..][0..int.limbs_len], - .positive = positive, - } }, + .storage = if (big_int.toInt(u64)) |x| + .{ .u64 = x } + else |_| if (big_int.toInt(i64)) |x| + .{ .i64 = x } + else |_| + .{ .big_int = big_int }, } }; } diff --git a/src/Sema.zig b/src/Sema.zig index d2e3e32214..788107f786 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16522,7 +16522,7 @@ fn zirAsm( break :empty try sema.structInitEmpty(block, clobbers_ty, src, src); } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen. const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber }); - needed_capacity += (asm_source.len + 3) / 4; + needed_capacity += asm_source.len / 4 + 1; const gpa = sema.gpa; try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); @@ -16562,7 +16562,8 @@ fn zirAsm( { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); @memcpy(buffer[0..asm_source.len], asm_source); - sema.air_extra.items.len += (asm_source.len + 3) / 4; + buffer[asm_source.len] = 0; + sema.air_extra.items.len += asm_source.len / 4 + 1; } return asm_air; } @@ -24846,7 +24847,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins }, .@"packed" => { const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + - (if (zcu.typeToStruct(parent_ty)) |struct_obj| pt.structPackedFieldBitOffset(struct_obj, field_index) else 0) - + (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) - actual_field_ptr_info.packed_offset.bit_offset), 8) catch return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{}); actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0) @@ -24873,7 +24874,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins // Logic lifted from type computation above - I'm just assuming it's correct. // `catch unreachable` since error case handled above. const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + - pt.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) - + zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) - actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable; const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); break :result Air.internedToRef(parent_ptr_val.toIntern()); diff --git a/src/Type.zig b/src/Type.zig index a199811c8e..9316bec11e 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -4166,7 +4166,7 @@ pub const generic_poison: Type = .{ .ip_index = .generic_poison_type }; pub fn smallestUnsignedBits(max: u64) u16 { return switch (max) { 0 => 0, - else => 1 + std.math.log2_int(u64, max), + else => @as(u16, 1) + std.math.log2_int(u64, max), }; } diff --git a/src/Zcu.zig b/src/Zcu.zig index d337f0b943..df35777231 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3891,6 +3891,29 @@ pub fn typeToPackedStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructTyp return s; } +/// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets +/// into the packed struct InternPool data rather than computing this on the +/// fly, however it was found to perform worse when measured on real world +/// projects. +pub fn structPackedFieldBitOffset( + zcu: *Zcu, + struct_type: InternPool.LoadedStructType, + field_index: u32, +) u16 { + const ip = &zcu.intern_pool; + assert(struct_type.layout == .@"packed"); + assert(struct_type.haveLayout(ip)); + var bit_sum: u64 = 0; + for (0..struct_type.field_types.len) |i| { + if (i == field_index) { + return @intCast(bit_sum); + } + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); + bit_sum += field_ty.bitSize(zcu); + } + unreachable; // index out of bounds +} + pub fn typeToUnion(zcu: *const Zcu, ty: Type) ?InternPool.LoadedUnionType { if (ty.ip_index == .none) return null; const ip = &zcu.intern_pool; @@ -4436,11 +4459,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu else => false, }, .stage2_aarch64 => switch (cc) { - .aarch64_aapcs, - .aarch64_aapcs_darwin, - .aarch64_aapcs_win, - => |opts| opts.incoming_stack_alignment == null, - .naked => true, + .aarch64_aapcs, .aarch64_aapcs_darwin, .naked => true, else => false, }, .stage2_x86 => switch (cc) { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 26f008e1c8..119b742a89 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3737,30 +3737,6 @@ pub fn intBitsForValue(pt: Zcu.PerThread, val: Value, sign: bool) u16 { } } -/// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets -/// into the packed struct InternPool data rather than computing this on the -/// fly, however it was found to perform worse when measured on real world -/// projects. -pub fn structPackedFieldBitOffset( - pt: Zcu.PerThread, - struct_type: InternPool.LoadedStructType, - field_index: u32, -) u16 { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - assert(struct_type.layout == .@"packed"); - assert(struct_type.haveLayout(ip)); - var bit_sum: u64 = 0; - for (0..struct_type.field_types.len) |i| { - if (i == field_index) { - return @intCast(bit_sum); - } - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); - bit_sum += field_ty.bitSize(zcu); - } - unreachable; // index out of bounds -} - pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -4381,8 +4357,11 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e try air.legalize(pt, features); } - var liveness: Air.Liveness = try .analyze(zcu, air.*, ip); - defer liveness.deinit(gpa); + var liveness: ?Air.Liveness = if (codegen.wantsLiveness(pt, nav)) + try .analyze(zcu, air.*, ip) + else + null; + defer if (liveness) |*l| l.deinit(gpa); if (build_options.enable_debug_extensions and comp.verbose_air) { const stderr = std.debug.lockStderrWriter(&.{}); @@ -4392,12 +4371,12 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e stderr.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)}) catch {}; } - if (std.debug.runtime_safety) { + if (std.debug.runtime_safety) verify_liveness: { var verify: Air.Liveness.Verify = .{ .gpa = gpa, .zcu = zcu, .air = air.*, - .liveness = liveness, + .liveness = liveness orelse break :verify_liveness, .intern_pool = ip, }; defer verify.deinit(); diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig deleted file mode 100644 index 9c4227a712..0000000000 --- a/src/arch/aarch64/bits.zig +++ /dev/null @@ -1,2063 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const testing = std.testing; - -/// Disjoint sets of registers. Every register must belong to -/// exactly one register class. -pub const RegisterClass = enum { - general_purpose, - stack_pointer, - floating_point, -}; - -/// Registers in the AArch64 instruction set -pub const Register = enum(u8) { - // zig fmt: off - // 64-bit general-purpose registers - x0, x1, x2, x3, x4, x5, x6, x7, - x8, x9, x10, x11, x12, x13, x14, x15, - x16, x17, x18, x19, x20, x21, x22, x23, - x24, x25, x26, x27, x28, x29, x30, xzr, - - // 32-bit general-purpose registers - w0, w1, w2, w3, w4, w5, w6, w7, - w8, w9, w10, w11, w12, w13, w14, w15, - w16, w17, w18, w19, w20, w21, w22, w23, - w24, w25, w26, w27, w28, w29, w30, wzr, - - // Stack pointer - sp, wsp, - - // 128-bit floating-point registers - q0, q1, q2, q3, q4, q5, q6, q7, - q8, q9, q10, q11, q12, q13, q14, q15, - q16, q17, q18, q19, q20, q21, q22, q23, - q24, q25, q26, q27, q28, q29, q30, q31, - - // 64-bit floating-point registers - d0, d1, d2, d3, d4, d5, d6, d7, - d8, d9, d10, d11, d12, d13, d14, d15, - d16, d17, d18, d19, d20, d21, d22, d23, - d24, d25, d26, d27, d28, d29, d30, d31, - - // 32-bit floating-point registers - s0, s1, s2, s3, s4, s5, s6, s7, - s8, s9, s10, s11, s12, s13, s14, s15, - s16, s17, s18, s19, s20, s21, s22, s23, - s24, s25, s26, s27, s28, s29, s30, s31, - - // 16-bit floating-point registers - h0, h1, h2, h3, h4, h5, h6, h7, - h8, h9, h10, h11, h12, h13, h14, h15, - h16, h17, h18, h19, h20, h21, h22, h23, - h24, h25, h26, h27, h28, h29, h30, h31, - - // 8-bit floating-point registers - b0, b1, b2, b3, b4, b5, b6, b7, - b8, b9, b10, b11, b12, b13, b14, b15, - b16, b17, b18, b19, b20, b21, b22, b23, - b24, b25, b26, b27, b28, b29, b30, b31, - // zig fmt: on - - pub fn class(self: Register) RegisterClass { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => .general_purpose, - @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => .general_purpose, - - @intFromEnum(Register.sp) => .stack_pointer, - @intFromEnum(Register.wsp) => .stack_pointer, - - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => .floating_point, - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => .floating_point, - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => .floating_point, - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => .floating_point, - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => .floating_point, - else => unreachable, - }; - } - - pub fn id(self: Register) u6 { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.x0))), - @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.w0))), - - @intFromEnum(Register.sp) => 32, - @intFromEnum(Register.wsp) => 32, - - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.q0) + 33)), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.d0) + 33)), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.s0) + 33)), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.h0) + 33)), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.b0) + 33)), - else => unreachable, - }; - } - - pub fn enc(self: Register) u5 { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.x0))), - @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.w0))), - - @intFromEnum(Register.sp) => 31, - @intFromEnum(Register.wsp) => 31, - - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.q0))), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.d0))), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.s0))), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.h0))), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.b0))), - else => unreachable, - }; - } - - /// Returns the bit-width of the register. - pub fn size(self: Register) u8 { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => 64, - @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => 32, - - @intFromEnum(Register.sp) => 64, - @intFromEnum(Register.wsp) => 32, - - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => 128, - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => 64, - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => 32, - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => 16, - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => 8, - else => unreachable, - }; - } - - /// Convert from a general-purpose register to its 64 bit alias. - pub fn toX(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.x0) + @intFromEnum(Register.x0)), - ), - @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.w0) + @intFromEnum(Register.x0)), - ), - else => unreachable, - }; - } - - /// Convert from a general-purpose register to its 32 bit alias. - pub fn toW(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.x0) + @intFromEnum(Register.w0)), - ), - @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.w0) + @intFromEnum(Register.w0)), - ), - else => unreachable, - }; - } - - /// Convert from a floating-point register to its 128 bit alias. - pub fn toQ(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.q0)), - ), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.q0)), - ), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.q0)), - ), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.q0)), - ), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.q0)), - ), - else => unreachable, - }; - } - - /// Convert from a floating-point register to its 64 bit alias. - pub fn toD(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.d0)), - ), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.d0)), - ), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.d0)), - ), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.d0)), - ), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.d0)), - ), - else => unreachable, - }; - } - - /// Convert from a floating-point register to its 32 bit alias. - pub fn toS(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.s0)), - ), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.s0)), - ), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.s0)), - ), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.s0)), - ), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.s0)), - ), - else => unreachable, - }; - } - - /// Convert from a floating-point register to its 16 bit alias. - pub fn toH(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.h0)), - ), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.h0)), - ), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.h0)), - ), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.h0)), - ), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.h0)), - ), - else => unreachable, - }; - } - - /// Convert from a floating-point register to its 8 bit alias. - pub fn toB(self: Register) Register { - return switch (@intFromEnum(self)) { - @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.b0)), - ), - @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.b0)), - ), - @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.b0)), - ), - @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.b0)), - ), - @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as( - Register, - @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.b0)), - ), - else => unreachable, - }; - } - - pub fn dwarfNum(self: Register) u5 { - return self.enc(); - } -}; - -test "Register.enc" { - try testing.expectEqual(@as(u5, 0), Register.x0.enc()); - try testing.expectEqual(@as(u5, 0), Register.w0.enc()); - - try testing.expectEqual(@as(u5, 31), Register.xzr.enc()); - try testing.expectEqual(@as(u5, 31), Register.wzr.enc()); - - try testing.expectEqual(@as(u5, 31), Register.sp.enc()); - try testing.expectEqual(@as(u5, 31), Register.sp.enc()); -} - -test "Register.size" { - try testing.expectEqual(@as(u8, 64), Register.x19.size()); - try testing.expectEqual(@as(u8, 32), Register.w3.size()); -} - -test "Register.toX/toW" { - try testing.expectEqual(Register.x0, Register.w0.toX()); - try testing.expectEqual(Register.x0, Register.x0.toX()); - - try testing.expectEqual(Register.w3, Register.w3.toW()); - try testing.expectEqual(Register.w3, Register.x3.toW()); -} - -/// Represents an instruction in the AArch64 instruction set -pub const Instruction = union(enum) { - move_wide_immediate: packed struct { - rd: u5, - imm16: u16, - hw: u2, - fixed: u6 = 0b100101, - opc: u2, - sf: u1, - }, - pc_relative_address: packed struct { - rd: u5, - immhi: u19, - fixed: u5 = 0b10000, - immlo: u2, - op: u1, - }, - load_store_register: packed struct { - rt: u5, - rn: u5, - offset: u12, - opc: u2, - op1: u2, - v: u1, - fixed: u3 = 0b111, - size: u2, - }, - load_store_register_pair: packed struct { - rt1: u5, - rn: u5, - rt2: u5, - imm7: u7, - load: u1, - encoding: u2, - fixed: u5 = 0b101_0_0, - opc: u2, - }, - load_literal: packed struct { - rt: u5, - imm19: u19, - fixed: u6 = 0b011_0_00, - opc: u2, - }, - exception_generation: packed struct { - ll: u2, - op2: u3, - imm16: u16, - opc: u3, - fixed: u8 = 0b1101_0100, - }, - unconditional_branch_register: packed struct { - op4: u5, - rn: u5, - op3: u6, - op2: u5, - opc: u4, - fixed: u7 = 0b1101_011, - }, - unconditional_branch_immediate: packed struct { - imm26: u26, - fixed: u5 = 0b00101, - op: u1, - }, - no_operation: packed struct { - fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, - }, - logical_shifted_register: packed struct { - rd: u5, - rn: u5, - imm6: u6, - rm: u5, - n: u1, - shift: u2, - fixed: u5 = 0b01010, - opc: u2, - sf: u1, - }, - add_subtract_immediate: packed struct { - rd: u5, - rn: u5, - imm12: u12, - sh: u1, - fixed: u6 = 0b100010, - s: u1, - op: u1, - sf: u1, - }, - logical_immediate: packed struct { - rd: u5, - rn: u5, - imms: u6, - immr: u6, - n: u1, - fixed: u6 = 0b100100, - opc: u2, - sf: u1, - }, - bitfield: packed struct { - rd: u5, - rn: u5, - imms: u6, - immr: u6, - n: u1, - fixed: u6 = 0b100110, - opc: u2, - sf: u1, - }, - add_subtract_shifted_register: packed struct { - rd: u5, - rn: u5, - imm6: u6, - rm: u5, - fixed_1: u1 = 0b0, - shift: u2, - fixed_2: u5 = 0b01011, - s: u1, - op: u1, - sf: u1, - }, - add_subtract_extended_register: packed struct { - rd: u5, - rn: u5, - imm3: u3, - option: u3, - rm: u5, - fixed: u8 = 0b01011_00_1, - s: u1, - op: u1, - sf: u1, - }, - conditional_branch: struct { - cond: u4, - o0: u1, - imm19: u19, - o1: u1, - fixed: u7 = 0b0101010, - }, - compare_and_branch: struct { - rt: u5, - imm19: u19, - op: u1, - fixed: u6 = 0b011010, - sf: u1, - }, - conditional_select: struct { - rd: u5, - rn: u5, - op2: u2, - cond: u4, - rm: u5, - fixed: u8 = 0b11010100, - s: u1, - op: u1, - sf: u1, - }, - data_processing_3_source: packed struct { - rd: u5, - rn: u5, - ra: u5, - o0: u1, - rm: u5, - op31: u3, - fixed: u5 = 0b11011, - op54: u2, - sf: u1, - }, - data_processing_2_source: packed struct { - rd: u5, - rn: u5, - opcode: u6, - rm: u5, - fixed_1: u8 = 0b11010110, - s: u1, - fixed_2: u1 = 0b0, - sf: u1, - }, - - pub const Condition = enum(u4) { - /// Integer: Equal - /// Floating point: Equal - eq, - /// Integer: Not equal - /// Floating point: Not equal or unordered - ne, - /// Integer: Carry set - /// Floating point: Greater than, equal, or unordered - cs, - /// Integer: Carry clear - /// Floating point: Less than - cc, - /// Integer: Minus, negative - /// Floating point: Less than - mi, - /// Integer: Plus, positive or zero - /// Floating point: Greater than, equal, or unordered - pl, - /// Integer: Overflow - /// Floating point: Unordered - vs, - /// Integer: No overflow - /// Floating point: Ordered - vc, - /// Integer: Unsigned higher - /// Floating point: Greater than, or unordered - hi, - /// Integer: Unsigned lower or same - /// Floating point: Less than or equal - ls, - /// Integer: Signed greater than or equal - /// Floating point: Greater than or equal - ge, - /// Integer: Signed less than - /// Floating point: Less than, or unordered - lt, - /// Integer: Signed greater than - /// Floating point: Greater than - gt, - /// Integer: Signed less than or equal - /// Floating point: Less than, equal, or unordered - le, - /// Integer: Always - /// Floating point: Always - al, - /// Integer: Always - /// Floating point: Always - nv, - - /// Converts a std.math.CompareOperator into a condition flag, - /// i.e. returns the condition that is true iff the result of the - /// comparison is true. Assumes signed comparison - pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { - return switch (op) { - .gte => .ge, - .gt => .gt, - .neq => .ne, - .lt => .lt, - .lte => .le, - .eq => .eq, - }; - } - - /// Converts a std.math.CompareOperator into a condition flag, - /// i.e. returns the condition that is true iff the result of the - /// comparison is true. Assumes unsigned comparison - pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { - return switch (op) { - .gte => .cs, - .gt => .hi, - .neq => .ne, - .lt => .cc, - .lte => .ls, - .eq => .eq, - }; - } - - /// Returns the condition which is true iff the given condition is - /// false (if such a condition exists) - pub fn negate(cond: Condition) Condition { - return switch (cond) { - .eq => .ne, - .ne => .eq, - .cs => .cc, - .cc => .cs, - .mi => .pl, - .pl => .mi, - .vs => .vc, - .vc => .vs, - .hi => .ls, - .ls => .hi, - .ge => .lt, - .lt => .ge, - .gt => .le, - .le => .gt, - .al => unreachable, - .nv => unreachable, - }; - } - }; - - pub fn toU32(self: Instruction) u32 { - return switch (self) { - .move_wide_immediate => |v| @as(u32, @bitCast(v)), - .pc_relative_address => |v| @as(u32, @bitCast(v)), - .load_store_register => |v| @as(u32, @bitCast(v)), - .load_store_register_pair => |v| @as(u32, @bitCast(v)), - .load_literal => |v| @as(u32, @bitCast(v)), - .exception_generation => |v| @as(u32, @bitCast(v)), - .unconditional_branch_register => |v| @as(u32, @bitCast(v)), - .unconditional_branch_immediate => |v| @as(u32, @bitCast(v)), - .no_operation => |v| @as(u32, @bitCast(v)), - .logical_shifted_register => |v| @as(u32, @bitCast(v)), - .add_subtract_immediate => |v| @as(u32, @bitCast(v)), - .logical_immediate => |v| @as(u32, @bitCast(v)), - .bitfield => |v| @as(u32, @bitCast(v)), - .add_subtract_shifted_register => |v| @as(u32, @bitCast(v)), - .add_subtract_extended_register => |v| @as(u32, @bitCast(v)), - // TODO once packed structs work, this can be refactored - .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), - .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), - .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31, - .data_processing_3_source => |v| @as(u32, @bitCast(v)), - .data_processing_2_source => |v| @as(u32, @bitCast(v)), - }; - } - - fn moveWideImmediate( - opc: u2, - rd: Register, - imm16: u16, - shift: u6, - ) Instruction { - assert(shift % 16 == 0); - assert(!(rd.size() == 32 and shift > 16)); - assert(!(rd.size() == 64 and shift > 48)); - - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.enc(), - .imm16 = imm16, - .hw = @as(u2, @intCast(shift / 16)), - .opc = opc, - .sf = switch (rd.size()) { - 32 => 0, - 64 => 1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { - assert(rd.size() == 64); - const imm21_u = @as(u21, @bitCast(imm21)); - return Instruction{ - .pc_relative_address = .{ - .rd = rd.enc(), - .immlo = @as(u2, @truncate(imm21_u)), - .immhi = @as(u19, @truncate(imm21_u >> 2)), - .op = op, - }, - }; - } - - pub const LoadStoreOffsetImmediate = union(enum) { - post_index: i9, - pre_index: i9, - unsigned: u12, - }; - - pub const LoadStoreOffsetRegister = struct { - rm: u5, - shift: union(enum) { - uxtw: u2, - lsl: u2, - sxtw: u2, - sxtx: u2, - }, - }; - - /// Represents the offset operand of a load or store instruction. - /// Data can be loaded from memory with either an immediate offset - /// or an offset that is stored in some register. - pub const LoadStoreOffset = union(enum) { - immediate: LoadStoreOffsetImmediate, - register: LoadStoreOffsetRegister, - - pub const none = LoadStoreOffset{ - .immediate = .{ .unsigned = 0 }, - }; - - pub fn toU12(self: LoadStoreOffset) u12 { - return switch (self) { - .immediate => |imm_type| switch (imm_type) { - .post_index => |v| (@as(u12, @intCast(@as(u9, @bitCast(v)))) << 2) + 1, - .pre_index => |v| (@as(u12, @intCast(@as(u9, @bitCast(v)))) << 2) + 3, - .unsigned => |v| v, - }, - .register => |r| switch (r.shift) { - .uxtw => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 16 + 2050, - .lsl => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 24 + 2050, - .sxtw => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 48 + 2050, - .sxtx => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 56 + 2050, - }, - }; - } - - pub fn imm(offset: u12) LoadStoreOffset { - return .{ - .immediate = .{ .unsigned = offset }, - }; - } - - pub fn imm_post_index(offset: i9) LoadStoreOffset { - return .{ - .immediate = .{ .post_index = offset }, - }; - } - - pub fn imm_pre_index(offset: i9) LoadStoreOffset { - return .{ - .immediate = .{ .pre_index = offset }, - }; - } - - pub fn reg(rm: Register) LoadStoreOffset { - return .{ - .register = .{ - .rm = rm.enc(), - .shift = .{ - .lsl = 0, - }, - }, - }; - } - - pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 32 and (shift == 0 or shift == 2)); - return .{ - .register = .{ - .rm = rm.enc(), - .shift = .{ - .uxtw = shift, - }, - }, - }; - } - - pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 64 and (shift == 0 or shift == 3)); - return .{ - .register = .{ - .rm = rm.enc(), - .shift = .{ - .lsl = shift, - }, - }, - }; - } - - pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 32 and (shift == 0 or shift == 2)); - return .{ - .register = .{ - .rm = rm.enc(), - .shift = .{ - .sxtw = shift, - }, - }, - }; - } - - pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 64 and (shift == 0 or shift == 3)); - return .{ - .register = .{ - .rm = rm.enc(), - .shift = .{ - .sxtx = shift, - }, - }, - }; - } - }; - - /// Which kind of load/store to perform - const LoadStoreVariant = enum { - /// 32 bits or 64 bits - str, - /// 8 bits, zero-extended - strb, - /// 16 bits, zero-extended - strh, - /// 32 bits or 64 bits - ldr, - /// 8 bits, zero-extended - ldrb, - /// 16 bits, zero-extended - ldrh, - /// 8 bits, sign extended - ldrsb, - /// 16 bits, sign extended - ldrsh, - /// 32 bits, sign extended - ldrsw, - }; - - fn loadStoreRegister( - rt: Register, - rn: Register, - offset: LoadStoreOffset, - variant: LoadStoreVariant, - ) Instruction { - assert(rn.size() == 64); - assert(rn.id() != Register.xzr.id()); - - const off = offset.toU12(); - - const op1: u2 = blk: { - switch (offset) { - .immediate => |imm| switch (imm) { - .unsigned => break :blk 0b01, - else => {}, - }, - else => {}, - } - break :blk 0b00; - }; - - const opc: u2 = blk: { - switch (variant) { - .ldr, .ldrh, .ldrb => break :blk 0b01, - .str, .strh, .strb => break :blk 0b00, - .ldrsb, - .ldrsh, - => switch (rt.size()) { - 32 => break :blk 0b11, - 64 => break :blk 0b10, - else => unreachable, // unexpected register size - }, - .ldrsw => break :blk 0b10, - } - }; - - const size: u2 = blk: { - switch (variant) { - .ldr, .str => switch (rt.size()) { - 32 => break :blk 0b10, - 64 => break :blk 0b11, - else => unreachable, // unexpected register size - }, - .ldrsw => break :blk 0b10, - .ldrh, .ldrsh, .strh => break :blk 0b01, - .ldrb, .ldrsb, .strb => break :blk 0b00, - } - }; - - return Instruction{ - .load_store_register = .{ - .rt = rt.enc(), - .rn = rn.enc(), - .offset = off, - .opc = opc, - .op1 = op1, - .v = 0, - .size = size, - }, - }; - } - - fn loadStoreRegisterPair( - rt1: Register, - rt2: Register, - rn: Register, - offset: i9, - encoding: u2, - load: bool, - ) Instruction { - assert(rn.size() == 64); - assert(rn.id() != Register.xzr.id()); - - switch (rt1.size()) { - 32 => { - assert(-256 <= offset and offset <= 252); - const imm7 = @as(u7, @truncate(@as(u9, @bitCast(offset >> 2)))); - return Instruction{ - .load_store_register_pair = .{ - .rt1 = rt1.enc(), - .rn = rn.enc(), - .rt2 = rt2.enc(), - .imm7 = imm7, - .load = @intFromBool(load), - .encoding = encoding, - .opc = 0b00, - }, - }; - }, - 64 => { - assert(-512 <= offset and offset <= 504); - const imm7 = @as(u7, @truncate(@as(u9, @bitCast(offset >> 3)))); - return Instruction{ - .load_store_register_pair = .{ - .rt1 = rt1.enc(), - .rn = rn.enc(), - .rt2 = rt2.enc(), - .imm7 = imm7, - .load = @intFromBool(load), - .encoding = encoding, - .opc = 0b10, - }, - }; - }, - else => unreachable, // unexpected register size - } - } - - fn loadLiteral(rt: Register, imm19: u19) Instruction { - return Instruction{ - .load_literal = .{ - .rt = rt.enc(), - .imm19 = imm19, - .opc = switch (rt.size()) { - 32 => 0b00, - 64 => 0b01, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn exceptionGeneration( - opc: u3, - op2: u3, - ll: u2, - imm16: u16, - ) Instruction { - return Instruction{ - .exception_generation = .{ - .ll = ll, - .op2 = op2, - .imm16 = imm16, - .opc = opc, - }, - }; - } - - fn unconditionalBranchRegister( - opc: u4, - op2: u5, - op3: u6, - rn: Register, - op4: u5, - ) Instruction { - assert(rn.size() == 64); - - return Instruction{ - .unconditional_branch_register = .{ - .op4 = op4, - .rn = rn.enc(), - .op3 = op3, - .op2 = op2, - .opc = opc, - }, - }; - } - - fn unconditionalBranchImmediate( - op: u1, - offset: i28, - ) Instruction { - return Instruction{ - .unconditional_branch_immediate = .{ - .imm26 = @as(u26, @bitCast(@as(i26, @intCast(offset >> 2)))), - .op = op, - }, - }; - } - - pub const LogicalShiftedRegisterShift = enum(u2) { lsl, lsr, asr, ror }; - - fn logicalShiftedRegister( - opc: u2, - n: u1, - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - assert(rd.size() == rn.size()); - assert(rd.size() == rm.size()); - if (rd.size() == 32) assert(amount < 32); - - return Instruction{ - .logical_shifted_register = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imm6 = amount, - .rm = rm.enc(), - .n = n, - .shift = @intFromEnum(shift), - .opc = opc, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, - }, - }, - }; - } - - fn addSubtractImmediate( - op: u1, - s: u1, - rd: Register, - rn: Register, - imm12: u12, - shift: bool, - ) Instruction { - assert(rd.size() == rn.size()); - assert(rn.id() != Register.xzr.id()); - - return Instruction{ - .add_subtract_immediate = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imm12 = imm12, - .sh = @intFromBool(shift), - .s = s, - .op = op, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn logicalImmediate( - opc: u2, - rd: Register, - rn: Register, - imms: u6, - immr: u6, - n: u1, - ) Instruction { - assert(rd.size() == rn.size()); - assert(!(rd.size() == 32 and n != 0)); - - return Instruction{ - .logical_immediate = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imms = imms, - .immr = immr, - .n = n, - .opc = opc, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn initBitfield( - opc: u2, - n: u1, - rd: Register, - rn: Register, - immr: u6, - imms: u6, - ) Instruction { - assert(rd.size() == rn.size()); - assert(!(rd.size() == 64 and n != 1)); - assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0))); - - return Instruction{ - .bitfield = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imms = imms, - .immr = immr, - .n = n, - .opc = opc, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - pub const AddSubtractShiftedRegisterShift = enum(u2) { lsl, lsr, asr, _ }; - - fn addSubtractShiftedRegister( - op: u1, - s: u1, - shift: AddSubtractShiftedRegisterShift, - rd: Register, - rn: Register, - rm: Register, - imm6: u6, - ) Instruction { - assert(rd.size() == rn.size()); - assert(rd.size() == rm.size()); - - return Instruction{ - .add_subtract_shifted_register = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imm6 = imm6, - .rm = rm.enc(), - .shift = @intFromEnum(shift), - .s = s, - .op = op, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - pub const AddSubtractExtendedRegisterOption = enum(u3) { - uxtb, - uxth, - uxtw, - uxtx, // serves also as lsl - sxtb, - sxth, - sxtw, - sxtx, - }; - - fn addSubtractExtendedRegister( - op: u1, - s: u1, - rd: Register, - rn: Register, - rm: Register, - extend: AddSubtractExtendedRegisterOption, - imm3: u3, - ) Instruction { - return Instruction{ - .add_subtract_extended_register = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imm3 = imm3, - .option = @intFromEnum(extend), - .rm = rm.enc(), - .s = s, - .op = op, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn conditionalBranch( - o0: u1, - o1: u1, - cond: Condition, - offset: i21, - ) Instruction { - assert(offset & 0b11 == 0b00); - - return Instruction{ - .conditional_branch = .{ - .cond = @intFromEnum(cond), - .o0 = o0, - .imm19 = @as(u19, @bitCast(@as(i19, @intCast(offset >> 2)))), - .o1 = o1, - }, - }; - } - - fn compareAndBranch( - op: u1, - rt: Register, - offset: i21, - ) Instruction { - assert(offset & 0b11 == 0b00); - - return Instruction{ - .compare_and_branch = .{ - .rt = rt.enc(), - .imm19 = @as(u19, @bitCast(@as(i19, @intCast(offset >> 2)))), - .op = op, - .sf = switch (rt.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn conditionalSelect( - op2: u2, - op: u1, - s: u1, - rd: Register, - rn: Register, - rm: Register, - cond: Condition, - ) Instruction { - assert(rd.size() == rn.size()); - assert(rd.size() == rm.size()); - - return Instruction{ - .conditional_select = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .op2 = op2, - .cond = @intFromEnum(cond), - .rm = rm.enc(), - .s = s, - .op = op, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn dataProcessing3Source( - op54: u2, - op31: u3, - o0: u1, - rd: Register, - rn: Register, - rm: Register, - ra: Register, - ) Instruction { - return Instruction{ - .data_processing_3_source = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .ra = ra.enc(), - .o0 = o0, - .rm = rm.enc(), - .op31 = op31, - .op54 = op54, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn dataProcessing2Source( - s: u1, - opcode: u6, - rd: Register, - rn: Register, - rm: Register, - ) Instruction { - assert(rd.size() == rn.size()); - assert(rd.size() == rm.size()); - - return Instruction{ - .data_processing_2_source = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .opcode = opcode, - .rm = rm.enc(), - .s = s, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - // Helper functions for assembly syntax functions - - // Move wide (immediate) - - pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { - return moveWideImmediate(0b00, rd, imm16, shift); - } - - pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction { - return moveWideImmediate(0b10, rd, imm16, shift); - } - - pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction { - return moveWideImmediate(0b11, rd, imm16, shift); - } - - // PC relative address - - pub fn adr(rd: Register, imm21: i21) Instruction { - return pcRelativeAddress(rd, imm21, 0b0); - } - - pub fn adrp(rd: Register, imm21: i21) Instruction { - return pcRelativeAddress(rd, imm21, 0b1); - } - - // Load or store register - - pub fn ldrLiteral(rt: Register, literal: u19) Instruction { - return loadLiteral(rt, literal); - } - - pub fn ldr(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .ldr); - } - - pub fn ldrh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .ldrh); - } - - pub fn ldrb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .ldrb); - } - - pub fn ldrsb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .ldrsb); - } - - pub fn ldrsh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .ldrsh); - } - - pub fn ldrsw(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .ldrsw); - } - - pub fn str(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .str); - } - - pub fn strh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .strh); - } - - pub fn strb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { - return loadStoreRegister(rt, rn, offset, .strb); - } - - // Load or store pair of registers - - pub const LoadStorePairOffset = struct { - encoding: enum(u2) { - post_index = 0b01, - signed = 0b10, - pre_index = 0b11, - }, - offset: i9, - - pub fn none() LoadStorePairOffset { - return .{ .encoding = .signed, .offset = 0 }; - } - - pub fn post_index(imm: i9) LoadStorePairOffset { - return .{ .encoding = .post_index, .offset = imm }; - } - - pub fn pre_index(imm: i9) LoadStorePairOffset { - return .{ .encoding = .pre_index, .offset = imm }; - } - - pub fn signed(imm: i9) LoadStorePairOffset { - return .{ .encoding = .signed, .offset = imm }; - } - }; - - pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @intFromEnum(offset.encoding), true); - } - - pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); - } - - pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @intFromEnum(offset.encoding), false); - } - - pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); - } - - // Exception generation - - pub fn svc(imm16: u16) Instruction { - return exceptionGeneration(0b000, 0b000, 0b01, imm16); - } - - pub fn hvc(imm16: u16) Instruction { - return exceptionGeneration(0b000, 0b000, 0b10, imm16); - } - - pub fn smc(imm16: u16) Instruction { - return exceptionGeneration(0b000, 0b000, 0b11, imm16); - } - - pub fn brk(imm16: u16) Instruction { - return exceptionGeneration(0b001, 0b000, 0b00, imm16); - } - - pub fn hlt(imm16: u16) Instruction { - return exceptionGeneration(0b010, 0b000, 0b00, imm16); - } - - // Unconditional branch (register) - - pub fn br(rn: Register) Instruction { - return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000); - } - - pub fn blr(rn: Register) Instruction { - return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000); - } - - pub fn ret(rn: ?Register) Instruction { - return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000); - } - - // Unconditional branch (immediate) - - pub fn b(offset: i28) Instruction { - return unconditionalBranchImmediate(0, offset); - } - - pub fn bl(offset: i28) Instruction { - return unconditionalBranchImmediate(1, offset); - } - - // Nop - - pub fn nop() Instruction { - return Instruction{ .no_operation = .{} }; - } - - // Logical (shifted register) - - pub fn andShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b00, 0b0, rd, rn, rm, shift, amount); - } - - pub fn bicShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b00, 0b1, rd, rn, rm, shift, amount); - } - - pub fn orrShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b01, 0b0, rd, rn, rm, shift, amount); - } - - pub fn ornShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b01, 0b1, rd, rn, rm, shift, amount); - } - - pub fn eorShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b10, 0b0, rd, rn, rm, shift, amount); - } - - pub fn eonShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b10, 0b1, rd, rn, rm, shift, amount); - } - - pub fn andsShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b11, 0b0, rd, rn, rm, shift, amount); - } - - pub fn bicsShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: LogicalShiftedRegisterShift, - amount: u6, - ) Instruction { - return logicalShiftedRegister(0b11, 0b1, rd, rn, rm, shift, amount); - } - - // Add/subtract (immediate) - - pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift); - } - - pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift); - } - - pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift); - } - - pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); - } - - // Logical (immediate) - - pub fn andImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction { - return logicalImmediate(0b00, rd, rn, imms, immr, n); - } - - pub fn orrImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction { - return logicalImmediate(0b01, rd, rn, imms, immr, n); - } - - pub fn eorImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction { - return logicalImmediate(0b10, rd, rn, imms, immr, n); - } - - pub fn andsImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction { - return logicalImmediate(0b11, rd, rn, imms, immr, n); - } - - // Bitfield - - pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { - const n: u1 = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }; - return initBitfield(0b00, n, rd, rn, immr, imms); - } - - pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { - const n: u1 = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }; - return initBitfield(0b01, n, rd, rn, immr, imms); - } - - pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { - const n: u1 = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }; - return initBitfield(0b10, n, rd, rn, immr, imms); - } - - pub fn asrImmediate(rd: Register, rn: Register, shift: u6) Instruction { - const imms = @as(u6, @intCast(rd.size() - 1)); - return sbfm(rd, rn, shift, imms); - } - - pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { - return sbfm(rd, rn, lsb, @as(u6, @intCast(lsb + width - 1))); - } - - pub fn sxtb(rd: Register, rn: Register) Instruction { - return sbfm(rd, rn, 0, 7); - } - - pub fn sxth(rd: Register, rn: Register) Instruction { - return sbfm(rd, rn, 0, 15); - } - - pub fn sxtw(rd: Register, rn: Register) Instruction { - assert(rd.size() == 64); - return sbfm(rd, rn, 0, 31); - } - - pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { - const size = @as(u6, @intCast(rd.size() - 1)); - return ubfm(rd, rn, size - shift + 1, size - shift); - } - - pub fn lsrImmediate(rd: Register, rn: Register, shift: u6) Instruction { - const imms = @as(u6, @intCast(rd.size() - 1)); - return ubfm(rd, rn, shift, imms); - } - - pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { - return ubfm(rd, rn, lsb, @as(u6, @intCast(lsb + width - 1))); - } - - pub fn uxtb(rd: Register, rn: Register) Instruction { - return ubfm(rd, rn, 0, 7); - } - - pub fn uxth(rd: Register, rn: Register) Instruction { - return ubfm(rd, rn, 0, 15); - } - - // Add/subtract (shifted register) - - pub fn addShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: AddSubtractShiftedRegisterShift, - imm6: u6, - ) Instruction { - return addSubtractShiftedRegister(0b0, 0b0, shift, rd, rn, rm, imm6); - } - - pub fn addsShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: AddSubtractShiftedRegisterShift, - imm6: u6, - ) Instruction { - return addSubtractShiftedRegister(0b0, 0b1, shift, rd, rn, rm, imm6); - } - - pub fn subShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: AddSubtractShiftedRegisterShift, - imm6: u6, - ) Instruction { - return addSubtractShiftedRegister(0b1, 0b0, shift, rd, rn, rm, imm6); - } - - pub fn subsShiftedRegister( - rd: Register, - rn: Register, - rm: Register, - shift: AddSubtractShiftedRegisterShift, - imm6: u6, - ) Instruction { - return addSubtractShiftedRegister(0b1, 0b1, shift, rd, rn, rm, imm6); - } - - // Add/subtract (extended register) - - pub fn addExtendedRegister( - rd: Register, - rn: Register, - rm: Register, - extend: AddSubtractExtendedRegisterOption, - imm3: u3, - ) Instruction { - return addSubtractExtendedRegister(0b0, 0b0, rd, rn, rm, extend, imm3); - } - - pub fn addsExtendedRegister( - rd: Register, - rn: Register, - rm: Register, - extend: AddSubtractExtendedRegisterOption, - imm3: u3, - ) Instruction { - return addSubtractExtendedRegister(0b0, 0b1, rd, rn, rm, extend, imm3); - } - - pub fn subExtendedRegister( - rd: Register, - rn: Register, - rm: Register, - extend: AddSubtractExtendedRegisterOption, - imm3: u3, - ) Instruction { - return addSubtractExtendedRegister(0b1, 0b0, rd, rn, rm, extend, imm3); - } - - pub fn subsExtendedRegister( - rd: Register, - rn: Register, - rm: Register, - extend: AddSubtractExtendedRegisterOption, - imm3: u3, - ) Instruction { - return addSubtractExtendedRegister(0b1, 0b1, rd, rn, rm, extend, imm3); - } - - // Conditional branch - - pub fn bCond(cond: Condition, offset: i21) Instruction { - return conditionalBranch(0b0, 0b0, cond, offset); - } - - // Compare and branch - - pub fn cbz(rt: Register, offset: i21) Instruction { - return compareAndBranch(0b0, rt, offset); - } - - pub fn cbnz(rt: Register, offset: i21) Instruction { - return compareAndBranch(0b1, rt, offset); - } - - // Conditional select - - pub fn csel(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { - return conditionalSelect(0b00, 0b0, 0b0, rd, rn, rm, cond); - } - - pub fn csinc(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { - return conditionalSelect(0b01, 0b0, 0b0, rd, rn, rm, cond); - } - - pub fn csinv(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { - return conditionalSelect(0b00, 0b1, 0b0, rd, rn, rm, cond); - } - - pub fn csneg(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { - return conditionalSelect(0b01, 0b1, 0b0, rd, rn, rm, cond); - } - - // Data processing (3 source) - - pub fn madd(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - return dataProcessing3Source(0b00, 0b000, 0b0, rd, rn, rm, ra); - } - - pub fn smaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - assert(rd.size() == 64 and rn.size() == 32 and rm.size() == 32 and ra.size() == 64); - return dataProcessing3Source(0b00, 0b001, 0b0, rd, rn, rm, ra); - } - - pub fn umaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - assert(rd.size() == 64 and rn.size() == 32 and rm.size() == 32 and ra.size() == 64); - return dataProcessing3Source(0b00, 0b101, 0b0, rd, rn, rm, ra); - } - - pub fn msub(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - return dataProcessing3Source(0b00, 0b000, 0b1, rd, rn, rm, ra); - } - - pub fn mul(rd: Register, rn: Register, rm: Register) Instruction { - return madd(rd, rn, rm, .xzr); - } - - pub fn smull(rd: Register, rn: Register, rm: Register) Instruction { - return smaddl(rd, rn, rm, .xzr); - } - - pub fn smulh(rd: Register, rn: Register, rm: Register) Instruction { - assert(rd.size() == 64); - return dataProcessing3Source(0b00, 0b010, 0b0, rd, rn, rm, .xzr); - } - - pub fn umull(rd: Register, rn: Register, rm: Register) Instruction { - return umaddl(rd, rn, rm, .xzr); - } - - pub fn umulh(rd: Register, rn: Register, rm: Register) Instruction { - assert(rd.size() == 64); - return dataProcessing3Source(0b00, 0b110, 0b0, rd, rn, rm, .xzr); - } - - pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction { - return msub(rd, rn, rm, .xzr); - } - - // Data processing (2 source) - - pub fn udiv(rd: Register, rn: Register, rm: Register) Instruction { - return dataProcessing2Source(0b0, 0b000010, rd, rn, rm); - } - - pub fn sdiv(rd: Register, rn: Register, rm: Register) Instruction { - return dataProcessing2Source(0b0, 0b000011, rd, rn, rm); - } - - pub fn lslv(rd: Register, rn: Register, rm: Register) Instruction { - return dataProcessing2Source(0b0, 0b001000, rd, rn, rm); - } - - pub fn lsrv(rd: Register, rn: Register, rm: Register) Instruction { - return dataProcessing2Source(0b0, 0b001001, rd, rn, rm); - } - - pub fn asrv(rd: Register, rn: Register, rm: Register) Instruction { - return dataProcessing2Source(0b0, 0b001010, rd, rn, rm); - } - - pub const asrRegister = asrv; - pub const lslRegister = lslv; - pub const lsrRegister = lsrv; -}; - -test { - testing.refAllDecls(@This()); -} - -test "serialize instructions" { - const Testcase = struct { - inst: Instruction, - expected: u32, - }; - - const testcases = [_]Testcase{ - .{ // orr x0, xzr, x1 - .inst = Instruction.orrShiftedRegister(.x0, .xzr, .x1, .lsl, 0), - .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, - }, - .{ // orn x0, xzr, x1 - .inst = Instruction.ornShiftedRegister(.x0, .xzr, .x1, .lsl, 0), - .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, - }, - .{ // movz x1, #4 - .inst = Instruction.movz(.x1, 4, 0), - .expected = 0b1_10_100101_00_0000000000000100_00001, - }, - .{ // movz x1, #4, lsl 16 - .inst = Instruction.movz(.x1, 4, 16), - .expected = 0b1_10_100101_01_0000000000000100_00001, - }, - .{ // movz x1, #4, lsl 32 - .inst = Instruction.movz(.x1, 4, 32), - .expected = 0b1_10_100101_10_0000000000000100_00001, - }, - .{ // movz x1, #4, lsl 48 - .inst = Instruction.movz(.x1, 4, 48), - .expected = 0b1_10_100101_11_0000000000000100_00001, - }, - .{ // movz w1, #4 - .inst = Instruction.movz(.w1, 4, 0), - .expected = 0b0_10_100101_00_0000000000000100_00001, - }, - .{ // movz w1, #4, lsl 16 - .inst = Instruction.movz(.w1, 4, 16), - .expected = 0b0_10_100101_01_0000000000000100_00001, - }, - .{ // svc #0 - .inst = Instruction.svc(0), - .expected = 0b1101_0100_000_0000000000000000_00001, - }, - .{ // svc #0x80 ; typical on Darwin - .inst = Instruction.svc(0x80), - .expected = 0b1101_0100_000_0000000010000000_00001, - }, - .{ // ret - .inst = Instruction.ret(null), - .expected = 0b1101_011_00_10_11111_0000_00_11110_00000, - }, - .{ // bl #0x10 - .inst = Instruction.bl(0x10), - .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, - }, - .{ // ldr x2, [x1] - .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.none), - .expected = 0b11_111_0_01_01_000000000000_00001_00010, - }, - .{ // ldr x2, [x1, #1]! - .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.imm_pre_index(1)), - .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, - }, - .{ // ldr x2, [x1], #-1 - .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.imm_post_index(-1)), - .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, - }, - .{ // ldr x2, [x1], (x3) - .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.reg(.x3)), - .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, - }, - .{ // ldr x2, label - .inst = Instruction.ldrLiteral(.x2, 0x1), - .expected = 0b01_011_0_00_0000000000000000001_00010, - }, - .{ // ldrh x7, [x4], #0xaa - .inst = Instruction.ldrh(.x7, .x4, Instruction.LoadStoreOffset.imm_post_index(0xaa)), - .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, - }, - .{ // ldrb x9, [x15, #0xff]! - .inst = Instruction.ldrb(.x9, .x15, Instruction.LoadStoreOffset.imm_pre_index(0xff)), - .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, - }, - .{ // str x2, [x1] - .inst = Instruction.str(.x2, .x1, Instruction.LoadStoreOffset.none), - .expected = 0b11_111_0_01_00_000000000000_00001_00010, - }, - .{ // str x2, [x1], (x3) - .inst = Instruction.str(.x2, .x1, Instruction.LoadStoreOffset.reg(.x3)), - .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, - }, - .{ // strh w0, [x1] - .inst = Instruction.strh(.w0, .x1, Instruction.LoadStoreOffset.none), - .expected = 0b01_111_0_01_00_000000000000_00001_00000, - }, - .{ // strb w8, [x9] - .inst = Instruction.strb(.w8, .x9, Instruction.LoadStoreOffset.none), - .expected = 0b00_111_0_01_00_000000000000_01001_01000, - }, - .{ // adr x2, #0x8 - .inst = Instruction.adr(.x2, 0x8), - .expected = 0b0_00_10000_0000000000000000010_00010, - }, - .{ // adr x2, -#0x8 - .inst = Instruction.adr(.x2, -0x8), - .expected = 0b0_00_10000_1111111111111111110_00010, - }, - .{ // adrp x2, #0x8 - .inst = Instruction.adrp(.x2, 0x8), - .expected = 0b1_00_10000_0000000000000000010_00010, - }, - .{ // adrp x2, -#0x8 - .inst = Instruction.adrp(.x2, -0x8), - .expected = 0b1_00_10000_1111111111111111110_00010, - }, - .{ // stp x1, x2, [sp, #8] - .inst = Instruction.stp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.signed(8)), - .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, - }, - .{ // ldp x1, x2, [sp, #8] - .inst = Instruction.ldp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.signed(8)), - .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, - }, - .{ // stp x1, x2, [sp, #-16]! - .inst = Instruction.stp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.pre_index(-16)), - .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, - }, - .{ // ldp x1, x2, [sp], #16 - .inst = Instruction.ldp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.post_index(16)), - .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, - }, - .{ // and x0, x4, x2 - .inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0), - .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, - }, - .{ // and x0, x4, x2, lsl #0x8 - .inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0x8), - .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, - }, - .{ // add x0, x10, #10 - .inst = Instruction.add(.x0, .x10, 10, false), - .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000, - }, - .{ // subs x0, x5, #11, lsl #12 - .inst = Instruction.subs(.x0, .x5, 11, true), - .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, - }, - .{ // b.hi #-4 - .inst = Instruction.bCond(.hi, -4), - .expected = 0b0101010_0_1111111111111111111_0_1000, - }, - .{ // cbz x10, #40 - .inst = Instruction.cbz(.x10, 40), - .expected = 0b1_011010_0_0000000000000001010_01010, - }, - .{ // add x0, x1, x2, lsl #5 - .inst = Instruction.addShiftedRegister(.x0, .x1, .x2, .lsl, 5), - .expected = 0b1_0_0_01011_00_0_00010_000101_00001_00000, - }, - .{ // csinc x1, x2, x4, eq - .inst = Instruction.csinc(.x1, .x2, .x4, .eq), - .expected = 0b1_0_0_11010100_00100_0000_0_1_00010_00001, - }, - .{ // mul x1, x4, x9 - .inst = Instruction.mul(.x1, .x4, .x9), - .expected = 0b1_00_11011_000_01001_0_11111_00100_00001, - }, - .{ // eor x3, x5, #1 - .inst = Instruction.eorImmediate(.x3, .x5, 0b000000, 0b000000, 0b1), - .expected = 0b1_10_100100_1_000000_000000_00101_00011, - }, - .{ // lslv x6, x9, x10 - .inst = Instruction.lslv(.x6, .x9, .x10), - .expected = 0b1_0_0_11010110_01010_0010_00_01001_00110, - }, - .{ // lsl x4, x2, #42 - .inst = Instruction.lslImmediate(.x4, .x2, 42), - .expected = 0b1_10_100110_1_010110_010101_00010_00100, - }, - .{ // lsl x4, x2, #63 - .inst = Instruction.lslImmediate(.x4, .x2, 63), - .expected = 0b1_10_100110_1_000001_000000_00010_00100, - }, - .{ // lsr x4, x2, #42 - .inst = Instruction.lsrImmediate(.x4, .x2, 42), - .expected = 0b1_10_100110_1_101010_111111_00010_00100, - }, - .{ // lsr x4, x2, #63 - .inst = Instruction.lsrImmediate(.x4, .x2, 63), - .expected = 0b1_10_100110_1_111111_111111_00010_00100, - }, - .{ // umull x0, w0, w1 - .inst = Instruction.umull(.x0, .w0, .w1), - .expected = 0b1_00_11011_1_01_00001_0_11111_00000_00000, - }, - .{ // smull x0, w0, w1 - .inst = Instruction.smull(.x0, .w0, .w1), - .expected = 0b1_00_11011_0_01_00001_0_11111_00000_00000, - }, - .{ // tst x0, #0xffffffff00000000 - .inst = Instruction.andsImmediate(.xzr, .x0, 0b011111, 0b100000, 0b1), - .expected = 0b1_11_100100_1_100000_011111_00000_11111, - }, - .{ // umulh x0, x1, x2 - .inst = Instruction.umulh(.x0, .x1, .x2), - .expected = 0b1_00_11011_1_10_00010_0_11111_00001_00000, - }, - .{ // smulh x0, x1, x2 - .inst = Instruction.smulh(.x0, .x1, .x2), - .expected = 0b1_00_11011_0_10_00010_0_11111_00001_00000, - }, - .{ // adds x0, x1, x2, sxtx - .inst = Instruction.addsExtendedRegister(.x0, .x1, .x2, .sxtx, 0), - .expected = 0b1_0_1_01011_00_1_00010_111_000_00001_00000, - }, - }; - - for (testcases) |case| { - const actual = case.inst.toU32(); - try testing.expectEqual(case.expected, actual); - } -} diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 0753cc5d16..ffff65d4d1 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -744,7 +744,7 @@ pub fn generate( src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, - liveness: *const Air.Liveness, + liveness: *const ?Air.Liveness, ) CodeGenError!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -767,7 +767,7 @@ pub fn generate( .pt = pt, .mod = mod, .bin_file = bin_file, - .liveness = liveness.*, + .liveness = liveness.*.?, .target = &mod.resolved_target.result, .owner = .{ .nav_index = func.owner_nav }, .args = undefined, // populated after `resolveCallingConventionValues` @@ -4584,7 +4584,7 @@ fn structFieldPtr(func: *Func, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const field_offset: i32 = switch (container_ty.containerLayout(zcu)) { .auto, .@"extern" => @intCast(container_ty.structFieldOffset(index, zcu)), .@"packed" => @divExact(@as(i32, ptr_container_ty.ptrInfo(zcu).packed_offset.bit_offset) + - (if (zcu.typeToStruct(container_ty)) |struct_obj| pt.structPackedFieldBitOffset(struct_obj, index) else 0) - + (if (zcu.typeToStruct(container_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, index) else 0) - ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8), }; @@ -4615,7 +4615,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { const field_off: u32 = switch (struct_ty.containerLayout(zcu)) { .auto, .@"extern" => @intCast(struct_ty.structFieldOffset(index, zcu) * 8), .@"packed" => if (zcu.typeToStruct(struct_ty)) |struct_type| - pt.structPackedFieldBitOffset(struct_type, index) + zcu.structPackedFieldBitOffset(struct_type, index) else 0, }; @@ -8059,7 +8059,7 @@ fn airAggregateInit(func: *Func, inst: Air.Inst.Index) !void { const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu)); const elem_abi_bits = elem_abi_size * 8; - const elem_off = pt.structPackedFieldBitOffset(struct_obj, elem_i); + const elem_off = zcu.structPackedFieldBitOffset(struct_obj, elem_i); const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size); const elem_bit_off = elem_off % elem_abi_bits; const elem_mcv = try func.resolveInst(elem); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 31a7f39d69..6ab5dea4ec 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -267,7 +267,7 @@ pub fn generate( src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, - liveness: *const Air.Liveness, + liveness: *const ?Air.Liveness, ) CodeGenError!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -288,7 +288,7 @@ pub fn generate( .gpa = gpa, .pt = pt, .air = air.*, - .liveness = liveness.*, + .liveness = liveness.*.?, .target = target, .bin_file = lf, .func_index = func_index, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 50d104a7bc..fca32627b9 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1173,7 +1173,7 @@ pub fn generate( src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, - liveness: *const Air.Liveness, + liveness: *const ?Air.Liveness, ) Error!Mir { _ = src_loc; _ = bin_file; @@ -1194,7 +1194,7 @@ pub fn generate( .gpa = gpa, .pt = pt, .air = air.*, - .liveness = liveness.*, + .liveness = liveness.*.?, .owner_nav = cg.owner_nav, .target = target, .ptr_size = switch (target.cpu.arch) { @@ -3776,7 +3776,7 @@ fn structFieldPtr( break :offset @as(u32, 0); } const struct_type = zcu.typeToStruct(struct_ty).?; - break :offset @divExact(pt.structPackedFieldBitOffset(struct_type, index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); + break :offset @divExact(zcu.structPackedFieldBitOffset(struct_type, index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); }, .@"union" => 0, else => unreachable, @@ -3812,7 +3812,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .@"packed" => switch (struct_ty.zigTypeTag(zcu)) { .@"struct" => result: { const packed_struct = zcu.typeToPackedStruct(struct_ty).?; - const offset = pt.structPackedFieldBitOffset(packed_struct, field_index); + const offset = zcu.structPackedFieldBitOffset(packed_struct, field_index); const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); const host_bits = backing_ty.intInfo(zcu).bits; @@ -5696,7 +5696,7 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .auto, .@"extern" => parent_ty.structFieldOffset(field_index, zcu), .@"packed" => offset: { const parent_ptr_offset = parent_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset; - const field_offset = if (zcu.typeToStruct(parent_ty)) |loaded_struct| pt.structPackedFieldBitOffset(loaded_struct, field_index) else 0; + const field_offset = if (zcu.typeToStruct(parent_ty)) |loaded_struct| zcu.structPackedFieldBitOffset(loaded_struct, field_index) else 0; const field_ptr_offset = field_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset; break :offset @divExact(parent_ptr_offset + field_offset - field_ptr_offset, 8); }, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6341f7e3d2..89a23d3514 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -878,7 +878,7 @@ pub fn generate( src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, - liveness: *const Air.Liveness, + liveness: *const ?Air.Liveness, ) codegen.CodeGenError!Mir { _ = bin_file; const zcu = pt.zcu; @@ -894,7 +894,7 @@ pub fn generate( .gpa = gpa, .pt = pt, .air = air.*, - .liveness = liveness.*, + .liveness = liveness.*.?, .target = &mod.resolved_target.result, .mod = mod, .owner = .{ .nav_index = func.owner_nav }, @@ -100674,11 +100674,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data; var ops = try cg.tempsFromOperands(inst, .{struct_field.struct_operand}); - try ops[0].toOffset(cg.fieldOffset( + try ops[0].toOffset(@intCast(codegen.fieldOffset( cg.typeOf(struct_field.struct_operand), ty_pl.ty.toType(), struct_field.field_index, - ), cg); + zcu, + )), cg); try ops[0].finish(inst, &.{struct_field.struct_operand}, &ops, cg); }, .struct_field_ptr_index_0, @@ -100688,7 +100689,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { => |air_tag| { const ty_op = air_datas[@intFromEnum(inst)].ty_op; var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); - try ops[0].toOffset(cg.fieldOffset( + try ops[0].toOffset(@intCast(codegen.fieldOffset( cg.typeOf(ty_op.operand), ty_op.ty.toType(), switch (air_tag) { @@ -100698,7 +100699,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .struct_field_ptr_index_2 => 2, .struct_field_ptr_index_3 => 3, }, - ), cg); + zcu, + )), cg); try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); }, .struct_field_val => { @@ -168108,11 +168110,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; const field_parent_ptr = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; var ops = try cg.tempsFromOperands(inst, .{field_parent_ptr.field_ptr}); - try ops[0].toOffset(-cg.fieldOffset( + try ops[0].toOffset(-@as(i32, @intCast(codegen.fieldOffset( ty_pl.ty.toType(), cg.typeOf(field_parent_ptr.field_ptr), field_parent_ptr.field_index, - ), cg); + zcu, + ))), cg); try ops[0].finish(inst, &.{field_parent_ptr.field_ptr}, &ops, cg); }, .wasm_memory_size, .wasm_memory_grow => unreachable, @@ -174809,18 +174812,6 @@ fn airStore(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn fieldOffset(self: *CodeGen, ptr_agg_ty: Type, ptr_field_ty: Type, field_index: u32) i32 { - const pt = self.pt; - const zcu = pt.zcu; - const agg_ty = ptr_agg_ty.childType(zcu); - return switch (agg_ty.containerLayout(zcu)) { - .auto, .@"extern" => @intCast(agg_ty.structFieldOffset(field_index, zcu)), - .@"packed" => @divExact(@as(i32, ptr_agg_ty.ptrInfo(zcu).packed_offset.bit_offset) + - (if (zcu.typeToStruct(agg_ty)) |loaded_struct| pt.structPackedFieldBitOffset(loaded_struct, field_index) else 0) - - ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8), - }; -} - fn genUnOp(self: *CodeGen, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue { const pt = self.pt; const zcu = pt.zcu; @@ -184575,7 +184566,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { } const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu)); const elem_abi_bits = elem_abi_size * 8; - const elem_off = pt.structPackedFieldBitOffset(loaded_struct, elem_i); + const elem_off = zcu.structPackedFieldBitOffset(loaded_struct, elem_i); const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size); const elem_bit_off = elem_off % elem_abi_bits; const elem_mcv = try self.resolveInst(elem); @@ -185625,21 +185616,19 @@ fn resolveCallingConventionValues( fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { @branchHint(.cold); const zcu = cg.pt.zcu; - switch (cg.owner) { - .nav_index => |i| return zcu.codegenFail(i, format, args), - .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args), - } - return error.CodegenFail; + return switch (cg.owner) { + .nav_index => |i| zcu.codegenFail(i, format, args), + .lazy_sym => |s| zcu.codegenFailType(s.ty, format, args), + }; } fn failMsg(cg: *CodeGen, msg: *Zcu.ErrorMsg) error{ OutOfMemory, CodegenFail } { @branchHint(.cold); const zcu = cg.pt.zcu; - switch (cg.owner) { - .nav_index => |i| return zcu.codegenFailMsg(i, msg), - .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg), - } - return error.CodegenFail; + return switch (cg.owner) { + .nav_index => |i| zcu.codegenFailMsg(i, msg), + .lazy_sym => |s| zcu.codegenFailTypeMsg(s.ty, msg), + }; } fn parseRegName(name: []const u8) ?Register { diff --git a/src/codegen.zig b/src/codegen.zig index 9bddc51963..d509234148 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -22,6 +22,8 @@ const Zir = std.zig.Zir; const Alignment = InternPool.Alignment; const dev = @import("dev.zig"); +pub const aarch64 = @import("codegen/aarch64.zig"); + pub const CodeGenError = GenerateSymbolError || error{ /// Indicates the error is already stored in Zcu `failed_codegen`. CodegenFail, @@ -48,7 +50,7 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature { fn importBackend(comptime backend: std.builtin.CompilerBackend) type { return switch (backend) { .other, .stage1 => unreachable, - .stage2_aarch64 => unreachable, + .stage2_aarch64 => aarch64, .stage2_arm => unreachable, .stage2_c => @import("codegen/c.zig"), .stage2_llvm => @import("codegen/llvm.zig"), @@ -71,6 +73,7 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co .stage2_c, .stage2_wasm, .stage2_x86_64, + .stage2_aarch64, .stage2_x86, .stage2_riscv64, .stage2_sparc64, @@ -82,10 +85,20 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co } } +pub fn wantsLiveness(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) bool { + const zcu = pt.zcu; + const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; + return switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { + else => true, + .stage2_aarch64 => false, + }; +} + /// Every code generation backend has a different MIR representation. However, we want to pass /// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a /// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`. pub const AnyMir = union { + aarch64: @import("codegen/aarch64/Mir.zig"), riscv64: @import("arch/riscv64/Mir.zig"), sparc64: @import("arch/sparc64/Mir.zig"), x86_64: @import("arch/x86_64/Mir.zig"), @@ -95,7 +108,6 @@ pub const AnyMir = union { pub inline fn tag(comptime backend: std.builtin.CompilerBackend) []const u8 { return switch (backend) { .stage2_aarch64 => "aarch64", - .stage2_arm => "arm", .stage2_riscv64 => "riscv64", .stage2_sparc64 => "sparc64", .stage2_x86_64 => "x86_64", @@ -110,7 +122,8 @@ pub const AnyMir = union { const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); switch (backend) { else => unreachable, - inline .stage2_riscv64, + inline .stage2_aarch64, + .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, .stage2_wasm, @@ -131,14 +144,15 @@ pub fn generateFunction( src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, - liveness: *const Air.Liveness, + liveness: *const ?Air.Liveness, ) CodeGenError!AnyMir { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; switch (target_util.zigBackend(target, false)) { else => unreachable, - inline .stage2_riscv64, + inline .stage2_aarch64, + .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, .stage2_wasm, @@ -173,7 +187,8 @@ pub fn emitFunction( const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { else => unreachable, - inline .stage2_riscv64, + inline .stage2_aarch64, + .stage2_riscv64, .stage2_sparc64, .stage2_x86_64, => |backend| { @@ -420,7 +435,7 @@ pub fn generateSymbol( const int_tag_ty = ty.intTagType(zcu); try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent); }, - .float => |float| switch (float.storage) { + .float => |float| storage: switch (float.storage) { .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)), .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)), .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)), @@ -429,7 +444,13 @@ pub fn generateSymbol( const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; try code.appendNTimes(gpa, 0, abi_size - 10); }, - .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)), + .f128 => |f128_val| switch (Type.fromInterned(float.ty).floatBits(target)) { + else => unreachable, + 16 => continue :storage .{ .f16 = @floatCast(f128_val) }, + 32 => continue :storage .{ .f32 = @floatCast(f128_val) }, + 64 => continue :storage .{ .f64 = @floatCast(f128_val) }, + 128 => writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)), + }, }, .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0), .slice => |slice| { @@ -1218,3 +1239,17 @@ pub fn errUnionErrorOffset(payload_ty: Type, zcu: *Zcu) u64 { return 0; } } + +pub fn fieldOffset(ptr_agg_ty: Type, ptr_field_ty: Type, field_index: u32, zcu: *Zcu) u64 { + const agg_ty = ptr_agg_ty.childType(zcu); + return switch (agg_ty.containerLayout(zcu)) { + .auto, .@"extern" => agg_ty.structFieldOffset(field_index, zcu), + .@"packed" => @divExact(@as(u64, ptr_agg_ty.ptrInfo(zcu).packed_offset.bit_offset) + + (if (zcu.typeToPackedStruct(agg_ty)) |loaded_struct| zcu.structPackedFieldBitOffset(loaded_struct, field_index) else 0) - + ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8), + }; +} + +test { + _ = aarch64; +} diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig new file mode 100644 index 0000000000..6f95fff58e --- /dev/null +++ b/src/codegen/aarch64.zig @@ -0,0 +1,194 @@ +pub const abi = @import("aarch64/abi.zig"); +pub const Assemble = @import("aarch64/Assemble.zig"); +pub const Disassemble = @import("aarch64/Disassemble.zig"); +pub const encoding = @import("aarch64/encoding.zig"); +pub const Mir = @import("aarch64/Mir.zig"); +pub const Select = @import("aarch64/Select.zig"); + +pub fn legalizeFeatures(_: *const std.Target) ?*Air.Legalize.Features { + return null; +} + +pub fn generate( + _: *link.File, + pt: Zcu.PerThread, + _: Zcu.LazySrcLoc, + func_index: InternPool.Index, + air: *const Air, + liveness: *const ?Air.Liveness, +) !Mir { + const zcu = pt.zcu; + const gpa = zcu.gpa; + const func = zcu.funcInfo(func_index); + const func_type = zcu.intern_pool.indexToKey(func.ty).func_type; + assert(liveness.* == null); + + const mod = zcu.navFileScope(func.owner_nav).mod.?; + var isel: Select = .{ + .pt = pt, + .target = &mod.resolved_target.result, + .air = air.*, + .nav_index = zcu.funcInfo(func_index).owner_nav, + + .def_order = .empty, + .blocks = .empty, + .loops = .empty, + .active_loops = .empty, + .loop_live = .{ + .set = .empty, + .list = .empty, + }, + .dom_start = 0, + .dom_len = 0, + .dom = .empty, + + .saved_registers = comptime .initEmpty(), + .instructions = .empty, + .literals = .empty, + .nav_relocs = .empty, + .uav_relocs = .empty, + .global_relocs = .empty, + .literal_relocs = .empty, + + .returns = false, + .va_list = undefined, + .stack_size = 0, + .stack_align = .@"16", + + .live_registers = comptime .initFill(.free), + .live_values = .empty, + .values = .empty, + }; + defer isel.deinit(); + + 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); + 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_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_len = @intFromEnum(saved_vra_end) - @intFromEnum(saved_vra_start); + + const frame_record = 2; + const named_stack_args: Select.Value.Indirect = .{ + .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 }, + }; + + // translate arg locations from caller-based to callee-based + for (air_args) |air_inst_index| { + assert(air.instructions.items(.tag)[@intFromEnum(air_inst_index)] == .arg); + const arg_vi = isel.live_values.get(air_inst_index).?; + const passed_vi = switch (arg_vi.parent(&isel)) { + .unallocated, .stack_slot => arg_vi, + .value, .constant => unreachable, + .address => |address_vi| address_vi, + }; + 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; + 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))) + else + isel.va_list.__gr_top.withOffset(@as(i8, -8) * + (@intFromEnum(saved_gra_end) - @intFromEnum(hint_ra))) }); + }, + .stack_slot => |stack_slot| { + assert(stack_slot.base == .sp); + passed_vi.setParent(&isel, .{ + .stack_slot = named_stack_args.withOffset(stack_slot.offset), + }); + }, + .address, .value, .constant => unreachable, + } + } + + ret: { + var ret_it: Select.CallAbiIterator = .init; + const ret_vi = try ret_it.ret(&isel, .fromInterned(func_type.return_type)) orelse break :ret; + tracking_log.debug("${d} <- %main", .{@intFromEnum(ret_vi)}); + try isel.live_values.putNoClobber(gpa, Select.Block.main, ret_vi); + } + + assert(!(try isel.blocks.getOrPut(gpa, Select.Block.main)).found_existing); + try isel.analyze(air_main_body); + try isel.finishAnalysis(); + isel.verify(false); + + isel.blocks.values()[0] = .{ + .live_registers = isel.live_registers, + .target_label = @intCast(isel.instructions.items.len), + }; + try isel.body(air_main_body); + if (isel.live_values.fetchRemove(Select.Block.main)) |ret_vi| { + switch (ret_vi.value.parent(&isel)) { + .unallocated, .stack_slot => {}, + .value, .constant => unreachable, + .address => |address_vi| try address_vi.liveIn( + &isel, + address_vi.hint(&isel).?, + comptime &.initFill(.free), + ), + } + ret_vi.value.deref(&isel); + } + 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 instructions = try isel.instructions.toOwnedSlice(gpa); + var mir: Mir = .{ + .prologue = instructions[prologue..epilogue], + .body = instructions[0..prologue], + .epilogue = instructions[epilogue..], + .literals = &.{}, + .nav_relocs = &.{}, + .uav_relocs = &.{}, + .global_relocs = &.{}, + .literal_relocs = &.{}, + }; + errdefer mir.deinit(gpa); + 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.global_relocs = try isel.global_relocs.toOwnedSlice(gpa); + mir.literal_relocs = try isel.literal_relocs.toOwnedSlice(gpa); + return mir; +} + +test { + _ = Assemble; +} + +const Air = @import("../Air.zig"); +const assert = std.debug.assert; +const InternPool = @import("../InternPool.zig"); +const link = @import("../link.zig"); +const std = @import("std"); +const tracking_log = std.log.scoped(.tracking); +const Zcu = @import("../Zcu.zig"); diff --git a/src/codegen/aarch64/Assemble.zig b/src/codegen/aarch64/Assemble.zig new file mode 100644 index 0000000000..235c445411 --- /dev/null +++ b/src/codegen/aarch64/Assemble.zig @@ -0,0 +1,1653 @@ +source: [*:0]const u8, +operands: std.StringHashMapUnmanaged(Operand), + +pub const Operand = union(enum) { + register: aarch64.encoding.Register, +}; + +pub fn nextInstruction(as: *Assemble) !?Instruction { + @setEvalBranchQuota(37_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; + }; + log.debug( + \\. + \\========================= + \\= Assembling "{f}" + \\========================= + \\ + , .{std.zig.fmtString(std.mem.span(original_source))}); + inline for (instructions) |instruction| { + next_pattern: { + as.source = original_source; + var symbols: Symbols: { + const symbols = @typeInfo(@TypeOf(instruction.symbols)).@"struct".fields; + var symbol_fields: [symbols.len]std.builtin.Type.StructField = undefined; + for (&symbol_fields, symbols) |*symbol_field, symbol| symbol_field.* = .{ + .name = symbol.name, + .type = zonCast(SymbolSpec, @field(instruction.symbols, symbol.name), .{}).Storage(), + .default_value_ptr = null, + .is_comptime = false, + .alignment = 0, + }; + break :Symbols @Type(.{ .@"struct" = .{ + .layout = .auto, + .fields = &symbol_fields, + .decls = &.{}, + .is_tuple = false, + } }); + } = undefined; + comptime var pattern_as: Assemble = .{ .source = instruction.pattern, .operands = undefined }; + inline while (true) { + const pattern_token = comptime pattern_as.nextToken(&ct_token_buf, .{ .placeholders = true }) catch |err| + @compileError(@errorName(err) ++ " while parsing '" ++ instruction.pattern ++ "'"); + const source_token = try as.nextToken(&token_buf, .{ .operands = true }); + log.debug("\"{f}\" -> \"{f}\"", .{ + std.zig.fmtString(pattern_token), + std.zig.fmtString(source_token), + }); + if (pattern_token.len == 0) { + if (source_token.len > 0 and source_token[0] != '\n') break :next_pattern; + const encode = @field(Instruction, @tagName(instruction.encode[0])); + const Encode = @TypeOf(encode); + var args: std.meta.ArgsTuple(Encode) = undefined; + inline for (&args, @typeInfo(Encode).@"fn".params, 1..instruction.encode.len) |*arg, param, encode_index| + arg.* = zonCast(param.type.?, instruction.encode[encode_index], symbols); + return @call(.auto, encode, args); + } else if (pattern_token[0] == '<') { + const symbol_name = comptime pattern_token[1 .. std.mem.indexOfScalarPos(u8, pattern_token, 1, '|') orelse + pattern_token.len - 1]; + 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; + } + } + log.debug("'{s}' not matched...", .{instruction.pattern}); + } + as.source = original_source; + log.debug("Nothing matched!\n", .{}); + return error.InvalidSyntax; +} + +fn zonCast(comptime Result: type, zon_value: anytype, symbols: anytype) Result { + const ZonValue = @TypeOf(zon_value); + const Symbols = @TypeOf(symbols); + switch (@typeInfo(ZonValue)) { + .void, .bool, .int, .float, .pointer, .comptime_float, .comptime_int, .@"enum" => return zon_value, + .@"struct" => |zon_struct| switch (@typeInfo(Result)) { + .@"struct" => |result_struct| { + comptime var used_zon_fields = 0; + var result: Result = undefined; + inline for (result_struct.fields) |result_field| @field(result, result_field.name) = if (@hasField(ZonValue, result_field.name)) result: { + used_zon_fields += 1; + break :result zonCast(@FieldType(Result, result_field.name), @field(zon_value, result_field.name), symbols); + } else result_field.defaultValue() orelse @compileError(std.fmt.comptimePrint("missing zon field '{s}': {} <- {any}", .{ result_field.name, Result, zon_value })); + if (used_zon_fields != zon_struct.fields.len) @compileError(std.fmt.comptimePrint("unused zon field: {} <- {any}", .{ Result, zon_value })); + return result; + }, + .@"union" => { + if (zon_struct.fields.len != 1) @compileError(std.fmt.comptimePrint("{} <- {any}", .{ Result, zon_value })); + const field_name = zon_struct.fields[0].name; + return @unionInit( + Result, + field_name, + zonCast(@FieldType(Result, field_name), @field(zon_value, field_name), symbols), + ); + }, + else => @compileError(std.fmt.comptimePrint("unsupported zon type: {} <- {any}", .{ Result, zon_value })), + }, + .enum_literal => if (@hasField(Symbols, @tagName(zon_value))) { + const symbol = @field(symbols, @tagName(zon_value)); + const Symbol = @TypeOf(symbol); + switch (@typeInfo(Result)) { + .@"enum" => switch (@typeInfo(Symbol)) { + .int => |symbol_int| { + var buf: [ + std.fmt.count("{d}", .{switch (symbol_int.signedness) { + .signed => std.math.minInt(Symbol), + .unsigned => std.math.maxInt(Symbol), + }}) + ]u8 = undefined; + return std.meta.stringToEnum(Result, std.fmt.bufPrint(&buf, "{d}", .{symbol}) catch unreachable).?; + }, + else => return symbol, + }, + else => return symbol, + } + } else return if (@hasDecl(Result, @tagName(zon_value))) @field(Result, @tagName(zon_value)) else zon_value, + else => @compileError(std.fmt.comptimePrint("unsupported zon type: {} <- {any}", .{ Result, zon_value })), + } +} + +const token_buf_len = "v31.b[15]".len; +fn nextToken(as: *Assemble, buf: *[token_buf_len]u8, comptime opts: struct { + operands: bool = false, + placeholders: bool = false, +}) ![]const u8 { + const invalid_syntax: u8 = 1; + while (true) c: switch (as.source[0]) { + 0 => return as.source[0..0], + '\t', '\n' + 1...'\r', ' ' => as.source = as.source[1..], + '\n', '!', '#', ',', '[', ']' => { + defer as.source = as.source[1..]; + return as.source[0..1]; + }, + '%' => if (opts.operands) { + if (as.source[1] != '[') continue :c invalid_syntax; + const name_start: usize = 2; + var index = name_start; + while (switch (as.source[index]) { + else => true, + ':', ']' => false, + }) index += 1; + const operand = as.operands.get(as.source[name_start..index]) orelse continue :c invalid_syntax; + const modifier = modifier: switch (as.source[index]) { + else => unreachable, + ':' => { + index += 1; + const modifier_start = index; + while (switch (as.source[index]) { + else => true, + ']' => false, + }) index += 1; + break :modifier as.source[modifier_start..index]; + }, + ']' => "", + }; + assert(as.source[index] == ']'); + const modified_operand: Operand = if (std.mem.eql(u8, modifier, "")) + operand + else if (std.mem.eql(u8, modifier, "w")) switch (operand) { + .register => |reg| .{ .register = reg.alias.w() }, + } else if (std.mem.eql(u8, modifier, "x")) switch (operand) { + .register => |reg| .{ .register = reg.alias.x() }, + } else if (std.mem.eql(u8, modifier, "b")) switch (operand) { + .register => |reg| .{ .register = reg.alias.b() }, + } else if (std.mem.eql(u8, modifier, "h")) switch (operand) { + .register => |reg| .{ .register = reg.alias.h() }, + } else if (std.mem.eql(u8, modifier, "s")) switch (operand) { + .register => |reg| .{ .register = reg.alias.s() }, + } else if (std.mem.eql(u8, modifier, "d")) switch (operand) { + .register => |reg| .{ .register = reg.alias.d() }, + } else if (std.mem.eql(u8, modifier, "q")) switch (operand) { + .register => |reg| .{ .register = reg.alias.q() }, + } else if (std.mem.eql(u8, modifier, "Z")) switch (operand) { + .register => |reg| .{ .register = reg.alias.z() }, + } else continue :c invalid_syntax; + switch (modified_operand) { + .register => |reg| { + as.source = as.source[index + 1 ..]; + return std.fmt.bufPrint(buf, "{f}", .{reg.fmt()}) catch unreachable; + }, + } + } else continue :c invalid_syntax, + '-', '0'...'9', 'A'...'Z', '_', 'a'...'z' => { + var index: usize = 1; + while (switch (as.source[index]) { + '0'...'9', 'A'...'Z', '_', 'a'...'z' => true, + else => false, + }) index += 1; + defer as.source = as.source[index..]; + return as.source[0..index]; + }, + '<' => if (opts.placeholders) { + var index: usize = 1; + while (switch (as.source[index]) { + 0 => return error.UnterminatedPlaceholder, + '>' => false, + else => true, + }) index += 1; + defer as.source = as.source[index + 1 ..]; + return as.source[0 .. index + 1]; + } else continue :c invalid_syntax, + else => { + if (!@inComptime()) log.debug("invalid token \"{f}\"", .{std.zig.fmtString(std.mem.span(as.source))}); + return error.InvalidSyntax; + }, + }; +} + +const SymbolSpec = union(enum) { + reg: struct { format: aarch64.encoding.Register.Format, allow_sp: bool = false }, + imm: struct { + type: std.builtin.Type.Int, + multiple_of: comptime_int = 1, + max_valid: ?comptime_int = null, + }, + extend: struct { size: aarch64.encoding.Register.IntegerSize }, + shift: struct { allow_ror: bool = true }, + barrier: struct { only_sy: bool = false }, + + fn Storage(comptime spec: SymbolSpec) type { + return switch (spec) { + .reg => aarch64.encoding.Register, + .imm => |imm| @Type(.{ .int = imm.type }), + .extend => Instruction.DataProcessingRegister.AddSubtractExtendedRegister.Option, + .shift => Instruction.DataProcessingRegister.Shift.Op, + .barrier => Instruction.BranchExceptionGeneratingSystem.Barriers.Option, + }; + } + + fn parse(comptime spec: SymbolSpec, token: []const u8) ?Storage(spec) { + const Result = Storage(spec); + switch (spec) { + .reg => |reg_spec| { + var buf: [token_buf_len]u8 = undefined; + const reg = Result.parse(std.ascii.lowerString(&buf, token[0..@min(token.len, buf.len)])) orelse { + log.debug("invalid register: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + }; + if (reg.format.integer != reg_spec.format.integer) { + log.debug("invalid register size: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + } + if (reg.alias == if (reg_spec.allow_sp) .zr else .sp) { + log.debug("invalid register usage: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + } + return reg; + }, + .imm => |imm_spec| { + const imm = std.fmt.parseInt(Result, token, 0) catch { + log.debug("invalid immediate: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + }; + if (@rem(imm, imm_spec.multiple_of) != 0) { + log.debug("invalid immediate usage: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + } + if (imm_spec.max_valid) |max_valid| if (imm > max_valid) { + log.debug("out of range immediate: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + }; + return imm; + }, + .extend => |extend_spec| { + const Option = Instruction.DataProcessingRegister.AddSubtractExtendedRegister.Option; + var buf: [ + max_len: { + var max_len = 0; + for (@typeInfo(Option).@"enum".fields) |field| max_len = @max(max_len, field.name.len); + break :max_len max_len; + } + 1 + ]u8 = undefined; + const extend = std.meta.stringToEnum(Option, std.ascii.lowerString( + &buf, + token[0..@min(token.len, buf.len)], + )) orelse { + log.debug("invalid extend: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + }; + if (extend.sf() != extend_spec.size) { + log.debug("invalid extend: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + } + return extend; + }, + .shift => |shift_spec| { + const ShiftOp = Instruction.DataProcessingRegister.Shift.Op; + var buf: [ + max_len: { + var max_len = 0; + for (@typeInfo(ShiftOp).@"enum".fields) |field| max_len = @max(max_len, field.name.len); + break :max_len max_len; + } + 1 + ]u8 = undefined; + const shift = std.meta.stringToEnum(ShiftOp, std.ascii.lowerString( + &buf, + token[0..@min(token.len, buf.len)], + )) orelse { + log.debug("invalid shift: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + }; + if (!shift_spec.allow_ror and shift == .ror) { + log.debug("invalid shift usage: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + } + return shift; + }, + .barrier => |barrier_spec| { + const Option = Instruction.BranchExceptionGeneratingSystem.Barriers.Option; + var buf: [ + max_len: { + var max_len = 0; + for (@typeInfo(Option).@"enum".fields) |field| max_len = @max(max_len, field.name.len); + break :max_len max_len; + } + 1 + ]u8 = undefined; + const barrier = std.meta.stringToEnum(Option, std.ascii.lowerString( + &buf, + token[0..@min(token.len, buf.len)], + )) orelse { + log.debug("invalid barrier: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + }; + if (barrier_spec.only_sy and barrier != .sy) { + log.debug("invalid barrier: \"{f}\"", .{std.zig.fmtString(token)}); + return null; + } + return barrier; + }, + } + } +}; + +test "add sub" { + var as: Assemble = .{ + .source = + \\ add w0, w0, w1 + \\ add w2, w3, w4 + \\ add wsp, w5, w6 + \\ add w7, wsp, w8 + \\ add wsp, wsp, w9 + \\ add w10, w10, wzr + \\ add w11, w12, wzr + \\ add wsp, w13, wzr + \\ add w14, wsp, wzr + \\ add wsp, wsp, wzr + \\ + \\ add x0, x0, x1 + \\ add x2, x3, x4 + \\ add sp, x5, x6 + \\ add x7, sp, x8 + \\ add sp, sp, x9 + \\ add x10, x10, xzr + \\ add x11, x12, xzr + \\ add sp, x13, xzr + \\ add x14, sp, xzr + \\ add sp, sp, xzr + \\ + \\ add w0, w0, w1 + \\ add w2, w3, w4, uxtb #0 + \\ add wsp, w5, w6, uxth #1 + \\ add w7, wsp, w8, uxtw #0 + \\ add wsp, wsp, w9, uxtw #2 + \\ add w10, w10, wzr, uxtw #3 + \\ add w11, w12, wzr, sxtb #4 + \\ add wsp, w13, wzr, sxth #0 + \\ add w14, wsp, wzr, sxtw #1 + \\ add wsp, wsp, wzr, sxtw #2 + \\ + \\ add x0, x0, x1 + \\ add x2, x3, w4, uxtb #0 + \\ add sp, x5, w6, uxth #1 + \\ add x7, sp, w8, uxtw #2 + \\ add sp, sp, x9, uxtx #0 + \\ add x10, x10, xzr, uxtx #3 + \\ add x11, x12, wzr, sxtb #4 + \\ add sp, x13, wzr, sxth #0 + \\ add x14, sp, wzr, sxtw #1 + \\ add sp, sp, xzr, sxtx #2 + \\ + \\ add w0, w0, #0 + \\ add w0, w1, #1, lsl #0 + \\ add wsp, w2, #2, lsl #12 + \\ add w3, wsp, #3, lsl #0 + \\ add wsp, wsp, #4095, lsl #12 + \\ add w0, w1, #0 + \\ add w2, w3, #0, lsl #0 + \\ add w4, wsp, #0 + \\ add w5, wsp, #0, lsl #0 + \\ add wsp, w6, #0 + \\ add wsp, w7, #0, lsl #0 + \\ add wsp, wsp, #0 + \\ add wsp, wsp, #0, lsl #0 + \\ + \\ add x0, x0, #0 + \\ add x0, x1, #1, lsl #0 + \\ add sp, x2, #2, lsl #12 + \\ add x3, sp, #3, lsl #0 + \\ add sp, sp, #4095, lsl #12 + \\ add x0, x1, #0 + \\ add x2, x3, #0, lsl #0 + \\ add x4, sp, #0 + \\ add x5, sp, #0, lsl #0 + \\ add sp, x6, #0 + \\ add sp, x7, #0, lsl #0 + \\ add sp, sp, #0 + \\ add sp, sp, #0, lsl #0 + \\ + \\ add w0, w0, w0 + \\ add w1, w1, w2, lsl #0 + \\ add w3, w4, w5, lsl #1 + \\ add w6, w6, wzr, lsl #31 + \\ add w7, wzr, w8, lsr #0 + \\ add w9, wzr, wzr, lsr #30 + \\ add wzr, w10, w11, lsr #31 + \\ add wzr, w12, wzr, asr #0x0 + \\ add wzr, wzr, w13, asr #0x10 + \\ add wzr, wzr, wzr, asr #0x1f + \\ + \\ add x0, x0, x0 + \\ add x1, x1, x2, lsl #0 + \\ add x3, x4, x5, lsl #1 + \\ add x6, x6, xzr, lsl #63 + \\ add x7, xzr, x8, lsr #0 + \\ add x9, xzr, xzr, lsr #62 + \\ add xzr, x10, x11, lsr #63 + \\ add xzr, x12, xzr, asr #0x0 + \\ add xzr, xzr, x13, asr #0x1F + \\ add xzr, xzr, xzr, asr #0x3f + \\ + \\ sub w0, w0, w1 + \\ sub w2, w3, w4 + \\ sub wsp, w5, w6 + \\ sub w7, wsp, w8 + \\ sub wsp, wsp, w9 + \\ sub w10, w10, wzr + \\ sub w11, w12, wzr + \\ sub wsp, w13, wzr + \\ sub w14, wsp, wzr + \\ sub wsp, wsp, wzr + \\ + \\ sub x0, x0, x1 + \\ sub x2, x3, x4 + \\ sub sp, x5, x6 + \\ sub x7, sp, x8 + \\ sub sp, sp, x9 + \\ sub x10, x10, xzr + \\ sub x11, x12, xzr + \\ sub sp, x13, xzr + \\ sub x14, sp, xzr + \\ sub sp, sp, xzr + \\ + \\ sub w0, w0, w1 + \\ sub w2, w3, w4, uxtb #0 + \\ sub wsp, w5, w6, uxth #1 + \\ sub w7, wsp, w8, uxtw #0 + \\ sub wsp, wsp, w9, uxtw #2 + \\ sub w10, w10, wzr, uxtw #3 + \\ sub w11, w12, wzr, sxtb #4 + \\ sub wsp, w13, wzr, sxth #0 + \\ sub w14, wsp, wzr, sxtw #1 + \\ sub wsp, wsp, wzr, sxtw #2 + \\ + \\ sub x0, x0, x1 + \\ sub x2, x3, w4, uxtb #0 + \\ sub sp, x5, w6, uxth #1 + \\ sub x7, sp, w8, uxtw #2 + \\ sub sp, sp, x9, uxtx #0 + \\ sub x10, x10, xzr, uxtx #3 + \\ sub x11, x12, wzr, sxtb #4 + \\ sub sp, x13, wzr, sxth #0 + \\ sub x14, sp, wzr, sxtw #1 + \\ sub sp, sp, xzr, sxtx #2 + \\ + \\ sub w0, w0, #0 + \\ sub w0, w1, #1, lsl #0 + \\ sub wsp, w2, #2, lsl #12 + \\ sub w3, wsp, #3, lsl #0 + \\ sub wsp, wsp, #4095, lsl #12 + \\ sub w0, w1, #0 + \\ sub w2, w3, #0, lsl #0 + \\ sub w4, wsp, #0 + \\ sub w5, wsp, #0, lsl #0 + \\ sub wsp, w6, #0 + \\ sub wsp, w7, #0, lsl #0 + \\ sub wsp, wsp, #0 + \\ sub wsp, wsp, #0, lsl #0 + \\ + \\ sub x0, x0, #0 + \\ sub x0, x1, #1, lsl #0 + \\ sub sp, x2, #2, lsl #12 + \\ sub x3, sp, #3, lsl #0 + \\ sub sp, sp, #4095, lsl #12 + \\ sub x0, x1, #0 + \\ sub x2, x3, #0, lsl #0 + \\ sub x4, sp, #0 + \\ sub x5, sp, #0, lsl #0 + \\ sub sp, x6, #0 + \\ sub sp, x7, #0, lsl #0 + \\ sub sp, sp, #0 + \\ sub sp, sp, #0, lsl #0 + \\ + \\ sub w0, w0, w0 + \\ sub w1, w1, w2, lsl #0 + \\ sub w3, w4, w5, lsl #1 + \\ sub w6, w6, wzr, lsl #31 + \\ sub w7, wzr, w8, lsr #0 + \\ sub w9, wzr, wzr, lsr #30 + \\ sub wzr, w10, w11, lsr #31 + \\ sub wzr, w12, wzr, asr #0x0 + \\ sub wzr, wzr, w13, asr #0x10 + \\ sub wzr, wzr, wzr, asr #0x1f + \\ + \\ sub x0, x0, x0 + \\ sub x1, x1, x2, lsl #0 + \\ sub x3, x4, x5, lsl #1 + \\ sub x6, x6, xzr, lsl #63 + \\ sub x7, xzr, x8, lsr #0 + \\ sub x9, xzr, xzr, lsr #62 + \\ sub xzr, x10, x11, lsr #63 + \\ sub xzr, x12, xzr, asr #0x0 + \\ sub xzr, xzr, x13, asr #0x1F + \\ sub xzr, xzr, xzr, asr #0x3f + \\ + \\ neg w0, w0 + \\ neg w1, w2, lsl #0 + \\ neg w3, wzr, lsl #7 + \\ neg wzr, w4, lsr #14 + \\ neg wzr, wzr, asr #21 + \\ + \\ neg x0, x0 + \\ neg x1, x2, lsl #0 + \\ neg x3, xzr, lsl #11 + \\ neg xzr, x4, lsr #22 + \\ neg xzr, xzr, asr #33 + , + .operands = .empty, + }; + + try std.testing.expectFmt("add w0, w0, w1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w2, w3, w4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, w5, w6", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w7, wsp, w8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, wsp, w9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w10, w10, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w11, w12, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, w13, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w14, wsp, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, wsp, wzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add x0, x0, x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x2, x3, x4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, x5, x6", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x7, sp, x8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, sp, x9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x10, x10, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x11, x12, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, x13, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x14, sp, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, sp, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add w0, w0, w1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w2, w3, w4, uxtb #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, w5, w6, uxth #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w7, wsp, w8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, wsp, w9, uxtw #2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w10, w10, wzr, uxtw #3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w11, w12, wzr, sxtb #4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, w13, wzr, sxth #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w14, wsp, wzr, sxtw #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, wsp, wzr, sxtw #2", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add x0, x0, x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x2, x3, w4, uxtb #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, x5, w6, uxth #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x7, sp, w8, uxtw #2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, sp, x9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x10, x10, xzr, uxtx #3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x11, x12, wzr, sxtb #4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, x13, wzr, sxth #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x14, sp, wzr, sxtw #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, sp, xzr, sxtx #2", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add w0, w0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w0, w1, #0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, w2, #0x2, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w3, wsp, #0x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wsp, wsp, #0xfff, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w0, w1, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w2, w3, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w4, wsp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w5, wsp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wsp, w6", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wsp, w7", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wsp, wsp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wsp, wsp", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add x0, x0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x0, x1, #0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, x2, #0x2, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x3, sp, #0x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add sp, sp, #0xfff, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x0, x1, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x2, x3, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x4, sp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x5, sp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov sp, x6", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov sp, x7", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov sp, sp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov sp, sp", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add w0, w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w1, w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w3, w4, w5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w6, w6, wzr, lsl #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w7, wzr, w8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add w9, wzr, wzr, lsr #30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wzr, w10, w11, lsr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wzr, w12, wzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wzr, wzr, w13, asr #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add wzr, wzr, wzr, asr #31", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("add x0, x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x1, x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x3, x4, x5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x6, x6, xzr, lsl #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x7, xzr, x8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add x9, xzr, xzr, lsr #62", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add xzr, x10, x11, lsr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add xzr, x12, xzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add xzr, xzr, x13, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("add xzr, xzr, xzr, asr #63", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub w0, w0, w1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w2, w3, w4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w5, w6", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w7, wsp, w8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, w9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w10, w10, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w11, w12, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w13, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w14, wsp, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, wzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub x0, x0, x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x2, x3, x4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x5, x6", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x7, sp, x8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, x9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x10, x10, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x11, x12, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x13, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x14, sp, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub w0, w0, w1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w2, w3, w4, uxtb #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w5, w6, uxth #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w7, wsp, w8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, w9, uxtw #2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w10, w10, wzr, uxtw #3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w11, w12, wzr, sxtb #4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w13, wzr, sxth #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w14, wsp, wzr, sxtw #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, wzr, sxtw #2", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub x0, x0, x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x2, x3, w4, uxtb #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x5, w6, uxth #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x7, sp, w8, uxtw #2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, x9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x10, x10, xzr, uxtx #3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x11, x12, wzr, sxtb #4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x13, wzr, sxth #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x14, sp, wzr, sxtw #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, xzr, sxtx #2", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub w0, w0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w0, w1, #0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w2, #0x2, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w3, wsp, #0x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, #0xfff, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w0, w1, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w2, w3, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w4, wsp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w5, wsp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w6, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, w7, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wsp, wsp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub x0, x0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x0, x1, #0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x2, #0x2, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x3, sp, #0x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, #0xfff, lsl #12", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x0, x1, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x2, x3, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x4, sp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x5, sp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x6, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, x7, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub sp, sp, #0x0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub w0, w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w1, w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w3, w4, w5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub w6, w6, wzr, lsl #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg w7, w8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg w9, wzr, lsr #30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wzr, w10, w11, lsr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub wzr, w12, wzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg wzr, w13, asr #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg wzr, wzr, asr #31", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sub x0, x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x1, x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x3, x4, x5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub x6, x6, xzr, lsl #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg x7, x8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg x9, xzr, lsr #62", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub xzr, x10, x11, lsr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sub xzr, x12, xzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg xzr, x13, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg xzr, xzr, asr #63", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("neg w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg w3, wzr, lsl #7", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg wzr, w4, lsr #14", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg wzr, wzr, asr #21", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("neg x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg x3, xzr, lsl #11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg xzr, x4, lsr #22", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("neg xzr, xzr, asr #33", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "bitfield" { + var as: Assemble = .{ + .source = + \\sbfm w0, w0, #0, #31 + \\sbfm w0, w0, #31, #0 + \\ + \\sbfm x0, x0, #0, #63 + \\sbfm x0, x0, #63, #0 + \\ + \\bfm w0, w0, #0, #31 + \\bfm w0, w0, #31, #0 + \\ + \\bfm x0, x0, #0, #63 + \\bfm x0, x0, #63, #0 + \\ + \\ubfm w0, w0, #0, #31 + \\ubfm w0, w0, #31, #0 + \\ + \\ubfm x0, x0, #0, #63 + \\ubfm x0, x0, #63, #0 + , + .operands = .empty, + }; + + try std.testing.expectFmt("sbfm w0, w0, #0, #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sbfm w0, w0, #31, #0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("sbfm x0, x0, #0, #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sbfm x0, x0, #63, #0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("bfm w0, w0, #0, #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("bfm w0, w0, #31, #0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("bfm x0, x0, #0, #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("bfm x0, x0, #63, #0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ubfm w0, w0, #0, #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ubfm w0, w0, #31, #0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ubfm x0, x0, #0, #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ubfm x0, x0, #63, #0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "branch register" { + var as: Assemble = .{ + .source = + \\ret + \\br x30 + \\blr x30 + \\ret x30 + \\br x29 + \\blr x29 + \\ret x29 + \\br x2 + \\blr x1 + \\ret x0 + , + .operands = .empty, + }; + + try std.testing.expectFmt("ret", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("br x30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("blr x30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ret", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("br x29", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("blr x29", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ret x29", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("br x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("blr x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ret x0", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "exception generating" { + var as: Assemble = .{ + .source = + \\SVC #0 + \\HVC #0x1 + \\SMC #0o15 + \\BRK #42 + \\HLT #0x42 + \\TCANCEL #123 + \\DCPS1 #1234 + \\DCPS2 #12345 + \\DCPS3 #65535 + \\DCPS3 #0x0 + \\DCPS2 #0 + \\DCPS1 + , + .operands = .empty, + }; + + try std.testing.expectFmt("svc #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("hvc #0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("smc #0xd", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("brk #0x2a", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("hlt #0x42", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tcancel #0x7b", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("dcps1 #0x4d2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("dcps2 #0x3039", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("dcps3 #0xffff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("dcps3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("dcps2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("dcps1", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "extract" { + var as: Assemble = .{ + .source = + \\extr W0, W1, W2, #0 + \\extr W3, W3, W4, #1 + \\extr W5, W5, W5, #31 + \\ + \\extr X0, X1, X2, #0 + \\extr X3, X3, X4, #1 + \\extr X5, X5, X5, #63 + , + .operands = .empty, + }; + + try std.testing.expectFmt("extr w0, w1, w2, #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("extr w3, w3, w4, #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("extr w5, w5, w5, #31", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("extr x0, x1, x2, #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("extr x3, x3, x4, #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("extr x5, x5, x5, #63", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "hints" { + var as: Assemble = .{ + .source = + \\NOP + \\hint #0 + \\YiElD + \\Hint #0x1 + \\WfE + \\hInt #02 + \\wFi + \\hiNt #0b11 + \\sEv + \\hinT #4 + \\sevl + \\HINT #0b101 + \\hint #0x7F + , + .operands = .empty, + }; + + try std.testing.expectFmt("nop", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("nop", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("yield", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("yield", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("wfe", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("wfe", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("wfi", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("wfi", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sev", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sev", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sevl", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("sevl", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("hint #0x7f", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "load store" { + var as: Assemble = .{ + .source = + \\ LDP w0, w1, [x2], #-256 + \\ LDP w3, w4, [x5], #0 + \\ LDP w6, w7, [sp], #252 + \\ LDP w0, w1, [x2, #-0x100]! + \\ LDP w3, w4, [x5, #0]! + \\ LDP w6, w7, [sp, #0xfc]! + \\ LDP w0, w1, [x2, #-256] + \\ LDP w3, w4, [x5] + \\ LDP w6, w7, [x8, #0] + \\ LDP w9, w10, [sp, #252] + \\ + \\ LDP x0, x1, [x2], #-512 + \\ LDP x3, x4, [x5], #0 + \\ LDP x6, x7, [sp], #504 + \\ LDP x0, x1, [x2, #-0x200]! + \\ LDP x3, x4, [x5, #0]! + \\ LDP x6, x7, [sp, #0x1f8]! + \\ LDP x0, x1, [x2, #-512] + \\ LDP x3, x4, [x5] + \\ LDP x6, x7, [x8, #0] + \\ LDP x9, x10, [sp, #504] + \\ + \\ LDR w0, [x1], #-256 + \\ LDR w2, [x3], #0 + \\ LDR w4, [sp], #255 + \\ LDR w0, [x1, #-0x100]! + \\ LDR w2, [x3, #0]! + \\ LDR w4, [sp, #0xff]! + \\ LDR w0, [x1, #0] + \\ LDR w2, [x3] + \\ LDR w4, [sp, #16380] + \\ + \\ LDR x0, [x1], #-256 + \\ LDR x2, [x3], #0 + \\ LDR x4, [sp], #255 + \\ LDR x0, [x1, #-0x100]! + \\ LDR x2, [x3, #0]! + \\ LDR x4, [sp, #0xff]! + \\ LDR x0, [x1, #0] + \\ LDR x2, [x3] + \\ LDR x4, [sp, #32760] + \\ + \\ STP w0, w1, [x2], #-256 + \\ STP w3, w4, [x5], #0 + \\ STP w6, w7, [sp], #252 + \\ STP w0, w1, [x2, #-0x100]! + \\ STP w3, w4, [x5, #0]! + \\ STP w6, w7, [sp, #0xfc]! + \\ STP w0, w1, [x2, #-256] + \\ STP w3, w4, [x5] + \\ STP w6, w7, [x8, #0] + \\ STP w9, w10, [sp, #252] + \\ + \\ STP x0, x1, [x2], #-512 + \\ STP x3, x4, [x5], #0 + \\ STP x6, x7, [sp], #504 + \\ STP x0, x1, [x2, #-0x200]! + \\ STP x3, x4, [x5, #0]! + \\ STP x6, x7, [sp, #0x1f8]! + \\ STP x0, x1, [x2, #-512] + \\ STP x3, x4, [x5] + \\ STP x6, x7, [x8, #0] + \\ STP x9, x10, [sp, #504] + \\ + \\ STR w0, [x1], #-256 + \\ STR w2, [x3], #0 + \\ STR w4, [sp], #255 + \\ STR w0, [x1, #-0x100]! + \\ STR w2, [x3, #0]! + \\ STR w4, [sp, #0xff]! + \\ STR w0, [x1, #0] + \\ STR w2, [x3] + \\ STR w4, [sp, #16380] + \\ + \\ STR x0, [x1], #-256 + \\ STR x2, [x3], #0 + \\ STR x4, [sp], #255 + \\ STR x0, [x1, #-0x100]! + \\ STR x2, [x3, #0]! + \\ STR x4, [sp, #0xff]! + \\ STR x0, [x1, #0] + \\ STR x2, [x3] + \\ STR x4, [sp, #32760] + , + .operands = .empty, + }; + + try std.testing.expectFmt("ldp w0, w1, [x2], #-0x100", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w3, w4, [x5], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w6, w7, [sp], #0xfc", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w0, w1, [x2, #-0x100]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w3, w4, [x5, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w6, w7, [sp, #0xfc]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w0, w1, [x2, #-0x100]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w3, w4, [x5]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w6, w7, [x8]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp w9, w10, [sp, #0xfc]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ldp x0, x1, [x2], #-0x200", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x3, x4, [x5], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x6, x7, [sp], #0x1f8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x0, x1, [x2, #-0x200]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x3, x4, [x5, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x6, x7, [sp, #0x1f8]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x0, x1, [x2, #-0x200]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x3, x4, [x5]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x6, x7, [x8]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldp x9, x10, [sp, #0x1f8]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ldr w0, [x1], #-0x100", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w2, [x3], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w4, [sp], #0xff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w0, [x1, #-0x100]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w2, [x3, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w4, [sp, #0xff]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w0, [x1]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w2, [x3]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr w4, [sp, #0x3ffc]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ldr x0, [x1], #-0x100", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x2, [x3], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x4, [sp], #0xff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x0, [x1, #-0x100]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x2, [x3, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x4, [sp, #0xff]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x0, [x1]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x2, [x3]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ldr x4, [sp, #0x7ff8]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("stp w0, w1, [x2], #-0x100", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w3, w4, [x5], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w6, w7, [sp], #0xfc", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w0, w1, [x2, #-0x100]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w3, w4, [x5, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w6, w7, [sp, #0xfc]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w0, w1, [x2, #-0x100]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w3, w4, [x5]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w6, w7, [x8]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp w9, w10, [sp, #0xfc]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("stp x0, x1, [x2], #-0x200", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x3, x4, [x5], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x6, x7, [sp], #0x1f8", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x0, x1, [x2, #-0x200]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x3, x4, [x5, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x6, x7, [sp, #0x1f8]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x0, x1, [x2, #-0x200]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x3, x4, [x5]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x6, x7, [x8]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("stp x9, x10, [sp, #0x1f8]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("str w0, [x1], #-0x100", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w2, [x3], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w4, [sp], #0xff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w0, [x1, #-0x100]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w2, [x3, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w4, [sp, #0xff]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w0, [x1]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w2, [x3]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str w4, [sp, #0x3ffc]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("str x0, [x1], #-0x100", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x2, [x3], #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x4, [sp], #0xff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x0, [x1, #-0x100]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x2, [x3, #0x0]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x4, [sp, #0xff]!", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x0, [x1]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x2, [x3]", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("str x4, [sp, #0x7ff8]", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "logical" { + var as: Assemble = .{ + .source = + \\ and w0, w0, w0 + \\ and w1, w1, w2, lsl #0 + \\ and w3, w4, w5, lsl #1 + \\ and w6, w6, wzr, lsl #31 + \\ and w7, wzr, w8, lsr #0 + \\ and w9, wzr, wzr, lsr #30 + \\ and wzr, w10, w11, lsr #31 + \\ and wzr, w12, wzr, asr #0x0 + \\ and wzr, wzr, w13, asr #0x10 + \\ and wzr, wzr, wzr, asr #0x1f + \\ and w0, w0, wzr + \\ and w1, w2, wzr, lsl #0 + \\ and w3, wzr, w3 + \\ and w4, wzr, w5, lsl #0 + \\ and w6, wzr, wzr + \\ and w7, wzr, wzr, lsl #0 + \\ and wzr, w8, wzr + \\ and wzr, w9, wzr, lsl #0 + \\ and wzr, wzr, w10 + \\ and wzr, wzr, w11, lsl #0 + \\ and wzr, wzr, wzr + \\ and wzr, wzr, wzr, lsl #0 + \\ + \\ and x0, x0, x0 + \\ and x1, x1, x2, lsl #0 + \\ and x3, x4, x5, lsl #1 + \\ and x6, x6, xzr, lsl #63 + \\ and x7, xzr, x8, lsr #0 + \\ and x9, xzr, xzr, lsr #62 + \\ and xzr, x10, x11, lsr #63 + \\ and xzr, x12, xzr, asr #0x0 + \\ and xzr, xzr, x13, asr #0x1F + \\ and xzr, xzr, xzr, asr #0x3f + \\ and x0, x0, xzr + \\ and x1, x2, xzr, lsl #0 + \\ and x3, xzr, x3 + \\ and x4, xzr, x5, lsl #0 + \\ and x6, xzr, xzr + \\ and x7, xzr, xzr, lsl #0 + \\ and xzr, x8, xzr + \\ and xzr, x9, xzr, lsl #0 + \\ and xzr, xzr, x10 + \\ and xzr, xzr, x11, lsl #0 + \\ and xzr, xzr, xzr + \\ and xzr, xzr, xzr, lsl #0 + \\ + \\ orr w0, w0, w0 + \\ orr w1, w1, w2, lsl #0 + \\ orr w3, w4, w5, lsl #1 + \\ orr w6, w6, wzr, lsl #31 + \\ orr w7, wzr, w8, lsr #0 + \\ orr w9, wzr, wzr, lsr #30 + \\ orr wzr, w10, w11, lsr #31 + \\ orr wzr, w12, wzr, asr #0x0 + \\ orr wzr, wzr, w13, asr #0x10 + \\ orr wzr, wzr, wzr, asr #0x1f + \\ orr w0, w0, wzr + \\ orr w1, w2, wzr, lsl #0 + \\ orr w3, wzr, w3 + \\ orr w4, wzr, w5, lsl #0 + \\ orr w6, wzr, wzr + \\ orr w7, wzr, wzr, lsl #0 + \\ orr wzr, w8, wzr + \\ orr wzr, w9, wzr, lsl #0 + \\ orr wzr, wzr, w10 + \\ orr wzr, wzr, w11, lsl #0 + \\ orr wzr, wzr, wzr + \\ orr wzr, wzr, wzr, lsl #0 + \\ + \\ orr x0, x0, x0 + \\ orr x1, x1, x2, lsl #0 + \\ orr x3, x4, x5, lsl #1 + \\ orr x6, x6, xzr, lsl #63 + \\ orr x7, xzr, x8, lsr #0 + \\ orr x9, xzr, xzr, lsr #62 + \\ orr xzr, x10, x11, lsr #63 + \\ orr xzr, x12, xzr, asr #0x0 + \\ orr xzr, xzr, x13, asr #0x1F + \\ orr xzr, xzr, xzr, asr #0x3f + \\ orr x0, x0, xzr + \\ orr x1, x2, xzr, lsl #0 + \\ orr x3, xzr, x3 + \\ orr x4, xzr, x5, lsl #0 + \\ orr x6, xzr, xzr + \\ orr x7, xzr, xzr, lsl #0 + \\ orr xzr, x8, xzr + \\ orr xzr, x9, xzr, lsl #0 + \\ orr xzr, xzr, x10 + \\ orr xzr, xzr, x11, lsl #0 + \\ orr xzr, xzr, xzr + \\ orr xzr, xzr, xzr, lsl #0 + \\ + \\ eor w0, w0, w0 + \\ eor w1, w1, w2, lsl #0 + \\ eor w3, w4, w5, lsl #1 + \\ eor w6, w6, wzr, lsl #31 + \\ eor w7, wzr, w8, lsr #0 + \\ eor w9, wzr, wzr, lsr #30 + \\ eor wzr, w10, w11, lsr #31 + \\ eor wzr, w12, wzr, asr #0x0 + \\ eor wzr, wzr, w13, asr #0x10 + \\ eor wzr, wzr, wzr, asr #0x1f + \\ eor w0, w0, wzr + \\ eor w1, w2, wzr, lsl #0 + \\ eor w3, wzr, w3 + \\ eor w4, wzr, w5, lsl #0 + \\ eor w6, wzr, wzr + \\ eor w7, wzr, wzr, lsl #0 + \\ eor wzr, w8, wzr + \\ eor wzr, w9, wzr, lsl #0 + \\ eor wzr, wzr, w10 + \\ eor wzr, wzr, w11, lsl #0 + \\ eor wzr, wzr, wzr + \\ eor wzr, wzr, wzr, lsl #0 + \\ + \\ eor x0, x0, x0 + \\ eor x1, x1, x2, lsl #0 + \\ eor x3, x4, x5, lsl #1 + \\ eor x6, x6, xzr, lsl #63 + \\ eor x7, xzr, x8, lsr #0 + \\ eor x9, xzr, xzr, lsr #62 + \\ eor xzr, x10, x11, lsr #63 + \\ eor xzr, x12, xzr, asr #0x0 + \\ eor xzr, xzr, x13, asr #0x1F + \\ eor xzr, xzr, xzr, asr #0x3f + \\ eor x0, x0, xzr + \\ eor x1, x2, xzr, lsl #0 + \\ eor x3, xzr, x3 + \\ eor x4, xzr, x5, lsl #0 + \\ eor x6, xzr, xzr + \\ eor x7, xzr, xzr, lsl #0 + \\ eor xzr, x8, xzr + \\ eor xzr, x9, xzr, lsl #0 + \\ eor xzr, xzr, x10 + \\ eor xzr, xzr, x11, lsl #0 + \\ eor xzr, xzr, xzr + \\ eor xzr, xzr, xzr, lsl #0 + \\ + \\ ands w0, w0, w0 + \\ ands w1, w1, w2, lsl #0 + \\ ands w3, w4, w5, lsl #1 + \\ ands w6, w6, wzr, lsl #31 + \\ ands w7, wzr, w8, lsr #0 + \\ ands w9, wzr, wzr, lsr #30 + \\ ands wzr, w10, w11, lsr #31 + \\ ands wzr, w12, wzr, asr #0x0 + \\ ands wzr, wzr, w13, asr #0x10 + \\ ands wzr, wzr, wzr, asr #0x1f + \\ ands w0, w0, wzr + \\ ands w1, w2, wzr, lsl #0 + \\ ands w3, wzr, w3 + \\ ands w4, wzr, w5, lsl #0 + \\ ands w6, wzr, wzr + \\ ands w7, wzr, wzr, lsl #0 + \\ ands wzr, w8, wzr + \\ ands wzr, w9, wzr, lsl #0 + \\ ands wzr, wzr, w10 + \\ ands wzr, wzr, w11, lsl #0 + \\ ands wzr, wzr, wzr + \\ ands wzr, wzr, wzr, lsl #0 + \\ + \\ ands x0, x0, x0 + \\ ands x1, x1, x2, lsl #0 + \\ ands x3, x4, x5, lsl #1 + \\ ands x6, x6, xzr, lsl #63 + \\ ands x7, xzr, x8, lsr #0 + \\ ands x9, xzr, xzr, lsr #62 + \\ ands xzr, x10, x11, lsr #63 + \\ ands xzr, x12, xzr, asr #0x0 + \\ ands xzr, xzr, x13, asr #0x1F + \\ ands xzr, xzr, xzr, asr #0x3f + \\ ands x0, x0, xzr + \\ ands x1, x2, xzr, lsl #0 + \\ ands x3, xzr, x3 + \\ ands x4, xzr, x5, lsl #0 + \\ ands x6, xzr, xzr + \\ ands x7, xzr, xzr, lsl #0 + \\ ands xzr, x8, xzr + \\ ands xzr, x9, xzr, lsl #0 + \\ ands xzr, xzr, x10 + \\ ands xzr, xzr, x11, lsl #0 + \\ ands xzr, xzr, xzr + \\ ands xzr, xzr, xzr, lsl #0 + , + .operands = .empty, + }; + + try std.testing.expectFmt("and w0, w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w1, w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w3, w4, w5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w6, w6, wzr, lsl #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w7, wzr, w8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w9, wzr, wzr, lsr #30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, w10, w11, lsr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, w12, wzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, wzr, w13, asr #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, wzr, wzr, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w0, w0, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w1, w2, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w3, wzr, w3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w4, wzr, w5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w6, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and w7, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, w8, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, w9, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, wzr, w10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, wzr, w11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and wzr, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("and x0, x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x1, x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x3, x4, x5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x6, x6, xzr, lsl #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x7, xzr, x8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x9, xzr, xzr, lsr #62", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, x10, x11, lsr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, x12, xzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, xzr, x13, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, xzr, xzr, asr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x0, x0, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x1, x2, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x3, xzr, x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x4, xzr, x5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x6, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and x7, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, x8, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, x9, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, xzr, x10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, xzr, x11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("and xzr, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("orr w0, w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w1, w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w3, w4, w5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w6, w6, wzr, lsl #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w7, wzr, w8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w9, wzr, wzr, lsr #30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr wzr, w10, w11, lsr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr wzr, w12, wzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr wzr, wzr, w13, asr #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr wzr, wzr, wzr, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w0, w0, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr w1, w2, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w3, w3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w4, w5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w6, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w7, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr wzr, w8, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr wzr, w9, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, w10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, w11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("orr x0, x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x1, x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x3, x4, x5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x6, x6, xzr, lsl #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x7, xzr, x8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x9, xzr, xzr, lsr #62", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr xzr, x10, x11, lsr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr xzr, x12, xzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr xzr, xzr, x13, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr xzr, xzr, xzr, asr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x0, x0, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr x1, x2, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x3, x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x4, x5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x6, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x7, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr xzr, x8, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("orr xzr, x9, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, x10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, x11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("eor w0, w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w1, w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w3, w4, w5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w6, w6, wzr, lsl #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w7, wzr, w8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w9, wzr, wzr, lsr #30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, w10, w11, lsr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, w12, wzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, wzr, w13, asr #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, wzr, wzr, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w0, w0, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w1, w2, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w3, wzr, w3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w4, wzr, w5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w6, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor w7, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, w8, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, w9, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, wzr, w10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, wzr, w11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor wzr, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("eor x0, x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x1, x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x3, x4, x5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x6, x6, xzr, lsl #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x7, xzr, x8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x9, xzr, xzr, lsr #62", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, x10, x11, lsr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, x12, xzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, xzr, x13, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, xzr, xzr, asr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x0, x0, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x1, x2, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x3, xzr, x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x4, xzr, x5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x6, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor x7, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, x8, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, x9, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, xzr, x10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, xzr, x11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("eor xzr, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ands w0, w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w1, w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w3, w4, w5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w6, w6, wzr, lsl #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w7, wzr, w8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w9, wzr, wzr, lsr #30", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst w10, w11, lsr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst w12, wzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst wzr, w13, asr #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst wzr, wzr, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w0, w0, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w1, w2, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w3, wzr, w3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w4, wzr, w5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w6, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands w7, wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst w8, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst w9, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst wzr, w10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst wzr, w11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("ands x0, x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x1, x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x3, x4, x5, lsl #1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x6, x6, xzr, lsl #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x7, xzr, x8, lsr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x9, xzr, xzr, lsr #62", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst x10, x11, lsr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst x12, xzr, asr #0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst xzr, x13, asr #31", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst xzr, xzr, asr #63", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x0, x0, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x1, x2, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x3, xzr, x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x4, xzr, x5", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x6, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("ands x7, xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst x8, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst x9, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst xzr, x10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst xzr, x11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("tst xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "mov" { + var as: Assemble = .{ + .source = + \\MOV W0, #0 + \\MOV WZR, #0xffff + \\ + \\MOV X0, #0 + \\MOV XZR, #0xffff + \\ + \\MOV W0, WSP + \\MOV WSP, W1 + \\MOV WSP, WSP + \\MOV X0, SP + \\MOV SP, X1 + \\MOV SP, SP + \\ + \\MOV W0, W0 + \\MOV W1, W2 + \\MOV W3, WZR + \\MOV WZR, W4 + \\MOV WZR, WZR + \\MOV X0, X0 + \\MOV X1, X2 + \\MOV X3, XZR + \\MOV XZR, X4 + \\MOV XZR, XZR + \\ + \\MOVK W0, #0 + \\MOVK W1, #1, lsl #0 + \\MOVK W2, #2, lsl #16 + \\MOVK X3, #3 + \\MOVK X4, #4, lsl #0x00 + \\MOVK X5, #5, lsl #0x10 + \\MOVK X6, #6, lsl #0x20 + \\MOVK X7, #7, lsl #0x30 + \\ + \\MOVN W0, #8 + \\MOVN W1, #9, lsl #0 + \\MOVN W2, #10, lsl #16 + \\MOVN X3, #11 + \\MOVN X4, #12, lsl #0x00 + \\MOVN X5, #13, lsl #0x10 + \\MOVN X6, #14, lsl #0x20 + \\MOVN X7, #15, lsl #0x30 + \\ + \\MOVN WZR, #0, lsl #0 + \\MOVN WZR, #0, lsl #16 + \\MOVN XZR, #0, lsl #0 + \\MOVN XZR, #0, lsl #16 + \\MOVN XZR, #0, lsl #32 + \\MOVN XZR, #0, lsl #48 + \\ + \\MOVN WZR, #0xffff, lsl #0 + \\MOVN WZR, #0xffff, lsl #16 + \\MOVN XZR, #0xffff, lsl #0 + \\MOVN XZR, #0xffff, lsl #16 + \\MOVN XZR, #0xffff, lsl #32 + \\MOVN XZR, #0xffff, lsl #48 + \\ + \\MOVZ W0, #16 + \\MOVZ W1, #17, lsl #0 + \\MOVZ W2, #18, lsl #16 + \\MOVZ X3, #19 + \\MOVZ X4, #20, lsl #0x00 + \\MOVZ X5, #21, lsl #0x10 + \\MOVZ X6, #22, lsl #0x20 + \\MOVZ X7, #23, lsl #0x30 + \\ + \\MOVZ WZR, #0, lsl #0 + \\MOVZ WZR, #0, lsl #16 + \\MOVZ XZR, #0, lsl #0 + \\MOVZ XZR, #0, lsl #16 + \\MOVZ XZR, #0, lsl #32 + \\MOVZ XZR, #0, lsl #48 + \\ + \\MOVZ WZR, #0xffff, lsl #0 + \\MOVZ WZR, #0xffff, lsl #16 + \\MOVZ XZR, #0xffff, lsl #0 + \\MOVZ XZR, #0xffff, lsl #16 + \\MOVZ XZR, #0xffff, lsl #32 + \\MOVZ XZR, #0xffff, lsl #48 + , + .operands = .empty, + }; + + try std.testing.expectFmt("mov w0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, #0xffff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #0xffff", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov w0, wsp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wsp, w1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wsp, wsp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x0, sp", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov sp, x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov sp, sp", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov w0, w0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w1, w2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w3, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, w4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, wzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x0, x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x1, x2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x3, xzr", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, x4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, xzr", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("movk w0, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk w1, #0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk w2, #0x2, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk x3, #0x3", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk x4, #0x4", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk x5, #0x5, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk x6, #0x6, lsl #32", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movk x7, #0x7, lsl #48", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov w0, #-0x9", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w1, #-0xa", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w2, #-0xa0001", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x3, #-0xc", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x4, #-0xd", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x5, #-0xd0001", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x6, #-0xe00000001", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x7, #-0xf000000000001", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov wzr, #-0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movn wzr, #0x0, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #-0x1", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movn xzr, #0x0, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movn xzr, #0x0, lsl #32", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movn xzr, #0x0, lsl #48", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("movn wzr, #0xffff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movn wzr, #0xffff, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #-0x10000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #-0xffff0001", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #-0xffff00000001", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #0xffffffffffff", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov w0, #0x10", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w1, #0x11", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov w2, #0x120000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x3, #0x13", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x4, #0x14", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x5, #0x150000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x6, #0x1600000000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov x7, #0x17000000000000", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov wzr, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movz wzr, #0x0, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movz xzr, #0x0, lsl #16", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movz xzr, #0x0, lsl #32", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("movz xzr, #0x0, lsl #48", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expectFmt("mov wzr, #0xffff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov wzr, #-0x10000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #0xffff", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #0xffff0000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #0xffff00000000", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("mov xzr, #-0x1000000000000", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} +test "reserved" { + var as: Assemble = .{ + .source = "\n\nudf #0x0\n\t\n\tudf\t#01234\n \nudf#65535", + .operands = .empty, + }; + + try std.testing.expectFmt("udf #0x0", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("udf #0x4d2", "{f}", .{(try as.nextInstruction()).?}); + try std.testing.expectFmt("udf #0xffff", "{f}", .{(try as.nextInstruction()).?}); + + try std.testing.expect(null == try as.nextInstruction()); +} + +const aarch64 = @import("../aarch64.zig"); +const Assemble = @This(); +const assert = std.debug.assert; +const Instruction = aarch64.encoding.Instruction; +const instructions = @import("instructions.zon"); +const std = @import("std"); +const log = std.log.scoped(.@"asm"); diff --git a/src/codegen/aarch64/Disassemble.zig b/src/codegen/aarch64/Disassemble.zig new file mode 100644 index 0000000000..e3b4df93d4 --- /dev/null +++ b/src/codegen/aarch64/Disassemble.zig @@ -0,0 +1,905 @@ +case: Case = .lower, +mnemonic_operands_separator: []const u8 = " ", +operands_separator: []const u8 = ", ", +enable_aliases: bool = true, + +pub const Case = enum { lower, upper }; + +pub fn printInstruction(dis: Disassemble, inst: Instruction, writer: *std.Io.Writer) std.Io.Writer.Error!void { + unallocated: switch (inst.decode()) { + .unallocated => break :unallocated, + .reserved => |reserved| switch (reserved.decode()) { + .unallocated => break :unallocated, + .udf => |udf| return writer.print("{f}{s}#0x{x}", .{ + fmtCase(.udf, dis.case), + dis.mnemonic_operands_separator, + udf.imm16, + }), + }, + .sme => {}, + .sve => {}, + .data_processing_immediate => |data_processing_immediate| switch (data_processing_immediate.decode()) { + .unallocated => break :unallocated, + .pc_relative_addressing => |pc_relative_addressing| { + const group = pc_relative_addressing.group; + const imm = (@as(i33, group.immhi) << 2 | @as(i33, group.immlo) << 0) + @as(i33, switch (group.op) { + .adr => Instruction.size, + .adrp => 0, + }); + return writer.print("{f}{s}{f}{s}.{c}0x{x}", .{ + fmtCase(group.op, dis.case), + dis.mnemonic_operands_separator, + group.Rd.decodeInteger(.doubleword, .{}).fmtCase(dis.case), + dis.operands_separator, + @as(u8, if (imm < 0) '-' else '+'), + switch (group.op) { + .adr => @abs(imm), + .adrp => @abs(imm) << 12, + }, + }); + }, + .add_subtract_immediate => |add_subtract_immediate| { + const group = add_subtract_immediate.group; + const op = group.op; + const S = group.S; + const sf = group.sf; + const sh = group.sh; + const imm12 = group.imm12; + const Rn = group.Rn.decodeInteger(sf, .{ .sp = true }); + const Rd = group.Rd.decodeInteger(sf, .{ .sp = !S }); + const elide_shift = sh == .@"0"; + if (dis.enable_aliases and op == .add and S == false and elide_shift and imm12 == 0 and + (Rn.alias == .sp or Rd.alias == .sp)) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(.mov, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + }) else try writer.print("{f}{s}{s}{f}{s}{f}{s}#0x{x}", .{ + fmtCase(op, dis.case), + if (S) "s" else "", + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + imm12, + }); + return if (!elide_shift) writer.print("{s}{f} #{s}", .{ + dis.operands_separator, + fmtCase(.lsl, dis.case), + @tagName(sh), + }); + }, + .add_subtract_immediate_with_tags => {}, + .logical_immediate => |logical_immediate| { + const decoded = logical_immediate.decode(); + if (decoded == .unallocated) break :unallocated; + const group = logical_immediate.group; + const sf = group.sf; + const decoded_imm = group.imm.decodeImmediate(sf); + const imm = switch (sf) { + .word => @as(i32, @bitCast(@as(u32, @intCast(decoded_imm)))), + .doubleword => @as(i64, @bitCast(decoded_imm)), + }; + const Rn = group.Rn.decodeInteger(sf, .{}); + const Rd = group.Rd.decodeInteger(sf, .{ .sp = decoded != .ands }); + return if (dis.enable_aliases and decoded == .orr and Rn.alias == .zr and !group.imm.moveWidePreferred(sf)) writer.print("{f}{s}{f}{s}#{s}0x{x}", .{ + fmtCase(.mov, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + if (imm < 0) "-" else "", + @abs(imm), + }) else if (dis.enable_aliases and decoded == .ands and Rd.alias == .zr) writer.print("{f}{s}{f}{s}#{s}0x{x}", .{ + fmtCase(.tst, dis.case), + dis.mnemonic_operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + if (imm < 0) "-" else "", + @abs(imm), + }) else writer.print("{f}{s}{f}{s}{f}{s}#0x{x}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + decoded_imm, + }); + }, + .move_wide_immediate => |move_wide_immediate| { + const decoded = move_wide_immediate.decode(); + if (decoded == .unallocated) break :unallocated; + const group = move_wide_immediate.group; + const sf = group.sf; + const hw = group.hw; + const imm16 = group.imm16; + const Rd = group.Rd.decodeInteger(sf, .{}); + const elide_shift = hw == .@"0"; + if (dis.enable_aliases and switch (decoded) { + .unallocated => unreachable, + .movz => elide_shift or group.imm16 != 0, + .movn => (elide_shift or group.imm16 != 0) and switch (sf) { + .word => group.imm16 != std.math.maxInt(u16), + .doubleword => true, + }, + .movk => false, + }) { + const decoded_imm = switch (sf) { + .word => @as(i32, @bitCast(@as(u32, group.imm16) << @intCast(hw.int()))), + .doubleword => @as(i64, @bitCast(@as(u64, group.imm16) << hw.int())), + }; + const imm = switch (decoded) { + .unallocated => unreachable, + .movz => decoded_imm, + .movn => ~decoded_imm, + .movk => unreachable, + }; + return writer.print("{f}{s}{f}{s}#{s}0x{x}", .{ + fmtCase(.mov, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + if (imm < 0) "-" else "", + @abs(imm), + }); + } + try writer.print("{f}{s}{f}{s}#0x{x}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + imm16, + }); + return if (!elide_shift) writer.print("{s}{f} #{s}", .{ + dis.operands_separator, + fmtCase(.lsl, dis.case), + @tagName(hw), + }); + }, + .bitfield => |bitfield| { + const decoded = bitfield.decode(); + if (decoded == .unallocated) break :unallocated; + const group = bitfield.group; + const sf = group.sf; + return writer.print("{f}{s}{f}{s}{f}{s}#{d}{s}#{d}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rd.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.imm.immr, + dis.operands_separator, + group.imm.imms, + }); + }, + .extract => |extract| { + const decoded = extract.decode(); + if (decoded == .unallocated) break :unallocated; + const group = extract.group; + const sf = group.sf; + return writer.print("{f}{s}{f}{s}{f}{s}{f}{s}#{d}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rd.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rm.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.imms, + }); + }, + }, + .branch_exception_generating_system => |branch_exception_generating_system| switch (branch_exception_generating_system.decode()) { + .unallocated => break :unallocated, + .conditional_branch_immediate => |conditional_branch_immediate| { + const decoded = conditional_branch_immediate.decode(); + if (decoded == .unallocated) break :unallocated; + const group = conditional_branch_immediate.group; + const imm = @as(i21, group.imm19); + return writer.print("{f}.{f}{s}.{c}0x{x}", .{ + fmtCase(decoded, dis.case), + fmtCase(group.cond, dis.case), + dis.mnemonic_operands_separator, + @as(u8, if (imm < 0) '-' else '+'), + @abs(imm) << 2, + }); + }, + .exception_generating => |exception_generating| { + const decoded = exception_generating.decode(); + switch (decoded) { + .unallocated => break :unallocated, + .svc, .hvc, .smc, .brk, .hlt, .tcancel => {}, + .dcps1, .dcps2, .dcps3 => switch (exception_generating.group.imm16) { + 0 => return writer.print("{f}", .{fmtCase(decoded, dis.case)}), + else => {}, + }, + } + return switch (exception_generating.group.imm16) { + 0 => writer.print("{f}{s}#0", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + }), + else => writer.print("{f}{s}#0x{x}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + exception_generating.group.imm16, + }), + }; + }, + .system_register_argument => {}, + .hints => |hints| switch (hints.decode()) { + .hint => |hint| return writer.print("{f}{s}#0x{x}", .{ + fmtCase(.hint, dis.case), + dis.mnemonic_operands_separator, + @as(u7, hint.CRm) << 3 | @as(u7, hint.op2) << 0, + }), + else => |decoded| return writer.print("{f}", .{fmtCase(decoded, dis.case)}), + }, + .barriers => {}, + .pstate => {}, + .system_result => {}, + .system => {}, + .system_register_move => {}, + .unconditional_branch_register => |unconditional_branch_register| { + const decoded = unconditional_branch_register.decode(); + if (decoded == .unallocated) break :unallocated; + const group = unconditional_branch_register.group; + const Rn = group.Rn.decodeInteger(.doubleword, .{}); + try writer.print("{f}", .{fmtCase(decoded, dis.case)}); + return if (decoded != .ret or Rn.alias != .r30) try writer.print("{s}{f}", .{ + dis.mnemonic_operands_separator, + Rn.fmtCase(dis.case), + }); + }, + .unconditional_branch_immediate => |unconditional_branch_immediate| { + const group = unconditional_branch_immediate.group; + const imm = @as(i28, group.imm26); + return writer.print("{f}{s}.{c}0x{x}", .{ + fmtCase(group.op, dis.case), + dis.mnemonic_operands_separator, + @as(u8, if (imm < 0) '-' else '+'), + @abs(imm) << 2, + }); + }, + .compare_branch_immediate => |compare_branch_immediate| { + const group = compare_branch_immediate.group; + const imm = @as(i21, group.imm19); + return writer.print("{f}{s}{f}{s}.{c}0x{x}", .{ + fmtCase(group.op, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(group.sf, .{}).fmtCase(dis.case), + dis.operands_separator, + @as(u8, if (imm < 0) '-' else '+'), + @abs(imm) << 2, + }); + }, + .test_branch_immediate => |test_branch_immediate| { + const group = test_branch_immediate.group; + const imm = @as(i16, group.imm14); + return writer.print("{f}{s}{f}{s}#0x{d}{s}.{c}0x{x}", .{ + fmtCase(group.op, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(@enumFromInt(group.b5), .{}).fmtCase(dis.case), + dis.operands_separator, + @as(u6, group.b5) << 5 | + @as(u6, group.b40) << 0, + dis.operands_separator, + @as(u8, if (imm < 0) '-' else '+'), + @abs(imm) << 2, + }); + }, + }, + .load_store => |load_store| switch (load_store.decode()) { + .unallocated => break :unallocated, + .register_literal => {}, + .memory => {}, + .no_allocate_pair_offset => {}, + .register_pair_post_indexed => |register_pair_post_indexed| switch (register_pair_post_indexed.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + if (decoded == .unallocated) break :unallocated; + const group = integer.group; + const sf: aarch64.encoding.Register.IntegerSize = @enumFromInt(group.opc >> 1); + return writer.print("{f}{s}{f}{s}{f}{s}[{f}]{s}#{s}0x{x}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rt2.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm7 < 0) "-" else "", + @as(u10, @abs(group.imm7)) << (@as(u2, 2) + @intFromEnum(sf)), + }); + }, + .vector => |vector| { + const decoded = vector.decode(); + if (decoded == .unallocated) break :unallocated; + const group = vector.group; + const vs = group.opc.decode(); + return writer.print("{f}{s}{f}{s}{f}{s}[{f}]{s}#{s}0x{x}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeVector(vs).fmtCase(dis.case), + dis.operands_separator, + group.Rt2.decodeVector(vs).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm7 < 0) "-" else "", + @as(u11, @abs(group.imm7)) << (@as(u3, 2) + @intFromEnum(vs)), + }); + }, + }, + .register_pair_offset => |register_pair_offset| switch (register_pair_offset.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + if (decoded == .unallocated) break :unallocated; + const group = integer.group; + const sf: aarch64.encoding.Register.IntegerSize = @enumFromInt(group.opc >> 1); + try writer.print("{f}{s}{f}{s}{f}{s}[{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rt2.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + }); + if (group.imm7 != 0) try writer.print("{s}#{s}0x{x}", .{ + dis.operands_separator, + if (group.imm7 < 0) "-" else "", + @as(u10, @abs(group.imm7)) << (@as(u2, 2) + @intFromEnum(sf)), + }); + return writer.writeByte(']'); + }, + .vector => |vector| { + const decoded = vector.decode(); + if (decoded == .unallocated) break :unallocated; + const group = vector.group; + const vs = group.opc.decode(); + try writer.print("{f}{s}{f}{s}{f}{s}[{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeVector(vs).fmtCase(dis.case), + dis.operands_separator, + group.Rt2.decodeVector(vs).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + }); + if (group.imm7 != 0) try writer.print("{s}#{s}0x{x}", .{ + dis.operands_separator, + if (group.imm7 < 0) "-" else "", + @as(u11, @abs(group.imm7)) << (@as(u3, 2) + @intFromEnum(vs)), + }); + return writer.writeByte(']'); + }, + }, + .register_pair_pre_indexed => |register_pair_pre_indexed| switch (register_pair_pre_indexed.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + if (decoded == .unallocated) break :unallocated; + const group = integer.group; + const sf: aarch64.encoding.Register.IntegerSize = @enumFromInt(group.opc >> 1); + return writer.print("{f}{s}{f}{s}{f}{s}[{f}{s}#{s}0x{x}]!", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rt2.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm7 < 0) "-" else "", + @as(u10, @abs(group.imm7)) << (@as(u2, 2) + @intFromEnum(sf)), + }); + }, + .vector => |vector| { + const decoded = vector.decode(); + if (decoded == .unallocated) break :unallocated; + const group = vector.group; + const vs = group.opc.decode(); + return writer.print("{f}{s}{f}{s}{f}{s}[{f}{s}#{s}0x{x}]!", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeVector(vs).fmtCase(dis.case), + dis.operands_separator, + group.Rt2.decodeVector(vs).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm7 < 0) "-" else "", + @as(u11, @abs(group.imm7)) << (@as(u3, 2) + @intFromEnum(vs)), + }); + }, + }, + .register_unscaled_immediate => {}, + .register_immediate_post_indexed => |register_immediate_post_indexed| switch (register_immediate_post_indexed.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + const sf: aarch64.encoding.Register.IntegerSize = switch (decoded) { + .unallocated => break :unallocated, + .strb, .ldrb, .strh, .ldrh => .word, + inline .ldrsb, .ldrsh => |encoded| switch (encoded.opc0) { + 0b0 => .doubleword, + 0b1 => .word, + }, + .ldrsw => .doubleword, + inline .str, .ldr => |encoded| encoded.sf, + }; + const group = integer.group; + return writer.print("{f}{s}{f}{s}[{f}]{s}#{s}0x{x}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm9 < 0) "-" else "", + @abs(group.imm9), + }); + }, + .vector => {}, + }, + .register_unprivileged => {}, + .register_immediate_pre_indexed => |register_immediate_pre_indexed| switch (register_immediate_pre_indexed.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + const sf: aarch64.encoding.Register.IntegerSize = switch (decoded) { + .unallocated => break :unallocated, + inline .ldrsb, .ldrsh => |encoded| switch (encoded.opc0) { + 0b0 => .doubleword, + 0b1 => .word, + }, + .strb, .ldrb, .strh, .ldrh => .word, + .ldrsw => .doubleword, + inline .str, .ldr => |encoded| encoded.sf, + }; + const group = integer.group; + return writer.print("{f}{s}{f}{s}[{f}{s}#{s}0x{x}]!", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm9 < 0) "-" else "", + @abs(group.imm9), + }); + }, + .vector => |vector| { + const decoded = vector.decode(); + if (decoded == .unallocated) break :unallocated; + const group = vector.group; + return writer.print("{f}{s}{f}{s}[{f}{s}#{s}0x{x}]!", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeVector(group.opc1.decode(group.size)).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + if (group.imm9 < 0) "-" else "", + @abs(group.imm9), + }); + }, + }, + .register_register_offset => |register_register_offset| switch (register_register_offset.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + const sf: aarch64.encoding.Register.IntegerSize = switch (decoded) { + .unallocated, .prfm => break :unallocated, + .strb, .ldrb, .strh, .ldrh => .word, + inline .ldrsb, .ldrsh => |encoded| switch (encoded.opc0) { + 0b0 => .doubleword, + 0b1 => .word, + }, + .ldrsw => .doubleword, + inline .str, .ldr => |encoded| encoded.sf, + }; + const group = integer.group; + try writer.print("{f}{s}{f}{s}[{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + dis.operands_separator, + group.Rm.decodeInteger(group.option.sf(), .{}).fmtCase(dis.case), + }); + if (group.option != .lsl or group.S) { + try writer.print("{s}{f}", .{ + dis.operands_separator, + fmtCase(group.option, dis.case), + }); + if (group.S) try writer.print(" #{d}", .{ + @intFromEnum(group.size), + }); + } + return writer.writeByte(']'); + }, + .vector => {}, + }, + .register_unsigned_immediate => |register_unsigned_immediate| switch (register_unsigned_immediate.decode()) { + .integer => |integer| { + const decoded = integer.decode(); + const sf: aarch64.encoding.Register.IntegerSize = switch (decoded) { + .unallocated, .prfm => break :unallocated, + .strb, .ldrb, .strh, .ldrh => .word, + inline .ldrsb, .ldrsh => |encoded| switch (encoded.opc0) { + 0b0 => .doubleword, + 0b1 => .word, + }, + .ldrsw => .doubleword, + inline .str, .ldr => |encoded| encoded.sf, + }; + const group = integer.group; + try writer.print("{f}{s}{f}{s}[{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rt.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(.doubleword, .{ .sp = true }).fmtCase(dis.case), + }); + if (group.imm12 > 0) try writer.print("{s}#0x{x}", .{ + dis.operands_separator, + @as(u15, group.imm12) << @intFromEnum(group.size), + }); + return writer.writeByte(']'); + }, + .vector => {}, + }, + }, + .data_processing_register => |data_processing_register| switch (data_processing_register.decode()) { + .unallocated => break :unallocated, + .data_processing_two_source => |data_processing_two_source| { + const decoded = data_processing_two_source.decode(); + if (decoded == .unallocated) break :unallocated; + const group = data_processing_two_source.group; + const sf = group.sf; + return writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rd.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rm.decodeInteger(sf, .{}).fmtCase(dis.case), + }); + }, + .data_processing_one_source => |data_processing_one_source| { + const decoded = data_processing_one_source.decode(); + if (decoded == .unallocated) break :unallocated; + const group = data_processing_one_source.group; + const sf = group.sf; + return writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rd.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(sf, .{}).fmtCase(dis.case), + }); + }, + .logical_shifted_register => |logical_shifted_register| { + const decoded = logical_shifted_register.decode(); + if (decoded == .unallocated) break :unallocated; + const group = logical_shifted_register.group; + const sf = group.sf; + const shift = group.shift; + const Rm = group.Rm.decodeInteger(sf, .{}); + const amount = group.imm6; + const Rn = group.Rn.decodeInteger(sf, .{}); + const Rd = group.Rd.decodeInteger(sf, .{}); + const elide_shift = shift == .lsl and amount == 0; + if (dis.enable_aliases and switch (decoded) { + else => false, + .orr => elide_shift, + .orn => true, + } and Rn.alias == .zr) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { mov, mvn }, switch (decoded) { + else => unreachable, + .orr => .mov, + .orn => .mvn, + }), dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }) else if (dis.enable_aliases and decoded == .ands and Rd.alias == .zr) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(.tst, dis.case), + dis.mnemonic_operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }) else try writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }); + return if (!elide_shift) writer.print("{s}{f} #{d}", .{ + dis.operands_separator, + fmtCase(shift, dis.case), + amount, + }); + }, + .add_subtract_shifted_register => |add_subtract_shifted_register| { + const decoded = add_subtract_shifted_register.decode(); + if (decoded == .unallocated) break :unallocated; + const group = add_subtract_shifted_register.group; + const sf = group.sf; + const shift = group.shift; + const Rm = group.Rm.decodeInteger(sf, .{}); + const imm6 = group.imm6; + const Rn = group.Rn.decodeInteger(sf, .{}); + const Rd = group.Rd.decodeInteger(sf, .{}); + if (dis.enable_aliases and group.S and Rd.alias == .zr) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { cmn, cmp }, switch (group.op) { + .add => .cmn, + .sub => .cmp, + }), dis.case), + dis.mnemonic_operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }) else if (dis.enable_aliases and group.op == .sub and Rn.alias == .zr) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { neg, negs }, switch (group.S) { + false => .neg, + true => .negs, + }), dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }) else try writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }); + return if (shift != .lsl or imm6 != 0) return writer.print("{s}{f} #{d}", .{ + dis.operands_separator, + fmtCase(shift, dis.case), + imm6, + }); + }, + .add_subtract_extended_register => |add_subtract_extended_register| { + const decoded = add_subtract_extended_register.decode(); + if (decoded == .unallocated) break :unallocated; + const group = add_subtract_extended_register.group; + const sf = group.sf; + const Rm = group.Rm.decodeInteger(group.option.sf(), .{}); + const Rn = group.Rn.decodeInteger(sf, .{ .sp = true }); + const Rd = group.Rd.decodeInteger(sf, .{ .sp = true }); + if (dis.enable_aliases and group.S and Rd.alias == .zr) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { cmn, cmp }, switch (group.op) { + .add => .cmn, + .sub => .cmp, + }), dis.case), + dis.mnemonic_operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }) else try writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }); + return if (group.option != @as(Instruction.DataProcessingRegister.AddSubtractExtendedRegister.Option, switch (sf) { + .word => .uxtw, + .doubleword => .uxtx, + }) or group.imm3 != 0) writer.print("{s}{f} #{d}", .{ + dis.operands_separator, + fmtCase(group.option, dis.case), + group.imm3, + }); + }, + .add_subtract_with_carry => |add_subtract_with_carry| { + const decoded = add_subtract_with_carry.decode(); + const group = add_subtract_with_carry.group; + const sf = group.sf; + const Rm = group.Rm.decodeInteger(sf, .{}); + const Rn = group.Rn.decodeInteger(sf, .{}); + const Rd = group.Rd.decodeInteger(sf, .{}); + return if (dis.enable_aliases and group.op == .sbc and Rn.alias == .zr) try writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { ngc, ngcs }, switch (group.S) { + false => .ngc, + true => .ngcs, + }), dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }) else try writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + }); + }, + .rotate_right_into_flags => {}, + .evaluate_into_flags => {}, + .conditional_compare_register => {}, + .conditional_compare_immediate => {}, + .conditional_select => |conditional_select| { + const decoded = conditional_select.decode(); + if (decoded == .unallocated) break :unallocated; + const group = conditional_select.group; + const sf = group.sf; + const Rm = group.Rm.decodeInteger(sf, .{}); + const cond = group.cond; + const Rn = group.Rn.decodeInteger(sf, .{}); + const Rd = group.Rd.decodeInteger(sf, .{}); + return if (dis.enable_aliases and group.op != group.op2 and Rm.alias == .zr and cond != .al and cond != .nv and Rn.alias == Rm.alias) writer.print("{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { cset, csetm }, switch (decoded) { + else => unreachable, + .csinc => .cset, + .csinv => .csetm, + }), dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + fmtCase(cond.invert(), dis.case), + }) else if (dis.enable_aliases and decoded != .csel and cond != .al and cond != .nv and Rn.alias == Rm.alias) writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(@as(enum { cinc, cinv, cneg }, switch (decoded) { + else => unreachable, + .csinc => .cinc, + .csinv => .cinv, + .csneg => .cneg, + }), dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + fmtCase(cond.invert(), dis.case), + }) else writer.print("{f}{s}{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + Rd.fmtCase(dis.case), + dis.operands_separator, + Rn.fmtCase(dis.case), + dis.operands_separator, + Rm.fmtCase(dis.case), + dis.operands_separator, + fmtCase(cond, dis.case), + }); + }, + .data_processing_three_source => |data_processing_three_source| { + const decoded = data_processing_three_source.decode(); + if (decoded == .unallocated) break :unallocated; + const group = data_processing_three_source.group; + const sf = group.sf; + try writer.print("{f}{s}{f}{s}{f}{s}{f}", .{ + fmtCase(decoded, dis.case), + dis.mnemonic_operands_separator, + group.Rd.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rn.decodeInteger(sf, .{}).fmtCase(dis.case), + dis.operands_separator, + group.Rm.decodeInteger(sf, .{}).fmtCase(dis.case), + }); + return switch (decoded) { + .unallocated => unreachable, + .madd, .msub, .smaddl, .smsubl, .umaddl, .umsubl => writer.print("{s}{f}", .{ + dis.operands_separator, + group.Ra.decodeInteger(sf, .{}).fmtCase(dis.case), + }), + .smulh, .umulh => {}, + }; + }, + }, + .data_processing_vector => {}, + } + return writer.print(".{f}{s}0x{x:0>8}", .{ + fmtCase(.word, dis.case), + dis.mnemonic_operands_separator, + @as(Instruction.Backing, @bitCast(inst)), + }); +} + +fn fmtCase(tag: anytype, case: Case) struct { + tag: []const u8, + case: Case, + pub fn format(data: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + for (data.tag) |c| try writer.writeByte(switch (data.case) { + .lower => std.ascii.toLower(c), + .upper => std.ascii.toUpper(c), + }); + } +} { + return .{ .tag = @tagName(tag), .case = case }; +} + +pub const RegisterFormatter = struct { + reg: aarch64.encoding.Register, + case: Case, + pub fn format(data: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + switch (data.reg.format) { + .alias => try writer.print("{f}", .{fmtCase(data.reg.alias, data.case)}), + .integer => |size| switch (data.reg.alias) { + .r0, + .r1, + .r2, + .r3, + .r4, + .r5, + .r6, + .r7, + .r8, + .r9, + .r10, + .r11, + .r12, + .r13, + .r14, + .r15, + .r16, + .r17, + .r18, + .r19, + .r20, + .r21, + .r22, + .r23, + .r24, + .r25, + .r26, + .r27, + .r28, + .r29, + .r30, + => |alias| try writer.print("{c}{d}", .{ + size.prefix(), + @intFromEnum(alias.encode(.{})), + }), + .zr => try writer.print("{c}{f}", .{ + size.prefix(), + fmtCase(data.reg.alias, data.case), + }), + else => try writer.print("{s}{f}", .{ + switch (size) { + .word => "w", + .doubleword => "", + }, + fmtCase(data.reg.alias, data.case), + }), + }, + .scalar => |size| try writer.print("{c}{d}", .{ + size.prefix(), + @intFromEnum(data.reg.alias.encode(.{ .V = true })), + }), + .vector => |arrangement| try writer.print("{f}.{f}", .{ + fmtCase(data.reg.alias, data.case), + fmtCase(arrangement, data.case), + }), + .element => |element| try writer.print("{f}.{c}[{d}]", .{ + fmtCase(data.reg.alias, data.case), + element.size.prefix(), + element.index, + }), + } + } +}; + +const aarch64 = @import("../aarch64.zig"); +const Disassemble = @This(); +const Instruction = aarch64.encoding.Instruction; +const std = @import("std"); diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig new file mode 100644 index 0000000000..1446238888 --- /dev/null +++ b/src/codegen/aarch64/Mir.zig @@ -0,0 +1,275 @@ +prologue: []const Instruction, +body: []const Instruction, +epilogue: []const Instruction, +literals: []const u32, +nav_relocs: []const Reloc.Nav, +uav_relocs: []const Reloc.Uav, +global_relocs: []const Reloc.Global, +literal_relocs: []const Reloc.Literal, + +pub const Reloc = struct { + label: u32, + addend: u64 align(@alignOf(u32)) = 0, + + pub const Nav = struct { + nav: InternPool.Nav.Index, + reloc: Reloc, + }; + + pub const Uav = struct { + uav: InternPool.Key.Ptr.BaseAddr.Uav, + reloc: Reloc, + }; + + pub const Global = struct { + global: [*:0]const u8, + reloc: Reloc, + }; + + pub const Literal = struct { + label: u32, + }; +}; + +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + assert(mir.body.ptr + mir.body.len == mir.prologue.ptr); + assert(mir.prologue.ptr + mir.prologue.len == mir.epilogue.ptr); + gpa.free(mir.body.ptr[0 .. mir.body.len + mir.prologue.len + mir.epilogue.len]); + gpa.free(mir.literals); + gpa.free(mir.nav_relocs); + gpa.free(mir.uav_relocs); + gpa.free(mir.global_relocs); + gpa.free(mir.literal_relocs); + mir.* = undefined; +} + +pub fn emit( + mir: Mir, + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + code: *std.ArrayListUnmanaged(u8), + debug_output: link.File.DebugInfoOutput, +) !void { + _ = debug_output; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.gpa; + const func = zcu.funcInfo(func_index); + const nav = ip.getNav(func.owner_nav); + const mod = zcu.navFileScope(func.owner_nav).mod.?; + const target = &mod.resolved_target.result; + mir_log.debug("{f}:", .{nav.fqn.fmt(ip)}); + + const func_align = switch (nav.status.fully_resolved.alignment) { + .none => switch (mod.optimize_mode) { + .Debug, .ReleaseSafe, .ReleaseFast => target_util.defaultFunctionAlignment(target), + .ReleaseSmall => target_util.minFunctionAlignment(target), + }, + else => |a| a.maxStrict(target_util.minFunctionAlignment(target)), + }; + const code_len = mir.prologue.len + mir.body.len + mir.epilogue.len; + const literals_align_gap = -%code_len & (@divExact( + @as(u5, @intCast(func_align.minStrict(.@"16").toByteUnits().?)), + Instruction.size, + ) - 1); + try code.ensureUnusedCapacity(gpa, Instruction.size * + (code_len + literals_align_gap + mir.literals.len)); + emitInstructionsForward(code, mir.prologue); + emitInstructionsBackward(code, mir.body); + const body_end: u32 = @intCast(code.items.len); + emitInstructionsBackward(code, mir.epilogue); + code.appendNTimesAssumeCapacity(0, Instruction.size * literals_align_gap); + code.appendSliceAssumeCapacity(@ptrCast(mir.literals)); + mir_log.debug("", .{}); + + for (mir.nav_relocs) |nav_reloc| try emitReloc( + lf, + zcu, + func.owner_nav, + switch (try @import("../../codegen.zig").genNavRef( + lf, + pt, + src_loc, + nav_reloc.nav, + &mod.resolved_target.result, + )) { + .sym_index => |sym_index| sym_index, + .fail => |em| return zcu.codegenFailMsg(func.owner_nav, em), + }, + mir.body[nav_reloc.reloc.label], + body_end - Instruction.size * (1 + nav_reloc.reloc.label), + nav_reloc.reloc.addend, + ); + for (mir.uav_relocs) |uav_reloc| try emitReloc( + lf, + zcu, + func.owner_nav, + switch (try lf.lowerUav( + pt, + uav_reloc.uav.val, + ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu), + src_loc, + )) { + .sym_index => |sym_index| sym_index, + .fail => |em| return zcu.codegenFailMsg(func.owner_nav, em), + }, + mir.body[uav_reloc.reloc.label], + body_end - Instruction.size * (1 + uav_reloc.reloc.label), + uav_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) + else if (lf.cast(.macho)) |mf| + try mf.getGlobalSymbol(std.mem.span(global_reloc.global), null) + else if (lf.cast(.coff)) |cf| + try cf.getGlobalSymbol(std.mem.span(global_reloc.global), "compiler_rt") + else + return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}), + mir.body[global_reloc.reloc.label], + body_end - Instruction.size * (1 + global_reloc.reloc.label), + global_reloc.reloc.addend, + ); + const literal_reloc_offset: i19 = @intCast(mir.epilogue.len + literals_align_gap); + for (mir.literal_relocs) |literal_reloc| { + var instruction = mir.body[literal_reloc.label]; + instruction.load_store.register_literal.group.imm19 += literal_reloc_offset; + instruction.write( + code.items[body_end - Instruction.size * (1 + literal_reloc.label) ..][0..Instruction.size], + ); + } +} + +fn emitInstructionsForward(code: *std.ArrayListUnmanaged(u8), instructions: []const Instruction) void { + for (instructions) |instruction| emitInstruction(code, instruction); +} +fn emitInstructionsBackward(code: *std.ArrayListUnmanaged(u8), instructions: []const Instruction) void { + var instruction_index = instructions.len; + while (instruction_index > 0) { + instruction_index -= 1; + emitInstruction(code, instructions[instruction_index]); + } +} +fn emitInstruction(code: *std.ArrayListUnmanaged(u8), instruction: Instruction) void { + mir_log.debug(" {f}", .{instruction}); + instruction.write(code.addManyAsArrayAssumeCapacity(Instruction.size)); +} + +fn emitReloc( + lf: *link.File, + zcu: *Zcu, + owner_nav: InternPool.Nav.Index, + sym_index: u32, + instruction: Instruction, + offset: u32, + addend: u64, +) !void { + 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).?; + const r_type: std.elf.R_AARCH64 = switch (decoded.decode()) { + else => unreachable, + .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) { + .adr => .ADR_PREL_LO21, + .adrp => .ADR_PREL_PG_HI21, + }, + .add_subtract_immediate => |add_subtract_immediate| switch (add_subtract_immediate.group.op) { + .add => .ADD_ABS_LO12_NC, + .sub => 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).?; + switch (decoded.decode()) { + else => unreachable, + .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) { + .adr => unreachable, + .adrp => try atom.addReloc(mf, .{ + .tag = .@"extern", + .offset = offset, + .target = sym_index, + .addend = @bitCast(addend), + .type = .page, + .meta = .{ + .pcrel = true, + .has_subtractor = false, + .length = 2, + .symbolnum = @intCast(sym_index), + }, + }), + }, + .add_subtract_immediate => |add_subtract_immediate| switch (add_subtract_immediate.group.op) { + .add => 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), + }, + }), + .sub => unreachable, + }, + } + }, + } +} + +const Air = @import("../../Air.zig"); +const assert = std.debug.assert; +const mir_log = std.log.scoped(.mir); +const Instruction = @import("encoding.zig").Instruction; +const InternPool = @import("../../InternPool.zig"); +const link = @import("../../link.zig"); +const Mir = @This(); +const std = @import("std"); +const target_util = @import("../../target.zig"); +const Zcu = @import("../../Zcu.zig"); +const ZigType = @import("../../Type.zig"); diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig new file mode 100644 index 0000000000..10cf4f40c2 --- /dev/null +++ b/src/codegen/aarch64/Select.zig @@ -0,0 +1,10981 @@ +pt: Zcu.PerThread, +target: *const std.Target, +air: Air, +nav_index: InternPool.Nav.Index, + +// Blocks +def_order: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, void), +blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, Block), +loops: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, Loop), +active_loops: std.ArrayListUnmanaged(Loop.Index), +loop_live: struct { + set: std.AutoArrayHashMapUnmanaged(struct { Loop.Index, Air.Inst.Index }, void), + list: std.ArrayListUnmanaged(Air.Inst.Index), +}, +dom_start: u32, +dom_len: u32, +dom: std.ArrayListUnmanaged(DomInt), + +// Wip Mir +saved_registers: std.enums.EnumSet(Register.Alias), +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), +global_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Global), +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, +}, +stack_size: u24, +stack_align: InternPool.Alignment, + +// Value Tracking +live_registers: LiveRegisters, +live_values: std.AutoHashMapUnmanaged(Air.Inst.Index, Value.Index), +values: std.ArrayListUnmanaged(Value), + +pub const LiveRegisters = std.enums.EnumArray(Register.Alias, Value.Index); + +pub const Block = struct { + live_registers: LiveRegisters, + target_label: u32, + + pub const main: Air.Inst.Index = @enumFromInt( + 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))); + } + try isel.merge(&block.live_registers, .{}); + } +}; + +pub const Loop = struct { + def_order: u32, + dom: u32, + depth: u32, + live: u32, + live_registers: LiveRegisters, + repeat_list: u32, + + pub const invalid: Air.Inst.Index = @enumFromInt( + std.math.maxInt(@typeInfo(Air.Inst.Index).@"enum".tag_type), + ); + + pub const Index = enum(u32) { + _, + + fn inst(li: Loop.Index, isel: *Select) Air.Inst.Index { + return isel.loops.keys()[@intFromEnum(li)]; + } + + fn get(li: Loop.Index, isel: *Select) *Loop { + return &isel.loops.values()[@intFromEnum(li)]; + } + }; + + pub const empty_list: u32 = std.math.maxInt(u32); + + fn branch(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); + isel.instructions.appendAssumeCapacity(@bitCast(repeat_list_tail)); + try isel.merge(&loop.live_registers, .{}); + } +}; + +pub fn deinit(isel: *Select) void { + const gpa = isel.pt.zcu.gpa; + + isel.def_order.deinit(gpa); + isel.blocks.deinit(gpa); + isel.loops.deinit(gpa); + isel.active_loops.deinit(gpa); + isel.loop_live.set.deinit(gpa); + isel.loop_live.list.deinit(gpa); + isel.dom.deinit(gpa); + + isel.instructions.deinit(gpa); + isel.literals.deinit(gpa); + isel.nav_relocs.deinit(gpa); + isel.uav_relocs.deinit(gpa); + isel.global_relocs.deinit(gpa); + isel.literal_relocs.deinit(gpa); + + isel.live_values.deinit(gpa); + isel.values.deinit(gpa); + + isel.* = undefined; +} + +pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.gpa; + const air_tags = isel.air.instructions.items(.tag); + const air_data = isel.air.instructions.items(.data); + var air_body_index: usize = 0; + var air_inst_index = air_body[air_body_index]; + const initial_def_order_len = isel.def_order.count(); + air_tag: switch (air_tags[@intFromEnum(air_inst_index)]) { + .arg, + .ret_addr, + .frame_addr, + .err_return_trace, + .save_err_return_trace_index, + .runtime_nav_ptr, + .c_va_start, + => { + 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)]; + }, + .add, + .add_safe, + .add_optimized, + .add_wrap, + .add_sat, + .sub, + .sub_safe, + .sub_optimized, + .sub_wrap, + .sub_sat, + .mul, + .mul_safe, + .mul_optimized, + .mul_wrap, + .mul_sat, + .div_float, + .div_float_optimized, + .div_trunc, + .div_trunc_optimized, + .div_floor, + .div_floor_optimized, + .div_exact, + .div_exact_optimized, + .rem, + .rem_optimized, + .mod, + .mod_optimized, + .max, + .min, + .bit_and, + .bit_or, + .shr, + .shr_exact, + .shl, + .shl_exact, + .shl_sat, + .xor, + .cmp_lt, + .cmp_lt_optimized, + .cmp_lte, + .cmp_lte_optimized, + .cmp_eq, + .cmp_eq_optimized, + .cmp_gte, + .cmp_gte_optimized, + .cmp_gt, + .cmp_gt_optimized, + .cmp_neq, + .cmp_neq_optimized, + .bool_and, + .bool_or, + .array_elem_val, + .slice_elem_val, + .ptr_elem_val, + => { + const bin_op = air_data[@intFromEnum(air_inst_index)].bin_op; + + try isel.analyzeUse(bin_op.lhs); + try isel.analyzeUse(bin_op.rhs); + 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)]; + }, + .ptr_add, + .ptr_sub, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, + .slice, + .slice_elem_ptr, + .ptr_elem_ptr, + => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; + + try isel.analyzeUse(bin_op.lhs); + try isel.analyzeUse(bin_op.rhs); + 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)]; + }, + .alloc => { + const ty = air_data[@intFromEnum(air_inst_index)].ty; + + isel.stack_align = isel.stack_align.maxStrict(ty.ptrAlignment(zcu)); + 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)]; + }, + .inferred_alloc, + .inferred_alloc_comptime, + .wasm_memory_size, + .wasm_memory_grow, + .work_item_id, + .work_group_size, + .work_group_id, + => unreachable, + .ret_ptr => { + const ty = air_data[@intFromEnum(air_inst_index)].ty; + + if (isel.live_values.get(Block.main)) |ret_vi| switch (ret_vi.parent(isel)) { + .unallocated, .stack_slot => isel.stack_align = isel.stack_align.maxStrict(ty.ptrAlignment(zcu)), + .value, .constant => unreachable, + .address => |address_vi| try isel.live_values.putNoClobber(gpa, air_inst_index, address_vi.ref(isel)), + }; + 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)]; + }, + .assembly => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.Asm, ty_pl.payload); + const operands: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0 .. extra.data.flags.outputs_len + extra.data.inputs_len]); + + for (operands) |operand| if (operand != .none) try isel.analyzeUse(operand); + if (ty_pl.ty != .void_type) 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)]; + }, + .not, + .clz, + .ctz, + .popcount, + .byte_swap, + .bit_reverse, + .abs, + .load, + .fptrunc, + .fpext, + .intcast, + .intcast_safe, + .trunc, + .optional_payload, + .optional_payload_ptr, + .optional_payload_ptr_set, + .wrap_optional, + .unwrap_errunion_payload, + .unwrap_errunion_err, + .unwrap_errunion_payload_ptr, + .unwrap_errunion_err_ptr, + .errunion_payload_ptr_set, + .wrap_errunion_payload, + .wrap_errunion_err, + .struct_field_ptr_index_0, + .struct_field_ptr_index_1, + .struct_field_ptr_index_2, + .struct_field_ptr_index_3, + .get_union_tag, + .ptr_slice_len_ptr, + .ptr_slice_ptr_ptr, + .array_to_slice, + .int_from_float, + .int_from_float_optimized, + .int_from_float_safe, + .int_from_float_optimized_safe, + .float_from_int, + .splat, + .error_set_has_value, + .addrspace_cast, + .c_va_arg, + .c_va_copy, + => { + const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op; + + try isel.analyzeUse(ty_op.operand); + 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)]; + }, + .bitcast => { + const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op; + maybe_noop: { + if (ty_op.ty.toInterned().? != isel.air.typeOf(ty_op.operand, ip).toIntern()) break :maybe_noop; + if (true) break :maybe_noop; + if (ty_op.operand.toIndex()) |src_air_inst_index| { + if (isel.hints.get(src_air_inst_index)) |hint_vpsi| { + try isel.hints.putNoClobber(gpa, air_inst_index, hint_vpsi); + } + } + } + try isel.analyzeUse(ty_op.operand); + 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)]; + }, + inline .block, .dbg_inline_block => |air_tag| { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(switch (air_tag) { + else => comptime unreachable, + .block => Air.Block, + .dbg_inline_block => Air.DbgInlineBlock, + }, ty_pl.payload); + const result_ty = ty_pl.ty.toInterned().?; + + if (result_ty == .noreturn_type) { + try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + + air_body_index += 1; + break :air_tag; + } + + assert(!(try isel.blocks.getOrPut(gpa, air_inst_index)).found_existing); + try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + const block_entry = isel.blocks.pop().?; + assert(block_entry.key == air_inst_index); + + if (result_ty != .void_type) 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)]; + }, + .loop => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.Block, ty_pl.payload); + + const initial_dom_start = isel.dom_start; + const initial_dom_len = isel.dom_len; + isel.dom_start = @intCast(isel.dom.items.len); + isel.dom_len = @intCast(isel.blocks.count()); + try isel.active_loops.append(gpa, @enumFromInt(isel.loops.count())); + try isel.loops.putNoClobber(gpa, air_inst_index, .{ + .def_order = @intCast(isel.def_order.count()), + .dom = isel.dom_start, + .depth = isel.dom_len, + .live = 0, + .live_registers = undefined, + .repeat_list = undefined, + }); + try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable); + try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + for ( + isel.dom.items[initial_dom_start..].ptr, + isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable], + ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom; + isel.dom_start = initial_dom_start; + isel.dom_len = initial_dom_len; + assert(isel.active_loops.pop().?.inst(isel) == air_inst_index); + + air_body_index += 1; + }, + .repeat, .trap, .unreach => air_body_index += 1, + .br => { + const br = air_data[@intFromEnum(air_inst_index)].br; + const block_index = isel.blocks.getIndex(br.block_inst).?; + if (block_index < isel.dom_len) isel.dom.items[isel.dom_start + block_index / @bitSizeOf(DomInt)] |= @as(DomInt, 1) << @truncate(block_index); + try isel.analyzeUse(br.operand); + + air_body_index += 1; + }, + .breakpoint, + .dbg_stmt, + .dbg_empty_stmt, + .dbg_var_ptr, + .dbg_var_val, + .dbg_arg_inline, + => { + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + .call, + .call_always_tail, + .call_never_tail, + .call_never_inline, + => { + const pl_op = air_data[@intFromEnum(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]); + isel.saved_registers.insert(.lr); + + try isel.analyzeUse(pl_op.operand); + var param_it: CallAbiIterator = .init; + for (args) |arg| { + 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, + .value, .constant => unreachable, + .address => |address_vi| address_vi.parent(isel), + }) { + .unallocated => {}, + .stack_slot => |stack_slot| { + assert(stack_slot.base == .sp); + isel.stack_size = @max(isel.stack_size, stack_slot.offset); + }, + .value, .constant, .address => unreachable, + } + + try isel.analyzeUse(arg); + } + + var ret_it: CallAbiIterator = .init; + if (try ret_it.ret(isel, isel.air.typeOfIndex(air_inst_index, ip))) |ret_vi| { + tracking_log.debug("${d} <- %{d}", .{ @intFromEnum(ret_vi), @intFromEnum(air_inst_index) }); + switch (ret_vi.parent(isel)) { + .unallocated, .stack_slot => {}, + .value, .constant => unreachable, + .address => |address_vi| { + defer address_vi.deref(isel); + const ret_value = ret_vi.get(isel); + ret_value.flags.parent_tag = .unallocated; + ret_value.parent_payload = .{ .unallocated = {} }; + }, + } + try isel.live_values.putNoClobber(gpa, air_inst_index, ret_vi); + + 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)]; + }, + .sqrt, + .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .floor, + .ceil, + .round, + .trunc_float, + .neg, + .neg_optimized, + .is_null, + .is_non_null, + .is_null_ptr, + .is_non_null_ptr, + .is_err, + .is_non_err, + .is_err_ptr, + .is_non_err_ptr, + .is_named_enum_value, + .tag_name, + .error_name, + .cmp_lt_errors_len, + => { + const un_op = air_data[@intFromEnum(air_inst_index)].un_op; + + try isel.analyzeUse(un_op); + 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)]; + }, + .cmp_vector, .cmp_vector_optimized => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.VectorCmp, ty_pl.payload).data; + + try isel.analyzeUse(extra.lhs); + try isel.analyzeUse(extra.rhs); + 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)]; + }, + .cond_br => { + const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; + const extra = isel.air.extraData(Air.CondBr, pl_op.payload); + + try isel.analyzeUse(pl_op.operand); + + try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.then_body_len])); + try isel.analyze(@ptrCast(isel.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len])); + + air_body_index += 1; + }, + .switch_br => { + const switch_br = isel.air.unwrapSwitch(air_inst_index); + + try isel.analyzeUse(switch_br.operand); + + var cases_it = switch_br.iterateCases(); + while (cases_it.next()) |case| try isel.analyze(case.body); + if (switch_br.else_body_len > 0) try isel.analyze(cases_it.elseBody()); + + air_body_index += 1; + }, + .loop_switch_br => { + const switch_br = isel.air.unwrapSwitch(air_inst_index); + + const initial_dom_start = isel.dom_start; + const initial_dom_len = isel.dom_len; + isel.dom_start = @intCast(isel.dom.items.len); + isel.dom_len = @intCast(isel.blocks.count()); + try isel.active_loops.append(gpa, @enumFromInt(isel.loops.count())); + try isel.loops.putNoClobber(gpa, air_inst_index, .{ + .def_order = @intCast(isel.def_order.count()), + .dom = isel.dom_start, + .depth = isel.dom_len, + .live = 0, + .live_registers = undefined, + .repeat_list = undefined, + }); + try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable); + + var cases_it = switch_br.iterateCases(); + while (cases_it.next()) |case| try isel.analyze(case.body); + if (switch_br.else_body_len > 0) try isel.analyze(cases_it.elseBody()); + + for ( + isel.dom.items[initial_dom_start..].ptr, + isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable], + ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom; + isel.dom_start = initial_dom_start; + isel.dom_len = initial_dom_len; + assert(isel.active_loops.pop().?.inst(isel) == air_inst_index); + + air_body_index += 1; + }, + .switch_dispatch => { + const br = air_data[@intFromEnum(air_inst_index)].br; + + try isel.analyzeUse(br.operand); + + air_body_index += 1; + }, + .@"try", .try_cold, .try_ptr, .try_ptr_cold => { + const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; + const extra = isel.air.extraData(Air.Try, pl_op.payload); + + try isel.analyzeUse(pl_op.operand); + 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; + + const block_index = 0; + assert(isel.blocks.keys()[block_index] == Block.main); + if (isel.dom_len > 0) isel.dom.items[isel.dom_start] |= 1 << block_index; + + try isel.analyzeUse(un_op); + + air_body_index += 1; + }, + .store, + .store_safe, + .set_union_tag, + .memset, + .memset_safe, + .memcpy, + .memmove, + .atomic_store_unordered, + .atomic_store_monotonic, + .atomic_store_release, + .atomic_store_seq_cst, + => { + const bin_op = air_data[@intFromEnum(air_inst_index)].bin_op; + + try isel.analyzeUse(bin_op.lhs); + try isel.analyzeUse(bin_op.rhs); + + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + .struct_field_ptr, .struct_field_val => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.StructField, ty_pl.payload).data; + + try isel.analyzeUse(extra.struct_operand); + 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)]; + }, + .slice_len => { + const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op; + + try isel.analyzeUse(ty_op.operand); + try isel.def_order.putNoClobber(gpa, air_inst_index, {}); + + const slice_vi = try isel.use(ty_op.operand); + var len_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 8, 8); + if (try len_part_it.only(isel)) |len_part_vi| + try isel.live_values.putNoClobber(gpa, air_inst_index, len_part_vi.ref(isel)); + + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + .slice_ptr => { + const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op; + + try isel.analyzeUse(ty_op.operand); + try isel.def_order.putNoClobber(gpa, air_inst_index, {}); + + const slice_vi = try isel.use(ty_op.operand); + var ptr_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 0, 8); + if (try ptr_part_it.only(isel)) |ptr_part_vi| + try isel.live_values.putNoClobber(gpa, air_inst_index, ptr_part_vi.ref(isel)); + + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + .reduce, .reduce_optimized => { + const reduce = air_data[@intFromEnum(air_inst_index)].reduce; + + try isel.analyzeUse(reduce.operand); + 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)]; + }, + .shuffle_one => { + const extra = isel.air.unwrapShuffleOne(zcu, air_inst_index); + + try isel.analyzeUse(extra.operand); + 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)]; + }, + .shuffle_two => { + const extra = isel.air.unwrapShuffleTwo(zcu, air_inst_index); + + try isel.analyzeUse(extra.operand_a); + try isel.analyzeUse(extra.operand_b); + 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)]; + }, + .select, .mul_add => { + const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; + const bin_op = isel.air.extraData(Air.Bin, pl_op.payload).data; + + try isel.analyzeUse(pl_op.operand); + try isel.analyzeUse(bin_op.lhs); + try isel.analyzeUse(bin_op.rhs); + 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)]; + }, + .cmpxchg_weak, .cmpxchg_strong => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.Cmpxchg, ty_pl.payload).data; + + try isel.analyzeUse(extra.ptr); + try isel.analyzeUse(extra.expected_value); + try isel.analyzeUse(extra.new_value); + 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)]; + }, + .atomic_load => { + const atomic_load = air_data[@intFromEnum(air_inst_index)].atomic_load; + + try isel.analyzeUse(atomic_load.ptr); + 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)]; + }, + .atomic_rmw => { + const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; + const extra = isel.air.extraData(Air.AtomicRmw, pl_op.payload).data; + + try isel.analyzeUse(extra.operand); + 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)]; + }, + .aggregate_init => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const elements: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[ty_pl.payload..][0..@intCast(ty_pl.ty.toType().arrayLen(zcu))]); + + for (elements) |element| try isel.analyzeUse(element); + 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)]; + }, + .union_init => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.UnionInit, ty_pl.payload).data; + + try isel.analyzeUse(extra.init); + 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)]; + }, + .prefetch => { + const prefetch = air_data[@intFromEnum(air_inst_index)].prefetch; + + try isel.analyzeUse(prefetch.ptr); + + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + .field_parent_ptr => { + const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; + const extra = isel.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; + + try isel.analyzeUse(extra.field_ptr); + 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)]; + }, + .set_err_return_trace, .c_va_end => { + const un_op = air_data[@intFromEnum(air_inst_index)].un_op; + + try isel.analyzeUse(un_op); + + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + .vector_store_elem => { + const vector_store_elem = air_data[@intFromEnum(air_inst_index)].vector_store_elem; + const bin_op = isel.air.extraData(Air.Bin, vector_store_elem.payload).data; + + try isel.analyzeUse(vector_store_elem.vector_ptr); + try isel.analyzeUse(bin_op.lhs); + try isel.analyzeUse(bin_op.rhs); + + air_body_index += 1; + air_inst_index = air_body[air_body_index]; + continue :air_tag air_tags[@intFromEnum(air_inst_index)]; + }, + } + assert(air_body_index == air_body.len); + isel.def_order.shrinkRetainingCapacity(initial_def_order_len); +} + +fn analyzeUse(isel: *Select, air_ref: Air.Inst.Ref) !void { + const air_inst_index = air_ref.toIndex() orelse return; + const def_order_index = isel.def_order.getIndex(air_inst_index).?; + + // Loop liveness + var active_loop_index = isel.active_loops.items.len; + while (active_loop_index > 0) { + const prev_active_loop_index = active_loop_index - 1; + const active_loop = isel.active_loops.items[prev_active_loop_index]; + if (def_order_index >= active_loop.get(isel).def_order) break; + active_loop_index = prev_active_loop_index; + } + if (active_loop_index < isel.active_loops.items.len) { + const active_loop = isel.active_loops.items[active_loop_index]; + const loop_live_gop = + try isel.loop_live.set.getOrPut(isel.pt.zcu.gpa, .{ active_loop, air_inst_index }); + if (!loop_live_gop.found_existing) active_loop.get(isel).live += 1; + } +} + +pub fn finishAnalysis(isel: *Select) !void { + const gpa = isel.pt.zcu.gpa; + + // Loop Liveness + if (isel.loops.count() > 0) { + try isel.loops.ensureUnusedCapacity(gpa, 1); + + const loop_live_len: u32 = @intCast(isel.loop_live.set.count()); + if (loop_live_len > 0) { + try isel.loop_live.list.resize(gpa, loop_live_len); + + const loops = isel.loops.values(); + for (loops[1..], loops[0 .. loops.len - 1]) |*loop, prev_loop| loop.live += prev_loop.live; + assert(loops[loops.len - 1].live == loop_live_len); + + for (isel.loop_live.set.keys()) |entry| { + const loop, const inst = entry; + const loop_live = &loop.get(isel).live; + loop_live.* -= 1; + isel.loop_live.list.items[loop_live.*] = inst; + } + assert(loops[0].live == 0); + } + + const invalid_gop = isel.loops.getOrPutAssumeCapacity(Loop.invalid); + assert(!invalid_gop.found_existing); + invalid_gop.value_ptr.live = loop_live_len; + } +} + +pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.gpa; + + { + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) { + _ => { + const ra = &live_reg_entry.value.get(isel).location_payload.small.register; + assert(ra.* == live_reg_entry.key); + ra.* = .zr; + live_reg_entry.value.* = .free; + }, + .allocating => live_reg_entry.value.* = .free, + .free => {}, + }; + } + + var air: struct { + isel: *Select, + tag_items: []const Air.Inst.Tag, + data_items: []const Air.Inst.Data, + body: []const Air.Inst.Index, + body_index: u32, + inst_index: Air.Inst.Index, + + fn tag(it: *@This(), inst_index: Air.Inst.Index) Air.Inst.Tag { + return it.tag_items[@intFromEnum(inst_index)]; + } + + fn data(it: *@This(), inst_index: Air.Inst.Index) Air.Inst.Data { + return it.data_items[@intFromEnum(inst_index)]; + } + + fn next(it: *@This()) ?Air.Inst.Tag { + if (it.body_index == 0) { + @branchHint(.unlikely); + return null; + } + it.body_index -= 1; + it.inst_index = it.body[it.body_index]; + wip_mir_log.debug("{f}", .{it.fmtAir(it.inst_index)}); + return it.tag(it.inst_index); + } + + fn fmtAir(it: @This(), inst: Air.Inst.Index) struct { + isel: *Select, + inst: Air.Inst.Index, + pub fn format(fmt_air: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + fmt_air.isel.air.writeInst(writer, fmt_air.inst, fmt_air.isel.pt, null); + } + } { + return .{ .isel = it.isel, .inst = inst }; + } + } = .{ + .isel = isel, + .tag_items = isel.air.instructions.items(.tag), + .data_items = isel.air.instructions.items(.data), + .body = air_body, + .body_index = @intCast(air_body.len), + .inst_index = undefined, + }; + air_tag: switch (air.next().?) { + else => |air_tag| return isel.fail("unimplemented {s}", .{@tagName(air_tag)}), + .arg => { + const arg_vi = isel.live_values.fetchRemove(air.inst_index).?.value; + defer arg_vi.deref(isel); + switch (arg_vi.parent(isel)) { + .unallocated, .stack_slot => if (arg_vi.hint(isel)) |arg_ra| { + try arg_vi.defLiveIn(isel, arg_ra, comptime &.initFill(.free)); + } else { + var arg_part_it = arg_vi.parts(isel); + while (arg_part_it.next()) |arg_part| { + try arg_part.defLiveIn(isel, arg_part.hint(isel).?, comptime &.initFill(.free)); + } + }, + .value, .constant => unreachable, + .address => |address_vi| try address_vi.defLiveIn(isel, address_vi.hint(isel).?, comptime &.initFill(.free)), + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .add, .add_optimized, .add_wrap, .sub, .sub_optimized, .sub_wrap => |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.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)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + 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 lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) continue :bits 32 else switch (air_tag) { + else => unreachable, + .add, .add_optimized => .fadd(res_ra.h(), lhs_ra.h(), rhs_ra.h()), + .sub, .sub_optimized => .fsub(res_ra.h(), lhs_ra.h(), rhs_ra.h()), + }, + 32 => switch (air_tag) { + else => unreachable, + .add, .add_optimized => .fadd(res_ra.s(), lhs_ra.s(), rhs_ra.s()), + .sub, .sub_optimized => .fsub(res_ra.s(), lhs_ra.s(), rhs_ra.s()), + }, + 64 => switch (air_tag) { + else => unreachable, + .add, .add_optimized => .fadd(res_ra.d(), lhs_ra.d(), rhs_ra.d()), + .sub, .sub_optimized => .fsub(res_ra.d(), lhs_ra.d(), rhs_ra.d()), + }, + }); + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (air_tag) { + else => unreachable, + .add, .add_optimized => switch (bits) { + else => unreachable, + 16 => "__addhf3", + 32 => "__addsf3", + 64 => "__adddf3", + 80 => "__addxf3", + 128 => "__addtf3", + }, + .sub, .sub_optimized => switch (bits) { + else => unreachable, + 16 => "__subhf3", + 32 => "__subsf3", + 64 => "__subdf3", + 80 => "__subxf3", + 128 => "__subtf3", + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .add_sat, .sub_sat => |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.bits) { + 0 => unreachable, + 32, 64 => |bits| switch (int_info.signedness) { + .signed => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + .unsigned => { + 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 unsat_res_ra = try isel.allocIntReg(); + defer isel.freeReg(unsat_res_ra); + switch (air_tag) { + else => unreachable, + .add_sat => switch (bits) { + else => unreachable, + 32 => { + try isel.emit(.csinv(res_ra.w(), unsat_res_ra.w(), .wzr, .invert(.cs))); + try isel.emit(.adds(unsat_res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() })); + }, + 64 => { + try isel.emit(.csinv(res_ra.x(), unsat_res_ra.x(), .xzr, .invert(.cs))); + try isel.emit(.adds(unsat_res_ra.x(), lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() })); + }, + }, + .sub_sat => switch (bits) { + else => unreachable, + 32 => { + try isel.emit(.csel(res_ra.w(), unsat_res_ra.w(), .wzr, .invert(.cc))); + try isel.emit(.subs(unsat_res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() })); + }, + 64 => { + try isel.emit(.csel(res_ra.x(), unsat_res_ra.x(), .xzr, .invert(.cc))); + try isel.emit(.subs(unsat_res_ra.x(), lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() })); + }, + }, + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + }, + 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, .mul_optimized, .mul_wrap => |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.isRuntimeFloat()) { + 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.bits) { + 0 => unreachable, + 1 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + switch (int_info.signedness) { + .signed => switch (air_tag) { + else => unreachable, + .mul => break :unused try isel.emit(.orr(res_ra.w(), .wzr, .{ .register = .wzr })), + .mul_wrap => {}, + }, + .unsigned => {}, + } + 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...32 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + switch (air_tag) { + else => unreachable, + .mul => {}, + .mul_wrap => switch (bits) { + else => unreachable, + 1...31 => try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 32 => {}, + }, + } + 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.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr)); + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 33...64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + switch (air_tag) { + else => unreachable, + .mul => {}, + .mul_wrap => switch (bits) { + else => unreachable, + 33...63 => try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 64 => {}, + }, + } + 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)); + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 65...128 => |bits| { + var res_hi64_it = res_vi.value.field(ty, 8, 8); + const res_hi64_vi = try res_hi64_it.only(isel); + const res_hi64_ra = try res_hi64_vi.?.defReg(isel); + var res_lo64_it = res_vi.value.field(ty, 0, 8); + const res_lo64_vi = try res_lo64_it.only(isel); + const res_lo64_ra = try res_lo64_vi.?.defReg(isel); + if (res_hi64_ra == null and res_lo64_ra == null) break :unused; + if (res_hi64_ra) |res_ra| switch (air_tag) { + else => unreachable, + .mul => {}, + .mul_wrap => switch (bits) { + else => unreachable, + 65...127 => try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 128 => {}, + }, + }; + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + const lhs_lo64_mat, const rhs_lo64_mat = lo64_mat: { + const res_hi64_lock: RegLock = if (res_hi64_ra != null and res_lo64_ra != null) + isel.lockReg(res_hi64_ra.?) + else + .empty; + defer res_hi64_lock.unlock(isel); + + var rhs_lo64_it = rhs_vi.field(ty, 0, 8); + const rhs_lo64_vi = try rhs_lo64_it.only(isel); + const rhs_lo64_mat = try rhs_lo64_vi.?.matReg(isel); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + const lhs_lo64_mat = try lhs_lo64_vi.?.matReg(isel); + break :lo64_mat .{ lhs_lo64_mat, rhs_lo64_mat }; + }; + if (res_lo64_ra) |res_ra| try isel.emit(.madd(res_ra.x(), lhs_lo64_mat.ra.x(), rhs_lo64_mat.ra.x(), .xzr)); + if (res_hi64_ra) |res_ra| { + var rhs_hi64_it = rhs_vi.field(ty, 8, 8); + const rhs_hi64_vi = try rhs_hi64_it.only(isel); + const rhs_hi64_mat = try rhs_hi64_vi.?.matReg(isel); + var lhs_hi64_it = lhs_vi.field(ty, 8, 8); + const lhs_hi64_vi = try lhs_hi64_it.only(isel); + const lhs_hi64_mat = try lhs_hi64_vi.?.matReg(isel); + const acc_ra = try isel.allocIntReg(); + defer isel.freeReg(acc_ra); + try isel.emit(.madd(res_ra.x(), lhs_hi64_mat.ra.x(), rhs_lo64_mat.ra.x(), acc_ra.x())); + try isel.emit(.madd(acc_ra.x(), lhs_lo64_mat.ra.x(), rhs_hi64_mat.ra.x(), acc_ra.x())); + try isel.emit(.umulh(acc_ra.x(), lhs_lo64_mat.ra.x(), rhs_lo64_mat.ra.x())); + try rhs_hi64_mat.finish(isel); + try lhs_hi64_mat.finish(isel); + } + try rhs_lo64_mat.finish(isel); + try lhs_lo64_mat.finish(isel); + }, + else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + } + } else switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + 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 lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) + continue :bits 32 + else + .fmul(res_ra.h(), lhs_ra.h(), rhs_ra.h()), + 32 => .fmul(res_ra.s(), lhs_ra.s(), rhs_ra.s()), + 64 => .fmul(res_ra.d(), lhs_ra.d(), rhs_ra.d()), + }); + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__mulhf3", + 32 => "__mulsf3", + 64 => "__muldf3", + 80 => "__mulxf3", + 128 => "__multf3", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + 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); + + 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.bits) { + 0 => unreachable, + 1 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + switch (int_info.signedness) { + .signed => try isel.emit(.orr(res_ra.w(), .wzr, .{ .register = .wzr })), + .unsigned => { + 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...32 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const saturated_ra = switch (int_info.signedness) { + .signed => try isel.allocIntReg(), + .unsigned => switch (bits) { + else => unreachable, + 2...31 => try isel.allocIntReg(), + 32 => .zr, + }, + }; + defer if (saturated_ra != .zr) isel.freeReg(saturated_ra); + const unwrapped_ra = try isel.allocIntReg(); + defer isel.freeReg(unwrapped_ra); + try isel.emit(switch (saturated_ra) { + else => .csel(res_ra.w(), unwrapped_ra.w(), saturated_ra.w(), .eq), + .zr => .csinv(res_ra.w(), unwrapped_ra.w(), saturated_ra.w(), .eq), + }); + switch (bits) { + else => unreachable, + 2...7, 9...15, 17...31 => switch (int_info.signedness) { + .signed => { + const wrapped_ra = try isel.allocIntReg(); + defer isel.freeReg(wrapped_ra); + switch (bits) { + else => unreachable, + 1...7, 9...15 => { + try isel.emit(.subs(.wzr, unwrapped_ra.w(), .{ .register = wrapped_ra.w() })); + try isel.emit(.sbfm(wrapped_ra.w(), unwrapped_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + 17...31 => { + try isel.emit(.subs(.xzr, unwrapped_ra.x(), .{ .register = wrapped_ra.x() })); + try isel.emit(.sbfm(wrapped_ra.x(), unwrapped_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + } + }, + .unsigned => switch (bits) { + else => unreachable, + 1...7, 9...15 => try isel.emit(.ands(.wzr, unwrapped_ra.w(), .{ .immediate = .{ + .N = .word, + .immr = @intCast(32 - bits), + .imms = @intCast(32 - bits - 1), + } })), + 17...31 => try isel.emit(.ands(.xzr, unwrapped_ra.x(), .{ .immediate = .{ + .N = .doubleword, + .immr = @intCast(64 - bits), + .imms = @intCast(64 - bits - 1), + } })), + }, + }, + 8 => try isel.emit(.subs(.wzr, unwrapped_ra.w(), .{ .extended_register = .{ + .register = unwrapped_ra.w(), + .extend = switch (int_info.signedness) { + .signed => .{ .sxtb = 0 }, + .unsigned => .{ .uxtb = 0 }, + }, + } })), + 16 => try isel.emit(.subs(.wzr, unwrapped_ra.w(), .{ .extended_register = .{ + .register = unwrapped_ra.w(), + .extend = switch (int_info.signedness) { + .signed => .{ .sxth = 0 }, + .unsigned => .{ .uxth = 0 }, + }, + } })), + 32 => try isel.emit(.subs(.xzr, unwrapped_ra.x(), .{ .extended_register = .{ + .register = unwrapped_ra.w(), + .extend = switch (int_info.signedness) { + .signed => .{ .sxtw = 0 }, + .unsigned => .{ .uxtw = 0 }, + }, + } })), + } + 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); + switch (int_info.signedness) { + .signed => { + try isel.emit(.eor(saturated_ra.w(), saturated_ra.w(), .{ .immediate = .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1 - 1), + } })); + try isel.emit(.sbfm(saturated_ra.w(), saturated_ra.w(), .{ + .N = .word, + .immr = @intCast(bits - 1), + .imms = @intCast(bits - 1 + 1 - 1), + })); + try isel.emit(.eor(saturated_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() })); + }, + .unsigned => switch (bits) { + else => unreachable, + 2...31 => try isel.movImmediate(saturated_ra.w(), @as(u32, std.math.maxInt(u32)) >> @intCast(32 - bits)), + 32 => {}, + }, + } + switch (bits) { + else => unreachable, + 2...16 => try isel.emit(.madd(unwrapped_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr)), + 17...32 => switch (int_info.signedness) { + .signed => try isel.emit(.smaddl(unwrapped_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr)), + .unsigned => try isel.emit(.umaddl(unwrapped_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr)), + }, + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 33...64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const saturated_ra = switch (int_info.signedness) { + .signed => try isel.allocIntReg(), + .unsigned => switch (bits) { + else => unreachable, + 33...63 => try isel.allocIntReg(), + 64 => .zr, + }, + }; + defer if (saturated_ra != .zr) isel.freeReg(saturated_ra); + const unwrapped_lo64_ra = try isel.allocIntReg(); + defer isel.freeReg(unwrapped_lo64_ra); + const unwrapped_hi64_ra = try isel.allocIntReg(); + defer isel.freeReg(unwrapped_hi64_ra); + try isel.emit(switch (saturated_ra) { + else => .csel(res_ra.x(), unwrapped_lo64_ra.x(), saturated_ra.x(), .eq), + .zr => .csinv(res_ra.x(), unwrapped_lo64_ra.x(), saturated_ra.x(), .eq), + }); + switch (int_info.signedness) { + .signed => switch (bits) { + else => unreachable, + 32...63 => { + const wrapped_lo64_ra = try isel.allocIntReg(); + defer isel.freeReg(wrapped_lo64_ra); + try isel.emit(.ccmp( + unwrapped_lo64_ra.x(), + .{ .register = wrapped_lo64_ra.x() }, + .{ .n = false, .z = false, .c = false, .v = false }, + .eq, + )); + try isel.emit(.subs(.xzr, unwrapped_hi64_ra.x(), .{ .shifted_register = .{ + .register = unwrapped_lo64_ra.x(), + .shift = .{ .asr = 63 }, + } })); + try isel.emit(.sbfm(wrapped_lo64_ra.x(), unwrapped_lo64_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + 64 => try isel.emit(.subs(.xzr, unwrapped_hi64_ra.x(), .{ .shifted_register = .{ + .register = unwrapped_lo64_ra.x(), + .shift = .{ .asr = @intCast(bits - 1) }, + } })), + }, + .unsigned => switch (bits) { + else => unreachable, + 32...63 => { + const overflow_ra = try isel.allocIntReg(); + defer isel.freeReg(overflow_ra); + try isel.emit(.subs(.xzr, overflow_ra.x(), .{ .immediate = 0 })); + try isel.emit(.orr(overflow_ra.x(), unwrapped_hi64_ra.x(), .{ .shifted_register = .{ + .register = unwrapped_lo64_ra.x(), + .shift = .{ .lsr = @intCast(bits) }, + } })); + }, + 64 => try isel.emit(.subs(.xzr, unwrapped_hi64_ra.x(), .{ .immediate = 0 })), + }, + } + 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); + switch (int_info.signedness) { + .signed => { + try isel.emit(.eor(saturated_ra.x(), saturated_ra.x(), .{ .immediate = .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1 - 1), + } })); + try isel.emit(.sbfm(saturated_ra.x(), saturated_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(bits - 1), + .imms = @intCast(bits - 1 + 1 - 1), + })); + try isel.emit(.eor(saturated_ra.x(), lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() })); + try isel.emit(.madd(unwrapped_lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr)); + try isel.emit(.smulh(unwrapped_hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x())); + }, + .unsigned => { + switch (bits) { + else => unreachable, + 32...63 => try isel.movImmediate(saturated_ra.x(), @as(u64, std.math.maxInt(u64)) >> @intCast(64 - bits)), + 64 => {}, + } + try isel.emit(.madd(unwrapped_lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr)); + try isel.emit(.umulh(unwrapped_hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x())); + }, + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 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; + }, + .div_float, .div_float_optimized => { + 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); + switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + 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 lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) + continue :bits 32 + else + .fdiv(res_ra.h(), lhs_ra.h(), rhs_ra.h()), + 32 => .fdiv(res_ra.s(), lhs_ra.s(), rhs_ra.s()), + 64 => .fdiv(res_ra.d(), lhs_ra.d(), rhs_ra.d()), + }); + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__divhf3", + 32 => "__divsf3", + 64 => "__divdf3", + 80 => "__divxf3", + 128 => "__divtf3", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .div_trunc, .div_trunc_optimized, .div_floor, .div_floor_optimized, .div_exact, .div_exact_optimized => |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.isRuntimeFloat()) { + 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.bits) { + 0 => unreachable, + 1...64 => |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 div_ra = div_ra: switch (air_tag) { + else => unreachable, + .div_trunc, .div_exact => res_ra, + .div_floor => switch (int_info.signedness) { + .signed => { + const div_ra = try isel.allocIntReg(); + errdefer isel.freeReg(div_ra); + const rem_ra = try isel.allocIntReg(); + defer isel.freeReg(rem_ra); + 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(.ccmp( + rem_ra.w(), + .{ .immediate = 0 }, + .{ .n = false, .z = false, .c = false, .v = false }, + .ne, + )); + try isel.emit(.eor(rem_ra.w(), rem_ra.w(), .{ .register = rhs_mat.ra.w() })); + try isel.emit(.subs(.wzr, rem_ra.w(), .{ .immediate = 0 })); + 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(.ccmp( + rem_ra.x(), + .{ .immediate = 0 }, + .{ .n = false, .z = false, .c = false, .v = false }, + .ne, + )); + try isel.emit(.eor(rem_ra.x(), rem_ra.x(), .{ .register = rhs_mat.ra.x() })); + try isel.emit(.subs(.xzr, rem_ra.x(), .{ .immediate = 0 })); + try isel.emit(.msub(rem_ra.x(), div_ra.x(), rhs_mat.ra.x(), lhs_mat.ra.x())); + }, + } + break :div_ra div_ra; + }, + .unsigned => res_ra, + }, + }; + defer if (div_ra != res_ra) isel.freeReg(div_ra); + try isel.emit(switch (bits) { + else => unreachable, + 1...32 => 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 => 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()), + }, + }); + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 65...128 => { + switch (air_tag) { + else => unreachable, + .div_trunc, .div_exact => {}, + .div_floor => switch (int_info.signedness) { + .signed => return isel.fail("unimplemented {s}", .{@tagName(air_tag)}), + .unsigned => {}, + }, + } + + try call.prepareReturn(isel); + var res_hi64_it = res_vi.value.field(ty, 8, 8); + const res_hi64_vi = try res_hi64_it.only(isel); + try call.returnLiveIn(isel, res_hi64_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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (int_info.signedness) { + .signed => "__divti3", + .unsigned => "__udivti3", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + var rhs_hi64_it = rhs_vi.field(ty, 8, 8); + const rhs_hi64_vi = try rhs_hi64_it.only(isel); + try call.paramLiveOut(isel, rhs_hi64_vi.?, .r3); + 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.?, .r2); + var lhs_hi64_it = lhs_vi.field(ty, 8, 8); + const lhs_hi64_vi = try lhs_hi64_it.only(isel); + try call.paramLiveOut(isel, lhs_hi64_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + try call.finishParams(isel); + }, + else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + } + } else switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + 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 lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) continue :bits 32 else { + switch (air_tag) { + else => unreachable, + .div_trunc, .div_trunc_optimized => try isel.emit(.frintz(res_ra.h(), res_ra.h())), + .div_floor, .div_floor_optimized => try isel.emit(.frintm(res_ra.h(), res_ra.h())), + .div_exact, .div_exact_optimized => {}, + } + try isel.emit(.fdiv(res_ra.h(), lhs_ra.h(), rhs_ra.h())); + }, + 32 => { + switch (air_tag) { + else => unreachable, + .div_trunc, .div_trunc_optimized => try isel.emit(.frintz(res_ra.s(), res_ra.s())), + .div_floor, .div_floor_optimized => try isel.emit(.frintm(res_ra.s(), res_ra.s())), + .div_exact, .div_exact_optimized => {}, + } + try isel.emit(.fdiv(res_ra.s(), lhs_ra.s(), rhs_ra.s())); + }, + 64 => { + switch (air_tag) { + else => unreachable, + .div_trunc, .div_trunc_optimized => try isel.emit(.frintz(res_ra.d(), res_ra.d())), + .div_floor, .div_floor_optimized => try isel.emit(.frintm(res_ra.d(), res_ra.d())), + .div_exact, .div_exact_optimized => {}, + } + try isel.emit(.fdiv(res_ra.d(), lhs_ra.d(), rhs_ra.d())); + }, + } + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + switch (air_tag) { + else => unreachable, + .div_trunc, .div_trunc_optimized => { + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__trunch", + 32 => "truncf", + 64 => "trunc", + 80 => "__truncx", + 128 => "truncq", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + }, + .div_floor, .div_floor_optimized => { + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__floorh", + 32 => "floorf", + 64 => "floor", + 80 => "__floorx", + 128 => "floorq", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + }, + .div_exact, .div_exact_optimized => {}, + } + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__divhf3", + 32 => "__divsf3", + 64 => "__divdf3", + 80 => "__divxf3", + 128 => "__divtf3", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .rem => |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.isRuntimeFloat()) { + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + const int_info = ty.intInfo(zcu); + if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + 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 div_ra = try isel.allocIntReg(); + defer isel.freeReg(div_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(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(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()), + }); + }, + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + } else { + const bits = ty.floatBits(isel.target); + + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__fmodh", + 32 => "fmodf", + 64 => "fmod", + 80 => "__fmodx", + 128 => "fmodq", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .ptr_add, .ptr_sub => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + + const ty_pl = air.data(air.inst_index).ty_pl; + const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; + const elem_size = ty_pl.ty.toType().elemType2(zcu).abiSize(zcu); + + const base_vi = try isel.use(bin_op.lhs); + var base_part_it = base_vi.field(ty_pl.ty.toType(), 0, 8); + const base_part_vi = try base_part_it.only(isel); + const base_part_mat = try base_part_vi.?.matReg(isel); + const index_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(res_ra, base_part_mat.ra, switch (air_tag) { + else => unreachable, + .ptr_add => .add, + .ptr_sub => .sub, + }, elem_size, index_vi); + try base_part_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .max, .min => |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.isRuntimeFloat()) { + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + const int_info = ty.intInfo(zcu); + if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + 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 cond: codegen.aarch64.encoding.ConditionCode = switch (air_tag) { + else => unreachable, + .max => switch (int_info.signedness) { + .signed => .ge, + .unsigned => .hs, + }, + .min => switch (int_info.signedness) { + .signed => .lt, + .unsigned => .lo, + }, + }; + switch (int_info.bits) { + else => unreachable, + 1...32 => { + try isel.emit(.csel(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), cond)); + try isel.emit(.subs(.wzr, lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() })); + }, + 33...64 => { + try isel.emit(.csel(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), cond)); + try isel.emit(.subs(.xzr, lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() })); + }, + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + } else switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + 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 lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) continue :bits 32 else switch (air_tag) { + else => unreachable, + .max => .fmaxnm(res_ra.h(), lhs_ra.h(), rhs_ra.h()), + .min => .fminnm(res_ra.h(), lhs_ra.h(), rhs_ra.h()), + }, + 32 => switch (air_tag) { + else => unreachable, + .max => .fmaxnm(res_ra.s(), lhs_ra.s(), rhs_ra.s()), + .min => .fminnm(res_ra.s(), lhs_ra.s(), rhs_ra.s()), + }, + 64 => switch (air_tag) { + else => unreachable, + .max => .fmaxnm(res_ra.d(), lhs_ra.d(), rhs_ra.d()), + .min => .fminnm(res_ra.d(), lhs_ra.d(), rhs_ra.d()), + }, + }); + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (air_tag) { + else => unreachable, + .max => switch (bits) { + else => unreachable, + 16 => "__fmaxh", + 32 => "fmaxf", + 64 => "fmax", + 80 => "__fmaxx", + 128 => "fmaxq", + }, + .min => switch (bits) { + else => unreachable, + 16 => "__fminh", + 32 => "fminf", + 64 => "fmin", + 80 => "__fminx", + 128 => "fminq", + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .add_with_overflow, .sub_with_overflow => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| { + defer res_vi.value.deref(isel); + + const ty_pl = air.data(air.inst_index).ty_pl; + const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; + const ty = isel.air.typeOf(bin_op.lhs, ip); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + const ty_size = lhs_vi.size(isel); + var overflow_it = res_vi.value.field(ty_pl.ty.toType(), ty_size, 1); + const overflow_vi = try overflow_it.only(isel); + var wrapped_it = res_vi.value.field(ty_pl.ty.toType(), 0, ty_size); + const wrapped_vi = try wrapped_it.only(isel); + try wrapped_vi.?.addOrSubtract(isel, ty, lhs_vi, switch (air_tag) { + else => unreachable, + .add_with_overflow => .add, + .sub_with_overflow => .sub, + }, rhs_vi, .{ .wrap = true, .overflow_ra = try overflow_vi.?.defReg(isel) orelse .zr }); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .alloc, .ret_ptr => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |ptr_vi| unused: { + defer ptr_vi.value.deref(isel); + switch (air_tag) { + else => unreachable, + .alloc => {}, + .ret_ptr => if (isel.live_values.get(Block.main)) |ret_vi| switch (ret_vi.parent(isel)) { + .unallocated, .stack_slot => {}, + .value, .constant => unreachable, + .address => break :unused, + }, + } + const ptr_ra = try ptr_vi.value.defReg(isel) orelse break :unused; + + const ty = air.data(air.inst_index).ty; + const slot_size = ty.childType(zcu).abiSize(zcu); + const slot_align = ty.ptrAlignment(zcu); + const slot_offset = slot_align.forward(isel.stack_size); + isel.stack_size = @intCast(slot_offset + slot_size); + const lo12: u12 = @truncate(slot_offset >> 0); + const hi12: u12 = @intCast(slot_offset >> 12); + if (hi12 > 0) try isel.emit(.add( + ptr_ra.x(), + if (lo12 > 0) ptr_ra.x() else .sp, + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0 or hi12 == 0) try isel.emit(.add(ptr_ra.x(), .sp, .{ .immediate = lo12 })); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .assembly => { + const ty_pl = air.data(air.inst_index).ty_pl; + const extra = isel.air.extraData(Air.Asm, ty_pl.payload); + var extra_index = extra.end; + const outputs: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra_index..][0..extra.data.flags.outputs_len]); + extra_index += outputs.len; + const inputs: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra_index..][0..extra.data.inputs_len]); + extra_index += inputs.len; + + var as: codegen.aarch64.Assemble = .{ + .source = undefined, + .operands = .empty, + }; + defer as.operands.deinit(gpa); + + for (outputs) |output| { + const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]); + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_index += (constraint.len + name.len + (2 + 3)) / 4; + + switch (output) { + else => return isel.fail("invalid constraint: '{s}'", .{constraint}), + .none => if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { + const output_reg = Register.parse(constraint["={".len .. constraint.len - "}".len]) orelse + return isel.fail("invalid constraint: '{s}'", .{constraint}); + const output_ra = output_reg.alias; + if (isel.live_values.fetchRemove(air.inst_index)) |output_vi| { + defer output_vi.value.deref(isel); + try output_vi.value.defLiveIn(isel, output_reg.alias, comptime &.initFill(.free)); + isel.freeReg(output_ra); + } + if (!std.mem.eql(u8, name, "_")) { + const operand_gop = try as.operands.getOrPut(gpa, name); + if (operand_gop.found_existing) return isel.fail("duplicate output name: '{s}'", .{name}); + operand_gop.value_ptr.* = .{ .register = switch (ty_pl.ty.toType().abiSize(zcu)) { + 0 => unreachable, + 1...4 => output_ra.w(), + 5...8 => output_ra.x(), + else => return isel.fail("too big output type: '{f}'", .{isel.fmtType(ty_pl.ty.toType())}), + } }; + } + } else if (std.mem.eql(u8, constraint, "=r")) { + const output_ra = if (isel.live_values.fetchRemove(air.inst_index)) |output_vi| output_ra: { + defer output_vi.value.deref(isel); + break :output_ra try output_vi.value.defReg(isel) orelse try isel.allocIntReg(); + } else try isel.allocIntReg(); + if (!std.mem.eql(u8, name, "_")) { + const operand_gop = try as.operands.getOrPut(gpa, name); + if (operand_gop.found_existing) return isel.fail("duplicate output name: '{s}'", .{name}); + operand_gop.value_ptr.* = .{ .register = switch (ty_pl.ty.toType().abiSize(zcu)) { + 0 => unreachable, + 1...4 => output_ra.w(), + 5...8 => output_ra.x(), + else => return isel.fail("too big output type: '{f}'", .{isel.fmtType(ty_pl.ty.toType())}), + } }; + } + } else return isel.fail("invalid constraint: '{s}'", .{constraint}), + } + } + + const input_mats = try gpa.alloc(Value.Materialize, inputs.len); + defer gpa.free(input_mats); + const inputs_extra_index = extra_index; + for (inputs, input_mats) |input, *input_mat| { + const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_index += (constraint.len + name.len + (2 + 3)) / 4; + + if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { + const input_reg = Register.parse(constraint["{".len .. constraint.len - "}".len]) orelse + return isel.fail("invalid constraint: '{s}'", .{constraint}); + input_mat.* = .{ .vi = try isel.use(input), .ra = input_reg.alias }; + if (!std.mem.eql(u8, name, "_")) { + const operand_gop = try as.operands.getOrPut(gpa, name); + if (operand_gop.found_existing) return isel.fail("duplicate input name: '{s}'", .{name}); + const input_ty = isel.air.typeOf(input, ip); + operand_gop.value_ptr.* = .{ .register = switch (input_ty.abiSize(zcu)) { + 0 => unreachable, + 1...4 => input_reg.alias.w(), + 5...8 => input_reg.alias.x(), + else => return isel.fail("too big input type: '{f}'", .{ + isel.fmtType(isel.air.typeOf(input, ip)), + }), + } }; + } + } else if (std.mem.eql(u8, constraint, "r")) { + const input_vi = try isel.use(input); + input_mat.* = try input_vi.matReg(isel); + if (!std.mem.eql(u8, name, "_")) { + const operand_gop = try as.operands.getOrPut(gpa, name); + if (operand_gop.found_existing) return isel.fail("duplicate input name: '{s}'", .{name}); + operand_gop.value_ptr.* = .{ .register = switch (input_vi.size(isel)) { + 0 => unreachable, + 1...4 => input_mat.ra.w(), + 5...8 => input_mat.ra.x(), + else => return isel.fail("too big input type: '{f}'", .{ + isel.fmtType(isel.air.typeOf(input, ip)), + }), + } }; + } + } else if (std.mem.eql(u8, name, "_")) { + input_mat.vi = try isel.use(input); + } else return isel.fail("invalid constraint: '{s}'", .{constraint}); + } + + const clobbers = ip.indexToKey(extra.data.clobbers).aggregate; + const clobbers_ty: ZigType = .fromInterned(clobbers.ty); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + switch (switch (clobbers.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }) { + else => unreachable, + .bool_false => continue, + .bool_true => {}, + } + const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + if (std.mem.eql(u8, clobber_name, "memory")) continue; + if (std.mem.eql(u8, clobber_name, "nzcv")) continue; + const clobber_reg = Register.parse(clobber_name) orelse + return isel.fail("unable to parse clobber: '{s}'", .{clobber_name}); + const live_vi = isel.live_registers.getPtr(clobber_reg.alias); + switch (live_vi.*) { + _ => {}, + .allocating => return isel.fail("clobbered twice: '{s}'", .{clobber_name}), + .free => live_vi.* = .allocating, + } + } + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + switch (switch (clobbers.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }) { + else => unreachable, + .bool_false => continue, + .bool_true => {}, + } + const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + if (std.mem.eql(u8, clobber_name, "memory")) continue; + if (std.mem.eql(u8, clobber_name, "nzcv")) continue; + const clobber_ra = Register.parse(clobber_name).?.alias; + const live_vi = isel.live_registers.getPtr(clobber_ra); + switch (live_vi.*) { + _ => { + if (!try isel.fill(clobber_ra)) + return isel.fail("unable to clobber: '{s}'", .{clobber_name}); + assert(live_vi.* == .free); + live_vi.* = .allocating; + }, + .allocating => {}, + .free => unreachable, + } + } + + as.source = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..])[0..extra.data.source_len :0]; + const asm_start = isel.instructions.items.len; + while (as.nextInstruction() catch |err| switch (err) { + error.InvalidSyntax => { + const remaining_source = std.mem.span(as.source); + return isel.fail("unable to assemble: '{s}'", .{std.mem.trim( + u8, + as.source[0 .. std.mem.indexOfScalar(u8, remaining_source, '\n') orelse remaining_source.len], + &std.ascii.whitespace, + )}); + }, + }) |instruction| try isel.emit(instruction); + std.mem.reverse(codegen.aarch64.encoding.Instruction, isel.instructions.items[asm_start..]); + + extra_index = inputs_extra_index; + for (input_mats) |input_mat| { + const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_index += (constraint.len + name.len + (2 + 3)) / 4; + + if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { + try input_mat.vi.liveOut(isel, input_mat.ra); + } else if (std.mem.eql(u8, constraint, "r")) { + try input_mat.finish(isel); + } else if (std.mem.eql(u8, name, "_")) { + try input_mat.vi.mat(isel); + } else unreachable; + } + + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + switch (switch (clobbers.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }) { + else => unreachable, + .bool_false => continue, + .bool_true => {}, + } + const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + if (std.mem.eql(u8, clobber_name, "memory")) continue; + if (std.mem.eql(u8, clobber_name, "cc")) continue; + isel.freeReg(Register.parse(clobber_name).?.alias); + } + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .bit_and, .bit_or, .xor, .bool_and, .bool_or => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| { + 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); + const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type) + .{ .signedness = .unsigned, .bits = 1 } + else if (ty.isAbiInt(zcu)) + ty.intInfo(zcu) + else + return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + var offset = res_vi.value.size(isel); + while (offset > 0) { + const size = @min(offset, 8); + offset -= size; + var res_part_it = res_vi.value.field(ty, offset, size); + const res_part_vi = try res_part_it.only(isel); + const res_part_ra = try res_part_vi.?.defReg(isel) orelse continue; + var lhs_part_it = lhs_vi.field(ty, offset, size); + const lhs_part_vi = try lhs_part_it.only(isel); + const lhs_part_mat = try lhs_part_vi.?.matReg(isel); + var rhs_part_it = rhs_vi.field(ty, offset, size); + const rhs_part_vi = try rhs_part_it.only(isel); + const rhs_part_mat = try rhs_part_vi.?.matReg(isel); + try isel.emit(switch (air_tag) { + else => unreachable, + .bit_and, .bool_and => switch (size) { + else => unreachable, + 1, 2, 4 => .@"and"(res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + 8 => .@"and"(res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + }, + .bit_or, .bool_or => switch (size) { + else => unreachable, + 1, 2, 4 => .orr(res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + 8 => .orr(res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + }, + .xor => switch (size) { + else => unreachable, + 1, 2, 4 => .eor(res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + 8 => .eor(res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + }, + }); + try rhs_part_mat.finish(isel); + try lhs_part_mat.finish(isel); + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .shr, .shr_exact, .shl, .shl_exact => |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.bits) { + 0 => unreachable, + 1...64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + switch (air_tag) { + else => unreachable, + .shr, .shr_exact, .shl_exact => {}, + .shl => switch (bits) { + else => unreachable, + 1...31 => try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 32 => {}, + 33...63 => try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 64 => {}, + }, + } + + 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(switch (air_tag) { + else => unreachable, + .shr, .shr_exact => switch (bits) { + else => unreachable, + 1...32 => switch (int_info.signedness) { + .signed => .asrv(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()), + .unsigned => .lsrv(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()), + }, + 33...64 => switch (int_info.signedness) { + .signed => .asrv(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()), + .unsigned => .lsrv(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()), + }, + }, + .shl, .shl_exact => switch (bits) { + else => unreachable, + 1...32 => .lslv(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()), + 33...64 => .lslv(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()), + }, + }); + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 65...128 => |bits| { + var res_hi64_it = res_vi.value.field(ty, 8, 8); + const res_hi64_vi = try res_hi64_it.only(isel); + const res_hi64_ra = try res_hi64_vi.?.defReg(isel); + var res_lo64_it = res_vi.value.field(ty, 0, 8); + const res_lo64_vi = try res_lo64_it.only(isel); + const res_lo64_ra = try res_lo64_vi.?.defReg(isel); + if (res_hi64_ra == null and res_lo64_ra == null) break :unused; + if (res_hi64_ra) |res_ra| switch (air_tag) { + else => unreachable, + .shr, .shr_exact, .shl_exact => {}, + .shl => switch (bits) { + else => unreachable, + 65...127 => try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 64 - 1), + }), + .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 64 - 1), + }), + }), + 128 => {}, + }, + }; + + const lhs_vi = try isel.use(bin_op.lhs); + const lhs_hi64_mat = lhs_hi64_mat: { + const res_lock: RegLock = switch (air_tag) { + else => unreachable, + .shr, .shr_exact => switch (int_info.signedness) { + .signed => if (res_lo64_ra) |res_ra| isel.lockReg(res_ra) else .empty, + .unsigned => .empty, + }, + .shl, .shl_exact => .empty, + }; + defer res_lock.unlock(isel); + var lhs_hi64_it = lhs_vi.field(ty, 8, 8); + const lhs_hi64_vi = try lhs_hi64_it.only(isel); + break :lhs_hi64_mat try lhs_hi64_vi.?.matReg(isel); + }; + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + const lhs_lo64_mat = try lhs_lo64_vi.?.matReg(isel); + const rhs_vi = try isel.use(bin_op.rhs); + const rhs_mat = try rhs_vi.matReg(isel); + const lo64_ra = lo64_ra: { + const res_lock: RegLock = switch (air_tag) { + else => unreachable, + .shr, .shr_exact => switch (int_info.signedness) { + .signed => if (res_lo64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty, + .unsigned => .empty, + }, + .shl, .shl_exact => if (res_hi64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty, + }; + defer res_lock.unlock(isel); + break :lo64_ra try isel.allocIntReg(); + }; + defer isel.freeReg(lo64_ra); + const hi64_ra = hi64_ra: { + const res_lock: RegLock = switch (air_tag) { + else => unreachable, + .shr, .shr_exact => if (res_lo64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty, + .shl, .shl_exact => .empty, + }; + defer res_lock.unlock(isel); + break :hi64_ra try isel.allocIntReg(); + }; + defer isel.freeReg(hi64_ra); + switch (air_tag) { + else => unreachable, + .shr, .shr_exact => { + if (res_hi64_ra) |res_ra| switch (int_info.signedness) { + .signed => { + try isel.emit(.csel(res_ra.x(), hi64_ra.x(), lo64_ra.x(), .eq)); + try isel.emit(.sbfm(lo64_ra.x(), lhs_hi64_mat.ra.x(), .{ + .N = .doubleword, + .immr = @intCast(bits - 64 - 1), + .imms = @intCast(bits - 64 - 1), + })); + }, + .unsigned => try isel.emit(.csel(res_ra.x(), hi64_ra.x(), .xzr, .eq)), + }; + if (res_lo64_ra) |res_ra| try isel.emit(.csel(res_ra.x(), lo64_ra.x(), hi64_ra.x(), .eq)); + switch (int_info.signedness) { + .signed => try isel.emit(.asrv(hi64_ra.x(), lhs_hi64_mat.ra.x(), rhs_mat.ra.x())), + .unsigned => try isel.emit(.lsrv(hi64_ra.x(), lhs_hi64_mat.ra.x(), rhs_mat.ra.x())), + } + }, + .shl, .shl_exact => { + if (res_lo64_ra) |res_ra| try isel.emit(.csel(res_ra.x(), lo64_ra.x(), .xzr, .eq)); + if (res_hi64_ra) |res_ra| try isel.emit(.csel(res_ra.x(), hi64_ra.x(), lo64_ra.x(), .eq)); + try isel.emit(.lslv(lo64_ra.x(), lhs_lo64_mat.ra.x(), rhs_mat.ra.x())); + }, + } + try isel.emit(.ands(.wzr, rhs_mat.ra.w(), .{ .immediate = .{ .N = .word, .immr = 32 - 6, .imms = 0 } })); + switch (air_tag) { + else => unreachable, + .shr, .shr_exact => if (res_lo64_ra) |_| { + try isel.emit(.orr( + lo64_ra.x(), + lo64_ra.x(), + .{ .shifted_register = .{ .register = hi64_ra.x(), .shift = .{ .lsl = 1 } } }, + )); + try isel.emit(.lslv(hi64_ra.x(), lhs_hi64_mat.ra.x(), hi64_ra.x())); + try isel.emit(.lsrv(lo64_ra.x(), lhs_lo64_mat.ra.x(), rhs_mat.ra.x())); + try isel.emit(.orn(hi64_ra.w(), .wzr, .{ .register = rhs_mat.ra.w() })); + }, + .shl, .shl_exact => if (res_hi64_ra) |_| { + try isel.emit(.orr( + hi64_ra.x(), + hi64_ra.x(), + .{ .shifted_register = .{ .register = lo64_ra.x(), .shift = .{ .lsr = 1 } } }, + )); + try isel.emit(.lsrv(lo64_ra.x(), lhs_lo64_mat.ra.x(), lo64_ra.x())); + try isel.emit(.lslv(hi64_ra.x(), lhs_hi64_mat.ra.x(), rhs_mat.ra.x())); + try isel.emit(.orn(lo64_ra.w(), .wzr, .{ .register = rhs_mat.ra.w() })); + }, + } + try rhs_mat.finish(isel); + try lhs_lo64_mat.finish(isel); + try lhs_hi64_mat.finish(isel); + break :unused; + }, + 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; + }, + .not => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = ty_op.ty.toType(); + const int_info: std.builtin.Type.Int = int_info: { + if (ty_op.ty == .bool_type) break :int_info .{ .signedness = .unsigned, .bits = 1 }; + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + break :int_info ty.intInfo(zcu); + }; + if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + const src_vi = try isel.use(ty_op.operand); + var offset = res_vi.value.size(isel); + while (offset > 0) { + const size = @min(offset, 8); + offset -= size; + var res_part_it = res_vi.value.field(ty, offset, size); + const res_part_vi = try res_part_it.only(isel); + const res_part_ra = try res_part_vi.?.defReg(isel) orelse continue; + var src_part_it = src_vi.field(ty, offset, size); + const src_part_vi = try src_part_it.only(isel); + const src_part_mat = try src_part_vi.?.matReg(isel); + try isel.emit(switch (int_info.signedness) { + .signed => switch (size) { + else => unreachable, + 1, 2, 4 => .orn(res_part_ra.w(), .wzr, .{ .register = src_part_mat.ra.w() }), + 8 => .orn(res_part_ra.x(), .xzr, .{ .register = src_part_mat.ra.x() }), + }, + .unsigned => switch (@min(int_info.bits - 8 * offset, 64)) { + else => unreachable, + 1...31 => |bits| .eor(res_part_ra.w(), src_part_mat.ra.w(), .{ .immediate = .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + } }), + 32 => .orn(res_part_ra.w(), .wzr, .{ .register = src_part_mat.ra.w() }), + 33...63 => |bits| .eor(res_part_ra.x(), src_part_mat.ra.x(), .{ .immediate = .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + } }), + 64 => .orn(res_part_ra.x(), .xzr, .{ .register = src_part_mat.ra.x() }), + }, + }); + try src_part_mat.finish(isel); + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .bitcast => |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_tag = dst_ty.zigTypeTag(zcu); + const src_ty = isel.air.typeOf(ty_op.operand, ip); + const src_tag = src_ty.zigTypeTag(zcu); + if (dst_ty.isAbiInt(zcu) and (src_tag == .bool or src_ty.isAbiInt(zcu))) { + const dst_int_info = dst_ty.intInfo(zcu); + const src_int_info: std.builtin.Type.Int = if (src_tag == .bool) .{ .signedness = undefined, .bits = 1 } else src_ty.intInfo(zcu); + assert(dst_int_info.bits == src_int_info.bits); + if (dst_tag != .@"struct" and src_tag != .@"struct" and src_tag != .bool and dst_int_info.signedness == src_int_info.signedness) { + try dst_vi.value.move(isel, ty_op.operand); + } else switch (dst_int_info.bits) { + 0 => unreachable, + 1...31 => |dst_bits| { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(switch (dst_int_info.signedness) { + .signed => .sbfm(dst_ra.w(), src_mat.ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(dst_bits - 1), + }), + .unsigned => .ubfm(dst_ra.w(), src_mat.ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(dst_bits - 1), + }), + }); + try src_mat.finish(isel); + }, + 32 => try dst_vi.value.move(isel, ty_op.operand), + 33...63 => |dst_bits| { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(switch (dst_int_info.signedness) { + .signed => .sbfm(dst_ra.x(), src_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(dst_bits - 1), + }), + .unsigned => .ubfm(dst_ra.x(), src_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(dst_bits - 1), + }), + }); + try src_mat.finish(isel); + }, + 64 => try dst_vi.value.move(isel, ty_op.operand), + 65...127 => |dst_bits| { + const src_vi = try isel.use(ty_op.operand); + var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi64_vi = try dst_hi64_it.only(isel); + if (try dst_hi64_vi.?.defReg(isel)) |dst_hi64_ra| { + var src_hi64_it = src_vi.field(src_ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + const src_hi64_mat = try src_hi64_vi.?.matReg(isel); + try isel.emit(switch (dst_int_info.signedness) { + .signed => .sbfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(dst_bits - 64 - 1), + }), + .unsigned => .ubfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(dst_bits - 64 - 1), + }), + }); + try src_hi64_mat.finish(isel); + } + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| { + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try src_lo64_vi.?.liveOut(isel, dst_lo64_ra); + } + }, + 128 => try dst_vi.value.move(isel, ty_op.operand), + else => return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + } + } else if ((dst_ty.isPtrAtRuntime(zcu) or dst_ty.isAbiInt(zcu)) and (src_ty.isPtrAtRuntime(zcu) or src_ty.isAbiInt(zcu))) { + try dst_vi.value.move(isel, ty_op.operand); + } else if (dst_ty.isSliceAtRuntime(zcu) and src_ty.isSliceAtRuntime(zcu)) { + try dst_vi.value.move(isel, ty_op.operand); + } else if (dst_tag == .error_union and src_tag == .error_union) { + assert(dst_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu) == + src_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu)); + if (dst_ty.errorUnionPayload(zcu).toIntern() == src_ty.errorUnionPayload(zcu).toIntern()) { + try dst_vi.value.move(isel, ty_op.operand); + } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else if (dst_tag == .float and src_tag == .float) { + assert(dst_ty.floatBits(isel.target) == src_ty.floatBits(isel.target)); + try dst_vi.value.move(isel, ty_op.operand); + } else if (dst_ty.isAbiInt(zcu) and src_tag == .float) { + const dst_int_info = dst_ty.intInfo(zcu); + assert(dst_int_info.bits == src_ty.floatBits(isel.target)); + switch (dst_int_info.bits) { + else => unreachable, + 16 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + switch (dst_int_info.signedness) { + .signed => try isel.emit(.smov(dst_ra.w(), src_mat.ra.@"h[]"(0))), + .unsigned => try isel.emit(if (isel.target.cpu.has(.aarch64, .fullfp16)) + .fmov(dst_ra.w(), .{ .register = src_mat.ra.h() }) + else + .umov(dst_ra.w(), src_mat.ra.@"h[]"(0))), + } + try src_mat.finish(isel); + }, + 32 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fmov(dst_ra.w(), .{ .register = src_mat.ra.s() })); + try src_mat.finish(isel); + }, + 64 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fmov(dst_ra.x(), .{ .register = src_mat.ra.d() })); + try src_mat.finish(isel); + }, + 80 => switch (dst_int_info.signedness) { + .signed => { + const src_vi = try isel.use(ty_op.operand); + var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi16_vi = try dst_hi16_it.only(isel); + if (try dst_hi16_vi.?.defReg(isel)) |dst_hi16_ra| { + var src_hi16_it = src_vi.field(src_ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + const src_hi16_mat = try src_hi16_vi.?.matReg(isel); + try isel.emit(.sbfm(dst_hi16_ra.x(), src_hi16_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = 16 - 1, + })); + try src_hi16_mat.finish(isel); + } + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| { + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try src_lo64_vi.?.liveOut(isel, dst_lo64_ra); + } + }, + else => try dst_vi.value.move(isel, ty_op.operand), + }, + 128 => { + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi64_vi = try dst_hi64_it.only(isel); + if (try dst_hi64_vi.?.defReg(isel)) |dst_hi64_ra| try isel.emit(.fmov(dst_hi64_ra.x(), .{ .register = src_mat.ra.@"d[]"(1) })); + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| try isel.emit(.fmov(dst_lo64_ra.x(), .{ .register = src_mat.ra.d() })); + try src_mat.finish(isel); + }, + } + } else if (dst_tag == .float and src_ty.isAbiInt(zcu)) { + const src_int_info = src_ty.intInfo(zcu); + assert(dst_ty.floatBits(isel.target) == src_int_info.bits); + switch (src_int_info.bits) { + else => unreachable, + 16 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fmov( + if (isel.target.cpu.has(.aarch64, .fullfp16)) dst_ra.h() else dst_ra.s(), + .{ .register = src_mat.ra.w() }, + )); + try src_mat.finish(isel); + }, + 32 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fmov(dst_ra.s(), .{ .register = src_mat.ra.w() })); + try src_mat.finish(isel); + }, + 64 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fmov(dst_ra.d(), .{ .register = src_mat.ra.x() })); + try src_mat.finish(isel); + }, + 80 => switch (src_int_info.signedness) { + .signed => { + const src_vi = try isel.use(ty_op.operand); + var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi16_vi = try dst_hi16_it.only(isel); + if (try dst_hi16_vi.?.defReg(isel)) |dst_hi16_ra| { + var src_hi16_it = src_vi.field(src_ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + const src_hi16_mat = try src_hi16_vi.?.matReg(isel); + try isel.emit(.ubfm(dst_hi16_ra.x(), src_hi16_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = 16 - 1, + })); + try src_hi16_mat.finish(isel); + } + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| { + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try src_lo64_vi.?.liveOut(isel, dst_lo64_ra); + } + }, + else => try dst_vi.value.move(isel, ty_op.operand), + }, + 128 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + var src_hi64_it = src_vi.field(src_ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + const src_hi64_mat = try src_hi64_vi.?.matReg(isel); + try isel.emit(.fmov(dst_ra.@"d[]"(1), .{ .register = src_hi64_mat.ra.x() })); + try src_hi64_mat.finish(isel); + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + const src_lo64_mat = try src_lo64_vi.?.matReg(isel); + try isel.emit(.fmov(dst_ra.d(), .{ .register = src_lo64_mat.ra.x() })); + try src_lo64_mat.finish(isel); + }, + } + } else if (dst_ty.isAbiInt(zcu) and src_tag == .array and src_ty.childType(zcu).isAbiInt(zcu)) { + const dst_int_info = dst_ty.intInfo(zcu); + const src_child_int_info = src_ty.childType(zcu).intInfo(zcu); + const src_len = src_ty.arrayLenIncludingSentinel(zcu); + 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 call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = "memcpy", + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(ty_op.operand); + try isel.movImmediate(.x2, src_child_size * src_len); + try call.paramAddress(isel, src_vi, .r1); + try call.paramAddress(isel, dst_vi.value, .r0); + try call.finishParams(isel); + } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else if (dst_tag == .array and dst_ty.childType(zcu).isAbiInt(zcu) and src_ty.isAbiInt(zcu)) { + const dst_child_int_info = dst_ty.childType(zcu).intInfo(zcu); + const src_int_info = src_ty.intInfo(zcu); + const dst_len = dst_ty.arrayLenIncludingSentinel(zcu); + 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 call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = "memcpy", + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(ty_op.operand); + try isel.movImmediate(.x2, dst_child_size * dst_len); + try call.paramAddress(isel, src_vi, .r1); + try call.paramAddress(isel, dst_vi.value, .r0); + try call.finishParams(isel); + } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + } else return isel.fail("bad {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; + }, + .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); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .loop => { + const ty_pl = air.data(air.inst_index).ty_pl; + const extra = isel.air.extraData(Air.Block, ty_pl.payload); + const loops = isel.loops.values(); + const loop_index = isel.loops.getIndex(air.inst_index).?; + const loop = &loops[loop_index]; + + tracking_log.debug("{f}", .{ + isel.fmtDom(air.inst_index, loop.dom, @intCast(isel.blocks.count())), + }); + tracking_log.debug("{f}", .{isel.fmtLoopLive(air.inst_index)}); + assert(loop.depth == isel.blocks.count()); + + if (false) { + // loops are dumb... + for (isel.loop_live.list.items[loop.live..loops[loop_index + 1].live]) |live_inst| { + const live_vi = try isel.use(live_inst.toRef()); + try live_vi.mat(isel); + } + + // IT'S DOM TIME!!! + for (isel.blocks.values(), 0..) |*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(); + while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) { + _ => |live_vi| try live_vi.mat(isel), + .allocating => unreachable, + .free => {}, + }; + } + } + + loop.live_registers = isel.live_registers; + loop.repeat_list = Loop.empty_list; + try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.merge(&loop.live_registers, .{ .fill_extra = true }); + + var repeat_label = loop.repeat_list; + assert(repeat_label != Loop.empty_list); + while (repeat_label != Loop.empty_list) { + const instruction = &isel.instructions.items[repeat_label]; + const next_repeat_label = instruction.*; + instruction.* = .b(-@as(i28, @intCast((isel.instructions.items.len - 1 - repeat_label) << 2))); + repeat_label = @bitCast(next_repeat_label); + } + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .repeat => { + const repeat = air.data(air.inst_index).repeat; + try isel.loops.getPtr(repeat.loop_inst).?.branch(isel); + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .br => { + const br = air.data(air.inst_index).br; + const block = isel.blocks.getPtr(br.block_inst).?; + try block.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; + }, + .trap => { + try isel.emit(.brk(0x1)); + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .breakpoint => { + try isel.emit(.brk(0xf000)); + 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); + const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]); + + try call.prepareReturn(isel); + const maybe_def_ret_vi = isel.live_values.fetchRemove(air.inst_index); + var maybe_ret_addr_vi: ?Value.Index = null; + if (maybe_def_ret_vi) |def_ret_vi| { + defer def_ret_vi.value.deref(isel); + + var ret_it: CallAbiIterator = .init; + const ret_vi = try ret_it.ret(isel, isel.air.typeOfIndex(air.inst_index, ip)); + defer ret_vi.?.deref(isel); + switch (ret_vi.?.parent(isel)) { + .unallocated, .stack_slot => if (ret_vi.?.hint(isel)) |ret_ra| { + try call.returnLiveIn(isel, def_ret_vi.value, ret_ra); + } else { + var def_ret_part_it = def_ret_vi.value.parts(isel); + var ret_part_it = ret_vi.?.parts(isel); + while (def_ret_part_it.next()) |ret_part_vi| { + try call.returnLiveIn(isel, ret_part_vi, ret_part_it.next().?.hint(isel).?); + } + }, + .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 call.finishReturn(isel); + + try call.prepareCallee(isel); + if (pl_op.operand.toInterned()) |ct_callee| { + try isel.nav_relocs.append(gpa, switch (ip.indexToKey(ct_callee)) { + else => unreachable, + inline .@"extern", .func => |func| .{ + .nav = func.owner_nav, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }, + .ptr => |ptr| .{ + .nav = ptr.base_addr.nav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }, + }); + try isel.emit(.bl(0)); + } else { + const callee_vi = try isel.use(pl_op.operand); + const callee_mat = try callee_vi.matReg(isel); + try isel.emit(.blr(callee_mat.ra.x())); + try callee_mat.finish(isel); + } + try call.finishCallee(isel); + + try call.prepareParams(isel); + if (maybe_ret_addr_vi) |ret_addr_vi| try call.paramAddress( + isel, + maybe_def_ret_vi.?.value, + 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; + 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; + }, + }; + 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(isel.pt.zcu.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).?); + } + } + } + try call.finishParams(isel); + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .clz => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = isel.air.typeOf(ty_op.operand, 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.bits) { + 0 => unreachable, + 1...64 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.clzLimb(res_ra, int_info, src_mat.ra); + try src_mat.finish(isel); + }, + 65...128 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + var src_hi64_it = src_vi.field(ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + const src_hi64_mat = try src_hi64_vi.?.matReg(isel); + var src_lo64_it = src_vi.field(ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + const src_lo64_mat = try src_lo64_vi.?.matReg(isel); + const lo64_ra = try isel.allocIntReg(); + defer isel.freeReg(lo64_ra); + const hi64_ra = try isel.allocIntReg(); + defer isel.freeReg(hi64_ra); + try isel.emit(.csel(res_ra.w(), lo64_ra.w(), hi64_ra.w(), .eq)); + try isel.emit(.add(lo64_ra.w(), lo64_ra.w(), .{ .immediate = @intCast(bits - 64) })); + try isel.emit(.subs(.xzr, src_hi64_mat.ra.x(), .{ .immediate = 0 })); + try isel.clzLimb(hi64_ra, .{ .signedness = int_info.signedness, .bits = bits - 64 }, src_hi64_mat.ra); + try isel.clzLimb(lo64_ra, .{ .signedness = .unsigned, .bits = 64 }, src_lo64_mat.ra); + try src_hi64_mat.finish(isel); + try src_lo64_mat.finish(isel); + }, + 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; + }, + .ctz => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = isel.air.typeOf(ty_op.operand, 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.bits) { + 0 => unreachable, + 1...64 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.ctzLimb(res_ra, int_info, src_mat.ra); + try src_mat.finish(isel); + }, + 65...128 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + var src_hi64_it = src_vi.field(ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + const src_hi64_mat = try src_hi64_vi.?.matReg(isel); + var src_lo64_it = src_vi.field(ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + const src_lo64_mat = try src_lo64_vi.?.matReg(isel); + const lo64_ra = try isel.allocIntReg(); + defer isel.freeReg(lo64_ra); + const hi64_ra = try isel.allocIntReg(); + defer isel.freeReg(hi64_ra); + try isel.emit(.csel(res_ra.w(), lo64_ra.w(), hi64_ra.w(), .ne)); + try isel.emit(.add(hi64_ra.w(), hi64_ra.w(), .{ .immediate = 64 })); + try isel.emit(.subs(.xzr, src_lo64_mat.ra.x(), .{ .immediate = 0 })); + try isel.ctzLimb(hi64_ra, .{ .signedness = .unsigned, .bits = 64 }, src_hi64_mat.ra); + try isel.ctzLimb(lo64_ra, .{ .signedness = int_info.signedness, .bits = bits - 64 }, src_lo64_mat.ra); + try src_hi64_mat.finish(isel); + try src_lo64_mat.finish(isel); + }, + 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; + }, + .popcount => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = isel.air.typeOf(ty_op.operand, ip); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + const int_info = ty.intInfo(zcu); + if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + const vec_ra = try isel.allocVecReg(); + defer isel.freeReg(vec_ra); + try isel.emit(.umov(res_ra.w(), vec_ra.@"b[]"(0))); + switch (int_info.bits) { + else => unreachable, + 1...8 => {}, + 9...16 => try isel.emit(.addp(vec_ra.@"8b"(), vec_ra.@"8b"(), .{ .vector = vec_ra.@"8b"() })), + 17...64 => try isel.emit(.addv(vec_ra.b(), vec_ra.@"8b"())), + } + try isel.emit(.cnt(vec_ra.@"8b"(), vec_ra.@"8b"())); + switch (int_info.bits) { + else => unreachable, + 1...31 => |bits| switch (int_info.signedness) { + .signed => { + try isel.emit(.fmov(vec_ra.s(), .{ .register = res_ra.w() })); + try isel.emit(.ubfm(res_ra.w(), src_mat.ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + .unsigned => try isel.emit(.fmov(vec_ra.s(), .{ .register = src_mat.ra.w() })), + }, + 32 => try isel.emit(.fmov(vec_ra.s(), .{ .register = src_mat.ra.w() })), + 33...63 => |bits| switch (int_info.signedness) { + .signed => { + try isel.emit(.fmov(vec_ra.d(), .{ .register = res_ra.x() })); + try isel.emit(.ubfm(res_ra.x(), src_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + .unsigned => try isel.emit(.fmov(vec_ra.d(), .{ .register = src_mat.ra.x() })), + }, + 64 => try isel.emit(.fmov(vec_ra.d(), .{ .register = src_mat.ra.x() })), + } + try src_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .byte_swap => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = ty_op.ty.toType(); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + const int_info = ty.intInfo(zcu); + if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + if (int_info.bits == 8) break :unused try res_vi.value.move(isel, ty_op.operand); + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + switch (int_info.bits) { + else => unreachable, + 16 => switch (int_info.signedness) { + .signed => { + try isel.emit(.sbfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 32 - 16, + .imms = 32 - 1, + })); + try isel.emit(.rev(res_ra.w(), src_mat.ra.w())); + }, + .unsigned => try isel.emit(.rev16(res_ra.w(), src_mat.ra.w())), + }, + 24 => { + switch (int_info.signedness) { + .signed => try isel.emit(.sbfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 32 - 24, + .imms = 32 - 1, + })), + .unsigned => try isel.emit(.ubfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = 32 - 24, + .imms = 32 - 1, + })), + } + try isel.emit(.rev(res_ra.w(), src_mat.ra.w())); + }, + 32 => try isel.emit(.rev(res_ra.w(), src_mat.ra.w())), + 40, 48, 56 => |bits| { + switch (int_info.signedness) { + .signed => try isel.emit(.sbfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(64 - bits), + .imms = 64 - 1, + })), + .unsigned => try isel.emit(.ubfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(64 - bits), + .imms = 64 - 1, + })), + } + try isel.emit(.rev(res_ra.x(), src_mat.ra.x())); + }, + 64 => try isel.emit(.rev(res_ra.x(), src_mat.ra.x())), + } + try src_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .bit_reverse => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = ty_op.ty.toType(); + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + const int_info = ty.intInfo(zcu); + if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + switch (int_info.bits) { + else => unreachable, + 1...31 => |bits| { + switch (int_info.signedness) { + .signed => try isel.emit(.sbfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = @intCast(32 - bits), + .imms = 32 - 1, + })), + .unsigned => try isel.emit(.ubfm(res_ra.w(), res_ra.w(), .{ + .N = .word, + .immr = @intCast(32 - bits), + .imms = 32 - 1, + })), + } + try isel.emit(.rbit(res_ra.w(), src_mat.ra.w())); + }, + 32 => try isel.emit(.rbit(res_ra.w(), src_mat.ra.w())), + 33...63 => |bits| { + switch (int_info.signedness) { + .signed => try isel.emit(.sbfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(64 - bits), + .imms = 64 - 1, + })), + .unsigned => try isel.emit(.ubfm(res_ra.x(), res_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(64 - bits), + .imms = 64 - 1, + })), + } + try isel.emit(.rbit(res_ra.x(), src_mat.ra.x())); + }, + 64 => try isel.emit(.rbit(res_ra.x(), src_mat.ra.x())), + } + try src_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .sqrt, .floor, .ceil, .round, .trunc_float => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const un_op = air.data(air.inst_index).un_op; + const ty = isel.air.typeOf(un_op, ip); + switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + const src_vi = try isel.use(un_op); + const src_mat = try src_vi.matReg(isel); + const src_ra = if (need_fcvt) try isel.allocVecReg() else src_mat.ra; + defer if (need_fcvt) isel.freeReg(src_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) continue :bits 32 else switch (air_tag) { + else => unreachable, + .sqrt => .fsqrt(res_ra.h(), src_ra.h()), + .floor => .frintm(res_ra.h(), src_ra.h()), + .ceil => .frintp(res_ra.h(), src_ra.h()), + .round => .frinta(res_ra.h(), src_ra.h()), + .trunc_float => .frintz(res_ra.h(), src_ra.h()), + }, + 32 => switch (air_tag) { + else => unreachable, + .sqrt => .fsqrt(res_ra.s(), src_ra.s()), + .floor => .frintm(res_ra.s(), src_ra.s()), + .ceil => .frintp(res_ra.s(), src_ra.s()), + .round => .frinta(res_ra.s(), src_ra.s()), + .trunc_float => .frintz(res_ra.s(), src_ra.s()), + }, + 64 => switch (air_tag) { + else => unreachable, + .sqrt => .fsqrt(res_ra.d(), src_ra.d()), + .floor => .frintm(res_ra.d(), src_ra.d()), + .ceil => .frintp(res_ra.d(), src_ra.d()), + .round => .frinta(res_ra.d(), src_ra.d()), + .trunc_float => .frintz(res_ra.d(), src_ra.d()), + }, + }); + if (need_fcvt) try isel.emit(.fcvt(src_ra.s(), src_mat.ra.h())); + try src_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (air_tag) { + else => unreachable, + .sqrt => switch (bits) { + else => unreachable, + 16 => "__sqrth", + 32 => "sqrtf", + 64 => "sqrt", + 80 => "__sqrtx", + 128 => "sqrtq", + }, + .floor => switch (bits) { + else => unreachable, + 16 => "__floorh", + 32 => "floorf", + 64 => "floor", + 80 => "__floorx", + 128 => "floorq", + }, + .ceil => switch (bits) { + else => unreachable, + 16 => "__ceilh", + 32 => "ceilf", + 64 => "ceil", + 80 => "__ceilx", + 128 => "ceilq", + }, + .round => switch (bits) { + else => unreachable, + 16 => "__roundh", + 32 => "roundf", + 64 => "round", + 80 => "__roundx", + 128 => "roundq", + }, + .trunc_float => switch (bits) { + else => unreachable, + 16 => "__trunch", + 32 => "truncf", + 64 => "trunc", + 80 => "__truncx", + 128 => "truncq", + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(un_op); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0), + 80 => { + var src_hi16_it = src_vi.field(ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + try call.paramLiveOut(isel, src_hi16_vi.?, .r1); + var src_lo64_it = src_vi.field(ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try call.paramLiveOut(isel, src_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .sin, .cos, .tan, .exp, .exp2, .log, .log2, .log10 => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| { + defer res_vi.value.deref(isel); + + const un_op = air.data(air.inst_index).un_op; + const ty = isel.air.typeOf(un_op, ip); + const bits = ty.floatBits(isel.target); + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (air_tag) { + else => unreachable, + .sin => switch (bits) { + else => unreachable, + 16 => "__sinh", + 32 => "sinf", + 64 => "sin", + 80 => "__sinx", + 128 => "sinq", + }, + .cos => switch (bits) { + else => unreachable, + 16 => "__cosh", + 32 => "cosf", + 64 => "cos", + 80 => "__cosx", + 128 => "cosq", + }, + .tan => switch (bits) { + else => unreachable, + 16 => "__tanh", + 32 => "tanf", + 64 => "tan", + 80 => "__tanx", + 128 => "tanq", + }, + .exp => switch (bits) { + else => unreachable, + 16 => "__exph", + 32 => "expf", + 64 => "exp", + 80 => "__expx", + 128 => "expq", + }, + .exp2 => switch (bits) { + else => unreachable, + 16 => "__exp2h", + 32 => "exp2f", + 64 => "exp2", + 80 => "__exp2x", + 128 => "exp2q", + }, + .log => switch (bits) { + else => unreachable, + 16 => "__logh", + 32 => "logf", + 64 => "log", + 80 => "__logx", + 128 => "logq", + }, + .log2 => switch (bits) { + else => unreachable, + 16 => "__log2h", + 32 => "log2f", + 64 => "log2", + 80 => "__log2x", + 128 => "log2q", + }, + .log10 => switch (bits) { + else => unreachable, + 16 => "__log10h", + 32 => "log10f", + 64 => "log10", + 80 => "__log10x", + 128 => "log10q", + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(un_op); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0), + 80 => { + var src_hi16_it = src_vi.field(ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + try call.paramLiveOut(isel, src_hi16_vi.?, .r1); + var src_lo64_it = src_vi.field(ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try call.paramLiveOut(isel, src_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .abs => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const ty = ty_op.ty.toType(); + if (!ty.isRuntimeFloat()) { + if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + switch (ty.intInfo(zcu).bits) { + 0 => unreachable, + 1...32 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.csneg(res_ra.w(), src_mat.ra.w(), src_mat.ra.w(), .pl)); + try isel.emit(.subs(.wzr, src_mat.ra.w(), .{ .immediate = 0 })); + try src_mat.finish(isel); + }, + 33...64 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.csneg(res_ra.x(), src_mat.ra.x(), src_mat.ra.x(), .pl)); + try isel.emit(.subs(.xzr, src_mat.ra.x(), .{ .immediate = 0 })); + try src_mat.finish(isel); + }, + 65...128 => { + var res_hi64_it = res_vi.value.field(ty, 8, 8); + const res_hi64_vi = try res_hi64_it.only(isel); + const res_hi64_ra = try res_hi64_vi.?.defReg(isel); + var res_lo64_it = res_vi.value.field(ty, 0, 8); + const res_lo64_vi = try res_lo64_it.only(isel); + const res_lo64_ra = try res_lo64_vi.?.defReg(isel); + if (res_hi64_ra == null and res_lo64_ra == null) break :unused; + const src_ty = isel.air.typeOf(ty_op.operand, ip); + const src_vi = try isel.use(ty_op.operand); + var src_hi64_it = src_vi.field(src_ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + const src_hi64_mat = try src_hi64_vi.?.matReg(isel); + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + const src_lo64_mat = try src_lo64_vi.?.matReg(isel); + const lo64_ra = try isel.allocIntReg(); + defer isel.freeReg(lo64_ra); + const hi64_ra, const mask_ra = alloc_ras: { + const res_lo64_lock: RegLock = if (res_lo64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty; + defer res_lo64_lock.unlock(isel); + break :alloc_ras .{ try isel.allocIntReg(), try isel.allocIntReg() }; + }; + defer { + isel.freeReg(hi64_ra); + isel.freeReg(mask_ra); + } + if (res_hi64_ra) |res_ra| try isel.emit(.sbc(res_ra.x(), hi64_ra.x(), mask_ra.x())); + try isel.emit(.subs( + if (res_lo64_ra) |res_ra| res_ra.x() else .xzr, + lo64_ra.x(), + .{ .register = mask_ra.x() }, + )); + if (res_hi64_ra) |_| try isel.emit(.eor(hi64_ra.x(), src_hi64_mat.ra.x(), .{ .register = mask_ra.x() })); + try isel.emit(.eor(lo64_ra.x(), src_lo64_mat.ra.x(), .{ .register = mask_ra.x() })); + try isel.emit(.sbfm(mask_ra.x(), src_hi64_mat.ra.x(), .{ + .N = .doubleword, + .immr = 64 - 1, + .imms = 64 - 1, + })); + try src_lo64_mat.finish(isel); + try src_hi64_mat.finish(isel); + }, + else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }), + } + } else switch (ty.floatBits(isel.target)) { + else => unreachable, + 16 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(if (isel.target.cpu.has(.aarch64, .fullfp16)) + .fabs(res_ra.h(), src_mat.ra.h()) + else + .bic(res_ra.@"4h"(), res_ra.@"4h"(), .{ .shifted_immediate = .{ + .immediate = 0b10000000, + .lsl = 8, + } })); + try src_mat.finish(isel); + }, + 32 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fabs(res_ra.s(), src_mat.ra.s())); + try src_mat.finish(isel); + }, + 64 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fabs(res_ra.d(), src_mat.ra.d())); + try src_mat.finish(isel); + }, + 80 => { + const src_vi = try isel.use(ty_op.operand); + var res_hi16_it = res_vi.value.field(ty, 8, 8); + const res_hi16_vi = try res_hi16_it.only(isel); + if (try res_hi16_vi.?.defReg(isel)) |res_hi16_ra| { + var src_hi16_it = src_vi.field(ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + const src_hi16_mat = try src_hi16_vi.?.matReg(isel); + try isel.emit(.@"and"(res_hi16_ra.w(), src_hi16_mat.ra.w(), .{ .immediate = .{ + .N = .word, + .immr = 0, + .imms = 15 - 1, + } })); + try src_hi16_mat.finish(isel); + } + var res_lo64_it = res_vi.value.field(ty, 0, 8); + const res_lo64_vi = try res_lo64_it.only(isel); + if (try res_lo64_vi.?.defReg(isel)) |res_lo64_ra| { + var src_lo64_it = src_vi.field(ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try src_lo64_vi.?.liveOut(isel, res_lo64_ra); + } + }, + 128 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + const neg_zero_ra = try isel.allocVecReg(); + defer isel.freeReg(neg_zero_ra); + try isel.emit(.bic(res_ra.@"16b"(), src_mat.ra.@"16b"(), .{ .register = neg_zero_ra.@"16b"() })); + try isel.literals.appendNTimes(gpa, 0, -%isel.literals.items.len % 4); + try isel.literal_relocs.append(gpa, .{ + .label = @intCast(isel.instructions.items.len), + }); + try isel.emit(.ldr(neg_zero_ra.q(), .{ + .literal = @intCast((isel.instructions.items.len + 1 + isel.literals.items.len) << 2), + })); + try isel.emitLiteral(&(.{0} ** 15 ++ .{0x80})); + try src_mat.finish(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .neg, .neg_optimized => { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const un_op = air.data(air.inst_index).un_op; + const ty = isel.air.typeOf(un_op, ip); + switch (ty.floatBits(isel.target)) { + else => unreachable, + 16 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(un_op); + const src_mat = try src_vi.matReg(isel); + if (isel.target.cpu.has(.aarch64, .fullfp16)) { + try isel.emit(.fneg(res_ra.h(), src_mat.ra.h())); + } else { + const neg_zero_ra = try isel.allocVecReg(); + defer isel.freeReg(neg_zero_ra); + try isel.emit(.eor(res_ra.@"8b"(), res_ra.@"8b"(), .{ .register = neg_zero_ra.@"8b"() })); + try isel.emit(.movi(neg_zero_ra.@"4h"(), 0b10000000, .{ .lsl = 8 })); + } + try src_mat.finish(isel); + }, + 32 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(un_op); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fneg(res_ra.s(), src_mat.ra.s())); + try src_mat.finish(isel); + }, + 64 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(un_op); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fneg(res_ra.d(), src_mat.ra.d())); + try src_mat.finish(isel); + }, + 80 => { + const src_vi = try isel.use(un_op); + var res_hi16_it = res_vi.value.field(ty, 8, 8); + const res_hi16_vi = try res_hi16_it.only(isel); + if (try res_hi16_vi.?.defReg(isel)) |res_hi16_ra| { + var src_hi16_it = src_vi.field(ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + const src_hi16_mat = try src_hi16_vi.?.matReg(isel); + try isel.emit(.eor(res_hi16_ra.w(), src_hi16_mat.ra.w(), .{ .immediate = .{ + .N = .word, + .immr = 32 - 15, + .imms = 1 - 1, + } })); + try src_hi16_mat.finish(isel); + } + var res_lo64_it = res_vi.value.field(ty, 0, 8); + const res_lo64_vi = try res_lo64_it.only(isel); + if (try res_lo64_vi.?.defReg(isel)) |res_lo64_ra| { + var src_lo64_it = src_vi.field(ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try src_lo64_vi.?.liveOut(isel, res_lo64_ra); + } + }, + 128 => { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(un_op); + const src_mat = try src_vi.matReg(isel); + const neg_zero_ra = try isel.allocVecReg(); + defer isel.freeReg(neg_zero_ra); + try isel.emit(.eor(res_ra.@"16b"(), src_mat.ra.@"16b"(), .{ .register = neg_zero_ra.@"16b"() })); + try isel.literals.appendNTimes(gpa, 0, -%isel.literals.items.len % 4); + try isel.literal_relocs.append(gpa, .{ + .label = @intCast(isel.instructions.items.len), + }); + try isel.emit(.ldr(neg_zero_ra.q(), .{ + .literal = @intCast((isel.instructions.items.len + 1 + isel.literals.items.len) << 2), + })); + try isel.emitLiteral(&(.{0} ** 15 ++ .{0x80})); + try src_mat.finish(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq => |air_tag| { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + var bin_op = air.data(air.inst_index).bin_op; + const ty = isel.air.typeOf(bin_op.lhs, ip); + if (!ty.isRuntimeFloat()) { + const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type) + .{ .signedness = .unsigned, .bits = 1 } + else if (ty.isAbiInt(zcu)) + ty.intInfo(zcu) + else if (ty.isPtrAtRuntime(zcu)) + .{ .signedness = .unsigned, .bits = 64 } + else + return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + if (int_info.bits > 256) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }); + + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) { + else => unreachable, + .cmp_lt => switch (int_info.signedness) { + .signed => .lt, + .unsigned => .lo, + }, + .cmp_lte => switch (int_info.bits) { + else => unreachable, + 1...64 => switch (int_info.signedness) { + .signed => .le, + .unsigned => .ls, + }, + 65...128 => { + std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); + continue :cond .cmp_gte; + }, + }, + .cmp_eq => .eq, + .cmp_gte => switch (int_info.signedness) { + .signed => .ge, + .unsigned => .hs, + }, + .cmp_gt => switch (int_info.bits) { + else => unreachable, + 1...64 => switch (int_info.signedness) { + .signed => .gt, + .unsigned => .hi, + }, + 65...128 => { + std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); + continue :cond .cmp_lt; + }, + }, + .cmp_neq => .ne, + }))); + + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + var part_offset = lhs_vi.size(isel); + while (part_offset > 0) { + const part_size = @min(part_offset, 8); + part_offset -= part_size; + var lhs_part_it = lhs_vi.field(ty, part_offset, part_size); + const lhs_part_vi = try lhs_part_it.only(isel); + const lhs_part_mat = try lhs_part_vi.?.matReg(isel); + var rhs_part_it = rhs_vi.field(ty, part_offset, part_size); + const rhs_part_vi = try rhs_part_it.only(isel); + const rhs_part_mat = try rhs_part_vi.?.matReg(isel); + try isel.emit(switch (part_size) { + else => unreachable, + 1...4 => switch (part_offset) { + 0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + else => switch (air_tag) { + else => unreachable, + .cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs( + .wzr, + lhs_part_mat.ra.w(), + rhs_part_mat.ra.w(), + ), + .cmp_eq, .cmp_neq => .ccmp( + lhs_part_mat.ra.w(), + .{ .register = rhs_part_mat.ra.w() }, + .{ .n = false, .z = false, .c = false, .v = false }, + .eq, + ), + }, + }, + 5...8 => switch (part_offset) { + 0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + else => switch (air_tag) { + else => unreachable, + .cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs( + .xzr, + lhs_part_mat.ra.x(), + rhs_part_mat.ra.x(), + ), + .cmp_eq, .cmp_neq => .ccmp( + lhs_part_mat.ra.x(), + .{ .register = rhs_part_mat.ra.x() }, + .{ .n = false, .z = false, .c = false, .v = false }, + .eq, + ), + }, + }, + }); + try rhs_part_mat.finish(isel); + try lhs_part_mat.finish(isel); + } + } else switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + const need_fcvt = switch (bits) { + else => unreachable, + 16 => !isel.target.cpu.has(.aarch64, .fullfp16), + 32, 64 => false, + }; + try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (air_tag) { + else => unreachable, + .cmp_lt => .lo, + .cmp_lte => .ls, + .cmp_eq => .eq, + .cmp_gte => .ge, + .cmp_gt => .gt, + .cmp_neq => .ne, + }))); + + 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 lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) + continue :bits 32 + else + .fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }), + 32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }), + 64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }), + }); + if (need_fcvt) { + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + + try call.prepareReturn(isel); + try call.returnFill(isel, .r0); + try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) { + else => unreachable, + .cmp_lt => .lt, + .cmp_lte => .le, + .cmp_eq => .eq, + .cmp_gte => { + std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); + continue :cond .cmp_lte; + }, + .cmp_gt => { + std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs); + continue :cond .cmp_lt; + }, + .cmp_neq => .ne, + }))); + try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 })); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__cmphf2", + 32 => "__cmpsf2", + 64 => "__cmpdf2", + 80 => "__cmpxf2", + 128 => "__cmptf2", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + 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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .cond_br => { + const pl_op = air.data(air.inst_index).pl_op; + const extra = isel.air.extraData(Air.CondBr, pl_op.payload); + + try isel.body(@ptrCast(isel.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len])); + const else_label = isel.instructions.items.len; + const else_live_registers = isel.live_registers; + try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.then_body_len])); + try isel.merge(&else_live_registers, .{}); + + const cond_vi = try isel.use(pl_op.operand); + const cond_mat = try cond_vi.matReg(isel); + try isel.emit(.tbz( + cond_mat.ra.x(), + 0, + @intCast((isel.instructions.items.len + 1 - else_label) << 2), + )); + try cond_mat.finish(isel); + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .switch_br => { + const switch_br = isel.air.unwrapSwitch(air.inst_index); + const cond_ty = isel.air.typeOf(switch_br.operand, ip); + const cond_int_info: std.builtin.Type.Int = if (cond_ty.toIntern() == .bool_type) + .{ .signedness = .unsigned, .bits = 1 } + else if (cond_ty.isAbiInt(zcu)) + cond_ty.intInfo(zcu) + else + return isel.fail("bad switch cond {f}", .{isel.fmtType(cond_ty)}); + + var final_case = true; + if (switch_br.else_body_len > 0) { + var cases_it = switch_br.iterateCases(); + while (cases_it.next()) |_| {} + try isel.body(cases_it.elseBody()); + assert(final_case); + final_case = false; + } + const zero_reg: Register = switch (cond_int_info.bits) { + else => unreachable, + 1...32 => .wzr, + 33...64 => .xzr, + }; + 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; + const next_live_registers = isel.live_registers; + try isel.body(case.body); + if (final_case) { + final_case = false; + continue; + } + try isel.merge(&next_live_registers, .{}); + 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) { + else => unreachable, + 1...32 => .{ cond_mat.?.ra.w(), temp_ra.w() }, + 33...64 => .{ cond_mat.?.ra.x(), temp_ra.x() }, + }; + } + if (case.ranges.len == 0 and case.items.len == 1 and Constant.fromInterned( + case.items[0].toInterned().?, + ).orderAgainstZero(zcu).compare(.eq)) { + try isel.emit(.cbnz( + cond_reg, + @intCast((isel.instructions.items.len + 1 - next_label) << 2), + )); + continue; + } + try isel.emit(.@"b."( + .invert(switch (case.ranges.len) { + 0 => .eq, + else => .ls, + }), + @intCast((isel.instructions.items.len + 1 - next_label) << 2), + )); + var case_range_index = case.ranges.len; + while (case_range_index > 0) { + case_range_index -= 1; + + const low_val: Constant = .fromInterned(case.ranges[case_range_index][0].toInterned().?); + var low_bigint_space: Constant.BigIntSpace = undefined; + const low_bigint = low_val.toBigInt(&low_bigint_space, zcu); + const low_int: i64 = if (low_bigint.positive) @bitCast( + low_bigint.toInt(u64) catch + return isel.fail("too big case range start: {f}", .{isel.fmtConstant(low_val)}), + ) else low_bigint.toInt(i64) catch + return isel.fail("too big case range start: {f}", .{isel.fmtConstant(low_val)}); + + const high_val: Constant = .fromInterned(case.ranges[case_range_index][1].toInterned().?); + var high_bigint_space: Constant.BigIntSpace = undefined; + const high_bigint = high_val.toBigInt(&high_bigint_space, zcu); + const high_int: i64 = if (high_bigint.positive) @bitCast( + high_bigint.toInt(u64) catch + return isel.fail("too big case range end: {f}", .{isel.fmtConstant(high_val)}), + ) else high_bigint.toInt(i64) catch + return isel.fail("too big case range end: {f}", .{isel.fmtConstant(high_val)}); + + 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", .{}); + } 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, + .{ .immediate = pos_imm }, + )) else if (std.math.cast(u12, -delta_int)) |neg_imm| try isel.emit(.adds( + zero_reg, + adjusted_reg, + .{ .immediate = neg_imm }, + )) else if (if (@as(i12, @truncate(delta_int)) == 0) + std.math.cast(u12, delta_int >> 12) + else + null) |pos_imm_lsr_12| try isel.emit(.subs( + zero_reg, + adjusted_reg, + .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } }, + )) else if (if (@as(i12, @truncate(-delta_int)) == 0) + std.math.cast(u12, -delta_int >> 12) + else + null) |neg_imm_lsr_12| try isel.emit(.adds( + zero_reg, + 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 })); + } + + 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 })); + } + }, + } + } + } + var case_item_index = case.items.len; + while (case_item_index > 0) { + case_item_index -= 1; + + const item_val: Constant = .fromInterned(case.items[case_item_index].toInterned().?); + var item_bigint_space: Constant.BigIntSpace = undefined; + const item_bigint = item_val.toBigInt(&item_bigint_space, zcu); + const item_int: i64 = if (item_bigint.positive) @bitCast( + item_bigint.toInt(u64) catch + return isel.fail("too big case item: {f}", .{isel.fmtConstant(item_val)}), + ) else item_bigint.toInt(i64) catch + return isel.fail("too big case item: {f}", .{isel.fmtConstant(item_val)}); + + if (case_item_index > 0) { + if (std.math.cast(u5, item_int)) |pos_imm| try isel.emit(.ccmp( + cond_reg, + .{ .immediate = pos_imm }, + .{ .n = false, .z = true, .c = false, .v = false }, + .ne, + )) else if (std.math.cast(u5, -item_int)) |neg_imm| try isel.emit(.ccmn( + cond_reg, + .{ .immediate = neg_imm }, + .{ .n = false, .z = true, .c = false, .v = false }, + .ne, + )) else { + try isel.movImmediate(temp_reg, @bitCast(item_int)); + try isel.emit(.ccmp( + cond_reg, + .{ .register = temp_reg }, + .{ .n = false, .z = true, .c = false, .v = false }, + .ne, + )); + } + } else { + if (std.math.cast(u12, item_int)) |pos_imm| try isel.emit(.subs( + zero_reg, + cond_reg, + .{ .immediate = pos_imm }, + )) else if (std.math.cast(u12, -item_int)) |neg_imm| try isel.emit(.adds( + zero_reg, + cond_reg, + .{ .immediate = neg_imm }, + )) else if (if (@as(i12, @truncate(item_int)) == 0) + std.math.cast(u12, item_int >> 12) + else + null) |pos_imm_lsr_12| try isel.emit(.subs( + zero_reg, + cond_reg, + .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } }, + )) else if (if (@as(i12, @truncate(-item_int)) == 0) + std.math.cast(u12, -item_int >> 12) + else + null) |neg_imm_lsr_12| try isel.emit(.adds( + zero_reg, + 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 })); + } + } + } + } + if (cond_mat) |mat| { + try mat.finish(isel); + isel.freeReg(temp_reg.alias); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .@"try", .try_cold => { + const pl_op = air.data(air.inst_index).pl_op; + const extra = isel.air.extraData(Air.Try, pl_op.payload); + const error_union_ty = isel.air.typeOf(pl_op.operand, ip); + 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_vi = try isel.use(pl_op.operand); + if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| { + defer payload_vi.value.deref(isel); + + var payload_part_it = error_union_vi.field( + error_union_ty, + codegen.errUnionPayloadOffset(payload_ty, zcu), + payload_vi.value.size(isel), + ); + const payload_part_vi = try payload_part_it.only(isel); + try payload_vi.value.copy(isel, payload_ty, payload_part_vi.?); + } + + 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, .{}); + + var error_set_part_it = error_union_vi.field( + error_union_ty, + codegen.errUnionErrorOffset(payload_ty, zcu), + ZigType.fromInterned(error_union_info.error_set_type).abiSize(zcu), + ); + 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(), + }, + @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; + }, + .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; + }, + .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => { + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .is_null, .is_non_null => |air_tag| { + 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; + + const un_op = air.data(air.inst_index).un_op; + const opt_ty = isel.air.typeOf(un_op, ip); + const payload_ty = opt_ty.optionalChild(zcu); + const payload_size = payload_ty.abiSize(zcu); + const has_value_offset, const has_value_size = if (!opt_ty.optionalReprIsPayload(zcu)) + .{ payload_size, 1 } + else if (payload_ty.isSlice(zcu)) + .{ 0, 8 } + else + .{ 0, payload_size }; + + try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(switch (air_tag) { + else => unreachable, + .is_null => .eq, + .is_non_null => .ne, + }))); + const opt_vi = try isel.use(un_op); + var has_value_part_it = opt_vi.field(opt_ty, has_value_offset, has_value_size); + const has_value_part_vi = try has_value_part_it.only(isel); + const has_value_part_mat = try has_value_part_vi.?.matReg(isel); + try isel.emit(switch (has_value_size) { + else => unreachable, + 1...4 => .subs(.wzr, has_value_part_mat.ra.w(), .{ .immediate = 0 }), + 5...8 => .subs(.xzr, has_value_part_mat.ra.x(), .{ .immediate = 0 }), + }); + try has_value_part_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .is_err, .is_non_err => |air_tag| { + 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; + + const un_op = air.data(air.inst_index).un_op; + const error_union_ty = isel.air.typeOf(un_op, ip); + const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type; + const error_set_ty: ZigType = .fromInterned(error_union_info.error_set_type); + const payload_ty: ZigType = .fromInterned(error_union_info.payload_type); + const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu); + const error_set_size = error_set_ty.abiSize(zcu); + + try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(switch (air_tag) { + else => unreachable, + .is_err => .ne, + .is_non_err => .eq, + }))); + const error_union_vi = try isel.use(un_op); + var error_set_part_it = error_union_vi.field(error_union_ty, error_set_offset, error_set_size); + 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(.ands(.wzr, error_set_part_mat.ra.w(), .{ .immediate = .{ + .N = .word, + .immr = 0, + .imms = @intCast(8 * error_set_size - 1), + } })); + try error_set_part_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .load => { + const ty_op = air.data(air.inst_index).ty_op; + const ptr_ty = isel.air.typeOf(ty_op.operand, ip); + const ptr_info = ptr_ty.ptrInfo(zcu); + if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed load", .{}); + + 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; + + try call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = "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); + }, + } + } + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .ret, .ret_safe => { + assert(isel.blocks.keys()[0] == Block.main); + try isel.blocks.values()[0].branch(isel); + if (isel.live_values.get(Block.main)) |ret_vi| { + const un_op = air.data(air.inst_index).un_op; + const src_vi = try isel.use(un_op); + switch (ret_vi.parent(isel)) { + .unallocated, .stack_slot => if (ret_vi.hint(isel)) |ret_ra| { + try src_vi.liveOut(isel, ret_ra); + } else { + var ret_part_it = ret_vi.parts(isel); + var src_part_it = src_vi.parts(isel); + if (src_part_it.only()) |_| { + try isel.values.ensureUnusedCapacity(gpa, ret_part_it.remaining); + src_vi.setParts(isel, ret_part_it.remaining); + while (ret_part_it.next()) |ret_part_vi| { + const src_part_vi = src_vi.addPart( + isel, + ret_part_vi.get(isel).offset_from_parent, + ret_part_vi.size(isel), + ); + switch (ret_part_vi.signedness(isel)) { + .signed => src_part_vi.setSignedness(isel, .signed), + .unsigned => {}, + } + if (ret_part_vi.isVector(isel)) src_part_vi.setIsVector(isel); + } + ret_part_it = ret_vi.parts(isel); + src_part_it = src_vi.parts(isel); + } + while (ret_part_it.next()) |ret_part_vi| { + const src_part_vi = src_part_it.next().?; + assert(ret_part_vi.get(isel).offset_from_parent == src_part_vi.get(isel).offset_from_parent); + assert(ret_part_vi.size(isel) == src_part_vi.size(isel)); + try src_part_vi.liveOut(isel, ret_part_vi.hint(isel).?); + } + }, + .value, .constant => unreachable, + .address => |address_vi| { + const ptr_mat = try address_vi.matReg(isel); + try src_vi.store(isel, isel.air.typeOf(un_op, ip), ptr_mat.ra, .{}); + try ptr_mat.finish(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .ret_load => { + const un_op = air.data(air.inst_index).un_op; + const ptr_ty = isel.air.typeOf(un_op, ip); + const ptr_info = ptr_ty.ptrInfo(zcu); + if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed load", .{}); + + assert(isel.blocks.keys()[0] == Block.main); + try isel.blocks.values()[0].branch(isel); + if (isel.live_values.get(Block.main)) |ret_vi| switch (ret_vi.parent(isel)) { + .unallocated, .stack_slot => { + var ret_part_it: Value.PartIterator = if (ret_vi.hint(isel)) |_| .initOne(ret_vi) else ret_vi.parts(isel); + while (ret_part_it.next()) |ret_part_vi| try ret_part_vi.liveOut(isel, ret_part_vi.hint(isel).?); + const ptr_vi = try isel.use(un_op); + const ptr_mat = try ptr_vi.matReg(isel); + _ = try ret_vi.load(isel, .fromInterned(ptr_info.child), ptr_mat.ra, .{}); + try ptr_mat.finish(isel); + }, + .value, .constant => unreachable, + .address => {}, + }; + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .store, .store_safe, .atomic_store_unordered => { + const bin_op = air.data(air.inst_index).bin_op; + 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; + }; + + const src_vi = try isel.use(bin_op.rhs); + const size = src_vi.size(isel); + if (ZigType.fromInterned(ptr_info.child).zigTypeTag(zcu) != .@"union") switch (size) { + 0 => unreachable, + 1...Value.max_parts => { + const ptr_vi = try isel.use(bin_op.lhs); + const ptr_mat = try ptr_vi.matReg(isel); + try src_vi.store(isel, isel.air.typeOf(bin_op.rhs, ip), ptr_mat.ra, .{ + .@"volatile" = ptr_info.flags.is_volatile, + }); + try ptr_mat.finish(isel); + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + break :air_tag; + }, + else => {}, + }; + try call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = "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(bin_op.lhs); + try isel.movImmediate(.x2, size); + try call.paramAddress(isel, src_vi, .r1); + try call.paramLiveOut(isel, ptr_vi, .r0); + try call.finishParams(isel); + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .unreach => if (air.next()) |next_air_tag| continue :air_tag next_air_tag, + .fptrunc, .fpext => { + 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_bits = dst_ty.floatBits(isel.target); + const src_ty = isel.air.typeOf(ty_op.operand, ip); + const src_bits = src_ty.floatBits(isel.target); + assert(dst_bits != src_bits); + switch (@max(dst_bits, src_bits)) { + else => unreachable, + 16, 32, 64 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(.fcvt(switch (dst_bits) { + else => unreachable, + 16 => dst_ra.h(), + 32 => dst_ra.s(), + 64 => dst_ra.d(), + }, switch (src_bits) { + else => unreachable, + 16 => src_mat.ra.h(), + 32 => src_mat.ra.s(), + 64 => src_mat.ra.d(), + })); + try src_mat.finish(isel); + }, + 80, 128 => { + try call.prepareReturn(isel); + switch (dst_bits) { + else => unreachable, + 16, 32, 64, 128 => try call.returnLiveIn(isel, dst_vi.value, .v0), + 80 => { + var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi16_vi = try dst_hi16_it.only(isel); + try call.returnLiveIn(isel, dst_hi16_vi.?, .r1); + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + try call.returnLiveIn(isel, dst_lo64_vi.?, .r0); + }, + } + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (dst_bits) { + else => unreachable, + 16 => switch (src_bits) { + else => unreachable, + 32 => "__truncsfhf2", + 64 => "__truncdfhf2", + 80 => "__truncxfhf2", + 128 => "__trunctfhf2", + }, + 32 => switch (src_bits) { + else => unreachable, + 16 => "__extendhfsf2", + 64 => "__truncdfsf2", + 80 => "__truncxfsf2", + 128 => "__trunctfsf2", + }, + 64 => switch (src_bits) { + else => unreachable, + 16 => "__extendhfdf2", + 32 => "__extendsfdf2", + 80 => "__truncxfdf2", + 128 => "__trunctfdf2", + }, + 80 => switch (src_bits) { + else => unreachable, + 16 => "__extendhfxf2", + 32 => "__extendsfxf2", + 64 => "__extenddfxf2", + 128 => "__trunctfxf2", + }, + 128 => switch (src_bits) { + else => unreachable, + 16 => "__extendhftf2", + 32 => "__extendsftf2", + 64 => "__extenddftf2", + 80 => "__extendxftf2", + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(ty_op.operand); + switch (src_bits) { + else => unreachable, + 16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0), + 80 => { + var src_hi16_it = src_vi.field(src_ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + try call.paramLiveOut(isel, src_hi16_vi.?, .r1); + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try call.paramLiveOut(isel, src_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .intcast => |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; + if ((dst_int_info.bits <= 8 and src_int_info.bits <= 8) or + (dst_int_info.bits > 8 and dst_int_info.bits <= 16 and + src_int_info.bits > 8 and src_int_info.bits <= 16) or + (dst_int_info.bits > 16 and dst_int_info.bits <= 32 and + src_int_info.bits > 16 and src_int_info.bits <= 32) or + (dst_int_info.bits > 32 and dst_int_info.bits <= 64 and + src_int_info.bits > 32 and src_int_info.bits <= 64) or + (dst_int_info.bits > 64 and src_int_info.bits > 64 and + (dst_int_info.bits - 1) / 128 == (src_int_info.bits - 1) / 128)) + { + try dst_vi.value.move(isel, ty_op.operand); + } else if (dst_int_info.bits <= 32 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 src_mat = try src_vi.matReg(isel); + try isel.emit(.orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() })); + try src_mat.finish(isel); + } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 32) { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + try isel.emit(if (can_be_negative) .sbfm(dst_ra.x(), src_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(src_int_info.bits - 1), + }) else .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() })); + try src_mat.finish(isel); + } else if (dst_int_info.bits <= 32 and src_int_info.bits <= 128) { + assert(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); + + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + const src_lo64_mat = try src_lo64_vi.?.matReg(isel); + try isel.emit(.orr(dst_ra.w(), .wzr, .{ .register = src_lo64_mat.ra.w() })); + try src_lo64_mat.finish(isel); + } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 128) { + assert(dst_int_info.bits > 32 and src_int_info.bits > 64); + const src_vi = try isel.use(ty_op.operand); + + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try dst_vi.value.copy(isel, dst_ty, src_lo64_vi.?); + } else if (dst_int_info.bits <= 128 and src_int_info.bits <= 64) { + assert(dst_int_info.bits > 64); + const src_vi = try isel.use(ty_op.operand); + + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + if (src_int_info.bits <= 32) unused_lo64: { + const dst_lo64_ra = try dst_lo64_vi.?.defReg(isel) orelse break :unused_lo64; + const src_mat = try src_vi.matReg(isel); + try isel.emit(if (can_be_negative) .sbfm(dst_lo64_ra.x(), src_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(src_int_info.bits - 1), + }) else .orr(dst_lo64_ra.w(), .wzr, .{ .register = src_mat.ra.w() })); + try src_mat.finish(isel); + } else try dst_lo64_vi.?.copy(isel, src_ty, src_vi); + + var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi64_vi = try dst_hi64_it.only(isel); + const dst_hi64_ra = try dst_hi64_vi.?.defReg(isel); + if (dst_hi64_ra) |dst_ra| switch (can_be_negative) { + false => try isel.emit(.orr(dst_ra.x(), .xzr, .{ .register = .xzr })), + true => { + const src_mat = try src_vi.matReg(isel); + try isel.emit(.sbfm(dst_ra.x(), src_mat.ra.x(), .{ + .N = .doubleword, + .immr = @intCast(src_int_info.bits - 1), + .imms = @intCast(src_int_info.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); + + const ty_op = air.data(air.inst_index).ty_op; + const dst_ty = ty_op.ty.toType(); + const src_ty = isel.air.typeOf(ty_op.operand, ip); + if (!dst_ty.isAbiInt(zcu) or !src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + const dst_int_info = dst_ty.intInfo(zcu); + switch (dst_int_info.bits) { + 0 => unreachable, + 1...64 => |dst_bits| { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + var src_part_it = src_vi.field(src_ty, 0, @min(src_vi.size(isel), 8)); + const src_part_vi = try src_part_it.only(isel); + const src_part_mat = try src_part_vi.?.matReg(isel); + try isel.emit(switch (dst_bits) { + else => unreachable, + 1...31 => |bits| switch (dst_int_info.signedness) { + .signed => .sbfm(dst_ra.w(), src_part_mat.ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(dst_ra.w(), src_part_mat.ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }, + 32 => .orr(dst_ra.w(), .wzr, .{ .register = src_part_mat.ra.w() }), + 33...63 => |bits| switch (dst_int_info.signedness) { + .signed => .sbfm(dst_ra.x(), src_part_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(dst_ra.x(), src_part_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }, + 64 => .orr(dst_ra.x(), .xzr, .{ .register = src_part_mat.ra.x() }), + }); + try src_part_mat.finish(isel); + }, + 65...128 => |dst_bits| switch (src_ty.intInfo(zcu).bits) { + 0 => unreachable, + 65...128 => { + const src_vi = try isel.use(ty_op.operand); + var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi64_vi = try dst_hi64_it.only(isel); + if (try dst_hi64_vi.?.defReg(isel)) |dst_hi64_ra| { + var src_hi64_it = src_vi.field(src_ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + const src_hi64_mat = try src_hi64_vi.?.matReg(isel); + try isel.emit(switch (dst_int_info.signedness) { + .signed => .sbfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(dst_bits - 64 - 1), + }), + .unsigned => .ubfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(dst_bits - 64 - 1), + }), + }); + try src_hi64_mat.finish(isel); + } + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| { + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try src_lo64_vi.?.liveOut(isel, dst_lo64_ra); + } + }, + else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }), + }, + 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; + }, + .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); + + const ty_op = air.data(air.inst_index).ty_op; + const opt_ty = isel.air.typeOf(ty_op.operand, ip); + if (opt_ty.optionalReprIsPayload(zcu)) { + try payload_vi.value.move(isel, ty_op.operand); + break :unused; + } + + const opt_vi = try isel.use(ty_op.operand); + var payload_part_it = opt_vi.field(opt_ty, 0, payload_vi.value.size(isel)); + const payload_part_vi = try payload_part_it.only(isel); + try payload_vi.value.copy(isel, ty_op.ty.toType(), payload_part_vi.?); + } + 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); + + const ty_op = air.data(air.inst_index).ty_op; + if (ty_op.ty.toType().optionalReprIsPayload(zcu)) { + try opt_vi.value.move(isel, ty_op.operand); + break :unused; + } + + const payload_size = isel.air.typeOf(ty_op.operand, ip).abiSize(zcu); + var payload_part_it = opt_vi.value.field(ty_op.ty.toType(), 0, payload_size); + const payload_part_vi = try payload_part_it.only(isel); + try payload_part_vi.?.move(isel, ty_op.operand); + var has_value_part_it = opt_vi.value.field(ty_op.ty.toType(), payload_size, 1); + const has_value_part_vi = try has_value_part_it.only(isel); + const has_value_part_ra = try has_value_part_vi.?.defReg(isel) orelse break :unused; + try isel.emit(.movz(has_value_part_ra.w(), 1, .{ .lsl = .@"0" })); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .unwrap_errunion_payload => { + if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| { + defer payload_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const error_union_ty = isel.air.typeOf(ty_op.operand, ip); + + const error_union_vi = try isel.use(ty_op.operand); + var payload_part_it = error_union_vi.field( + error_union_ty, + codegen.errUnionPayloadOffset(ty_op.ty.toType(), zcu), + payload_vi.value.size(isel), + ); + const payload_part_vi = try payload_part_it.only(isel); + try payload_vi.value.copy(isel, ty_op.ty.toType(), payload_part_vi.?); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .unwrap_errunion_err => { + if (isel.live_values.fetchRemove(air.inst_index)) |error_set_vi| { + defer error_set_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const error_union_ty = isel.air.typeOf(ty_op.operand, ip); + + const error_union_vi = try isel.use(ty_op.operand); + var error_set_part_it = error_union_vi.field( + error_union_ty, + codegen.errUnionErrorOffset(error_union_ty.errorUnionPayload(zcu), zcu), + error_set_vi.value.size(isel), + ); + const error_set_part_vi = try error_set_part_it.only(isel); + try error_set_vi.value.copy(isel, ty_op.ty.toType(), error_set_part_vi.?); + } + 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); + + const ty_op = air.data(air.inst_index).ty_op; + const error_union_ty = ty_op.ty.toType(); + const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type; + const error_set_ty: ZigType = .fromInterned(error_union_info.error_set_type); + const payload_ty: ZigType = .fromInterned(error_union_info.payload_type); + const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu); + const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu); + const error_set_size = error_set_ty.abiSize(zcu); + const payload_size = payload_ty.abiSize(zcu); + + 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); + try payload_part_vi.?.move(isel, ty_op.operand); + 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); + if (try error_set_part_vi.?.defReg(isel)) |error_set_part_ra| try isel.emit(switch (error_set_size) { + else => unreachable, + 1...4 => .orr(error_set_part_ra.w(), .wzr, .{ .register = .wzr }), + 5...8 => .orr(error_set_part_ra.x(), .xzr, .{ .register = .xzr }), + }); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .wrap_errunion_err => { + if (isel.live_values.fetchRemove(air.inst_index)) |error_union_vi| { + defer error_union_vi.value.deref(isel); + + const ty_op = air.data(air.inst_index).ty_op; + const error_union_ty = ty_op.ty.toType(); + const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type; + const error_set_ty: ZigType = .fromInterned(error_union_info.error_set_type); + const payload_ty: ZigType = .fromInterned(error_union_info.payload_type); + const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu); + const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu); + const error_set_size = error_set_ty.abiSize(zcu); + const payload_size = payload_ty.abiSize(zcu); + + 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, + } }), + }); + } + 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; + }, + .struct_field_ptr => { + if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: { + defer dst_vi.value.deref(isel); + const ty_pl = air.data(air.inst_index).ty_pl; + const extra = isel.air.extraData(Air.StructField, ty_pl.payload).data; + switch (codegen.fieldOffset( + isel.air.typeOf(extra.struct_operand, ip), + ty_pl.ty.toType(), + extra.field_index, + zcu, + )) { + 0 => try dst_vi.value.move(isel, extra.struct_operand), + else => |field_offset| { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(extra.struct_operand); + const src_mat = try src_vi.matReg(isel); + const lo12: u12 = @truncate(field_offset >> 0); + const hi12: u12 = @intCast(field_offset >> 12); + if (hi12 > 0) try isel.emit(.add( + dst_ra.x(), + if (lo12 > 0) dst_ra.x() else src_mat.ra.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0) try isel.emit(.add(dst_ra.x(), src_mat.ra.x(), .{ .immediate = lo12 })); + try src_mat.finish(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .struct_field_ptr_index_0, + .struct_field_ptr_index_1, + .struct_field_ptr_index_2, + .struct_field_ptr_index_3, + => |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; + switch (codegen.fieldOffset( + isel.air.typeOf(ty_op.operand, ip), + ty_op.ty.toType(), + switch (air_tag) { + else => unreachable, + .struct_field_ptr_index_0 => 0, + .struct_field_ptr_index_1 => 1, + .struct_field_ptr_index_2 => 2, + .struct_field_ptr_index_3 => 3, + }, + zcu, + )) { + 0 => try dst_vi.value.move(isel, ty_op.operand), + else => |field_offset| { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + const lo12: u12 = @truncate(field_offset >> 0); + const hi12: u12 = @intCast(field_offset >> 12); + if (hi12 > 0) try isel.emit(.add( + dst_ra.x(), + if (lo12 > 0) dst_ra.x() else src_mat.ra.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0) try isel.emit(.add(dst_ra.x(), src_mat.ra.x(), .{ .immediate = lo12 })); + try src_mat.finish(isel); + }, + } + } + 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| { + defer field_vi.value.deref(isel); + + const ty_pl = air.data(air.inst_index).ty_pl; + const extra = isel.air.extraData(Air.StructField, ty_pl.payload).data; + const agg_ty = isel.air.typeOf(extra.struct_operand, ip); + const field_ty = ty_pl.ty.toType(); + const field_bit_offset, const field_bit_size, const is_packed = switch (agg_ty.containerLayout(zcu)) { + .auto, .@"extern" => .{ + 8 * agg_ty.structFieldOffset(extra.field_index, zcu), + 8 * field_ty.abiSize(zcu), + false, + }, + .@"packed" => .{ + if (zcu.typeToPackedStruct(agg_ty)) |loaded_struct| + zcu.structPackedFieldBitOffset(loaded_struct, extra.field_index) + else + 0, + field_ty.bitSize(zcu), + true, + }, + }; + if (is_packed) return isel.fail("packed field of {f}", .{ + isel.fmtType(agg_ty), + }); + + 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); + } + } + } + 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); + const ty_pl = air.data(air.inst_index).ty_pl; + const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; + var ptr_part_it = slice_vi.value.field(ty_pl.ty.toType(), 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + try ptr_part_vi.?.move(isel, bin_op.lhs); + var len_part_it = slice_vi.value.field(ty_pl.ty.toType(), 8, 8); + const len_part_vi = try len_part_it.only(isel); + try len_part_vi.?.move(isel, bin_op.rhs); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .slice_len => { + if (isel.live_values.fetchRemove(air.inst_index)) |len_vi| { + defer len_vi.value.deref(isel); + const ty_op = air.data(air.inst_index).ty_op; + const slice_vi = try isel.use(ty_op.operand); + var len_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 8, 8); + const len_part_vi = try len_part_it.only(isel); + try len_vi.value.copy(isel, ty_op.ty.toType(), len_part_vi.?); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .slice_ptr => { + if (isel.live_values.fetchRemove(air.inst_index)) |ptr_vi| { + defer ptr_vi.value.deref(isel); + const ty_op = air.data(air.inst_index).ty_op; + const slice_vi = try isel.use(ty_op.operand); + var ptr_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + try ptr_vi.value.copy(isel, ty_op.ty.toType(), ptr_part_vi.?); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .array_elem_val => { + if (isel.live_values.fetchRemove(air.inst_index)) |elem_vi| unused: { + defer elem_vi.value.deref(isel); + + const bin_op = air.data(air.inst_index).bin_op; + const array_ty = isel.air.typeOf(bin_op.lhs, ip); + const elem_ty = array_ty.childType(zcu); + const elem_size = elem_ty.abiSize(zcu); + if (elem_size <= 16 and array_ty.arrayLenIncludingSentinel(zcu) <= Value.max_parts) if (bin_op.rhs.toInterned()) |index_val| { + const elem_offset = elem_size * Constant.fromInterned(index_val).toUnsignedInt(zcu); + const array_vi = try isel.use(bin_op.lhs); + var elem_part_it = array_vi.field(array_ty, elem_offset, elem_size); + const elem_part_vi = try elem_part_it.only(isel); + try elem_vi.value.copy(isel, elem_ty, elem_part_vi.?); + break :unused; + }; + switch (elem_size) { + 0 => unreachable, + 1, 2, 4, 8 => { + const elem_ra = try elem_vi.value.defReg(isel) orelse break :unused; + const array_ptr_ra = try isel.allocIntReg(); + defer isel.freeReg(array_ptr_ra); + const index_vi = try isel.use(bin_op.rhs); + const index_mat = try index_vi.matReg(isel); + try isel.emit(switch (elem_size) { + else => unreachable, + 1 => if (elem_vi.value.isVector(isel)) .ldr(elem_ra.b(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }) else switch (elem_vi.value.signedness(isel)) { + .signed => .ldrsb(elem_ra.w(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }), + .unsigned => .ldrb(elem_ra.w(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }), + }, + 2 => if (elem_vi.value.isVector(isel)) .ldr(elem_ra.h(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }) else switch (elem_vi.value.signedness(isel)) { + .signed => .ldrsh(elem_ra.w(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }), + .unsigned => .ldrh(elem_ra.w(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }), + }, + 4 => .ldr(if (elem_vi.value.isVector(isel)) elem_ra.s() else elem_ra.w(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 2 }, + } }), + 8 => .ldr(if (elem_vi.value.isVector(isel)) elem_ra.d() else elem_ra.x(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 3 }, + } }), + 16 => .ldr(elem_ra.q(), .{ .extended_register = .{ + .base = array_ptr_ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 4 }, + } }), + }); + try index_mat.finish(isel); + const array_vi = try isel.use(bin_op.lhs); + try array_vi.address(isel, 0, array_ptr_ra); + }, + else => { + const ptr_ra = try isel.allocIntReg(); + defer isel.freeReg(ptr_ra); + if (!try elem_vi.value.load(isel, elem_ty, ptr_ra, .{})) break :unused; + const index_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(ptr_ra, ptr_ra, .add, elem_size, index_vi); + const array_vi = try isel.use(bin_op.lhs); + try array_vi.address(isel, 0, ptr_ra); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .slice_elem_val => { + if (isel.live_values.fetchRemove(air.inst_index)) |elem_vi| unused: { + defer elem_vi.value.deref(isel); + + const bin_op = air.data(air.inst_index).bin_op; + const slice_ty = isel.air.typeOf(bin_op.lhs, ip); + const ptr_info = slice_ty.ptrInfo(zcu); + const elem_size = elem_vi.value.size(isel); + const elem_is_vector = elem_vi.value.isVector(isel); + if (switch (elem_size) { + 0 => unreachable, + 1, 2, 4, 8 => true, + 16 => elem_is_vector, + else => false, + }) { + const elem_ra = try elem_vi.value.defReg(isel) orelse break :unused; + const slice_vi = try isel.use(bin_op.lhs); + const index_vi = try isel.use(bin_op.rhs); + var ptr_part_it = slice_vi.field(slice_ty, 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + const base_mat = try ptr_part_vi.?.matReg(isel); + const index_mat = try index_vi.matReg(isel); + try isel.emit(switch (elem_size) { + else => unreachable, + 1 => if (elem_is_vector) .ldr(elem_ra.b(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }) else switch (elem_vi.value.signedness(isel)) { + .signed => .ldrsb(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }), + .unsigned => .ldrb(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }), + }, + 2 => if (elem_is_vector) .ldr(elem_ra.h(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }) else switch (elem_vi.value.signedness(isel)) { + .signed => .ldrsh(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }), + .unsigned => .ldrh(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }), + }, + 4 => .ldr(if (elem_is_vector) elem_ra.s() else elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 2 }, + } }), + 8 => .ldr(if (elem_is_vector) elem_ra.d() else elem_ra.x(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 3 }, + } }), + 16 => .ldr(elem_ra.q(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 4 }, + } }), + }); + try index_mat.finish(isel); + try base_mat.finish(isel); + break :unused; + } else { + const elem_ptr_ra = try isel.allocIntReg(); + defer isel.freeReg(elem_ptr_ra); + if (!try elem_vi.value.load(isel, slice_ty.elemType2(zcu), elem_ptr_ra, .{ + .@"volatile" = ptr_info.flags.is_volatile, + })) break :unused; + const slice_vi = try isel.use(bin_op.lhs); + var ptr_part_it = slice_vi.field(slice_ty, 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + const ptr_part_mat = try ptr_part_vi.?.matReg(isel); + const index_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(elem_ptr_ra, ptr_part_mat.ra, .add, elem_size, index_vi); + try ptr_part_mat.finish(isel); + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .slice_elem_ptr => { + if (isel.live_values.fetchRemove(air.inst_index)) |elem_ptr_vi| unused: { + defer elem_ptr_vi.value.deref(isel); + const elem_ptr_ra = try elem_ptr_vi.value.defReg(isel) orelse break :unused; + + const ty_pl = air.data(air.inst_index).ty_pl; + const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; + const elem_size = ty_pl.ty.toType().childType(zcu).abiSize(zcu); + + const slice_vi = try isel.use(bin_op.lhs); + var ptr_part_it = slice_vi.field(isel.air.typeOf(bin_op.lhs, ip), 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + const ptr_part_mat = try ptr_part_vi.?.matReg(isel); + const index_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(elem_ptr_ra, ptr_part_mat.ra, .add, elem_size, index_vi); + try ptr_part_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .ptr_elem_val => { + if (isel.live_values.fetchRemove(air.inst_index)) |elem_vi| unused: { + defer elem_vi.value.deref(isel); + + const bin_op = air.data(air.inst_index).bin_op; + const ptr_ty = isel.air.typeOf(bin_op.lhs, ip); + const ptr_info = ptr_ty.ptrInfo(zcu); + const elem_size = elem_vi.value.size(isel); + switch (elem_size) { + 0 => unreachable, + 1, 2, 4, 8 => { + const elem_ra = try elem_vi.value.defReg(isel) orelse break :unused; + const base_vi = try isel.use(bin_op.lhs); + const index_vi = try isel.use(bin_op.rhs); + const base_mat = try base_vi.matReg(isel); + const index_mat = try index_vi.matReg(isel); + try isel.emit(switch (elem_size) { + else => unreachable, + 1 => switch (elem_vi.value.signedness(isel)) { + .signed => .ldrsb(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }), + .unsigned => .ldrb(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 0 }, + } }), + }, + 2 => switch (elem_vi.value.signedness(isel)) { + .signed => .ldrsh(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }), + .unsigned => .ldrh(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 1 }, + } }), + }, + 4 => .ldr(elem_ra.w(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 2 }, + } }), + 8 => .ldr(elem_ra.x(), .{ .extended_register = .{ + .base = base_mat.ra.x(), + .index = index_mat.ra.x(), + .extend = .{ .lsl = 3 }, + } }), + }); + try index_mat.finish(isel); + try base_mat.finish(isel); + }, + else => { + const elem_ptr_ra = try isel.allocIntReg(); + defer isel.freeReg(elem_ptr_ra); + if (!try elem_vi.value.load(isel, ptr_ty.elemType2(zcu), elem_ptr_ra, .{ + .@"volatile" = ptr_info.flags.is_volatile, + })) break :unused; + const base_vi = try isel.use(bin_op.lhs); + const base_mat = try base_vi.matReg(isel); + const index_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(elem_ptr_ra, base_mat.ra, .add, elem_size, index_vi); + try base_mat.finish(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .ptr_elem_ptr => { + if (isel.live_values.fetchRemove(air.inst_index)) |elem_ptr_vi| unused: { + defer elem_ptr_vi.value.deref(isel); + const elem_ptr_ra = try elem_ptr_vi.value.defReg(isel) orelse break :unused; + + const ty_pl = air.data(air.inst_index).ty_pl; + const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; + const elem_size = ty_pl.ty.toType().childType(zcu).abiSize(zcu); + + const base_vi = try isel.use(bin_op.lhs); + const base_mat = try base_vi.matReg(isel); + const index_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(elem_ptr_ra, base_mat.ra, .add, elem_size, index_vi); + try base_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .array_to_slice => { + if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| { + defer slice_vi.value.deref(isel); + const ty_op = air.data(air.inst_index).ty_op; + var ptr_part_it = slice_vi.value.field(ty_op.ty.toType(), 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + try ptr_part_vi.?.move(isel, ty_op.operand); + var len_part_it = slice_vi.value.field(ty_op.ty.toType(), 8, 8); + const len_part_vi = try len_part_it.only(isel); + if (try len_part_vi.?.defReg(isel)) |len_ra| try isel.movImmediate( + len_ra.x(), + isel.air.typeOf(ty_op.operand, ip).childType(zcu).arrayLen(zcu), + ); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .int_from_float, .int_from_float_optimized => |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 src_ty = isel.air.typeOf(ty_op.operand, ip); + if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + const dst_int_info = dst_ty.intInfo(zcu); + const src_bits = src_ty.floatBits(isel.target); + switch (@max(dst_int_info.bits, src_bits)) { + 0 => unreachable, + 1...64 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const need_fcvt = switch (src_bits) { + else => unreachable, + 16 => !isel.target.cpu.has(.aarch64, .fullfp16), + 32, 64 => false, + }; + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + const src_ra = if (need_fcvt) try isel.allocVecReg() else src_mat.ra; + defer if (need_fcvt) isel.freeReg(src_ra); + const dst_reg = switch (dst_int_info.bits) { + else => unreachable, + 1...32 => dst_ra.w(), + 33...64 => dst_ra.x(), + }; + const src_reg = switch (src_bits) { + else => unreachable, + 16 => if (need_fcvt) src_ra.s() else src_ra.h(), + 32 => src_ra.s(), + 64 => src_ra.d(), + }; + try isel.emit(switch (dst_int_info.signedness) { + .signed => .fcvtzs(dst_reg, src_reg), + .unsigned => .fcvtzu(dst_reg, src_reg), + }); + if (need_fcvt) try isel.emit(.fcvt(src_reg, src_mat.ra.h())); + try src_mat.finish(isel); + }, + 65...128 => { + try call.prepareReturn(isel); + switch (dst_int_info.bits) { + else => unreachable, + 1...64 => try call.returnLiveIn(isel, dst_vi.value, .r0), + 65...128 => { + var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi64_vi = try dst_hi64_it.only(isel); + try call.returnLiveIn(isel, dst_hi64_vi.?, .r1); + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + try call.returnLiveIn(isel, dst_lo64_vi.?, .r0); + }, + } + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (dst_int_info.bits) { + else => unreachable, + 1...32 => switch (dst_int_info.signedness) { + .signed => switch (src_bits) { + else => unreachable, + 16 => "__fixhfsi", + 32 => "__fixsfsi", + 64 => "__fixdfsi", + 80 => "__fixxfsi", + 128 => "__fixtfsi", + }, + .unsigned => switch (src_bits) { + else => unreachable, + 16 => "__fixunshfsi", + 32 => "__fixunssfsi", + 64 => "__fixunsdfsi", + 80 => "__fixunsxfsi", + 128 => "__fixunstfsi", + }, + }, + 33...64 => switch (dst_int_info.signedness) { + .signed => switch (src_bits) { + else => unreachable, + 16 => "__fixhfdi", + 32 => "__fixsfdi", + 64 => "__fixdfdi", + 80 => "__fixxfdi", + 128 => "__fixtfdi", + }, + .unsigned => switch (src_bits) { + else => unreachable, + 16 => "__fixunshfdi", + 32 => "__fixunssfdi", + 64 => "__fixunsdfdi", + 80 => "__fixunsxfdi", + 128 => "__fixunstfdi", + }, + }, + 65...128 => switch (dst_int_info.signedness) { + .signed => switch (src_bits) { + else => unreachable, + 16 => "__fixhfti", + 32 => "__fixsfti", + 64 => "__fixdfti", + 80 => "__fixxfti", + 128 => "__fixtfti", + }, + .unsigned => switch (src_bits) { + else => unreachable, + 16 => "__fixunshfti", + 32 => "__fixunssfti", + 64 => "__fixunsdfti", + 80 => "__fixunsxfti", + 128 => "__fixunstfti", + }, + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(ty_op.operand); + switch (src_bits) { + else => unreachable, + 16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0), + 80 => { + var src_hi16_it = src_vi.field(src_ty, 8, 8); + const src_hi16_vi = try src_hi16_it.only(isel); + try call.paramLiveOut(isel, src_hi16_vi.?, .r1); + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try call.paramLiveOut(isel, src_lo64_vi.?, .r0); + }, + } + try call.finishParams(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; + }, + .float_from_int => |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 src_ty = isel.air.typeOf(ty_op.operand, ip); + const dst_bits = dst_ty.floatBits(isel.target); + if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }); + const src_int_info = src_ty.intInfo(zcu); + switch (@max(dst_bits, src_int_info.bits)) { + 0 => unreachable, + 1...64 => { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const need_fcvt = switch (dst_bits) { + else => unreachable, + 16 => !isel.target.cpu.has(.aarch64, .fullfp16), + 32, 64 => false, + }; + if (need_fcvt) try isel.emit(.fcvt(dst_ra.h(), dst_ra.s())); + const src_vi = try isel.use(ty_op.operand); + const src_mat = try src_vi.matReg(isel); + const dst_reg = switch (dst_bits) { + else => unreachable, + 16 => if (need_fcvt) dst_ra.s() else dst_ra.h(), + 32 => dst_ra.s(), + 64 => dst_ra.d(), + }; + const src_reg = switch (src_int_info.bits) { + else => unreachable, + 1...32 => src_mat.ra.w(), + 33...64 => src_mat.ra.x(), + }; + try isel.emit(switch (src_int_info.signedness) { + .signed => .scvtf(dst_reg, src_reg), + .unsigned => .ucvtf(dst_reg, src_reg), + }); + try src_mat.finish(isel); + }, + 65...128 => { + try call.prepareReturn(isel); + switch (dst_bits) { + else => unreachable, + 16, 32, 64, 128 => try call.returnLiveIn(isel, dst_vi.value, .v0), + 80 => { + var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8); + const dst_hi16_vi = try dst_hi16_it.only(isel); + try call.returnLiveIn(isel, dst_hi16_vi.?, .r1); + var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8); + const dst_lo64_vi = try dst_lo64_it.only(isel); + try call.returnLiveIn(isel, dst_lo64_vi.?, .r0); + }, + } + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (src_int_info.bits) { + else => unreachable, + 1...32 => switch (src_int_info.signedness) { + .signed => switch (dst_bits) { + else => unreachable, + 16 => "__floatsihf", + 32 => "__floatsisf", + 64 => "__floatsidf", + 80 => "__floatsixf", + 128 => "__floatsitf", + }, + .unsigned => switch (dst_bits) { + else => unreachable, + 16 => "__floatunsihf", + 32 => "__floatunsisf", + 64 => "__floatunsidf", + 80 => "__floatunsixf", + 128 => "__floatunsitf", + }, + }, + 33...64 => switch (src_int_info.signedness) { + .signed => switch (dst_bits) { + else => unreachable, + 16 => "__floatdihf", + 32 => "__floatdisf", + 64 => "__floatdidf", + 80 => "__floatdixf", + 128 => "__floatditf", + }, + .unsigned => switch (dst_bits) { + else => unreachable, + 16 => "__floatundihf", + 32 => "__floatundisf", + 64 => "__floatundidf", + 80 => "__floatundixf", + 128 => "__floatunditf", + }, + }, + 65...128 => switch (src_int_info.signedness) { + .signed => switch (dst_bits) { + else => unreachable, + 16 => "__floattihf", + 32 => "__floattisf", + 64 => "__floattidf", + 80 => "__floattixf", + 128 => "__floattitf", + }, + .unsigned => switch (dst_bits) { + else => unreachable, + 16 => "__floatuntihf", + 32 => "__floatuntisf", + 64 => "__floatuntidf", + 80 => "__floatuntixf", + 128 => "__floatuntitf", + }, + }, + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const src_vi = try isel.use(ty_op.operand); + switch (src_int_info.bits) { + else => unreachable, + 1...64 => try call.paramLiveOut(isel, src_vi, .r0), + 65...128 => { + var src_hi64_it = src_vi.field(src_ty, 8, 8); + const src_hi64_vi = try src_hi64_it.only(isel); + try call.paramLiveOut(isel, src_hi64_vi.?, .r1); + var src_lo64_it = src_vi.field(src_ty, 0, 8); + const src_lo64_vi = try src_lo64_it.only(isel); + try call.paramLiveOut(isel, src_lo64_vi.?, .r0); + }, + } + try call.finishParams(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; + }, + .memset => |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 (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 }, + 2, 4, 8 => |size| { + const dst_vi = try isel.use(bin_op.lhs); + const ptr_ra = try isel.allocIntReg(); + const fill_vi = try isel.use(bin_op.rhs); + const fill_mat = try fill_vi.matReg(isel); + const len_mat: Value.Materialize = len_mat: switch (dst_info.flags.size) { + .one => .{ .vi = undefined, .ra = try isel.allocIntReg() }, + .many => unreachable, + .slice => { + var dst_len_it = dst_vi.field(dst_ty, 8, 8); + const dst_len_vi = try dst_len_it.only(isel); + break :len_mat try dst_len_vi.?.matReg(isel); + }, + .c => unreachable, + }; + + const skip_label = isel.instructions.items.len; + _ = try isel.instructions.addOne(gpa); + try isel.emit(.sub(len_mat.ra.x(), len_mat.ra.x(), .{ .immediate = 1 })); + try isel.emit(switch (size) { + else => unreachable, + 2 => .strh(fill_mat.ra.w(), .{ .post_index = .{ .base = ptr_ra.x(), .index = 2 } }), + 4 => .str(fill_mat.ra.w(), .{ .post_index = .{ .base = ptr_ra.x(), .index = 4 } }), + 8 => .str(fill_mat.ra.x(), .{ .post_index = .{ .base = ptr_ra.x(), .index = 8 } }), + }); + isel.instructions.items[skip_label] = .cbnz( + len_mat.ra.x(), + -@as(i21, @intCast((isel.instructions.items.len - 1 - skip_label) << 2)), + ); + switch (dst_info.flags.size) { + .one => { + const len_imm = ZigType.fromInterned(dst_info.child).arrayLen(zcu); + assert(len_imm > 0); + try isel.movImmediate(len_mat.ra.x(), len_imm); + isel.freeReg(len_mat.ra); + try fill_mat.finish(isel); + isel.freeReg(ptr_ra); + try dst_vi.liveOut(isel, ptr_ra); + }, + .many => unreachable, + .slice => { + try isel.emit(.cbz( + len_mat.ra.x(), + @intCast((isel.instructions.items.len + 1 - skip_label) << 2), + )); + try len_mat.finish(isel); + try fill_mat.finish(isel); + isel.freeReg(ptr_ra); + var dst_ptr_it = dst_vi.field(dst_ty, 0, 8); + const dst_ptr_vi = try dst_ptr_it.only(isel); + try dst_ptr_vi.?.liveOut(isel, ptr_ra); + }, + .c => unreachable, + } + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + break :air_tag; + }, + else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty) }), + } + }; + + try call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = "memset", + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const dst_vi = try isel.use(bin_op.lhs); + switch (dst_info.flags.size) { + .one => { + try isel.movImmediate(.x2, ZigType.fromInterned(dst_info.child).abiSize(zcu)); + switch (fill_byte) { + .constant => |byte| try isel.movImmediate(.w1, byte), + .value => |byte| try call.paramLiveOut(isel, try isel.use(byte), .r1), + } + try call.paramLiveOut(isel, dst_vi, .r0); + }, + .many => unreachable, + .slice => { + var dst_ptr_it = dst_vi.field(dst_ty, 0, 8); + const dst_ptr_vi = try dst_ptr_it.only(isel); + var dst_len_it = dst_vi.field(dst_ty, 8, 8); + const dst_len_vi = try dst_len_it.only(isel); + try isel.elemPtr(.r2, .zr, .add, ZigType.fromInterned(dst_info.child).abiSize(zcu), dst_len_vi.?); + switch (fill_byte) { + .constant => |byte| try isel.movImmediate(.w1, byte), + .value => |byte| try call.paramLiveOut(isel, try isel.use(byte), .r1), + } + try call.paramLiveOut(isel, dst_ptr_vi.?, .r0); + }, + .c => unreachable, + } + try call.finishParams(isel); + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .memcpy, .memmove => |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); + + try call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = @tagName(air_tag), + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + switch (dst_info.flags.size) { + .one => { + const dst_vi = try isel.use(bin_op.lhs); + const src_vi = try isel.use(bin_op.rhs); + try isel.movImmediate(.x2, ZigType.fromInterned(dst_info.child).abiSize(zcu)); + try call.paramLiveOut(isel, src_vi, .r1); + try call.paramLiveOut(isel, dst_vi, .r0); + }, + .many => unreachable, + .slice => { + const dst_vi = try isel.use(bin_op.lhs); + var dst_ptr_it = dst_vi.field(dst_ty, 0, 8); + const dst_ptr_vi = try dst_ptr_it.only(isel); + var dst_len_it = dst_vi.field(dst_ty, 8, 8); + const dst_len_vi = try dst_len_it.only(isel); + const src_vi = try isel.use(bin_op.rhs); + try isel.elemPtr(.r2, .zr, .add, ZigType.fromInterned(dst_info.child).abiSize(zcu), dst_len_vi.?); + try call.paramLiveOut(isel, src_vi, .r1); + try call.paramLiveOut(isel, dst_ptr_vi.?, .r0); + }, + .c => unreachable, + } + try call.finishParams(isel); + + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .atomic_load => { + const atomic_load = air.data(air.inst_index).atomic_load; + const ptr_ty = isel.air.typeOf(atomic_load.ptr, ip); + const ptr_info = ptr_ty.ptrInfo(zcu); + if (atomic_load.order != .unordered) return isel.fail("ordered atomic load", .{}); + if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed atomic load", .{}); + + if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| { + defer dst_vi.value.deref(isel); + var ptr_mat: ?Value.Materialize = null; + var dst_part_it = dst_vi.value.parts(isel); + while (dst_part_it.next()) |dst_part_vi| { + const dst_ra = try dst_part_vi.defReg(isel) orelse continue; + if (ptr_mat == null) { + const ptr_vi = try isel.use(atomic_load.ptr); + ptr_mat = try ptr_vi.matReg(isel); + } + try isel.emit(switch (dst_part_vi.size(isel)) { + else => |size| return isel.fail("bad atomic load size of {d} from {f}", .{ + size, isel.fmtType(ptr_ty), + }), + 1 => switch (dst_part_vi.signedness(isel)) { + .signed => .ldrsb(dst_ra.w(), .{ .unsigned_offset = .{ + .base = ptr_mat.?.ra.x(), + .offset = @intCast(dst_part_vi.get(isel).offset_from_parent), + } }), + .unsigned => .ldrb(dst_ra.w(), .{ .unsigned_offset = .{ + .base = ptr_mat.?.ra.x(), + .offset = @intCast(dst_part_vi.get(isel).offset_from_parent), + } }), + }, + 2 => switch (dst_part_vi.signedness(isel)) { + .signed => .ldrsh(dst_ra.w(), .{ .unsigned_offset = .{ + .base = ptr_mat.?.ra.x(), + .offset = @intCast(dst_part_vi.get(isel).offset_from_parent), + } }), + .unsigned => .ldrh(dst_ra.w(), .{ .unsigned_offset = .{ + .base = ptr_mat.?.ra.x(), + .offset = @intCast(dst_part_vi.get(isel).offset_from_parent), + } }), + }, + 4 => .ldr(dst_ra.w(), .{ .unsigned_offset = .{ + .base = ptr_mat.?.ra.x(), + .offset = @intCast(dst_part_vi.get(isel).offset_from_parent), + } }), + 8 => .ldr(dst_ra.x(), .{ .unsigned_offset = .{ + .base = ptr_mat.?.ra.x(), + .offset = @intCast(dst_part_vi.get(isel).offset_from_parent), + } }), + }); + } + if (ptr_mat) |mat| try mat.finish(isel); + } else if (ptr_info.flags.is_volatile) return isel.fail("volatile atomic load", .{}); + + 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); + + const ty_pl = air.data(air.inst_index).ty_pl; + const agg_ty = ty_pl.ty.toType(); + switch (ip.indexToKey(agg_ty.toIntern())) { + .array_type => |array_type| { + const elems: []const Air.Inst.Ref = + @ptrCast(isel.air.extra.items[ty_pl.payload..][0..@intCast(array_type.len)]); + var elem_offset: u64 = 0; + const elem_size = ZigType.fromInterned(array_type.child).abiSize(zcu); + for (elems) |elem| { + var agg_part_it = agg_vi.value.field(agg_ty, elem_offset, elem_size); + const agg_part_vi = try agg_part_it.only(isel); + try agg_part_vi.?.move(isel, elem); + elem_offset += elem_size; + } + switch (array_type.sentinel) { + .none => {}, + else => |sentinel| { + var agg_part_it = agg_vi.value.field(agg_ty, elem_offset, elem_size); + const agg_part_vi = try agg_part_it.only(isel); + try agg_part_vi.?.move(isel, .fromIntern(sentinel)); + }, + } + }, + .struct_type => { + const loaded_struct = ip.loadStructType(agg_ty.toIntern()); + const elems: []const Air.Inst.Ref = + @ptrCast(isel.air.extra.items[ty_pl.payload..][0..loaded_struct.field_types.len]); + var field_offset: u64 = 0; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + while (field_it.next()) |field_index| { + const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + field_offset = field_ty.structFieldAlignment( + loaded_struct.fieldAlign(ip, field_index), + loaded_struct.layout, + zcu, + ).forward(field_offset); + const field_size = field_ty.abiSize(zcu); + if (field_size == 0) continue; + var agg_part_it = agg_vi.value.field(agg_ty, field_offset, field_size); + const agg_part_vi = try agg_part_it.only(isel); + try agg_part_vi.?.move(isel, elems[field_index]); + field_offset += field_size; + } + assert(field_offset == agg_vi.value.size(isel)); + }, + .tuple_type => |tuple_type| { + const elems: []const Air.Inst.Ref = + @ptrCast(isel.air.extra.items[ty_pl.payload..][0..tuple_type.types.len]); + var field_offset: u64 = 0; + for ( + tuple_type.types.get(ip), + tuple_type.values.get(ip), + elems, + ) |field_ty_index, field_val, elem| { + if (field_val != .none) continue; + const field_ty: ZigType = .fromInterned(field_ty_index); + field_offset = field_ty.abiAlignment(zcu).forward(field_offset); + const field_size = field_ty.abiSize(zcu); + if (field_size == 0) continue; + var agg_part_it = agg_vi.value.field(agg_ty, field_offset, field_size); + const agg_part_vi = try agg_part_it.only(isel); + try agg_part_vi.?.move(isel, elem); + field_offset += field_size; + } + assert(field_offset == agg_vi.value.size(isel)); + }, + else => return isel.fail("aggregate init {f}", .{isel.fmtType(agg_ty)}), + } + } + 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); + + 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)}); + + try un_vi.value.defAddr(isel, un_ty, null, comptime &.initFill(.free)) orelse break :unused; + + try call.prepareReturn(isel); + try call.finishReturn(isel); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = "memcpy", + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + 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.finishParams(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .prefetch => { + const prefetch = air.data(air.inst_index).prefetch; + if (!(prefetch.rw == .write and prefetch.cache == .instruction)) { + const maybe_slice_ty = isel.air.typeOf(prefetch.ptr, ip); + const maybe_slice_vi = try isel.use(prefetch.ptr); + const ptr_vi = if (maybe_slice_ty.isSlice(zcu)) ptr_vi: { + var ptr_part_it = maybe_slice_vi.field(maybe_slice_ty, 0, 8); + const ptr_part_vi = try ptr_part_it.only(isel); + break :ptr_vi ptr_part_vi.?; + } else maybe_slice_vi; + const ptr_mat = try ptr_vi.matReg(isel); + try isel.emit(.prfm(.{ + .policy = switch (prefetch.locality) { + 1, 2, 3 => .keep, + 0 => .strm, + }, + .target = switch (prefetch.locality) { + 0, 3 => .l1, + 2 => .l2, + 1 => .l3, + }, + .type = switch (prefetch.rw) { + .read => switch (prefetch.cache) { + .data => .pld, + .instruction => .pli, + }, + .write => switch (prefetch.cache) { + .data => .pst, + .instruction => unreachable, + }, + }, + }, .{ .base = ptr_mat.ra.x() })); + try ptr_mat.finish(isel); + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .mul_add => { + if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: { + defer res_vi.value.deref(isel); + + const pl_op = air.data(air.inst_index).pl_op; + const bin_op = isel.air.extraData(Air.Bin, pl_op.payload).data; + const ty = isel.air.typeOf(pl_op.operand, ip); + switch (ty.floatBits(isel.target)) { + else => unreachable, + 16, 32, 64 => |bits| { + const res_ra = try res_vi.value.defReg(isel) orelse break :unused; + 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())); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + const addend_vi = try isel.use(pl_op.operand); + const lhs_mat = try lhs_vi.matReg(isel); + const rhs_mat = try rhs_vi.matReg(isel); + const addend_mat = try addend_vi.matReg(isel); + const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra; + defer if (need_fcvt) isel.freeReg(lhs_ra); + const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra; + defer if (need_fcvt) isel.freeReg(rhs_ra); + const addend_ra = if (need_fcvt) try isel.allocVecReg() else addend_mat.ra; + defer if (need_fcvt) isel.freeReg(addend_ra); + try isel.emit(bits: switch (bits) { + else => unreachable, + 16 => if (need_fcvt) + continue :bits 32 + else + .fmadd(res_ra.h(), lhs_ra.h(), rhs_ra.h(), addend_ra.h()), + 32 => .fmadd(res_ra.s(), lhs_ra.s(), rhs_ra.s(), addend_ra.s()), + 64 => .fmadd(res_ra.d(), lhs_ra.d(), rhs_ra.d(), addend_ra.d()), + }); + if (need_fcvt) { + try isel.emit(.fcvt(addend_ra.s(), addend_mat.ra.h())); + try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h())); + try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h())); + } + try addend_mat.finish(isel); + try rhs_mat.finish(isel); + try lhs_mat.finish(isel); + }, + 80, 128 => |bits| { + 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); + + try call.prepareCallee(isel); + try isel.global_relocs.append(gpa, .{ + .global = switch (bits) { + else => unreachable, + 16 => "__fmah", + 32 => "fmaf", + 64 => "fma", + 80 => "__fmax", + 128 => "fmaq", + }, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.bl(0)); + try call.finishCallee(isel); + + try call.prepareParams(isel); + const lhs_vi = try isel.use(bin_op.lhs); + const rhs_vi = try isel.use(bin_op.rhs); + const addend_vi = try isel.use(pl_op.operand); + switch (bits) { + else => unreachable, + 16, 32, 64, 128 => { + try call.paramLiveOut(isel, addend_vi, .v2); + try call.paramLiveOut(isel, rhs_vi, .v1); + try call.paramLiveOut(isel, lhs_vi, .v0); + }, + 80 => { + var addend_hi16_it = addend_vi.field(ty, 8, 8); + const addend_hi16_vi = try addend_hi16_it.only(isel); + try call.paramLiveOut(isel, addend_hi16_vi.?, .r5); + var addend_lo64_it = addend_vi.field(ty, 0, 8); + const addend_lo64_vi = try addend_lo64_it.only(isel); + try call.paramLiveOut(isel, addend_lo64_vi.?, .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.?, .r3); + 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.?, .r2); + var lhs_hi16_it = lhs_vi.field(ty, 8, 8); + const lhs_hi16_vi = try lhs_hi16_it.only(isel); + try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1); + var lhs_lo64_it = lhs_vi.field(ty, 0, 8); + const lhs_lo64_vi = try lhs_lo64_it.only(isel); + try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0); + }, + } + try call.finishParams(isel); + }, + } + } + if (air.next()) |next_air_tag| continue :air_tag next_air_tag; + }, + .field_parent_ptr => { + if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: { + defer dst_vi.value.deref(isel); + const ty_pl = air.data(air.inst_index).ty_pl; + const extra = isel.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; + switch (codegen.fieldOffset( + ty_pl.ty.toType(), + isel.air.typeOf(extra.field_ptr, ip), + extra.field_index, + zcu, + )) { + 0 => try dst_vi.value.move(isel, extra.field_ptr), + else => |field_offset| { + const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused; + const src_vi = try isel.use(extra.field_ptr); + const src_mat = try src_vi.matReg(isel); + const lo12: u12 = @truncate(field_offset >> 0); + const hi12: u12 = @intCast(field_offset >> 12); + if (hi12 > 0) try isel.emit(.sub( + dst_ra.x(), + if (lo12 > 0) dst_ra.x() else src_mat.ra.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0) try isel.emit(.sub(dst_ra.x(), src_mat.ra.x(), .{ .immediate = lo12 })); + try src_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); + const ptr_ra = try ptr_vi.value.defReg(isel) orelse break :unused; + + 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, .{ + .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, .{ + .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, .{ + .nav = ty_nav.nav, + .reloc = .{ .label = @intCast(isel.instructions.items.len) }, + }); + try isel.emit(.adrp(ptr_ra.x(), 0)); + }, + } else try isel.movImmediate(ptr_ra.x(), isel.pt.navAlignment(ty_nav.nav).forward(0xaaaaaaaaaaaaaaaa)); + } + 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, + .int_from_float_optimized_safe, + .wasm_memory_size, + .wasm_memory_grow, + .work_item_id, + .work_group_size, + .work_group_id, + => unreachable, + } + assert(air.body_index == 0); +} + +pub fn verify(isel: *Select, check_values: bool) void { + if (!std.debug.runtime_safety) return; + assert(isel.blocks.count() == 1 and isel.blocks.keys()[0] == Select.Block.main); + assert(isel.active_loops.items.len == 0); + assert(isel.dom_start == 0 and isel.dom_len == 0); + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) { + _ => { + isel.dumpValues(.all); + unreachable; + }, + .allocating, .free => {}, + }; + if (check_values) for (isel.values.items) |value| if (value.refs != 0) { + isel.dumpValues(.only_referenced); + unreachable; + }; +} + +/// Stack Frame Layout +/// +-+-----------------------------------+ +/// |R| allocated stack | +/// +-+-----------------------------------+ +/// |S| caller frame record | +---------------+ +/// +-+-----------------------------------+ <-| entry/exit FP | +/// |R| caller frame | +---------------+ +/// +-+-----------------------------------+ +/// |R| variable incoming stack arguments | +---------------+ +/// +-+-----------------------------------+ <-| __stack | +/// |S| named incoming stack arguments | +---------------+ +/// +-+-----------------------------------+ <-| entry/exit SP | +/// |S| incoming gr arguments | | __gr_top | +/// +-+-----------------------------------+ +---------------+ +/// |S| alignment gap | +/// +-+-----------------------------------+ +/// |S| frame record | +----------+ +/// +-+-----------------------------------+ <-| FP | +/// |S| incoming vr arguments | | __vr_top | +/// +-+-----------------------------------+ +----------+ +/// |L| alignment gap | +/// +-+-----------------------------------+ +/// |L| callee saved vr area | +/// +-+-----------------------------------+ +/// |L| callee saved gr area | +----------------------+ +/// +-+-----------------------------------+ <-| prologue/epilogue SP | +/// |R| realignment gap | +----------------------+ +/// +-+-----------------------------------+ +/// |L| locals | +/// +-+-----------------------------------+ +/// |S| outgoing stack arguments | +----+ +/// +-+-----------------------------------+ <-| SP | +/// |R| unallocated stack | +----+ +/// +-+-----------------------------------+ +/// [S] Size computed by `analyze`, can be used by the body. +/// [L] Size computed by `layout`, can be used by the prologue/epilogue. +/// [R] Size unknown until runtime, can vary from one call to the next. +/// +/// Constraints that led to this layout: +/// * FP to __stack/__gr_top/__vr_top must only pass through [S] +/// * SP to outgoing stack arguments/locals must only pass through [S] +/// * entry/exit SP to prologue/epilogue SP must only pass through [S/L] +/// * all save areas must be at a positive offset from prologue/epilogue SP +/// * the entry/exit SP to prologue/epilogue SP distance must +/// - be a multiple of 16 due to hardware restrictions on the value of SP +/// - conform to the limit from the first matching condition in the +/// following list due to instruction encoding limitations +/// 1. callee saved gr count >= 2: multiple of 8 of at most 504 bytes +/// 2. callee saved vr count >= 2: multiple of 8 of at most 504 bytes +/// 3. callee saved gr count >= 1: at most 255 bytes +/// 4. callee saved vr count >= 1: at most 255 bytes +/// 5. variable incoming vr argument count >= 2: multiple of 16 of at most 1008 bytes +/// 6. variable incoming vr argument count >= 1: at most 255 bytes +/// 7. have frame record: multiple of 8 of at most 504 bytes +pub fn layout( + isel: *Select, + incoming: CallAbiIterator, + have_va: bool, + saved_gra_len: u7, + saved_vra_len: u7, + mod: *const Package.Module, +) !usize { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + const nav = ip.getNav(isel.nav_index); + 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); + + var saves_buf: [10 + 8 + 8 + 2 + 8]struct { + class: enum { integer, vector }, + needs_restore: bool, + register: Register, + offset: u10, + size: u5, + } = undefined; + const saves, const saves_size, const frame_record_offset = saves: { + var saves_len: usize = 0; + var saves_size: u10 = 0; + var save_ra: Register.Alias = undefined; + + // callee saved gr area + save_ra = .r19; + while (save_ra != .r29) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) { + if (!isel.saved_registers.contains(save_ra)) continue; + saves_size = std.mem.alignForward(u10, saves_size, 8); + saves_buf[saves_len] = .{ + .class = .integer, + .needs_restore = true, + .register = save_ra.x(), + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + } + var deferred_gr = if (saves_size == 8 or (saves_size % 16 != 0 and saved_gra_len % 2 != 0)) gr: { + saves_len -= 1; + saves_size -= 8; + break :gr saves_buf[saves_len].register; + } else null; + defer assert(deferred_gr == null); + + // callee saved vr area + save_ra = .v8; + while (save_ra != .v16) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) { + if (!isel.saved_registers.contains(save_ra)) continue; + saves_size = std.mem.alignForward(u10, saves_size, 8); + saves_buf[saves_len] = .{ + .class = .vector, + .needs_restore = true, + .register = save_ra.d(), + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + } + if (deferred_gr != null and saved_gra_len % 2 == 0) { + saves_size = std.mem.alignForward(u10, saves_size, 8); + saves_buf[saves_len] = .{ + .class = .integer, + .needs_restore = true, + .register = deferred_gr.?, + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + deferred_gr = null; + } + if (saves_size % 16 != 0 and saved_vra_len % 2 != 0) { + const prev_save = &saves_buf[saves_len - 1]; + switch (prev_save.class) { + .integer => {}, + .vector => { + prev_save.register = prev_save.register.alias.q(); + prev_save.size = 16; + saves_size += 8; + }, + } + } + + // 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)) { + saves_size = std.mem.alignForward(u10, saves_size, 16); + saves_buf[saves_len] = .{ + .class = .vector, + .needs_restore = false, + .register = save_ra.q(), + .offset = saves_size, + .size = 16, + }; + saves_len += 1; + saves_size += 16; + } + + // frame record + saves_size = std.mem.alignForward(u10, saves_size, 16); + const frame_record_offset = saves_size; + saves_buf[saves_len] = .{ + .class = .integer, + .needs_restore = true, + .register = .fp, + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + + saves_size = std.mem.alignForward(u10, saves_size, 8); + saves_buf[saves_len] = .{ + .class = .integer, + .needs_restore = true, + .register = .lr, + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + + // incoming gr arguments + if (deferred_gr) |gr| { + saves_size = std.mem.alignForward(u10, saves_size, 8); + saves_buf[saves_len] = .{ + .class = .integer, + .needs_restore = true, + .register = gr, + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + deferred_gr = null; + } + 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)) { + saves_size = std.mem.alignForward(u10, saves_size, 8); + saves_buf[saves_len] = .{ + .class = .integer, + .needs_restore = false, + .register = save_ra.x(), + .offset = saves_size, + .size = 8, + }; + saves_len += 1; + saves_size += 8; + } + + assert(InternPool.Alignment.@"16".check(saves_size)); + break :saves .{ saves_buf[0..saves_len], saves_size, frame_record_offset }; + }; + + { + 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; + } + } + + const scratch_reg: Register = if (isel.stack_align == .@"16") + .sp + else if (stack_size == 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(), + } })); + } + wip_mir_log.debug("", .{}); + } + + const epilogue = isel.instructions.items.len; + if (isel.returns) { + try isel.emit(.ret(.lr)); + var save_index: usize = 0; + 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 + saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset) + { + try isel.emit(.ldp( + saves[save_index + 0].register, + saves[save_index + 1].register, + switch (saves[save_index + 0].offset) { + 0 => .{ .post_index = .{ + .base = .sp, + .index = @intCast(saves_size), + } }, + else => |offset| .{ .signed_offset = .{ + .base = .sp, + .offset = @intCast(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 = .{ + .base = .sp, + .index = @intCast(saves_size), + } }, + else => |offset| .{ .unsigned_offset = .{ + .base = .sp, + .offset = @intCast(offset), + } }, + }, + )); + save_index += 1; + } else save_index += 1; + } + if (isel.stack_align != .@"16" or (stack_size_low > 0 and stack_size_high > 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_low > 0) try isel.emit(.add(.sp, .sp, .{ + .immediate = stack_size_low, + })); + } + wip_mir_log.debug("{f}:\n", .{nav.fqn.fmt(ip)}); + } + return epilogue; +} + +fn fmtDom(isel: *Select, inst: Air.Inst.Index, start: u32, len: u32) struct { + isel: *Select, + inst: Air.Inst.Index, + start: u32, + len: u32, + pub fn format(data: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("%{d} -> {{", .{@intFromEnum(data.inst)}); + var first = true; + for (data.isel.blocks.keys()[0..data.len], 0..) |block_inst_index, dom_index| { + if (@as(u1, @truncate(data.isel.dom.items[ + data.start + dom_index / @bitSizeOf(DomInt) + ] >> @truncate(dom_index))) == 0) continue; + if (first) { + first = false; + } else { + try writer.writeByte(','); + } + switch (block_inst_index) { + Block.main => try writer.writeAll(" %main"), + else => try writer.print(" %{d}", .{@intFromEnum(block_inst_index)}), + } + } + if (!first) try writer.writeByte(' '); + try writer.writeByte('}'); + } +} { + return .{ .isel = isel, .inst = inst, .start = start, .len = len }; +} + +fn fmtLoopLive(isel: *Select, loop_inst: Air.Inst.Index) struct { + isel: *Select, + inst: Air.Inst.Index, + pub fn format(data: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + const loops = data.isel.loops.values(); + const loop_index = data.isel.loops.getIndex(data.inst).?; + const live_insts = + data.isel.loop_live.list.items[loops[loop_index].live..loops[loop_index + 1].live]; + + try writer.print("%{d} <- {{", .{@intFromEnum(data.inst)}); + var first = true; + for (live_insts) |live_inst| { + if (first) { + first = false; + } else { + try writer.writeByte(','); + } + try writer.print(" %{d}", .{@intFromEnum(live_inst)}); + } + if (!first) try writer.writeByte(' '); + try writer.writeByte('}'); + } +} { + return .{ .isel = isel, .inst = loop_inst }; +} + +fn fmtType(isel: *Select, ty: ZigType) ZigType.Formatter { + return ty.fmt(isel.pt); +} + +fn fmtConstant(isel: *Select, constant: Constant) @typeInfo(@TypeOf(Constant.fmtValue)).@"fn".return_type.? { + return constant.fmtValue(isel.pt); +} + +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 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); + switch (isel.target.cpu.arch.endian()) { + .little => @memcpy(literals, words), + .big => for (words, 0..) |word, word_index| { + literals[literals.len - 1 - word_index] = @byteSwap(word); + }, + } +} + +fn fail(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { + @branchHint(.cold); + return isel.pt.zcu.codegenFail(isel.nav_index, format, args); +} + +/// dst = src +fn movImmediate(isel: *Select, dst_reg: Register, src_imm: u64) !void { + const sf = dst_reg.format.integer; + if (src_imm == 0) { + const zr: Register = switch (sf) { + .word => .wzr, + .doubleword => .xzr, + }; + return isel.emit(.orr(dst_reg, zr, .{ .register = zr })); + } + + const Part = u16; + const min_part: Part = std.math.minInt(Part); + const max_part: Part = std.math.maxInt(Part); + + const parts: [4]Part = @bitCast(switch (sf) { + .word => @as(u32, @intCast(src_imm)), + .doubleword => @as(u64, @intCast(src_imm)), + }); + const width: u7 = switch (sf) { + .word => 32, + .doubleword => 64, + }; + const parts_len: u3 = @intCast(@divExact(width, @bitSizeOf(Part))); + var equal_min_count: u3 = 0; + var equal_max_count: u3 = 0; + for (parts[0..parts_len]) |part| { + equal_min_count += @intFromBool(part == min_part); + equal_max_count += @intFromBool(part == max_part); + } + + const equal_fill_count, const fill_part: Part = if (equal_min_count >= equal_max_count) + .{ equal_min_count, min_part } + else + .{ equal_max_count, max_part }; + var remaining_parts = @max(parts_len - equal_fill_count, 1); + + if (remaining_parts > 1) { + var elem_width: u8 = 2; + while (elem_width <= width) : (elem_width <<= 1) { + const emask = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - elem_width); + const rmask = @divExact(@as(u64, switch (sf) { + .word => std.math.maxInt(u32), + .doubleword => std.math.maxInt(u64), + }), emask); + const elem = src_imm & emask; + if (src_imm != elem * rmask) continue; + const imask: u64 = @bitCast(@as(i64, @bitCast(elem << 63)) >> 63); + const lsb0 = elem ^ (imask & emask); + const lsb1 = (lsb0 - 1) | lsb0; + if ((lsb1 +% 1) & lsb1 == 0) { + const lo: u6 = @intCast(@ctz(lsb0)); + const hi: u6 = @intCast(@clz(lsb0) - (64 - elem_width)); + const mid: u6 = @intCast(elem_width - lo - hi); + const smask: u6 = @truncate(imask); + const mid_masked = mid & ~smask; + return isel.emit(.orr( + dst_reg, + switch (sf) { + .word => .wzr, + .doubleword => .xzr, + }, + .{ .immediate = .{ + .N = @enumFromInt(elem_width >> 6), + .immr = hi + mid_masked, + .imms = ((((lo + hi) & smask) | mid_masked) - 1) | -%@as(u6, @truncate(elem_width)) << 1, + } }, + )); + } + } + } + + var part_index = parts_len; + while (part_index > 0) { + part_index -= 1; + if (part_index >= remaining_parts and parts[part_index] == fill_part) continue; + remaining_parts -= 1; + try isel.emit(if (remaining_parts > 0) .movk( + dst_reg, + parts[part_index], + .{ .lsl = @enumFromInt(part_index) }, + ) else switch (fill_part) { + else => unreachable, + min_part => .movz( + dst_reg, + parts[part_index], + .{ .lsl = @enumFromInt(part_index) }, + ), + max_part => .movn( + dst_reg, + ~parts[part_index], + .{ .lsl = @enumFromInt(part_index) }, + ), + }); + } + assert(remaining_parts == 0); +} + +/// elem_ptr = base +- elem_size * index +/// elem_ptr, base, and index may alias +fn elemPtr( + isel: *Select, + elem_ptr_ra: Register.Alias, + base_ra: Register.Alias, + op: codegen.aarch64.encoding.Instruction.AddSubtractOp, + elem_size: u64, + index_vi: Value.Index, +) !void { + const index_mat = try index_vi.matReg(isel); + switch (@popCount(elem_size)) { + 0 => unreachable, + 1 => try isel.emit(switch (op) { + .add => switch (base_ra) { + else => .add(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{ + .register = index_mat.ra.x(), + .shift = .{ .lsl = @intCast(@ctz(elem_size)) }, + } }), + .zr => switch (@ctz(elem_size)) { + 0 => .orr(elem_ptr_ra.x(), .xzr, .{ .register = index_mat.ra.x() }), + else => |shift| .ubfm(elem_ptr_ra.x(), index_mat.ra.x(), .{ + .N = .doubleword, + .immr = @intCast(64 - shift), + .imms = @intCast(63 - shift), + }), + }, + }, + .sub => .sub(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{ + .register = index_mat.ra.x(), + .shift = .{ .lsl = @intCast(@ctz(elem_size)) }, + } }), + }), + 2 => { + const shift: u6 = @intCast(@ctz(elem_size)); + const temp_ra = temp_ra: switch (op) { + .add => switch (base_ra) { + else => { + const temp_ra = try isel.allocIntReg(); + errdefer isel.freeReg(temp_ra); + try isel.emit(.add(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{ + .register = temp_ra.x(), + .shift = .{ .lsl = shift }, + } })); + break :temp_ra temp_ra; + }, + .zr => { + if (shift > 0) try isel.emit(.ubfm(elem_ptr_ra.x(), elem_ptr_ra.x(), .{ + .N = .doubleword, + .immr = -%shift, + .imms = ~shift, + })); + break :temp_ra elem_ptr_ra; + }, + }, + .sub => { + const temp_ra = try isel.allocIntReg(); + errdefer isel.freeReg(temp_ra); + try isel.emit(.sub(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{ + .register = temp_ra.x(), + .shift = .{ .lsl = shift }, + } })); + break :temp_ra temp_ra; + }, + }; + defer if (temp_ra != elem_ptr_ra) isel.freeReg(temp_ra); + try isel.emit(.add(temp_ra.x(), index_mat.ra.x(), .{ .shifted_register = .{ + .register = index_mat.ra.x(), + .shift = .{ .lsl = @intCast(63 - @clz(elem_size) - shift) }, + } })); + }, + else => { + const elem_size_lsb1 = (elem_size - 1) | elem_size; + if ((elem_size_lsb1 +% 1) & elem_size_lsb1 == 0) { + const shift: u6 = @intCast(@ctz(elem_size)); + const temp_ra = temp_ra: switch (op) { + .add => { + const temp_ra = try isel.allocIntReg(); + errdefer isel.freeReg(temp_ra); + try isel.emit(.sub(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{ + .register = temp_ra.x(), + .shift = .{ .lsl = shift }, + } })); + break :temp_ra temp_ra; + }, + .sub => switch (base_ra) { + else => { + const temp_ra = try isel.allocIntReg(); + errdefer isel.freeReg(temp_ra); + try isel.emit(.add(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{ + .register = temp_ra.x(), + .shift = .{ .lsl = shift }, + } })); + break :temp_ra temp_ra; + }, + .zr => { + if (shift > 0) try isel.emit(.ubfm(elem_ptr_ra.x(), elem_ptr_ra.x(), .{ + .N = .doubleword, + .immr = -%shift, + .imms = ~shift, + })); + break :temp_ra elem_ptr_ra; + }, + }, + }; + defer if (temp_ra != elem_ptr_ra) isel.freeReg(temp_ra); + try isel.emit(.sub(temp_ra.x(), index_mat.ra.x(), .{ .shifted_register = .{ + .register = index_mat.ra.x(), + .shift = .{ .lsl = @intCast(64 - @clz(elem_size) - shift) }, + } })); + } else { + try isel.emit(switch (op) { + .add => .madd(elem_ptr_ra.x(), index_mat.ra.x(), elem_ptr_ra.x(), base_ra.x()), + .sub => .msub(elem_ptr_ra.x(), index_mat.ra.x(), elem_ptr_ra.x(), base_ra.x()), + }); + try isel.movImmediate(elem_ptr_ra.x(), elem_size); + } + }, + } + try index_mat.finish(isel); +} + +fn clzLimb( + isel: *Select, + res_ra: Register.Alias, + src_int_info: std.builtin.Type.Int, + src_ra: Register.Alias, +) !void { + switch (src_int_info.bits) { + else => unreachable, + 1...31 => |bits| { + try isel.emit(.sub(res_ra.w(), res_ra.w(), .{ + .immediate = @intCast(32 - bits), + })); + switch (src_int_info.signedness) { + .signed => { + try isel.emit(.clz(res_ra.w(), res_ra.w())); + try isel.emit(.ubfm(res_ra.w(), src_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + .unsigned => try isel.emit(.clz(res_ra.w(), src_ra.w())), + } + }, + 32 => try isel.emit(.clz(res_ra.w(), src_ra.w())), + 33...63 => |bits| { + try isel.emit(.sub(res_ra.w(), res_ra.w(), .{ + .immediate = @intCast(64 - bits), + })); + switch (src_int_info.signedness) { + .signed => { + try isel.emit(.clz(res_ra.x(), res_ra.x())); + try isel.emit(.ubfm(res_ra.x(), src_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + })); + }, + .unsigned => try isel.emit(.clz(res_ra.x(), src_ra.x())), + } + }, + 64 => try isel.emit(.clz(res_ra.x(), src_ra.x())), + } +} + +fn ctzLimb( + isel: *Select, + res_ra: Register.Alias, + src_int_info: std.builtin.Type.Int, + src_ra: Register.Alias, +) !void { + switch (src_int_info.bits) { + else => unreachable, + 1...31 => |bits| { + try isel.emit(.clz(res_ra.w(), res_ra.w())); + try isel.emit(.rbit(res_ra.w(), res_ra.w())); + try isel.emit(.orr(res_ra.w(), src_ra.w(), .{ .immediate = .{ + .N = .word, + .immr = @intCast(32 - bits), + .imms = @intCast(32 - bits - 1), + } })); + }, + 32 => { + try isel.emit(.clz(res_ra.w(), res_ra.w())); + try isel.emit(.rbit(res_ra.w(), src_ra.w())); + }, + 33...63 => |bits| { + try isel.emit(.clz(res_ra.x(), res_ra.x())); + try isel.emit(.rbit(res_ra.x(), res_ra.x())); + try isel.emit(.orr(res_ra.x(), src_ra.x(), .{ .immediate = .{ + .N = .doubleword, + .immr = @intCast(64 - bits), + .imms = @intCast(64 - bits - 1), + } })); + }, + 64 => { + try isel.emit(.clz(res_ra.x(), res_ra.x())); + try isel.emit(.rbit(res_ra.x(), src_ra.x())); + }, + } +} + +fn storeReg( + isel: *Select, + ra: Register.Alias, + size: u64, + base_ra: Register.Alias, + offset: i65, +) !void { + switch (size) { + 0 => unreachable, + 1 => { + if (std.math.cast(u12, offset)) |unsigned_offset| return isel.emit(if (ra.isVector()) .str( + ra.b(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + ) else .strb( + ra.w(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + )); + if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(if (ra.isVector()) + .stur(ra.b(), base_ra.x(), signed_offset) + else + .sturb(ra.w(), base_ra.x(), signed_offset)); + }, + 2 => { + if (std.math.cast(u13, offset)) |unsigned_offset| if (unsigned_offset % 2 == 0) + return isel.emit(if (ra.isVector()) .str( + ra.h(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + ) else .strh( + ra.w(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + )); + if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(if (ra.isVector()) + .stur(ra.h(), base_ra.x(), signed_offset) + else + .sturh(ra.w(), base_ra.x(), signed_offset)); + }, + 3 => { + const hi8_ra = try isel.allocIntReg(); + defer isel.freeReg(hi8_ra); + try isel.storeReg(hi8_ra, 1, base_ra, offset + 2); + try isel.storeReg(ra, 2, base_ra, offset); + return isel.emit(.ubfm(hi8_ra.w(), ra.w(), .{ + .N = .word, + .immr = 16, + .imms = 16 + 8 - 1, + })); + }, + 4 => { + if (std.math.cast(u14, offset)) |unsigned_offset| if (unsigned_offset % 4 == 0) return isel.emit(.str( + if (ra.isVector()) ra.s() else ra.w(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + )); + if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.stur( + if (ra.isVector()) ra.s() else ra.w(), + base_ra.x(), + signed_offset, + )); + }, + 5 => { + const hi8_ra = try isel.allocIntReg(); + defer isel.freeReg(hi8_ra); + try isel.storeReg(hi8_ra, 1, base_ra, offset + 4); + try isel.storeReg(ra, 4, base_ra, offset); + return isel.emit(.ubfm(hi8_ra.x(), ra.x(), .{ + .N = .doubleword, + .immr = 32, + .imms = 32 + 8 - 1, + })); + }, + 6 => { + const hi16_ra = try isel.allocIntReg(); + defer isel.freeReg(hi16_ra); + try isel.storeReg(hi16_ra, 2, base_ra, offset + 4); + try isel.storeReg(ra, 4, base_ra, offset); + return isel.emit(.ubfm(hi16_ra.x(), ra.x(), .{ + .N = .doubleword, + .immr = 32, + .imms = 32 + 16 - 1, + })); + }, + 7 => { + const hi16_ra = try isel.allocIntReg(); + defer isel.freeReg(hi16_ra); + const hi8_ra = try isel.allocIntReg(); + defer isel.freeReg(hi8_ra); + try isel.storeReg(hi8_ra, 1, base_ra, offset + 6); + try isel.storeReg(hi16_ra, 2, base_ra, offset + 4); + try isel.storeReg(ra, 4, base_ra, offset); + try isel.emit(.ubfm(hi8_ra.x(), ra.x(), .{ + .N = .doubleword, + .immr = 32 + 16, + .imms = 32 + 16 + 8 - 1, + })); + return isel.emit(.ubfm(hi16_ra.x(), ra.x(), .{ + .N = .doubleword, + .immr = 32, + .imms = 32 + 16 - 1, + })); + }, + 8 => { + if (std.math.cast(u15, offset)) |unsigned_offset| if (unsigned_offset % 8 == 0) return isel.emit(.str( + if (ra.isVector()) ra.d() else ra.x(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + )); + if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.stur( + if (ra.isVector()) ra.d() else ra.x(), + base_ra.x(), + signed_offset, + )); + }, + 16 => { + if (std.math.cast(u16, offset)) |unsigned_offset| if (unsigned_offset % 16 == 0) return isel.emit(.str( + ra.q(), + .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = unsigned_offset, + } }, + )); + if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.stur(ra.q(), base_ra.x(), signed_offset)); + }, + else => return isel.fail("bad store size: {d}", .{size}), + } + const ptr_ra = try isel.allocIntReg(); + defer isel.freeReg(ptr_ra); + try isel.storeReg(ra, size, ptr_ra, 0); + if (std.math.cast(u24, offset)) |pos_offset| { + const lo12: u12 = @truncate(pos_offset >> 0); + const hi12: u12 = @intCast(pos_offset >> 12); + if (hi12 > 0) try isel.emit(.add( + ptr_ra.x(), + if (lo12 > 0) ptr_ra.x() else base_ra.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0 or hi12 == 0) try isel.emit(.add(ptr_ra.x(), base_ra.x(), .{ .immediate = lo12 })); + } else if (std.math.cast(u24, -offset)) |neg_offset| { + const lo12: u12 = @truncate(neg_offset >> 0); + const hi12: u12 = @intCast(neg_offset >> 12); + if (hi12 > 0) try isel.emit(.sub( + ptr_ra.x(), + if (lo12 > 0) ptr_ra.x() else base_ra.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0 or hi12 == 0) try isel.emit(.sub(ptr_ra.x(), base_ra.x(), .{ .immediate = lo12 })); + } else { + try isel.emit(.add(ptr_ra.x(), base_ra.x(), .{ .register = ptr_ra.x() })); + try isel.movImmediate(ptr_ra.x(), @truncate(@as(u65, @bitCast(offset)))); + } +} + +const DomInt = u8; + +pub const Value = struct { + refs: u32, + flags: Flags, + offset_from_parent: u64, + parent_payload: Parent.Payload, + location_payload: Location.Payload, + parts: Value.Index, + + /// Must be at least 16 to compute call abi. + /// Must be at least 16, the largest hardware alignment. + pub const max_parts = 16; + pub const PartsLen = std.math.IntFittingRange(0, Value.max_parts); + + comptime { + if (!std.debug.runtime_safety) assert(@sizeOf(Value) == 32); + } + + pub const Flags = packed struct(u32) { + alignment: InternPool.Alignment, + parent_tag: Parent.Tag, + location_tag: Location.Tag, + parts_len_minus_one: std.math.IntFittingRange(0, Value.max_parts - 1), + unused: u18 = 0, + }; + + pub const Parent = union(enum(u3)) { + unallocated: void, + stack_slot: Indirect, + address: Value.Index, + value: Value.Index, + constant: Constant, + + pub const Tag = @typeInfo(Parent).@"union".tag_type.?; + pub const Payload = @Type(.{ .@"union" = .{ + .layout = .auto, + .tag_type = null, + .fields = @typeInfo(Parent).@"union".fields, + .decls = &.{}, + } }); + }; + + pub const Location = union(enum(u1)) { + large: struct { + size: u64, + }, + small: struct { + size: u5, + signedness: std.builtin.Signedness, + is_vector: bool, + hint: Register.Alias, + register: Register.Alias, + }, + + pub const Tag = @typeInfo(Location).@"union".tag_type.?; + pub const Payload = @Type(.{ .@"union" = .{ + .layout = .auto, + .tag_type = null, + .fields = @typeInfo(Location).@"union".fields, + .decls = &.{}, + } }); + }; + + pub const Indirect = packed struct(u32) { + base: Register.Alias, + offset: i25, + + pub fn withOffset(ind: Indirect, offset: i25) Indirect { + return .{ + .base = ind.base, + .offset = ind.offset + offset, + }; + } + }; + + pub const Index = enum(u32) { + allocating = std.math.maxInt(u32) - 1, + free = std.math.maxInt(u32) - 0, + _, + + fn get(vi: Value.Index, isel: *Select) *Value { + return &isel.values.items[@intFromEnum(vi)]; + } + + fn setAlignment(vi: Value.Index, isel: *Select, new_alignment: InternPool.Alignment) void { + vi.get(isel).flags.alignment = new_alignment; + } + + pub fn alignment(vi: Value.Index, isel: *Select) InternPool.Alignment { + return vi.get(isel).flags.alignment; + } + + pub fn setParent(vi: Value.Index, isel: *Select, new_parent: Parent) void { + const value = vi.get(isel); + assert(value.flags.parent_tag == .unallocated); + value.flags.parent_tag = new_parent; + value.parent_payload = switch (new_parent) { + .unallocated => unreachable, + inline else => |payload, tag| @unionInit(Parent.Payload, @tagName(tag), payload), + }; + if (value.refs > 0) switch (new_parent) { + .unallocated => unreachable, + .stack_slot, .constant => {}, + .address, .value => |parent_vi| _ = parent_vi.ref(isel), + }; + } + + pub fn parent(vi: Value.Index, isel: *Select) Parent { + const value = vi.get(isel); + return switch (value.flags.parent_tag) { + inline else => |tag| @unionInit( + Parent, + @tagName(tag), + @field(value.parent_payload, @tagName(tag)), + ), + }; + } + + pub fn valueParent(initial_vi: Value.Index, isel: *Select) struct { u64, Value.Index } { + var offset: u64 = 0; + var vi = initial_vi; + parent: switch (vi.parent(isel)) { + else => return .{ offset, vi }, + .value => |parent_vi| { + offset += vi.position(isel)[0]; + vi = parent_vi; + continue :parent parent_vi.parent(isel); + }, + } + } + + pub fn location(vi: Value.Index, isel: *Select) Location { + const value = vi.get(isel); + return switch (value.flags.location_tag) { + inline else => |tag| @unionInit( + Location, + @tagName(tag), + @field(value.location_payload, @tagName(tag)), + ), + }; + } + + pub fn position(vi: Value.Index, isel: *Select) struct { u64, u64 } { + return .{ vi.get(isel).offset_from_parent, vi.size(isel) }; + } + + pub fn size(vi: Value.Index, isel: *Select) u64 { + return switch (vi.location(isel)) { + inline else => |loc| loc.size, + }; + } + + fn setHint(vi: Value.Index, isel: *Select, new_hint: Register.Alias) void { + vi.get(isel).location_payload.small.hint = new_hint; + } + + pub fn hint(vi: Value.Index, isel: *Select) ?Register.Alias { + return switch (vi.location(isel)) { + .large => null, + .small => |loc| switch (loc.hint) { + .zr => null, + else => |hint_reg| hint_reg, + }, + }; + } + + fn setSignedness(vi: Value.Index, isel: *Select, new_signedness: std.builtin.Signedness) void { + const value = vi.get(isel); + assert(value.location_payload.small.size <= 2); + value.location_payload.small.signedness = new_signedness; + } + + pub fn signedness(vi: Value.Index, isel: *Select) std.builtin.Signedness { + const value = vi.get(isel); + return switch (value.flags.location_tag) { + .large => .unsigned, + .small => value.location_payload.small.signedness, + }; + } + + fn setIsVector(vi: Value.Index, isel: *Select) void { + const is_vector = &vi.get(isel).location_payload.small.is_vector; + assert(!is_vector.*); + is_vector.* = true; + } + + pub fn isVector(vi: Value.Index, isel: *Select) bool { + const value = vi.get(isel); + return switch (value.flags.location_tag) { + .large => false, + .small => value.location_payload.small.is_vector, + }; + } + + pub fn register(vi: Value.Index, isel: *Select) ?Register.Alias { + return switch (vi.location(isel)) { + .large => null, + .small => |loc| switch (loc.register) { + .zr => null, + else => |reg| reg, + }, + }; + } + + pub fn isUsed(vi: Value.Index, isel: *Select) bool { + return vi.valueParent(isel)[1].parent(isel) != .unallocated or vi.hasRegisterRecursive(isel); + } + + fn hasRegisterRecursive(vi: Value.Index, isel: *Select) bool { + if (vi.register(isel)) |_| return true; + var part_it = vi.parts(isel); + if (part_it.only() == null) while (part_it.next()) |part_vi| if (part_vi.hasRegisterRecursive(isel)) return true; + return false; + } + + fn setParts(vi: Value.Index, isel: *Select, parts_len: Value.PartsLen) void { + assert(parts_len > 1); + const value = vi.get(isel); + assert(value.flags.parts_len_minus_one == 0); + value.parts = @enumFromInt(isel.values.items.len); + value.flags.parts_len_minus_one = @intCast(parts_len - 1); + } + + fn addPart(vi: Value.Index, isel: *Select, part_offset: u64, part_size: u64) Value.Index { + const part_vi = isel.initValueAdvanced(vi.alignment(isel), part_offset, part_size); + tracking_log.debug("${d} <- ${d}[{d}]", .{ + @intFromEnum(part_vi), + @intFromEnum(vi), + part_offset, + }); + part_vi.setParent(isel, .{ .value = vi }); + return part_vi; + } + + pub fn parts(vi: Value.Index, isel: *Select) Value.PartIterator { + const value = vi.get(isel); + return switch (value.flags.parts_len_minus_one) { + 0 => .initOne(vi), + else => |parts_len_minus_one| .{ + .vi = value.parts, + .remaining = @as(Value.PartsLen, parts_len_minus_one) + 1, + }, + }; + } + + fn containingParts(vi: Value.Index, isel: *Select, part_offset: u64, part_size: u64) Value.PartIterator { + const start_vi = vi.partAtOffset(isel, part_offset); + const start_offset, const start_size = start_vi.position(isel); + if (part_offset >= start_offset and part_size <= start_size) return .initOne(start_vi); + const end_vi = vi.partAtOffset(isel, part_size - 1 + part_offset); + return .{ + .vi = start_vi, + .remaining = @intCast(@intFromEnum(end_vi) - @intFromEnum(start_vi) + 1), + }; + } + comptime { + _ = containingParts; + } + + fn partAtOffset(vi: Value.Index, isel: *Select, offset: u64) Value.Index { + const SearchPartIndex = std.math.IntFittingRange(0, Value.max_parts * 2 - 1); + const value = vi.get(isel); + var last: SearchPartIndex = value.flags.parts_len_minus_one; + if (last == 0) return vi; + var first: SearchPartIndex = 0; + last += 1; + while (true) { + const mid = (first + last) / 2; + const mid_vi: Value.Index = @enumFromInt(@intFromEnum(value.parts) + mid); + if (mid == first) return mid_vi; + if (offset < mid_vi.get(isel).offset_from_parent) last = mid else first = mid; + } + } + + fn field( + vi: Value.Index, + ty: ZigType, + field_offset: u64, + field_size: u64, + ) Value.FieldPartIterator { + assert(field_size > 0); + return .{ + .vi = vi, + .ty = ty, + .field_offset = field_offset, + .field_size = field_size, + .next_offset = 0, + }; + } + + fn ref(initial_vi: Value.Index, isel: *Select) Value.Index { + var vi = initial_vi; + while (true) { + const refs = &vi.get(isel).refs; + refs.* += 1; + if (refs.* > 1) return initial_vi; + switch (vi.parent(isel)) { + .unallocated, .stack_slot, .constant => {}, + .address, .value => |parent_vi| { + vi = parent_vi; + continue; + }, + } + return initial_vi; + } + } + + pub fn deref(initial_vi: Value.Index, isel: *Select) void { + var vi = initial_vi; + while (true) { + const refs = &vi.get(isel).refs; + refs.* -= 1; + if (refs.* > 0) return; + switch (vi.parent(isel)) { + .unallocated, .constant => {}, + .stack_slot => { + // reuse stack slot + }, + .address, .value => |parent_vi| { + vi = parent_vi; + continue; + }, + } + return; + } + } + + fn move(dst_vi: Value.Index, isel: *Select, src_ref: Air.Inst.Ref) !void { + try dst_vi.copy( + isel, + isel.air.typeOf(src_ref, &isel.pt.zcu.intern_pool), + try isel.use(src_ref), + ); + } + + fn copy(dst_vi: Value.Index, isel: *Select, ty: ZigType, src_vi: Value.Index) !void { + try dst_vi.copyAdvanced(isel, src_vi, .{ + .ty = ty, + .dst_vi = dst_vi, + .dst_offset = 0, + .src_vi = src_vi, + .src_offset = 0, + }); + } + + fn copyAdvanced(dst_vi: Value.Index, isel: *Select, src_vi: Value.Index, root: struct { + ty: ZigType, + dst_vi: Value.Index, + dst_offset: u64, + src_vi: Value.Index, + src_offset: u64, + }) !void { + if (dst_vi == src_vi) return; + 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| { + 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); + try dst_field_vi.?.copyAdvanced(isel, src_part_vi, .{ + .ty = root.ty, + .dst_vi = root.dst_vi, + .dst_offset = root.dst_offset + src_part_offset, + .src_vi = root.src_vi, + .src_offset = root.src_offset + src_part_offset, + }); + } + } else while (dst_part_it.next()) |dst_part_vi| { + const dst_part_offset, const dst_part_size = dst_part_vi.position(isel); + var src_field_it = root.src_vi.field(root.ty, root.src_offset + dst_part_offset, dst_part_size); + const src_part_vi = try src_field_it.only(isel); + try dst_part_vi.copyAdvanced(isel, src_part_vi.?, .{ + .ty = root.ty, + .dst_vi = root.dst_vi, + .dst_offset = root.dst_offset + dst_part_offset, + .src_vi = root.src_vi, + .src_offset = root.src_offset + dst_part_offset, + }); + } + } + + fn addOrSubtract( + res_vi: Value.Index, + isel: *Select, + ty: ZigType, + lhs_vi: Value.Index, + op: codegen.aarch64.encoding.Instruction.AddSubtractOp, + rhs_vi: Value.Index, + opts: struct { + wrap: bool, + overflow_ra: Register.Alias = .zr, + }, + ) !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; + while (part_offset > 0) : (need_wrap = false) { + const part_size = @min(part_offset, 8); + part_offset -= part_size; + var wrapped_res_part_it = res_vi.field(ty, part_offset, part_size); + const wrapped_res_part_vi = try wrapped_res_part_it.only(isel); + const wrapped_res_part_ra = try wrapped_res_part_vi.?.defReg(isel) orelse if (need_carry) .zr else continue; + 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) { + .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 }; + }, + }, + } + 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 }; + 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) { + else => unreachable, + 1...4 => switch (int_info.signedness) { + .signed => .sbfm(wrapped_part_ra.w(), unwrapped_part_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @truncate(int_info.bits - 1), + }), + .unsigned => .ubfm(wrapped_part_ra.w(), unwrapped_part_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @truncate(int_info.bits - 1), + }), + }, + 5...8 => switch (int_info.signedness) { + .signed => .sbfm(wrapped_part_ra.x(), unwrapped_part_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @truncate(int_info.bits - 1), + }), + .unsigned => .ubfm(wrapped_part_ra.x(), unwrapped_part_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @truncate(int_info.bits - 1), + }), + }, + }); + break :unwrapped_res_part_ra unwrapped_part_ra; + }; + defer if (unwrapped_res_part_ra != wrapped_res_part_ra) isel.freeReg(unwrapped_res_part_ra); + var lhs_part_it = lhs_vi.field(ty, part_offset, part_size); + const lhs_part_vi = try lhs_part_it.only(isel); + const lhs_part_mat = try lhs_part_vi.?.matReg(isel); + var rhs_part_it = rhs_vi.field(ty, part_offset, part_size); + const rhs_part_vi = try rhs_part_it.only(isel); + const rhs_part_mat = try rhs_part_vi.?.matReg(isel); + try isel.emit(switch (part_size) { + else => unreachable, + 1...4 => switch (op) { + .add => switch (part_offset) { + 0 => switch (need_carry) { + false => .add(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + true => .adds(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + }, + else => switch (need_carry) { + false => .adc(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()), + true => .adcs(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()), + }, + }, + .sub => switch (part_offset) { + 0 => switch (need_carry) { + false => .sub(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + true => .subs(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }), + }, + else => switch (need_carry) { + false => .sbc(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()), + true => .sbcs(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()), + }, + }, + }, + 5...8 => switch (op) { + .add => switch (part_offset) { + 0 => switch (need_carry) { + false => .add(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + true => .adds(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + }, + else => switch (need_carry) { + false => .adc(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()), + true => .adcs(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()), + }, + }, + .sub => switch (part_offset) { + 0 => switch (need_carry) { + false => .sub(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + true => .subs(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }), + }, + else => switch (need_carry) { + false => .sbc(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()), + true => .sbcs(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()), + }, + }, + }, + }); + try rhs_part_mat.finish(isel); + try lhs_part_mat.finish(isel); + need_carry = true; + } + } + + const MemoryAccessOptions = struct { + root_vi: Value.Index = .free, + offset: u64 = 0, + @"volatile": bool = false, + split: bool = true, + wrap: ?std.builtin.Type.Int = null, + expected_live_registers: *const LiveRegisters = &.initFill(.free), + }; + + fn load( + vi: Value.Index, + isel: *Select, + root_ty: ZigType, + base_ra: Register.Alias, + opts: MemoryAccessOptions, + ) !bool { + const root_vi = switch (opts.root_vi) { + _ => |root_vi| root_vi, + .allocating => unreachable, + .free => vi, + }; + var part_it = 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 false; + var subpart_it = root_vi.field(root_ty, opts.offset, part_size - 1); + _ = try subpart_it.next(isel); + part_it = vi.parts(isel); + assert(part_it.only() == null); + break :only; + } + const part_ra = if (try part_vi.defReg(isel)) |part_ra| + part_ra + else if (opts.@"volatile") + .zr + else + return false; + if (part_ra != .zr) { + const live_vi = isel.live_registers.getPtr(part_ra); + assert(live_vi.* == .free); + live_vi.* = .allocating; + } + if (opts.wrap) |int_info| switch (int_info.bits) { + else => unreachable, + 1...7, 9...15, 17...31 => |bits| try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(part_ra.w(), part_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(part_ra.w(), part_ra.w(), .{ + .N = .word, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 8, 16, 32 => {}, + 33...63 => |bits| try isel.emit(switch (int_info.signedness) { + .signed => .sbfm(part_ra.x(), part_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + .unsigned => .ubfm(part_ra.x(), part_ra.x(), .{ + .N = .doubleword, + .immr = 0, + .imms = @intCast(bits - 1), + }), + }), + 64 => {}, + }; + try isel.emit(emit: switch (part_size) { + else => return isel.fail("bad load size of {d}", .{part_size}), + 1 => if (part_is_vector) .ldr(part_ra.b(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }) else switch (part_vi.signedness(isel)) { + .signed => .ldrsb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + .unsigned => .ldrb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + }, + 2 => if (part_is_vector) .ldr(part_ra.h(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }) else switch (part_vi.signedness(isel)) { + .signed => .ldrsh(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + .unsigned => .ldrh(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + }, + 3 => { + const lo16_ra = try isel.allocIntReg(); + defer isel.freeReg(lo16_ra); + try isel.emit(.orr(part_ra.w(), lo16_ra.w(), .{ .shifted_register = .{ + .register = part_ra.w(), + .shift = .{ .lsl = 16 }, + } })); + try isel.emit(switch (part_vi.signedness(isel)) { + .signed => .ldrsb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 2), + } }), + .unsigned => .ldrb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 2), + } }), + }); + break :emit .ldrh(lo16_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }); + }, + 4 => .ldr(if (part_is_vector) part_ra.s() else part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + 5 => { + const lo32_ra = try isel.allocIntReg(); + defer isel.freeReg(lo32_ra); + try isel.emit(.orr(part_ra.x(), lo32_ra.x(), .{ .shifted_register = .{ + .register = part_ra.x(), + .shift = .{ .lsl = 32 }, + } })); + try isel.emit(switch (part_vi.signedness(isel)) { + .signed => .ldrsb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 4), + } }), + .unsigned => .ldrb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 4), + } }), + }); + break :emit .ldr(lo32_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }); + }, + 7 => { + const lo32_ra = try isel.allocIntReg(); + defer isel.freeReg(lo32_ra); + const lo48_ra = try isel.allocIntReg(); + defer isel.freeReg(lo48_ra); + try isel.emit(.orr(part_ra.x(), lo48_ra.x(), .{ .shifted_register = .{ + .register = part_ra.x(), + .shift = .{ .lsl = 32 + 16 }, + } })); + try isel.emit(switch (part_vi.signedness(isel)) { + .signed => .ldrsb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 4 + 2), + } }), + .unsigned => .ldrb(part_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 4 + 2), + } }), + }); + try isel.emit(.orr(lo48_ra.x(), lo32_ra.x(), .{ .shifted_register = .{ + .register = lo48_ra.x(), + .shift = .{ .lsl = 32 }, + } })); + try isel.emit(.ldrh(lo48_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset + 4), + } })); + break :emit .ldr(lo32_ra.w(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }); + }, + 8 => .ldr(if (part_is_vector) part_ra.d() else part_ra.x(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + 16 => .ldr(part_ra.q(), .{ .unsigned_offset = .{ + .base = base_ra.x(), + .offset = @intCast(opts.offset), + } }), + }); + if (part_ra != .zr) { + const live_vi = isel.live_registers.getPtr(part_ra); + assert(live_vi.* == .allocating); + switch (opts.expected_live_registers.get(part_ra)) { + _ => {}, + .allocating => unreachable, + .free => live_vi.* = .free, + } + } + return true; + } + var used = false; + while (part_it.next()) |part_vi| used |= try part_vi.load(isel, root_ty, base_ra, .{ + .root_vi = root_vi, + .offset = opts.offset + part_vi.get(isel).offset_from_parent, + .@"volatile" = opts.@"volatile", + .split = opts.split, + .wrap = switch (part_it.remaining) { + else => null, + 0 => if (opts.wrap) |wrap| .{ + .signedness = wrap.signedness, + .bits = @intCast(wrap.bits - 8 * part_vi.position(isel)[0]), + } else null, + }, + .expected_live_registers = opts.expected_live_registers, + }); + return used; + } + + fn store( + vi: Value.Index, + isel: *Select, + root_ty: ZigType, + base_ra: Register.Alias, + opts: MemoryAccessOptions, + ) !void { + const root_vi = switch (opts.root_vi) { + _ => |root_vi| root_vi, + .allocating => unreachable, + .free => vi, + }; + var part_it = 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 = vi.parts(isel); + assert(part_it.only() == null); + break :only; + } + const part_mat = try part_vi.matReg(isel); + try isel.storeReg(part_mat.ra, part_size, base_ra, opts.offset); + return part_mat.finish(isel); + } + while (part_it.next()) |part_vi| try part_vi.store(isel, root_ty, base_ra, .{ + .root_vi = root_vi, + .offset = opts.offset + part_vi.get(isel).offset_from_parent, + .@"volatile" = opts.@"volatile", + .split = opts.split, + .wrap = switch (part_it.remaining) { + else => null, + 0 => if (opts.wrap) |wrap| .{ + .signedness = wrap.signedness, + .bits = @intCast(wrap.bits - 8 * part_vi.position(isel)[0]), + } else null, + }, + .expected_live_registers = opts.expected_live_registers, + }); + } + + fn mat(vi: Value.Index, isel: *Select) !void { + if (false) { + var part_it: Value.PartIterator = if (vi.size(isel) > 8) vi.parts(isel) else .initOne(vi); + if (part_it.only()) |part_vi| only: { + const mat_ra = mat_ra: { + if (part_vi.register(isel)) |mat_ra| { + part_vi.get(isel).location_payload.small.register = .zr; + const live_vi = isel.live_registers.getPtr(mat_ra); + assert(live_vi.* == part_vi); + live_vi.* = .allocating; + break :mat_ra mat_ra; + } + if (part_vi.hint(isel)) |hint_ra| { + const live_vi = isel.live_registers.getPtr(hint_ra); + if (live_vi.* == .free) { + live_vi.* = .allocating; + isel.saved_registers.insert(hint_ra); + break :mat_ra hint_ra; + } + } + 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)) + switch (if (part_is_vector) isel.tryAllocVecReg() else isel.tryAllocIntReg()) { + .allocated => |ra| break :mat_ra ra, + .fill_candidate, .out_of_registers => {}, + }; + _, const parent_vi = vi.valueParent(isel); + switch (parent_vi.parent(isel)) { + .unallocated => parent_vi.setParent(isel, .{ .stack_slot = parent_vi.allocStackSlot(isel) }), + else => {}, + } + break :only; + }; + assert(isel.live_registers.get(mat_ra) == .allocating); + try Value.Materialize.finish(.{ .vi = part_vi, .ra = mat_ra }, isel); + } else while (part_it.next()) |part_vi| try part_vi.mat(isel); + } else { + _, const parent_vi = vi.valueParent(isel); + switch (parent_vi.parent(isel)) { + .unallocated => parent_vi.setParent(isel, .{ .stack_slot = parent_vi.allocStackSlot(isel) }), + else => {}, + } + } + } + + fn matReg(vi: Value.Index, isel: *Select) !Value.Materialize { + const mat_ra = mat_ra: { + if (vi.register(isel)) |mat_ra| { + vi.get(isel).location_payload.small.register = .zr; + const live_vi = isel.live_registers.getPtr(mat_ra); + assert(live_vi.* == vi); + live_vi.* = .allocating; + break :mat_ra mat_ra; + } + if (vi.hint(isel)) |hint_ra| { + const live_vi = isel.live_registers.getPtr(hint_ra); + if (live_vi.* == .free) { + live_vi.* = .allocating; + isel.saved_registers.insert(hint_ra); + break :mat_ra hint_ra; + } + } + break :mat_ra if (vi.isVector(isel)) try isel.allocVecReg() else try isel.allocIntReg(); + }; + assert(isel.live_registers.get(mat_ra) == .allocating); + return .{ .vi = vi, .ra = mat_ra }; + } + + fn defAddr( + def_vi: Value.Index, + isel: *Select, + def_ty: ZigType, + wrap: ?std.builtin.Type.Int, + expected_live_registers: *const LiveRegisters, + ) !?void { + if (!def_vi.isUsed(isel)) return null; + const offset_from_parent: i65, const parent_vi = def_vi.valueParent(isel); + const stack_slot, const allocated = switch (parent_vi.parent(isel)) { + .unallocated => .{ parent_vi.allocStackSlot(isel), true }, + .stack_slot => |stack_slot| .{ stack_slot, false }, + else => unreachable, + }; + _ = try def_vi.load(isel, def_ty, stack_slot.base, .{ + .offset = @intCast(stack_slot.offset + offset_from_parent), + .split = false, + .wrap = wrap, + .expected_live_registers = expected_live_registers, + }); + if (allocated) parent_vi.setParent(isel, .{ .stack_slot = stack_slot }); + } + + fn defReg(def_vi: Value.Index, isel: *Select) !?Register.Alias { + var vi = def_vi; + var offset: i65 = 0; + var def_ra: ?Register.Alias = null; + while (true) { + if (vi.register(isel)) |ra| { + vi.get(isel).location_payload.small.register = .zr; + const live_vi = isel.live_registers.getPtr(ra); + assert(live_vi.* == vi); + if (def_ra == null and vi != def_vi) { + var part_it = vi.parts(isel); + assert(part_it.only() == null); + + const first_part_vi = part_it.next().?; + const first_part_value = first_part_vi.get(isel); + assert(first_part_value.offset_from_parent == 0); + first_part_value.location_payload.small.register = ra; + live_vi.* = first_part_vi; + + const vi_size = vi.size(isel); + while (part_it.next()) |part_vi| { + const part_offset, const part_size = part_vi.position(isel); + 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) { + else => unreachable, + 2 => if (isel.target.cpu.has(.aarch64, .fullfp16)) + .fmov(ra.h(), .{ .register = part_mat.ra.h() }) + else + continue :size 4, + 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"() }), + }; + } else switch (vi_size) { + else => unreachable, + 1...4 => .bfm(ra.w(), part_mat.ra.w(), .{ + .N = .word, + .immr = @as(u5, @truncate(32 - 8 * part_offset)), + .imms = @intCast(8 * part_size - 1), + }), + 5...8 => .bfm(ra.x(), part_mat.ra.x(), .{ + .N = .doubleword, + .immr = @as(u6, @truncate(64 - 8 * part_offset)), + .imms = @intCast(8 * part_size - 1), + }), + }); + try part_mat.finish(isel); + } + vi = def_vi; + offset = 0; + continue; + } + live_vi.* = .free; + def_ra = ra; + } + offset += vi.get(isel).offset_from_parent; + switch (vi.parent(isel)) { + else => unreachable, + .unallocated => return def_ra, + .stack_slot => |stack_slot| { + offset += stack_slot.offset; + const def_is_vector = def_vi.isVector(isel); + const ra = def_ra orelse if (def_is_vector) try isel.allocVecReg() else try isel.allocIntReg(); + defer if (def_ra == null) isel.freeReg(ra); + try isel.storeReg(ra, def_vi.size(isel), stack_slot.base, offset); + return ra; + }, + .value => |parent_vi| vi = parent_vi, + } + } + } + + pub fn liveIn( + vi: Value.Index, + isel: *Select, + src_ra: Register.Alias, + expected_live_registers: *const LiveRegisters, + ) !void { + const src_live_vi = isel.live_registers.getPtr(src_ra); + if (vi.register(isel)) |dst_ra| { + const dst_live_vi = isel.live_registers.getPtr(dst_ra); + assert(dst_live_vi.* == vi); + if (dst_ra == src_ra) { + src_live_vi.* = .allocating; + return; + } + dst_live_vi.* = .allocating; + if (try isel.fill(src_ra)) { + assert(src_live_vi.* == .free); + src_live_vi.* = .allocating; + } + assert(src_live_vi.* == .allocating); + try isel.emit(switch (dst_ra.isVector()) { + false => switch (src_ra.isVector()) { + false => switch (vi.size(isel)) { + else => unreachable, + 1...4 => .orr(dst_ra.w(), .wzr, .{ .register = src_ra.w() }), + 5...8 => .orr(dst_ra.x(), .xzr, .{ .register = src_ra.x() }), + }, + true => switch (vi.size(isel)) { + else => unreachable, + 2 => .fmov(dst_ra.w(), .{ .register = src_ra.h() }), + 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)) { + else => unreachable, + 2 => .fmov(dst_ra.h(), .{ .register = src_ra.w() }), + 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() }), + 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"() }), + }, + }, + }); + assert(dst_live_vi.* == .allocating); + dst_live_vi.* = switch (expected_live_registers.get(dst_ra)) { + _ => .allocating, + .allocating => .allocating, + .free => .free, + }; + } else if (try isel.fill(src_ra)) { + assert(src_live_vi.* == .free); + src_live_vi.* = .allocating; + } + assert(src_live_vi.* == .allocating); + vi.get(isel).location_payload.small.register = src_ra; + } + + pub fn defLiveIn( + vi: Value.Index, + isel: *Select, + src_ra: Register.Alias, + 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); + 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), + } }), + }); + }, + else => unreachable, + } + try vi.spillReg(isel, src_ra, 0, expected_live_registers); + } + + fn spillReg( + vi: Value.Index, + isel: *Select, + src_ra: Register.Alias, + start_offset: u64, + expected_live_registers: *const LiveRegisters, + ) !void { + assert(isel.live_registers.get(src_ra) == .allocating); + var part_it = vi.parts(isel); + if (part_it.only()) |part_vi| { + const dst_ra = part_vi.register(isel) orelse return; + if (dst_ra == src_ra) return; + 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) { + else => unreachable, + 2 => .fmov(dst_ra.h(), .{ .register = part_ra.w() }), + 4 => .fmov(dst_ra.s(), .{ .register = part_ra.w() }), + 8 => .fmov(dst_ra.d(), .{ .register = part_ra.x() }), + }); + try isel.emit(switch (start_offset + part_size) { + else => unreachable, + 1...4 => |end_offset| switch (part_vi.signedness(isel)) { + .signed => .sbfm(part_ra.w(), src_ra.w(), .{ + .N = .word, + .immr = @intCast(8 * start_offset), + .imms = @intCast(8 * end_offset - 1), + }), + .unsigned => .ubfm(part_ra.w(), src_ra.w(), .{ + .N = .word, + .immr = @intCast(8 * start_offset), + .imms = @intCast(8 * end_offset - 1), + }), + }, + 5...8 => |end_offset| switch (part_vi.signedness(isel)) { + .signed => .sbfm(part_ra.x(), src_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(8 * start_offset), + .imms = @intCast(8 * end_offset - 1), + }), + .unsigned => .ubfm(part_ra.x(), src_ra.x(), .{ + .N = .doubleword, + .immr = @intCast(8 * start_offset), + .imms = @intCast(8 * end_offset - 1), + }), + }, + }); + const value_ra = &part_vi.get(isel).location_payload.small.register; + assert(value_ra.* == dst_ra); + value_ra.* = .zr; + const dst_live_vi = isel.live_registers.getPtr(dst_ra); + assert(dst_live_vi.* == part_vi); + dst_live_vi.* = switch (expected_live_registers.get(dst_ra)) { + _ => .allocating, + .allocating => unreachable, + .free => .free, + }; + } else while (part_it.next()) |part_vi| try part_vi.spillReg( + isel, + src_ra, + start_offset + part_vi.get(isel).offset_from_parent, + expected_live_registers, + ); + } + + fn liveOut(vi: Value.Index, isel: *Select, ra: Register.Alias) !void { + assert(try isel.fill(ra)); + const live_vi = isel.live_registers.getPtr(ra); + assert(live_vi.* == .free); + live_vi.* = .allocating; + try Value.Materialize.finish(.{ .vi = vi, .ra = ra }, isel); + } + + fn allocStackSlot(vi: Value.Index, isel: *Select) Value.Indirect { + const offset = vi.alignment(isel).forward(isel.stack_size); + isel.stack_size = @intCast(offset + vi.size(isel)); + tracking_log.debug("${d} -> [sp, #0x{x}]", .{ @intFromEnum(vi), @abs(offset) }); + return .{ + .base = .sp, + .offset = @intCast(offset), + }; + } + + fn address(initial_vi: Value.Index, isel: *Select, initial_offset: u64, ptr_ra: Register.Alias) !void { + var vi = initial_vi; + var offset: i65 = vi.get(isel).offset_from_parent + initial_offset; + parent: switch (vi.parent(isel)) { + .unallocated => { + const stack_slot = vi.allocStackSlot(isel); + vi.setParent(isel, .{ .stack_slot = stack_slot }); + continue :parent .{ .stack_slot = stack_slot }; + }, + .stack_slot => |stack_slot| { + offset += stack_slot.offset; + const lo12: u12 = @truncate(@abs(offset) >> 0); + const hi12: u12 = @intCast(@abs(offset) >> 12); + if (hi12 > 0) try isel.emit(if (offset >= 0) .add( + ptr_ra.x(), + if (lo12 > 0) ptr_ra.x() else stack_slot.base.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + ) else .sub( + ptr_ra.x(), + if (lo12 > 0) ptr_ra.x() else stack_slot.base.x(), + .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, + )); + if (lo12 > 0 or hi12 == 0) try isel.emit(if (offset >= 0) .add( + ptr_ra.x(), + stack_slot.base.x(), + .{ .immediate = lo12 }, + ) else .sub( + ptr_ra.x(), + stack_slot.base.x(), + .{ .immediate = lo12 }, + )); + }, + .address => |address_vi| try address_vi.liveOut(isel, ptr_ra), + .value => |parent_vi| { + vi = parent_vi; + offset += vi.get(isel).offset_from_parent; + continue :parent vi.parent(isel); + }, + .constant => |constant| { + const pt = isel.pt; + const zcu = pt.zcu; + switch (true) { + false => { + try isel.uav_relocs.append(zcu.gpa, .{ + .uav = .{ + .val = constant.toIntern(), + .orig_ty = (try pt.singleConstPtrType(constant.typeOf(zcu))).toIntern(), + }, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = @intCast(offset), + }, + }); + try isel.emit(.adr(ptr_ra.x(), 0)); + }, + true => { + try isel.uav_relocs.append(zcu.gpa, .{ + .uav = .{ + .val = constant.toIntern(), + .orig_ty = (try pt.singleConstPtrType(constant.typeOf(zcu))).toIntern(), + }, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = @intCast(offset), + }, + }); + try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 })); + try isel.uav_relocs.append(zcu.gpa, .{ + .uav = .{ + .val = constant.toIntern(), + .orig_ty = (try pt.singleConstPtrType(constant.typeOf(zcu))).toIntern(), + }, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = @intCast(offset), + }, + }); + try isel.emit(.adrp(ptr_ra.x(), 0)); + }, + } + }, + } + } + }; + + pub const PartIterator = struct { + vi: Value.Index, + remaining: Value.PartsLen, + + fn initOne(vi: Value.Index) PartIterator { + return .{ .vi = vi, .remaining = 1 }; + } + + pub fn next(it: *PartIterator) ?Value.Index { + if (it.remaining == 0) return null; + it.remaining -= 1; + defer it.vi = @enumFromInt(@intFromEnum(it.vi) + 1); + return it.vi; + } + + pub fn only(it: PartIterator) ?Value.Index { + return if (it.remaining == 1) it.vi else null; + } + }; + + const FieldPartIterator = struct { + vi: Value.Index, + ty: ZigType, + field_offset: u64, + field_size: u64, + next_offset: u64, + + fn next(it: *FieldPartIterator, isel: *Select) !?struct { offset: u64, vi: Value.Index } { + const next_offset = it.next_offset; + const next_part_size = it.field_size - next_offset; + if (next_part_size == 0) return null; + var next_part_offset = it.field_offset + next_offset; + + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + var vi = it.vi; + var ty = it.ty; + var ty_size = vi.size(isel); + assert(ty_size == ty.abiSize(zcu)); + var offset: u64 = 0; + var size = ty_size; + assert(next_part_offset + next_part_size <= size); + while (next_part_offset > 0 or next_part_size < size) { + const part_vi = vi.partAtOffset(isel, next_part_offset); + if (part_vi != vi) { + vi = part_vi; + const part_offset, size = part_vi.position(isel); + assert(part_offset <= next_part_offset and part_offset + size > next_part_offset); + offset += part_offset; + next_part_offset -= part_offset; + continue; + } + try isel.values.ensureUnusedCapacity(zcu.gpa, Value.max_parts); + type_key: switch (ip.indexToKey(ty.toIntern())) { + else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}), + .int_type => |int_type| switch (int_type.bits) { + 0 => unreachable, + 1...64 => unreachable, + 65...256 => |bits| if (offset == 0 and size == ty_size) { + const parts_len = std.math.divCeil(u16, bits, 64) catch unreachable; + vi.setParts(isel, @intCast(parts_len)); + for (0..parts_len) |part_index| _ = vi.addPart(isel, 8 * part_index, 8); + }, + else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}), + }, + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .one, .many, .c => unreachable, + .slice => if (offset == 0 and size == ty_size) { + vi.setParts(isel, 2); + _ = vi.addPart(isel, 0, 8); + _ = 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)}), + }, + .array_type => |array_type| { + const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0; + const array_len = array_type.lenIncludingSentinel(); + if (array_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)}); + const alignment = vi.alignment(isel); + const Part = struct { offset: u64, size: u64 }; + var parts: [Value.max_parts]Part = undefined; + var parts_len: Value.PartsLen = 0; + const elem_ty: ZigType = .fromInterned(array_type.child); + const elem_size = elem_ty.abiSize(zcu); + const elem_signedness = if (ty.isAbiInt(zcu)) elem_signedness: { + const elem_int_info = elem_ty.intInfo(zcu); + break :elem_signedness if (elem_int_info.bits <= 16) elem_int_info.signedness else null; + } else null; + const elem_is_vector = elem_size <= 16 and + CallAbiIterator.homogeneousAggregateBaseType(zcu, elem_ty.toIntern()) != null; + var elem_end: u64 = 0; + for (0..@intCast(array_len)) |_| { + const elem_begin = elem_end; + if (elem_begin >= offset + size) break; + elem_end = elem_begin + elem_size; + if (elem_end <= offset) continue; + if (offset >= elem_begin and offset + size <= elem_begin + elem_size) { + ty = elem_ty; + ty_size = elem_size; + offset -= elem_begin; + continue :type_key ip.indexToKey(elem_ty.toIntern()); + } + if (parts_len > 0) combine: { + const prev_part = &parts[parts_len - 1]; + const combined_size = elem_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; + continue; + } + parts[parts_len] = .{ .offset = elem_begin, .size = elem_size }; + 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 (elem_signedness) |signedness| subpart_vi.setSignedness(isel, signedness); + if (elem_is_vector) subpart_vi.setIsVector(isel); + } + }, + .anyframe_type => unreachable, + .error_union_type => |error_union_type| { + 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 alignment = vi.alignment(isel); + const payload_ty: ZigType = .fromInterned(error_union_type.payload_type); + const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu); + const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu); + const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness, is_vector: bool }; + var parts: [2]Part = undefined; + var parts_len: Value.PartsLen = 0; + var field_end: u64 = 0; + for (0..2) |field_index| { + const field_ty: ZigType, const field_begin = switch (@as(enum { error_set, payload }, switch (field_index) { + 0 => if (error_set_offset < payload_offset) .error_set else .payload, + 1 => if (error_set_offset < payload_offset) .payload else .error_set, + else => unreachable, + })) { + .error_set => .{ .fromInterned(error_union_type.error_set_type), error_set_offset }, + .payload => .{ payload_ty, payload_offset }, + }; + if (field_begin >= offset + size) break; + const field_size = field_ty.abiSize(zcu); + if (field_size == 0) continue; + field_end = field_begin + field_size; + if (field_end <= offset) continue; + if (offset >= field_begin and offset + size <= field_begin + field_size) { + ty = field_ty; + ty_size = field_size; + offset -= field_begin; + continue :type_key ip.indexToKey(field_ty.toIntern()); + } + const field_signedness = if (field_ty.isAbiInt(zcu)) field_signedness: { + const field_int_info = field_ty.intInfo(zcu); + break :field_signedness if (field_int_info.bits <= 16) field_int_info.signedness else null; + } else null; + const field_is_vector = field_size <= 16 and + CallAbiIterator.homogeneousAggregateBaseType(zcu, field_ty.toIntern()) != 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; + prev_part.is_vector &= field_is_vector; + continue; + } + parts[parts_len] = .{ + .offset = field_begin, + .size = field_size, + .signedness = field_signedness, + .is_vector = field_is_vector, + }; + 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); + if (part.is_vector) subpart_vi.setIsVector(isel); + } + }, + .simple_type => |simple_type| switch (simple_type) { + .f16, .f32, .f64, .f128, .c_longdouble => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}), + .f80 => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 80 } }, + .usize, + .isize, + .c_char, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + => continue :type_key .{ .int_type = ty.intInfo(zcu) }, + .anyopaque, + .void, + .type, + .comptime_int, + .comptime_float, + .noreturn, + .null, + .undefined, + .enum_literal, + .adhoc_inferred_error_set, + .generic_poison, + => unreachable, + .bool => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 1 } }, + .anyerror => continue :type_key .{ .int_type = .{ + .signedness = .unsigned, + .bits = zcu.errorSetBits(), + } }, + }, + .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()); + 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)}); + const alignment = vi.alignment(isel); + const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness, is_vector: bool }; + var parts: [Value.max_parts]Part = undefined; + var parts_len: Value.PartsLen = 0; + var field_end: u64 = 0; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + while (field_it.next()) |field_index| { + const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + const field_begin = switch (loaded_struct.fieldAlign(ip, field_index)) { + .none => field_ty.abiAlignment(zcu), + else => |field_align| field_align, + }.forward(field_end); + if (field_begin >= offset + size) break; + const field_size = field_ty.abiSize(zcu); + field_end = field_begin + field_size; + if (field_end <= offset) continue; + if (offset >= field_begin and offset + size <= field_begin + field_size) { + ty = field_ty; + ty_size = field_size; + offset -= field_begin; + continue :type_key ip.indexToKey(field_ty.toIntern()); + } + const field_signedness = if (field_ty.isAbiInt(zcu)) field_signedness: { + const field_int_info = field_ty.intInfo(zcu); + break :field_signedness if (field_int_info.bits <= 16) field_int_info.signedness else null; + } else null; + const field_is_vector = field_size <= 16 and + CallAbiIterator.homogeneousAggregateBaseType(zcu, field_ty.toIntern()) != 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; + prev_part.is_vector &= field_is_vector; + continue; + } + parts[parts_len] = .{ + .offset = field_begin, + .size = field_size, + .signedness = field_signedness, + .is_vector = field_is_vector, + }; + 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); + if (part.is_vector) subpart_vi.setIsVector(isel); + } + }, + .tuple_type => |tuple_type| { + const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0; + if (tuple_type.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)}); + const alignment = vi.alignment(isel); + const Part = struct { offset: u64, size: u64, is_vector: bool }; + var parts: [Value.max_parts]Part = undefined; + var parts_len: Value.PartsLen = 0; + var field_end: u64 = 0; + for (tuple_type.types.get(ip), tuple_type.values.get(ip)) |field_type, field_value| { + if (field_value != .none) continue; + const field_ty: ZigType = .fromInterned(field_type); + const field_begin = field_ty.abiAlignment(zcu).forward(field_end); + if (field_begin >= offset + size) break; + const field_size = field_ty.abiSize(zcu); + if (field_size == 0) continue; + field_end = field_begin + field_size; + if (field_end <= offset) continue; + if (offset >= field_begin and offset + size <= field_begin + field_size) { + ty = field_ty; + ty_size = field_size; + offset -= field_begin; + continue :type_key ip.indexToKey(field_ty.toIntern()); + } + const field_is_vector = field_size <= 16 and + CallAbiIterator.homogeneousAggregateBaseType(zcu, field_ty.toIntern()) != 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.is_vector &= field_is_vector; + continue; + } + parts[parts_len] = .{ .offset = field_begin, .size = field_size, .is_vector = field_is_vector }; + 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.is_vector) subpart_vi.setIsVector(isel); + } + }, + .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, + .inferred_error_set_type, + => continue :type_key .{ .simple_type = .anyerror }, + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .empty_enum_value, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .memoized_call, + => unreachable, // values, not types + } + } + it.next_offset = next_offset + size; + return .{ .offset = next_part_offset - next_offset, .vi = vi }; + } + + fn only(it: *FieldPartIterator, isel: *Select) !?Value.Index { + const part = try it.next(isel); + assert(part.?.offset == 0); + return if (try it.next(isel)) |_| null else part.?.vi; + } + }; + + const Materialize = struct { + vi: Value.Index, + ra: Register.Alias, + + fn finish(mat: Value.Materialize, isel: *Select) error{ OutOfMemory, CodegenFail }!void { + const live_vi = isel.live_registers.getPtr(mat.ra); + assert(live_vi.* == .allocating); + var vi = mat.vi; + var offset: i65 = 0; + const size = mat.vi.size(isel); + free: while (true) { + 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() }), + 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"() }), + } else switch (size) { + else => unreachable, + 1...4 => .orr(mat.ra.w(), .wzr, .{ .register = ra.w() }), + 5...8 => .orr(mat.ra.x(), .xzr, .{ .register = ra.x() }), + } else switch (offset + size) { + else => unreachable, + 1...4 => |end_offset| switch (mat.vi.signedness(isel)) { + .signed => .sbfm(mat.ra.w(), ra.w(), .{ + .N = .word, + .immr = @intCast(8 * offset), + .imms = @intCast(8 * end_offset - 1), + }), + .unsigned => .ubfm(mat.ra.w(), ra.w(), .{ + .N = .word, + .immr = @intCast(8 * offset), + .imms = @intCast(8 * end_offset - 1), + }), + }, + 5...8 => |end_offset| switch (mat.vi.signedness(isel)) { + .signed => .sbfm(mat.ra.x(), ra.x(), .{ + .N = .doubleword, + .immr = @intCast(8 * offset), + .imms = @intCast(8 * end_offset - 1), + }), + .unsigned => .ubfm(mat.ra.x(), ra.x(), .{ + .N = .doubleword, + .immr = @intCast(8 * offset), + .imms = @intCast(8 * end_offset - 1), + }), + }, + }); + mat.vi.get(isel).location_payload.small.register = mat.ra; + live_vi.* = mat.vi; + return; + } + offset += vi.get(isel).offset_from_parent; + switch (vi.parent(isel)) { + .unallocated => { + mat.vi.get(isel).location_payload.small.register = mat.ra; + live_vi.* = mat.vi; + return; + }, + .stack_slot => |stack_slot| { + offset += stack_slot.offset; + break :free try isel.emit(switch (size) { + else => unreachable, + 1 => if (mat.ra.isVector()) .ldr(mat.ra.b(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }) else switch (mat.vi.signedness(isel)) { + .signed => .ldrsb(mat.ra.w(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + .unsigned => .ldrb(mat.ra.w(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + }, + 2 => if (mat.ra.isVector()) .ldr(mat.ra.h(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }) else switch (mat.vi.signedness(isel)) { + .signed => .ldrsh(mat.ra.w(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + .unsigned => .ldrh(mat.ra.w(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + }, + 4 => .ldr(if (mat.ra.isVector()) mat.ra.s() else mat.ra.w(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + 8 => .ldr(if (mat.ra.isVector()) mat.ra.d() else mat.ra.x(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + 16 => .ldr(mat.ra.q(), .{ .unsigned_offset = .{ + .base = stack_slot.base.x(), + .offset = @intCast(offset), + } }), + }); + }, + .address => |base_vi| { + const base_mat = try base_vi.matReg(isel); + try isel.emit(switch (size) { + else => unreachable, + 1 => if (mat.ra.isVector()) .ldr(mat.ra.b(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }) else switch (mat.vi.signedness(isel)) { + .signed => .ldrsb(mat.ra.w(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + .unsigned => .ldrb(mat.ra.w(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + }, + 2 => if (mat.ra.isVector()) .ldr(mat.ra.h(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }) else switch (mat.vi.signedness(isel)) { + .signed => .ldrsh(mat.ra.w(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + .unsigned => .ldrh(mat.ra.w(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + }, + 4 => .ldr(if (mat.ra.isVector()) mat.ra.s() else mat.ra.w(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + 8 => .ldr(if (mat.ra.isVector()) mat.ra.d() else mat.ra.x(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + 16 => .ldr(mat.ra.q(), .{ .unsigned_offset = .{ + .base = base_mat.ra.x(), + .offset = @intCast(offset), + } }), + }); + break :free try base_mat.finish(isel); + }, + .value => |parent_vi| vi = parent_vi, + .constant => |initial_constant| { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + var constant = initial_constant.toIntern(); + var constant_key = ip.indexToKey(constant); + while (true) { + constant_key: switch (constant_key) { + .int_type, + .ptr_type, + .array_type, + .vector_type, + .opt_type, + .anyframe_type, + .error_union_type, + .simple_type, + .struct_type, + .tuple_type, + .union_type, + .opaque_type, + .enum_type, + .func_type, + .error_set_type, + .inferred_error_set_type, + + .enum_literal, + .empty_enum_value, + .memoized_call, + => unreachable, // not a runtime value + .undef => break :free try isel.emit(if (mat.ra.isVector()) .movi(switch (size) { + else => unreachable, + 1...8 => mat.ra.@"8b"(), + 9...16 => mat.ra.@"16b"(), + }, 0xaa, .{ .lsl = 0 }) else switch (size) { + else => unreachable, + 1...4 => .orr(mat.ra.w(), .wzr, .{ .immediate = .{ + .N = .word, + .immr = 0b000001, + .imms = 0b111100, + } }), + 5...8 => .orr(mat.ra.x(), .xzr, .{ .immediate = .{ + .N = .word, + .immr = 0b000001, + .imms = 0b111100, + } }), + }), + .simple_value => |simple_value| switch (simple_value) { + .undefined, .void, .null, .empty_tuple, .@"unreachable" => unreachable, + .true => continue :constant_key .{ .int = .{ + .ty = .bool_type, + .storage = .{ .u64 = 1 }, + } }, + .false => continue :constant_key .{ .int = .{ + .ty = .bool_type, + .storage = .{ .u64 = 0 }, + } }, + }, + .int => |int| break :free storage: switch (int.storage) { + .u64 => |imm| try isel.movImmediate(switch (size) { + else => unreachable, + 1...4 => mat.ra.w(), + 5...8 => mat.ra.x(), + }, @bitCast(std.math.shr(u64, imm, 8 * offset))), + .i64 => |imm| switch (size) { + else => unreachable, + 1...4 => try isel.movImmediate(mat.ra.w(), @as(u32, @bitCast(@as(i32, @truncate(std.math.shr(i64, imm, 8 * offset)))))), + 5...8 => try isel.movImmediate(mat.ra.x(), @bitCast(std.math.shr(i64, imm, 8 * offset))), + }, + .big_int => |big_int| { + assert(size == 8); + var imm: u64 = 0; + const limb_bits = @bitSizeOf(std.math.big.Limb); + const limbs = @divExact(64, limb_bits); + var limb_index: usize = @intCast(@divExact(offset, @divExact(limb_bits, 8)) + limbs); + for (0..limbs) |_| { + limb_index -= 1; + if (limb_index >= big_int.limbs.len) continue; + if (limb_bits < 64) imm <<= limb_bits; + imm |= big_int.limbs[limb_index]; + } + if (!big_int.positive) { + limb_index = @min(limb_index, big_int.limbs.len); + imm = while (limb_index > 0) { + limb_index -= 1; + if (big_int.limbs[limb_index] != 0) break ~imm; + } else -%imm; + } + try isel.movImmediate(mat.ra.x(), imm); + }, + .lazy_align => |ty| continue :storage .{ + .u64 = ZigType.fromInterned(ty).abiAlignment(zcu).toByteUnits().?, + }, + .lazy_size => |ty| continue :storage .{ + .u64 = ZigType.fromInterned(ty).abiSize(zcu), + }, + }, + .err => |err| continue :constant_key .{ .int = .{ + .ty = err.ty, + .storage = .{ .u64 = ip.getErrorValueIfExists(err.name).? }, + } }, + .error_union => |error_union| { + const error_union_type = ip.indexToKey(error_union.ty).error_union_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; + continue :constant_key switch (error_union.val) { + .err_name => |err_name| .{ .err = .{ + .ty = error_union_type.error_set_type, + .name = err_name, + } }, + .payload => .{ .int = .{ + .ty = error_union_type.error_set_type, + .storage = .{ .u64 = 0 }, + } }, + }; + } + 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; + }, + } + }, + .enum_tag => |enum_tag| continue :constant_key .{ .int = ip.indexToKey(enum_tag.int).int }, + .float => |float| storage: switch (float.storage) { + .f16 => |imm| { + if (!mat.ra.isVector()) continue :constant_key .{ .int = .{ + .ty = .u16_type, + .storage = .{ .u64 = @as(u16, @bitCast(imm)) }, + } }; + const feat_fp16 = isel.target.cpu.has(.aarch64, .fullfp16); + if (feat_fp16) { + const Repr = std.math.FloatRepr(f16); + const repr: Repr = @bitCast(imm); + if (repr.mantissa & std.math.maxInt(Repr.Mantissa) >> 5 == 0 and switch (repr.exponent) { + .denormal, .infinite => false, + else => std.math.cast(i3, repr.exponent.unbias() - 1) != null, + }) break :free try isel.emit(.fmov(mat.ra.h(), .{ .immediate = imm })); + } + const bits: u16 = @bitCast(imm); + if (bits == 0) break :free try isel.emit(.movi(mat.ra.d(), 0b00000000, .replicate)); + if (bits & std.math.maxInt(u8) == 0) break :free try isel.emit(.movi( + mat.ra.@"4h"(), + @intCast(@shrExact(bits, 8)), + .{ .lsl = 8 }, + )); + const temp_ra = try isel.allocIntReg(); + defer isel.freeReg(temp_ra); + try isel.emit(.fmov(if (feat_fp16) mat.ra.h() else mat.ra.s(), .{ .register = temp_ra.w() })); + break :free try isel.movImmediate(temp_ra.w(), bits); + }, + .f32 => |imm| { + if (!mat.ra.isVector()) continue :constant_key .{ .int = .{ + .ty = .u32_type, + .storage = .{ .u64 = @as(u32, @bitCast(imm)) }, + } }; + const Repr = std.math.FloatRepr(f32); + const repr: Repr = @bitCast(imm); + if (repr.mantissa & std.math.maxInt(Repr.Mantissa) >> 5 == 0 and switch (repr.exponent) { + .denormal, .infinite => false, + else => std.math.cast(i3, repr.exponent.unbias() - 1) != null, + }) break :free try isel.emit(.fmov(mat.ra.s(), .{ .immediate = @floatCast(imm) })); + const bits: u32 = @bitCast(imm); + if (bits == 0) break :free try isel.emit(.movi(mat.ra.d(), 0b00000000, .replicate)); + if (bits & std.math.maxInt(u24) == 0) break :free try isel.emit(.movi( + mat.ra.@"2s"(), + @intCast(@shrExact(bits, 24)), + .{ .lsl = 24 }, + )); + const temp_ra = try isel.allocIntReg(); + defer isel.freeReg(temp_ra); + try isel.emit(.fmov(mat.ra.s(), .{ .register = temp_ra.w() })); + break :free try isel.movImmediate(temp_ra.w(), bits); + }, + .f64 => |imm| { + if (!mat.ra.isVector()) continue :constant_key .{ .int = .{ + .ty = .u64_type, + .storage = .{ .u64 = @as(u64, @bitCast(imm)) }, + } }; + const Repr = std.math.FloatRepr(f64); + const repr: Repr = @bitCast(imm); + if (repr.mantissa & std.math.maxInt(Repr.Mantissa) >> 5 == 0 and switch (repr.exponent) { + .denormal, .infinite => false, + else => std.math.cast(i3, repr.exponent.unbias() - 1) != null, + }) break :free try isel.emit(.fmov(mat.ra.d(), .{ .immediate = @floatCast(imm) })); + const bits: u64 = @bitCast(imm); + if (bits == 0) break :free try isel.emit(.movi(mat.ra.d(), 0b00000000, .replicate)); + const temp_ra = try isel.allocIntReg(); + defer isel.freeReg(temp_ra); + try isel.emit(.fmov(mat.ra.d(), .{ .register = temp_ra.x() })); + break :free try isel.movImmediate(temp_ra.x(), bits); + }, + .f80 => |imm| break :free try isel.movImmediate( + mat.ra.x(), + @truncate(std.math.shr(u80, @bitCast(imm), 8 * offset)), + ), + .f128 => |imm| switch (ZigType.fromInterned(float.ty).floatBits(isel.target)) { + else => unreachable, + 16 => continue :storage .{ .f16 = @floatCast(imm) }, + 32 => continue :storage .{ .f32 = @floatCast(imm) }, + 64 => continue :storage .{ .f64 = @floatCast(imm) }, + 128 => { + const bits: u128 = @bitCast(imm); + const hi64: u64 = @intCast(bits >> 64); + const lo64: u64 = @truncate(bits >> 0); + const temp_ra = try isel.allocIntReg(); + defer isel.freeReg(temp_ra); + switch (hi64) { + 0 => {}, + else => { + try isel.emit(.fmov(mat.ra.@"d[]"(1), .{ .register = temp_ra.x() })); + try isel.movImmediate(temp_ra.x(), hi64); + }, + } + break :free switch (lo64) { + 0 => try isel.emit(.movi(switch (hi64) { + else => mat.ra.d(), + 0 => mat.ra.@"2d"(), + }, 0b00000000, .replicate)), + else => { + try isel.emit(.fmov(mat.ra.d(), .{ .register = temp_ra.x() })); + try isel.movImmediate(temp_ra.x(), lo64); + }, + }; + }, + }, + }, + .ptr => |ptr| { + assert(offset == 0 and size == 8); + break :free switch (ptr.base_addr) { + .nav => |nav| if (ZigType.fromInterned(ip.getNav(nav).typeOf(ip)).isFnOrHasRuntimeBits(zcu)) switch (true) { + false => { + try isel.nav_relocs.append(zcu.gpa, .{ + .nav = nav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }); + try isel.emit(.adr(mat.ra.x(), 0)); + }, + true => { + try isel.nav_relocs.append(zcu.gpa, .{ + .nav = nav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }); + try isel.emit(.add(mat.ra.x(), mat.ra.x(), .{ .immediate = 0 })); + try isel.nav_relocs.append(zcu.gpa, .{ + .nav = nav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }); + try isel.emit(.adrp(mat.ra.x(), 0)); + }, + } else continue :constant_key .{ .int = .{ + .ty = .usize_type, + .storage = .{ .u64 = isel.pt.navAlignment(nav).forward(0xaaaaaaaaaaaaaaaa) }, + } }, + .uav => |uav| if (ZigType.fromInterned(ip.typeOf(uav.val)).isFnOrHasRuntimeBits(zcu)) switch (true) { + false => { + try isel.uav_relocs.append(zcu.gpa, .{ + .uav = uav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }); + try isel.emit(.adr(mat.ra.x(), 0)); + }, + true => { + try isel.uav_relocs.append(zcu.gpa, .{ + .uav = uav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }); + try isel.emit(.add(mat.ra.x(), mat.ra.x(), .{ .immediate = 0 })); + try isel.uav_relocs.append(zcu.gpa, .{ + .uav = uav, + .reloc = .{ + .label = @intCast(isel.instructions.items.len), + .addend = ptr.byte_offset, + }, + }); + try isel.emit(.adrp(mat.ra.x(), 0)); + }, + } else continue :constant_key .{ .int = .{ + .ty = .usize_type, + .storage = .{ .u64 = ZigType.fromInterned(uav.orig_ty).ptrAlignment(zcu).forward(0xaaaaaaaaaaaaaaaa) }, + } }, + .int => continue :constant_key .{ .int = .{ + .ty = .usize_type, + .storage = .{ .u64 = ptr.byte_offset }, + } }, + .eu_payload => |base| { + 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); + 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); + continue :constant_key .{ .ptr = base_ptr }; + }, + .comptime_alloc, .comptime_field, .arr_elem => unreachable, + }; + }, + .slice => |slice| switch (offset) { + 0 => continue :constant_key .{ .ptr = ip.indexToKey(slice.ptr).ptr }, + else => { + assert(offset == @divExact(isel.target.ptrBitWidth(), 8)); + offset = 0; + continue :constant_key .{ .int = ip.indexToKey(slice.len).int }; + }, + }, + .opt => |opt| { + const child_ty = ip.indexToKey(opt.ty).opt_type; + const child_size = ZigType.fromInterned(child_ty).abiSize(zcu); + if (offset == child_size and size == 1) { + offset = 0; + continue :constant_key .{ .simple_value = switch (opt.val) { + .none => .false, + else => .true, + } }; + } + const opt_ty: ZigType = .fromInterned(opt.ty); + if (offset + size <= child_size) continue :constant_key switch (opt.val) { + .none => if (opt_ty.optionalReprIsPayload(zcu)) .{ .int = .{ + .ty = opt.ty, + .storage = .{ .u64 = 0 }, + } } else .{ .undef = child_ty }, + else => |child| { + constant = child; + constant_key = ip.indexToKey(child); + continue :constant_key constant_key; + }, + }; + }, + .aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) { + else => unreachable, + .array_type => |array_type| { + const elem_size = ZigType.fromInterned(array_type.child).abiSize(zcu); + const elem_offset = @mod(offset, elem_size); + if (size <= elem_size - elem_offset) { + defer offset = elem_offset; + continue :constant_key switch (aggregate.storage) { + .bytes => |bytes| .{ .int = .{ .ty = .u8_type, .storage = .{ + .u64 = bytes.toSlice(array_type.lenIncludingSentinel(), ip)[@intCast(@divFloor(offset, elem_size))], + } } }, + .elems => |elems| { + constant = elems[@intCast(@divFloor(offset, elem_size))]; + constant_key = ip.indexToKey(constant); + continue :constant_key constant_key; + }, + .repeated_elem => |repeated_elem| { + constant = repeated_elem; + constant_key = ip.indexToKey(repeated_elem); + continue :constant_key constant_key; + }, + }; + } + }, + .vector_type => {}, + .struct_type => { + const loaded_struct = ip.loadStructType(aggregate.ty); + switch (loaded_struct.layout) { + .auto => { + var field_offset: u64 = 0; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + while (field_it.next()) |field_index| { + if (loaded_struct.fieldIsComptime(ip, field_index)) continue; + const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + field_offset = field_ty.structFieldAlignment( + loaded_struct.fieldAlign(ip, field_index), + loaded_struct.layout, + zcu, + ).forward(field_offset); + const field_size = field_ty.abiSize(zcu); + if (offset >= field_offset and offset + size <= field_offset + field_size) { + offset -= field_offset; + constant = switch (aggregate.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }; + constant_key = ip.indexToKey(constant); + continue :constant_key constant_key; + } + field_offset += field_size; + } + }, + .@"extern", .@"packed" => {}, + } + }, + .tuple_type => |tuple_type| { + var field_offset: u64 = 0; + for (tuple_type.types.get(ip), tuple_type.values.get(ip), 0..) |field_type, field_value, field_index| { + if (field_value != .none) continue; + const field_ty: ZigType = .fromInterned(field_type); + field_offset = field_ty.abiAlignment(zcu).forward(field_offset); + const field_size = field_ty.abiSize(zcu); + if (offset >= field_offset and offset + size <= field_offset + field_size) { + offset -= field_offset; + constant = switch (aggregate.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }; + constant_key = ip.indexToKey(constant); + continue :constant_key constant_key; + } + field_offset += field_size; + } + }, + }, + else => {}, + } + var buffer: [16]u8 = @splat(0); + if (ZigType.fromInterned(constant_key.typeOf()).abiSize(zcu) <= buffer.len and + try isel.writeToMemory(.fromInterned(constant), &buffer)) + { + constant_key = if (mat.ra.isVector()) .{ .float = switch (size) { + else => unreachable, + 2 => .{ .ty = .f16_type, .storage = .{ .f16 = @bitCast(std.mem.readInt( + u16, + buffer[@intCast(offset)..][0..2], + isel.target.cpu.arch.endian(), + )) } }, + 4 => .{ .ty = .f32_type, .storage = .{ .f32 = @bitCast(std.mem.readInt( + u32, + buffer[@intCast(offset)..][0..4], + isel.target.cpu.arch.endian(), + )) } }, + 8 => .{ .ty = .f64_type, .storage = .{ .f64 = @bitCast(std.mem.readInt( + u64, + buffer[@intCast(offset)..][0..8], + isel.target.cpu.arch.endian(), + )) } }, + 16 => .{ .ty = .f128_type, .storage = .{ .f128 = @bitCast(std.mem.readInt( + u128, + buffer[@intCast(offset)..][0..16], + isel.target.cpu.arch.endian(), + )) } }, + } } else .{ .int = .{ + .ty = .u64_type, + .storage = .{ .u64 = switch (size) { + else => unreachable, + inline 1...8 => |ct_size| std.mem.readInt( + @Type(.{ .int = .{ .signedness = .unsigned, .bits = 8 * ct_size } }), + buffer[@intCast(offset)..][0..ct_size], + isel.target.cpu.arch.endian(), + ), + } }, + } }; + offset = 0; + continue; + } + return isel.fail("unsupported value <{f}, {f}>", .{ + isel.fmtType(.fromInterned(constant_key.typeOf())), + isel.fmtConstant(.fromInterned(constant)), + }); + } + }, + } + } + live_vi.* = .free; + } + }; +}; +fn initValue(isel: *Select, ty: ZigType) Value.Index { + const zcu = isel.pt.zcu; + return isel.initValueAdvanced(ty.abiAlignment(zcu), 0, ty.abiSize(zcu)); +} +fn initValueAdvanced( + isel: *Select, + parent_alignment: InternPool.Alignment, + offset_from_parent: u64, + size: u64, +) Value.Index { + defer isel.values.addOneAssumeCapacity().* = .{ + .refs = 0, + .flags = .{ + .alignment = .fromLog2Units(@min(parent_alignment.toLog2Units(), @ctz(offset_from_parent))), + .parent_tag = .unallocated, + .location_tag = if (size > 16) .large else .small, + .parts_len_minus_one = 0, + }, + .offset_from_parent = offset_from_parent, + .parent_payload = .{ .unallocated = {} }, + .location_payload = if (size > 16) .{ .large = .{ + .size = size, + } } else .{ .small = .{ + .size = @intCast(size), + .signedness = .unsigned, + .is_vector = false, + .hint = .zr, + .register = .zr, + } }, + .parts = undefined, + }; + return @enumFromInt(isel.values.items.len); +} +pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { + errdefer |err| @panic(@errorName(err)); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + + const zcu = isel.pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + const nav = ip.getNav(isel.nav_index); + + var reverse_live_values: std.AutoArrayHashMapUnmanaged(Value.Index, std.ArrayListUnmanaged(Air.Inst.Index)) = .empty; + defer { + for (reverse_live_values.values()) |*list| list.deinit(gpa); + reverse_live_values.deinit(gpa); + } + { + try reverse_live_values.ensureTotalCapacity(gpa, isel.live_values.count()); + var live_val_it = isel.live_values.iterator(); + while (live_val_it.next()) |live_val_entry| switch (live_val_entry.value_ptr.*) { + _ => { + const gop = reverse_live_values.getOrPutAssumeCapacity(live_val_entry.value_ptr.*); + if (!gop.found_existing) gop.value_ptr.* = .empty; + try gop.value_ptr.append(gpa, live_val_entry.key_ptr.*); + }, + .allocating, .free => unreachable, + }; + } + + var reverse_live_registers: std.AutoHashMapUnmanaged(Value.Index, Register.Alias) = .empty; + defer reverse_live_registers.deinit(gpa); + { + try reverse_live_registers.ensureTotalCapacity(gpa, @typeInfo(Register.Alias).@"enum".fields.len); + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) { + _ => reverse_live_registers.putAssumeCapacityNoClobber(live_reg_entry.value.*, live_reg_entry.key), + .allocating, .free => {}, + }; + } + + var roots: std.AutoArrayHashMapUnmanaged(Value.Index, u32) = .empty; + defer roots.deinit(gpa); + { + try roots.ensureTotalCapacity(gpa, isel.values.items.len); + var vi: Value.Index = @enumFromInt(isel.values.items.len); + while (@intFromEnum(vi) > 0) { + vi = @enumFromInt(@intFromEnum(vi) - 1); + if (which == .only_referenced and vi.get(isel).refs == 0) continue; + while (true) switch (vi.parent(isel)) { + .unallocated, .stack_slot, .constant => break, + .value => |parent_vi| vi = parent_vi, + .address => |address_vi| break roots.putAssumeCapacity(address_vi, 0), + }; + roots.putAssumeCapacity(vi, 0); + } + } + + try stderr.print("# Begin {s} Value Dump: {f}:\n", .{ @typeName(Select), nav.fqn.fmt(ip) }); + while (roots.pop()) |root_entry| { + const vi = root_entry.key; + const value = vi.get(isel); + try stderr.splatByteAll(' ', 2 * (@as(usize, 1) + root_entry.value)); + try stderr.print("${d}", .{@intFromEnum(vi)}); + { + var first = true; + if (reverse_live_values.get(vi)) |aiis| for (aiis.items) |aii| { + if (aii == Block.main) { + try stderr.print("{s}%main", .{if (first) " <- " else ", "}); + } else { + try stderr.print("{s}%{d}", .{ if (first) " <- " else ", ", @intFromEnum(aii) }); + } + first = false; + }; + if (reverse_live_registers.get(vi)) |ra| { + try stderr.print("{s}{s}", .{ if (first) " <- " else ", ", @tagName(ra) }); + first = false; + } + } + try stderr.writeByte(':'); + switch (value.flags.parent_tag) { + .unallocated => if (value.offset_from_parent != 0) try stderr.print(" +0x{x}", .{value.offset_from_parent}), + .stack_slot => { + try stderr.print(" [{s}, #{s}0x{x}", .{ + @tagName(value.parent_payload.stack_slot.base), + if (value.parent_payload.stack_slot.offset < 0) "-" else "", + @abs(value.parent_payload.stack_slot.offset), + }); + if (value.offset_from_parent != 0) try stderr.print("+0x{x}", .{value.offset_from_parent}); + try stderr.writeByte(']'); + }, + .value => try stderr.print(" ${d}+0x{x}", .{ @intFromEnum(value.parent_payload.value), value.offset_from_parent }), + .address => try stderr.print(" ${d}[0x{x}]", .{ @intFromEnum(value.parent_payload.address), value.offset_from_parent }), + .constant => try stderr.print(" <{f}, {f}>", .{ + isel.fmtType(value.parent_payload.constant.typeOf(zcu)), + isel.fmtConstant(value.parent_payload.constant), + }), + } + try stderr.print(" align({s})", .{@tagName(value.flags.alignment)}); + switch (value.flags.location_tag) { + .large => try stderr.print(" size=0x{x} large", .{value.location_payload.large.size}), + .small => { + const loc = value.location_payload.small; + try stderr.print(" size=0x{x}", .{loc.size}); + switch (loc.signedness) { + .unsigned => {}, + .signed => try stderr.writeAll(" signed"), + } + if (loc.hint != .zr) try stderr.print(" hint={s}", .{@tagName(loc.hint)}); + if (loc.register != .zr) try stderr.print(" loc={s}", .{@tagName(loc.register)}); + }, + } + try stderr.print(" refs={d}\n", .{value.refs}); + + var part_index = value.flags.parts_len_minus_one; + if (part_index > 0) while (true) : (part_index -= 1) { + roots.putAssumeCapacityNoClobber( + @enumFromInt(@intFromEnum(value.parts) + part_index), + root_entry.value + 1, + ); + if (part_index == 0) break; + }; + } + try stderr.print("# End {s} Value Dump: {f}\n\n", .{ @typeName(Select), nav.fqn.fmt(ip) }); +} + +fn hasRepeatedByteRepr(isel: *Select, constant: Constant) error{OutOfMemory}!?u8 { + const zcu = isel.pt.zcu; + const ty = constant.typeOf(zcu); + const abi_size = std.math.cast(usize, ty.abiSize(zcu)) orelse return null; + const byte_buffer = try zcu.gpa.alloc(u8, abi_size); + defer zcu.gpa.free(byte_buffer); + return if (try isel.writeToMemory(constant, byte_buffer) and + std.mem.allEqual(u8, byte_buffer[1..], byte_buffer[0])) byte_buffer[0] else null; +} + +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())) { + .int_type, + .ptr_type, + .array_type, + .vector_type, + .opt_type, + .anyframe_type, + .error_union_type, + .simple_type, + .struct_type, + .tuple_type, + .union_type, + .opaque_type, + .enum_type, + .func_type, + .error_set_type, + .inferred_error_set_type, + + .enum_literal, + .empty_enum_value, + .memoized_call, + => unreachable, // not a runtime value + .opt => |opt| { + const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu)); + switch (opt.val) { + .none => if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) { + buffer[child_size] = @intFromBool(false); + } else @memset(buffer[0..child_size], 0x00), + else => |child_constant| { + if (!try isel.writeToMemory(.fromInterned(child_constant), buffer[0..child_size])) return false; + if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) buffer[child_size] = @intFromBool(true); + }, + } + return true; + }, + .aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) { + else => unreachable, + .array_type => |array_type| { + var elem_offset: usize = 0; + const elem_size: usize = @intCast(ZigType.fromInterned(array_type.child).abiSize(zcu)); + const len_including_sentinel: usize = @intCast(array_type.lenIncludingSentinel()); + switch (aggregate.storage) { + .bytes => |bytes| @memcpy(buffer[0..len_including_sentinel], bytes.toSlice(len_including_sentinel, ip)), + .elems => |elems| for (elems) |elem| { + if (!try isel.writeToMemory(.fromInterned(elem), buffer[elem_offset..][0..elem_size])) return false; + elem_offset += elem_size; + }, + .repeated_elem => |repeated_elem| for (0..len_including_sentinel) |_| { + if (!try isel.writeToMemory(.fromInterned(repeated_elem), buffer[elem_offset..][0..elem_size])) return false; + elem_offset += elem_size; + }, + } + return true; + }, + .vector_type => {}, + .struct_type => { + const loaded_struct = ip.loadStructType(aggregate.ty); + switch (loaded_struct.layout) { + .auto => { + var field_offset: u64 = 0; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + while (field_it.next()) |field_index| { + if (loaded_struct.fieldIsComptime(ip, field_index)) continue; + const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + field_offset = field_ty.structFieldAlignment( + loaded_struct.fieldAlign(ip, field_index), + loaded_struct.layout, + zcu, + ).forward(field_offset); + const field_size = field_ty.abiSize(zcu); + if (!try isel.writeToMemory(.fromInterned(switch (aggregate.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false; + field_offset += field_size; + } + return true; + }, + .@"extern", .@"packed" => {}, + } + }, + .tuple_type => |tuple_type| { + var field_offset: u64 = 0; + for (tuple_type.types.get(ip), tuple_type.values.get(ip), 0..) |field_type, field_value, field_index| { + if (field_value != .none) continue; + const field_ty: ZigType = .fromInterned(field_type); + field_offset = field_ty.abiAlignment(zcu).forward(field_offset); + const field_size = field_ty.abiSize(zcu); + if (!try isel.writeToMemory(.fromInterned(switch (aggregate.storage) { + .bytes => unreachable, + .elems => |elems| elems[field_index], + .repeated_elem => |repeated_elem| repeated_elem, + }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false; + field_offset += field_size; + } + return true; + }, + }, + else => {}, + } + constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false, + }; + return true; +} + +const TryAllocRegResult = union(enum) { + allocated: Register.Alias, + fill_candidate: Register.Alias, + out_of_registers, +}; + +fn tryAllocIntReg(isel: *Select) TryAllocRegResult { + var failed_result: TryAllocRegResult = .out_of_registers; + var ra: Register.Alias = .r0; + while (true) : (ra = @enumFromInt(@intFromEnum(ra) + 1)) { + if (ra == .r18) continue; // The Platform Register + if (ra == Register.Alias.fp) continue; + const live_vi = isel.live_registers.getPtr(ra); + switch (live_vi.*) { + _ => switch (failed_result) { + .allocated => unreachable, + .fill_candidate => {}, + .out_of_registers => failed_result = .{ .fill_candidate = ra }, + }, + .allocating => {}, + .free => { + live_vi.* = .allocating; + isel.saved_registers.insert(ra); + return .{ .allocated = ra }; + }, + } + if (ra == Register.Alias.lr) return failed_result; + } +} + +fn allocIntReg(isel: *Select) !Register.Alias { + switch (isel.tryAllocIntReg()) { + .allocated => |ra| return ra, + .fill_candidate => |ra| { + assert(try isel.fillMemory(ra)); + const live_vi = isel.live_registers.getPtr(ra); + assert(live_vi.* == .free); + live_vi.* = .allocating; + return ra; + }, + .out_of_registers => return isel.fail("ran out of registers", .{}), + } +} + +fn tryAllocVecReg(isel: *Select) TryAllocRegResult { + var failed_result: TryAllocRegResult = .out_of_registers; + var ra: Register.Alias = .v0; + while (true) : (ra = @enumFromInt(@intFromEnum(ra) + 1)) { + const live_vi = isel.live_registers.getPtr(ra); + switch (live_vi.*) { + _ => switch (failed_result) { + .allocated => unreachable, + .fill_candidate => {}, + .out_of_registers => failed_result = .{ .fill_candidate = ra }, + }, + .allocating => {}, + .free => { + live_vi.* = .allocating; + isel.saved_registers.insert(ra); + return .{ .allocated = ra }; + }, + } + if (ra == Register.Alias.v31) return failed_result; + } +} + +fn allocVecReg(isel: *Select) !Register.Alias { + switch (isel.tryAllocVecReg()) { + .allocated => |ra| return ra, + .fill_candidate => |ra| { + assert(try isel.fillMemory(ra)); + return ra; + }, + .out_of_registers => return isel.fail("ran out of registers", .{}), + } +} + +const RegLock = struct { + ra: Register.Alias, + const empty: RegLock = .{ .ra = .zr }; + fn unlock(lock: RegLock, isel: *Select) void { + switch (lock.ra) { + else => |ra| isel.freeReg(ra), + .zr => {}, + } + } +}; +fn lockReg(isel: *Select, ra: Register.Alias) RegLock { + assert(ra != .zr); + const live_vi = isel.live_registers.getPtr(ra); + assert(live_vi.* == .free); + live_vi.* = .allocating; + return .{ .ra = ra }; +} +fn tryLockReg(isel: *Select, ra: Register.Alias) RegLock { + assert(ra != .zr); + const live_vi = isel.live_registers.getPtr(ra); + switch (live_vi.*) { + _ => unreachable, + .allocating => return .{ .ra = .zr }, + .free => { + live_vi.* = .allocating; + return .{ .ra = ra }; + }, + } +} + +fn freeReg(isel: *Select, ra: Register.Alias) void { + assert(ra != .zr); + const live_vi = isel.live_registers.getPtr(ra); + assert(live_vi.* == .allocating); + live_vi.* = .free; +} + +fn use(isel: *Select, air_ref: Air.Inst.Ref) !Value.Index { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + try isel.values.ensureUnusedCapacity(zcu.gpa, 1); + const vi, const ty = if (air_ref.toIndex()) |air_inst_index| vi_ty: { + const live_gop = try isel.live_values.getOrPut(zcu.gpa, air_inst_index); + if (live_gop.found_existing) return live_gop.value_ptr.*; + const ty = isel.air.typeOf(air_ref, ip); + const vi = isel.initValue(ty); + tracking_log.debug("${d} <- %{d}", .{ + @intFromEnum(vi), + @intFromEnum(air_inst_index), + }); + live_gop.value_ptr.* = vi.ref(isel); + break :vi_ty .{ vi, ty }; + } else vi_ty: { + const constant: Constant = .fromInterned(air_ref.toInterned().?); + const ty = constant.typeOf(zcu); + const vi = isel.initValue(ty); + tracking_log.debug("${d} <- <{f}, {f}>", .{ + @intFromEnum(vi), + isel.fmtType(ty), + isel.fmtConstant(constant), + }); + vi.setParent(isel, .{ .constant = constant }); + break :vi_ty .{ vi, ty }; + }; + if (ty.isAbiInt(zcu)) { + const int_info = ty.intInfo(zcu); + if (int_info.bits <= 16) vi.setSignedness(isel, int_info.signedness); + } else if (vi.size(isel) <= 16 and + CallAbiIterator.homogeneousAggregateBaseType(zcu, ty.toIntern()) != null) vi.setIsVector(isel); + return vi; +} + +fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail }!bool { + switch (dst_ra) { + else => {}, + Register.Alias.fp, .zr, .sp, .pc, .fpcr, .fpsr, .ffr => return false, + } + const dst_live_vi = isel.live_registers.getPtr(dst_ra); + const dst_vi = switch (dst_live_vi.*) { + _ => |dst_vi| dst_vi, + .allocating => return false, + .free => return true, + }; + const src_ra = src_ra: { + if (dst_vi.hint(isel)) |hint_ra| { + assert(dst_live_vi.* == dst_vi); + dst_live_vi.* = .allocating; + defer dst_live_vi.* = dst_vi; + if (try isel.fill(hint_ra)) { + isel.saved_registers.insert(hint_ra); + break :src_ra hint_ra; + } + } + switch (if (dst_vi.isVector(isel)) isel.tryAllocVecReg() else isel.tryAllocIntReg()) { + .allocated => |ra| break :src_ra ra, + .fill_candidate, .out_of_registers => return isel.fillMemory(dst_ra), + } + }; + try dst_vi.liveIn(isel, src_ra, comptime &.initFill(.free)); + const src_live_vi = isel.live_registers.getPtr(src_ra); + assert(src_live_vi.* == .allocating); + src_live_vi.* = dst_vi; + return true; +} + +fn fillMemory(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail }!bool { + const dst_live_vi = isel.live_registers.getPtr(dst_ra); + const dst_vi = switch (dst_live_vi.*) { + _ => |dst_vi| dst_vi, + .allocating => return false, + .free => return true, + }; + const dst_vi_ra = &dst_vi.get(isel).location_payload.small.register; + assert(dst_vi_ra.* == dst_ra); + const base_ra = if (dst_ra.isVector()) try isel.allocIntReg() else dst_ra; + defer if (base_ra != dst_ra) isel.freeReg(base_ra); + try isel.emit(switch (dst_vi.size(isel)) { + else => unreachable, + 1 => if (dst_ra.isVector()) + .ldr(dst_ra.b(), .{ .base = base_ra.x() }) + else switch (dst_vi.signedness(isel)) { + .signed => .ldrsb(dst_ra.w(), .{ .base = base_ra.x() }), + .unsigned => .ldrb(dst_ra.w(), .{ .base = base_ra.x() }), + }, + 2 => if (dst_ra.isVector()) + .ldr(dst_ra.h(), .{ .base = base_ra.x() }) + else switch (dst_vi.signedness(isel)) { + .signed => .ldrsh(dst_ra.w(), .{ .base = base_ra.x() }), + .unsigned => .ldrh(dst_ra.w(), .{ .base = base_ra.x() }), + }, + 4 => .ldr(if (dst_ra.isVector()) dst_ra.s() else dst_ra.w(), .{ .base = base_ra.x() }), + 8 => .ldr(if (dst_ra.isVector()) dst_ra.d() else dst_ra.x(), .{ .base = base_ra.x() }), + 16 => .ldr(dst_ra.q(), .{ .base = base_ra.x() }), + }); + dst_vi_ra.* = .zr; + try dst_vi.address(isel, 0, base_ra); + dst_live_vi.* = .free; + return true; +} + +/// Merges possibly differing value tracking into a consistent state. +/// +/// At a conditional branch, if a value is expected in the same register on both +/// paths, or only expected in a register on only one path, tracking is updated: +/// +/// $0 -> r0 // final state is now consistent with both paths +/// b.cond else +/// then: +/// $0 -> r0 // updated if not already consistent with else +/// ... +/// b end +/// else: +/// $0 -> r0 +/// ... +/// end: +/// +/// At a conditional branch, if a value is expected in different registers on +/// each path, mov instructions are emitted: +/// +/// $0 -> r0 // final state is now consistent with both paths +/// b.cond else +/// then: +/// $0 -> r0 // updated to be consistent with else +/// mov x1, x0 // emitted to merge the inconsistent states +/// $0 -> r1 +/// ... +/// b end +/// else: +/// $0 -> r0 +/// ... +/// end: +/// +/// At a loop, a value that is expected in a register at the repeats is updated: +/// +/// $0 -> r0 // final state is now consistent with all paths +/// loop: +/// $0 -> r0 // updated to be consistent with the repeats +/// ... +/// $0 -> r0 +/// b.cond loop +/// ... +/// $0 -> r0 +/// b loop +/// +/// At a loop, a value that is expected in a register at the top is filled: +/// +/// $0 -> [sp, #A] // final state is now consistent with all paths +/// loop: +/// $0 -> [sp, #A] // updated to be consistent with the repeats +/// ldr x0, [sp, #A] // emitted to merge the inconsistent states +/// $0 -> r0 +/// ... +/// $0 -> [sp, #A] +/// b.cond loop +/// ... +/// $0 -> [sp, #A] +/// b loop +/// +/// At a loop, if a value that is expected in different registers on each path, +/// mov instructions are emitted: +/// +/// $0 -> r0 // final state is now consistent with all paths +/// loop: +/// $0 -> r0 // updated to be consistent with the repeats +/// mov x1, x0 // emitted to merge the inconsistent states +/// $0 -> r1 +/// ... +/// $0 -> r0 +/// b.cond loop +/// ... +/// $0 -> r0 +/// b loop +fn merge( + isel: *Select, + expected_live_registers: *const LiveRegisters, + comptime opts: struct { fill_extra: bool = false }, +) !void { + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| { + const ra = live_reg_entry.key; + const actual_vi = live_reg_entry.value; + const expected_vi = expected_live_registers.get(ra); + switch (expected_vi) { + else => switch (actual_vi.*) { + _ => {}, + .allocating => unreachable, + .free => actual_vi.* = .allocating, + }, + .free => {}, + } + } + live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| { + const ra = live_reg_entry.key; + const actual_vi = live_reg_entry.value; + const expected_vi = expected_live_registers.get(ra); + switch (expected_vi) { + _ => { + switch (actual_vi.*) { + _ => _ = if (opts.fill_extra) { + assert(try isel.fillMemory(ra)); + assert(actual_vi.* == .free); + }, + .allocating => actual_vi.* = .free, + .free => unreachable, + } + try expected_vi.liveIn(isel, ra, expected_live_registers); + }, + .allocating => if (if (opts.fill_extra) try isel.fillMemory(ra) else try isel.fill(ra)) { + assert(actual_vi.* == .free); + actual_vi.* = .allocating; + }, + .free => if (opts.fill_extra) assert(try isel.fillMemory(ra) and actual_vi.* == .free), + } + } + live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| { + const ra = live_reg_entry.key; + const actual_vi = live_reg_entry.value; + const expected_vi = expected_live_registers.get(ra); + switch (expected_vi) { + _ => { + assert(actual_vi.* == .allocating and expected_vi.register(isel) == ra); + actual_vi.* = expected_vi; + }, + .allocating => assert(actual_vi.* == .allocating), + .free => if (opts.fill_extra) assert(actual_vi.* == .free), + } + } +} + +const call = struct { + const param_reg: Value.Index = @enumFromInt(@intFromEnum(Value.Index.allocating) - 2); + const callee_clobbered_reg: Value.Index = @enumFromInt(@intFromEnum(Value.Index.allocating) - 1); + const caller_saved_regs: LiveRegisters = .init(.{ + .r0 = param_reg, + .r1 = param_reg, + .r2 = param_reg, + .r3 = param_reg, + .r4 = param_reg, + .r5 = param_reg, + .r6 = param_reg, + .r7 = param_reg, + .r8 = param_reg, + .r9 = callee_clobbered_reg, + .r10 = callee_clobbered_reg, + .r11 = callee_clobbered_reg, + .r12 = callee_clobbered_reg, + .r13 = callee_clobbered_reg, + .r14 = callee_clobbered_reg, + .r15 = callee_clobbered_reg, + .r16 = callee_clobbered_reg, + .r17 = callee_clobbered_reg, + .r18 = callee_clobbered_reg, + .r19 = .free, + .r20 = .free, + .r21 = .free, + .r22 = .free, + .r23 = .free, + .r24 = .free, + .r25 = .free, + .r26 = .free, + .r27 = .free, + .r28 = .free, + .r29 = .free, + .r30 = callee_clobbered_reg, + .zr = .free, + .sp = .free, + + .pc = .free, + + .v0 = param_reg, + .v1 = param_reg, + .v2 = param_reg, + .v3 = param_reg, + .v4 = param_reg, + .v5 = param_reg, + .v6 = param_reg, + .v7 = param_reg, + .v8 = .free, + .v9 = .free, + .v10 = .free, + .v11 = .free, + .v12 = .free, + .v13 = .free, + .v14 = .free, + .v15 = .free, + .v16 = callee_clobbered_reg, + .v17 = callee_clobbered_reg, + .v18 = callee_clobbered_reg, + .v19 = callee_clobbered_reg, + .v20 = callee_clobbered_reg, + .v21 = callee_clobbered_reg, + .v22 = callee_clobbered_reg, + .v23 = callee_clobbered_reg, + .v24 = callee_clobbered_reg, + .v25 = callee_clobbered_reg, + .v26 = callee_clobbered_reg, + .v27 = callee_clobbered_reg, + .v28 = callee_clobbered_reg, + .v29 = callee_clobbered_reg, + .v30 = callee_clobbered_reg, + .v31 = callee_clobbered_reg, + + .fpcr = .free, + .fpsr = .free, + + .p0 = callee_clobbered_reg, + .p1 = callee_clobbered_reg, + .p2 = callee_clobbered_reg, + .p3 = callee_clobbered_reg, + .p4 = callee_clobbered_reg, + .p5 = callee_clobbered_reg, + .p6 = callee_clobbered_reg, + .p7 = callee_clobbered_reg, + .p8 = callee_clobbered_reg, + .p9 = callee_clobbered_reg, + .p10 = callee_clobbered_reg, + .p11 = callee_clobbered_reg, + .p12 = callee_clobbered_reg, + .p13 = callee_clobbered_reg, + .p14 = callee_clobbered_reg, + .p15 = callee_clobbered_reg, + + .ffr = .free, + }); + fn prepareReturn(isel: *Select) !void { + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| switch (caller_saved_regs.get(live_reg_entry.key)) { + else => unreachable, + param_reg, callee_clobbered_reg => switch (live_reg_entry.value.*) { + _ => {}, + .allocating => unreachable, + .free => live_reg_entry.value.* = .allocating, + }, + .free => {}, + }; + } + fn returnFill(isel: *Select, ra: Register.Alias) !void { + const live_vi = isel.live_registers.getPtr(ra); + if (try isel.fill(ra)) { + assert(live_vi.* == .free); + live_vi.* = .allocating; + } + assert(live_vi.* == .allocating); + } + fn returnLiveIn(isel: *Select, vi: Value.Index, ra: Register.Alias) !void { + try vi.defLiveIn(isel, ra, &caller_saved_regs); + } + fn finishReturn(isel: *Select) !void { + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| { + switch (live_reg_entry.value.*) { + _ => |live_vi| switch (live_vi.size(isel)) { + else => unreachable, + 1, 2, 4, 8 => {}, + 16 => { + assert(try isel.fillMemory(live_reg_entry.key)); + assert(live_reg_entry.value.* == .free); + switch (caller_saved_regs.get(live_reg_entry.key)) { + else => unreachable, + param_reg, callee_clobbered_reg => live_reg_entry.value.* = .allocating, + .free => {}, + } + continue; + }, + }, + .allocating, .free => {}, + } + switch (caller_saved_regs.get(live_reg_entry.key)) { + else => unreachable, + param_reg, callee_clobbered_reg => switch (live_reg_entry.value.*) { + _ => { + assert(try isel.fill(live_reg_entry.key)); + assert(live_reg_entry.value.* == .free); + live_reg_entry.value.* = .allocating; + }, + .allocating => {}, + .free => unreachable, + }, + .free => {}, + } + } + } + fn prepareCallee(isel: *Select) !void { + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| switch (caller_saved_regs.get(live_reg_entry.key)) { + else => unreachable, + param_reg => assert(live_reg_entry.value.* == .allocating), + callee_clobbered_reg => isel.freeReg(live_reg_entry.key), + .free => {}, + }; + } + fn finishCallee(_: *Select) !void {} + fn prepareParams(_: *Select) !void {} + fn paramLiveOut(isel: *Select, vi: Value.Index, ra: Register.Alias) !void { + isel.freeReg(ra); + try vi.liveOut(isel, ra); + const live_vi = isel.live_registers.getPtr(ra); + if (live_vi.* == .free) live_vi.* = .allocating; + } + fn paramAddress(isel: *Select, vi: Value.Index, ra: Register.Alias) !void { + isel.freeReg(ra); + try vi.address(isel, 0, ra); + const live_vi = isel.live_registers.getPtr(ra); + if (live_vi.* == .free) live_vi.* = .allocating; + } + fn finishParams(isel: *Select) !void { + var live_reg_it = isel.live_registers.iterator(); + while (live_reg_it.next()) |live_reg_entry| switch (caller_saved_regs.get(live_reg_entry.key)) { + else => unreachable, + param_reg => switch (live_reg_entry.value.*) { + _ => {}, + .allocating => live_reg_entry.value.* = .free, + .free => unreachable, + }, + callee_clobbered_reg, .free => {}, + }; + } +}; + +pub const CallAbiIterator = struct { + /// Next General-purpose Register Number + ngrn: Register.Alias, + /// Next SIMD and Floating-point Register Number + nsrn: Register.Alias, + /// next stacked argument address + nsaa: u24, + + pub const ngrn_start: Register.Alias = .r0; + pub const ngrn_end: Register.Alias = .r8; + pub const nsrn_start: Register.Alias = .v0; + pub const nsrn_end: Register.Alias = .v8; + pub const nsaa_start: u42 = 0; + + pub const init: CallAbiIterator = .{ + // A.1 + .ngrn = ngrn_start, + // A.2 + .nsrn = nsrn_start, + // A.3 + .nsaa = nsaa_start, + }; + + pub fn param(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + + if (ty.isNoReturn(zcu) or !ty.hasRuntimeBitsIgnoreComptime(zcu)) return null; + try isel.values.ensureUnusedCapacity(zcu.gpa, Value.max_parts); + const wip_vi = isel.initValue(ty); + type_key: switch (ip.indexToKey(ty.toIntern())) { + else => return isel.fail("CallAbiIterator.param({f})", .{isel.fmtType(ty)}), + .int_type => |int_type| switch (int_type.bits) { + 0 => unreachable, + 1...16 => { + wip_vi.setSignedness(isel, int_type.signedness); + // C.7 + it.integer(isel, wip_vi); + }, + // C.7 + 17...64 => it.integer(isel, wip_vi), + // C.9 + 65...128 => it.integers(isel, wip_vi, @splat(@divExact(wip_vi.size(isel), 2))), + else => it.indirect(isel, wip_vi), + }, + .array_type => switch (wip_vi.size(isel)) { + 0 => unreachable, + 1...8 => it.integer(isel, wip_vi), + 9...16 => |size| it.integers(isel, wip_vi, .{ 8, size - 8 }), + else => it.indirect(isel, wip_vi), + }, + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .one, .many, .c => continue :type_key .{ .int_type = .{ + .signedness = .unsigned, + .bits = 64, + } }, + .slice => it.integers(isel, wip_vi, @splat(8)), + }, + .opt_type => |child_type| if (ty.optionalReprIsPayload(zcu)) + continue :type_key ip.indexToKey(child_type) + else switch (ZigType.fromInterned(child_type).abiSize(zcu)) { + 0 => continue :type_key .{ .simple_type = .bool }, + 1...7 => it.integer(isel, wip_vi), + 8...15 => |child_size| it.integers(isel, wip_vi, .{ 8, child_size - 7 }), + else => return isel.fail("CallAbiIterator.param({f})", .{isel.fmtType(ty)}), + }, + .anyframe_type => unreachable, + .error_union_type => |error_union_type| switch (wip_vi.size(isel)) { + 0 => unreachable, + 1...8 => it.integer(isel, wip_vi), + 9...16 => { + var sizes: [2]u64 = @splat(0); + const payload_ty: ZigType = .fromInterned(error_union_type.payload_type); + { + 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 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 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), + }, + .simple_type => |simple_type| switch (simple_type) { + .f16, .f32, .f64, .f128, .c_longdouble => it.vector(isel, wip_vi), + .f80 => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 80 } }, + .usize, + .isize, + .c_char, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + => continue :type_key .{ .int_type = ty.intInfo(zcu) }, + // B.1 + .anyopaque => it.indirect(isel, wip_vi), + .bool => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 1 } }, + .anyerror => continue :type_key .{ .int_type = .{ + .signedness = .unsigned, + .bits = zcu.errorSetBits(), + } }, + .void, + .type, + .comptime_int, + .comptime_float, + .noreturn, + .null, + .undefined, + .enum_literal, + .adhoc_inferred_error_set, + .generic_poison, + => unreachable, + }, + .struct_type => { + const size = wip_vi.size(isel); + const loaded_struct = ip.loadStructType(ty.toIntern()); + if (size <= 16 * 4) homogeneous_aggregate: { + const fdt = homogeneousStructBaseType(zcu, &loaded_struct) orelse break :homogeneous_aggregate; + const parts_len = @shrExact(size, fdt.log2Size()); + if (parts_len > 4) break :homogeneous_aggregate; + it.vectors(isel, wip_vi, fdt, @intCast(parts_len)); + break :type_key; + } + switch (size) { + 0 => unreachable, + 1...8 => it.integer(isel, wip_vi), + 9...16 => { + var part_offset: u64 = 0; + var part_sizes: [2]u64 = undefined; + var parts_len: Value.PartsLen = 0; + var next_field_end: u64 = 0; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + while (part_offset < size) { + const field_end = next_field_end; + const next_field_begin = if (field_it.next()) |field_index| next_field_begin: { + const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + const next_field_begin = switch (loaded_struct.fieldAlign(ip, field_index)) { + .none => field_ty.abiAlignment(zcu), + else => |field_align| field_align, + }.forward(field_end); + next_field_end = next_field_begin + field_ty.abiSize(zcu); + break :next_field_begin next_field_begin; + } else std.mem.alignForward(u64, size, 8); + while (next_field_begin - part_offset >= 8) { + const part_size = field_end - part_offset; + part_sizes[parts_len] = part_size; + assert(part_offset + part_size <= size); + parts_len += 1; + part_offset = next_field_begin; + } + } + assert(parts_len == part_sizes.len); + it.integers(isel, wip_vi, part_sizes); + }, + else => it.indirect(isel, wip_vi), + } + }, + .tuple_type => |tuple_type| { + const size = wip_vi.size(isel); + if (size <= 16 * 4) homogeneous_aggregate: { + const fdt = homogeneousTupleBaseType(zcu, tuple_type) orelse break :homogeneous_aggregate; + const parts_len = @shrExact(size, fdt.log2Size()); + if (parts_len > 4) break :homogeneous_aggregate; + it.vectors(isel, wip_vi, fdt, @intCast(parts_len)); + break :type_key; + } + switch (size) { + 0 => unreachable, + 1...8 => it.integer(isel, wip_vi), + 9...16 => { + var part_offset: u64 = 0; + var part_sizes: [2]u64 = undefined; + var parts_len: Value.PartsLen = 0; + var next_field_end: u64 = 0; + var field_index: usize = 0; + while (part_offset < size) { + const field_end = next_field_end; + const next_field_begin = while (field_index < tuple_type.types.len) { + defer field_index += 1; + if (tuple_type.values.get(ip)[field_index] != .none) continue; + const field_ty: ZigType = .fromInterned(tuple_type.types.get(ip)[field_index]); + const next_field_begin = field_ty.abiAlignment(zcu).forward(field_end); + next_field_end = next_field_begin + field_ty.abiSize(zcu); + break next_field_begin; + } else std.mem.alignForward(u64, size, 8); + while (next_field_begin - part_offset >= 8) { + const part_size = @min(field_end - part_offset, 8); + part_sizes[parts_len] = part_size; + assert(part_offset + part_size <= size); + parts_len += 1; + part_offset += part_size; + if (part_offset >= field_end) part_offset = next_field_begin; + } + } + assert(parts_len == part_sizes.len); + it.integers(isel, wip_vi, part_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, + .inferred_error_set_type, + => continue :type_key .{ .simple_type = .anyerror }, + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .empty_enum_value, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .memoized_call, + => unreachable, // values, not types + } + return wip_vi.ref(isel); + } + + 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)) { + .unallocated, .stack_slot => {}, + .value, .constant => unreachable, + .address => |address_vi| { + assert(address_vi.hint(isel) == ngrn_start); + address_vi.setHint(isel, ngrn_end); + }, + } + return wip_vi; + } + + pub const FundamentalDataType = enum { + half, + single, + double, + quad, + vector64, + vector128, + fn log2Size(fdt: FundamentalDataType) u3 { + return switch (fdt) { + .half => 1, + .single => 2, + .double, .vector64 => 3, + .quad, .vector128 => 4, + }; + } + fn size(fdt: FundamentalDataType) u64 { + return @as(u64, 1) << fdt.log2Size(); + } + }; + fn homogeneousAggregateBaseType(zcu: *Zcu, initial_ty: InternPool.Index) ?FundamentalDataType { + const ip = &zcu.intern_pool; + var ty = initial_ty; + return type_key: switch (ip.indexToKey(ty)) { + else => null, + .array_type => |array_type| { + ty = array_type.child; + continue :type_key ip.indexToKey(ty); + }, + .vector_type => switch (ZigType.fromInterned(ty).abiSize(zcu)) { + else => null, + 8 => .vector64, + 16 => .vector128, + }, + .simple_type => |simple_type| switch (simple_type) { + .f16 => .half, + .f32 => .single, + .f64 => .double, + .f128 => .quad, + .c_longdouble => switch (zcu.getTarget().cTypeBitSize(.longdouble)) { + else => unreachable, + 16 => .half, + 32 => .single, + 64 => .double, + 80 => null, + 128 => .quad, + }, + else => null, + }, + .struct_type => homogeneousStructBaseType(zcu, &ip.loadStructType(ty)), + .tuple_type => |tuple_type| homogeneousTupleBaseType(zcu, tuple_type), + }; + } + fn homogeneousStructBaseType(zcu: *Zcu, loaded_struct: *const InternPool.LoadedStructType) ?FundamentalDataType { + const ip = &zcu.intern_pool; + var common_fdt: ?FundamentalDataType = null; + for (0.., loaded_struct.field_types.get(ip)) |field_index, field_ty| { + if (loaded_struct.fieldIsComptime(ip, field_index)) continue; + if (loaded_struct.fieldAlign(ip, field_index) != .none) return null; + if (!ZigType.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; + const fdt = homogeneousAggregateBaseType(zcu, field_ty); + if (common_fdt == null) common_fdt = fdt else if (fdt != common_fdt) return null; + } + return common_fdt; + } + fn homogeneousTupleBaseType(zcu: *Zcu, tuple_type: InternPool.Key.TupleType) ?FundamentalDataType { + const ip = &zcu.intern_pool; + var common_fdt: ?FundamentalDataType = null; + for (tuple_type.values.get(ip), tuple_type.types.get(ip)) |field_val, field_ty| { + if (field_val != .none) continue; + const fdt = homogeneousAggregateBaseType(zcu, field_ty); + if (common_fdt == null) common_fdt = fdt else if (fdt != common_fdt) return null; + } + return common_fdt; + } + + const Spec = struct { + offset: u64, + size: u64, + }; + + fn stack(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void { + // C.12 + it.nsaa = @intCast(wip_vi.alignment(isel).forward(it.nsaa)); + const parent_vi = switch (wip_vi.parent(isel)) { + .unallocated, .stack_slot => wip_vi, + .address, .constant => unreachable, + .value => |parent_vi| parent_vi, + }; + switch (parent_vi.parent(isel)) { + .unallocated => parent_vi.setParent(isel, .{ .stack_slot = .{ + .base = .sp, + .offset = it.nsaa, + } }), + .stack_slot => {}, + .address, .value, .constant => unreachable, + } + it.nsaa += @intCast(wip_vi.size(isel)); + } + + fn integer(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void { + assert(wip_vi.size(isel) <= 8); + const natural_alignment = wip_vi.alignment(isel); + assert(natural_alignment.order(.@"16").compare(.lte)); + wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8")); + if (it.ngrn == ngrn_end) return it.stack(isel, wip_vi); + wip_vi.setHint(isel, it.ngrn); + it.ngrn = @enumFromInt(@intFromEnum(it.ngrn) + 1); + } + + fn integers(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index, part_sizes: [2]u64) void { + assert(wip_vi.size(isel) <= 16); + const natural_alignment = wip_vi.alignment(isel); + assert(natural_alignment.order(.@"16").compare(.lte)); + wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8")); + // C.8 + if (natural_alignment == .@"16") it.ngrn = @enumFromInt(std.mem.alignForward( + @typeInfo(Register.Alias).@"enum".tag_type, + @intFromEnum(it.ngrn), + 2, + )); + if (it.ngrn == ngrn_end) return it.stack(isel, wip_vi); + wip_vi.setParts(isel, part_sizes.len); + for (0.., part_sizes) |part_index, part_size| + it.integer(isel, wip_vi.addPart(isel, 8 * part_index, part_size)); + } + + fn vector(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void { + assert(wip_vi.size(isel) <= 16); + const natural_alignment = wip_vi.alignment(isel); + assert(natural_alignment.order(.@"16").compare(.lte)); + wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8")); + wip_vi.setIsVector(isel); + if (it.nsrn == nsrn_end) return it.stack(isel, wip_vi); + wip_vi.setHint(isel, it.nsrn); + it.nsrn = @enumFromInt(@intFromEnum(it.nsrn) + 1); + } + + fn vectors( + it: *CallAbiIterator, + isel: *Select, + wip_vi: Value.Index, + fdt: FundamentalDataType, + parts_len: Value.PartsLen, + ) void { + const fdt_log2_size = fdt.log2Size(); + assert(wip_vi.size(isel) == @shlExact(@as(u9, parts_len), fdt_log2_size)); + const natural_alignment = wip_vi.alignment(isel); + assert(natural_alignment.order(.@"16").compare(.lte)); + wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8")); + if (@intFromEnum(it.nsrn) > @intFromEnum(nsrn_end) - parts_len) return it.stack(isel, wip_vi); + if (parts_len == 1) return it.vector(isel, wip_vi); + wip_vi.setParts(isel, parts_len); + const fdt_size = @as(u64, 1) << fdt_log2_size; + for (0..parts_len) |part_index| + it.vector(isel, wip_vi.addPart(isel, part_index << fdt_log2_size, fdt_size)); + } + + fn indirect(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void { + const wip_address_vi = isel.initValue(.usize); + wip_vi.setParent(isel, .{ .address = wip_address_vi }); + it.integer(isel, wip_address_vi); + } +}; + +const Air = @import("../../Air.zig"); +const assert = std.debug.assert; +const codegen = @import("../../codegen.zig"); +const Constant = @import("../../Value.zig"); +const InternPool = @import("../../InternPool.zig"); +const Package = @import("../../Package.zig"); +const Register = codegen.aarch64.encoding.Register; +const Select = @This(); +const std = @import("std"); +const tracking_log = std.log.scoped(.tracking); +const wip_mir_log = std.log.scoped(.@"wip-mir"); +const Zcu = @import("../../Zcu.zig"); +const ZigType = @import("../../Type.zig"); diff --git a/src/codegen/aarch64/abi.zig b/src/codegen/aarch64/abi.zig index 0cd0b389b1..9587415287 100644 --- a/src/codegen/aarch64/abi.zig +++ b/src/codegen/aarch64/abi.zig @@ -1,7 +1,5 @@ +const assert = @import("std").debug.assert; const std = @import("std"); -const builtin = @import("builtin"); -const bits = @import("../../arch/aarch64/bits.zig"); -const Register = bits.Register; const Type = @import("../../Type.zig"); const Zcu = @import("../../Zcu.zig"); @@ -15,7 +13,7 @@ pub const Class = union(enum) { /// For `float_array` the second element will be the amount of floats. pub fn classifyType(ty: Type, zcu: *Zcu) Class { - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); var maybe_float_bits: ?u16 = null; switch (ty.zigTypeTag(zcu)) { @@ -47,11 +45,11 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { return .byval; }, .optional => { - std.debug.assert(ty.isPtrLikeOptional(zcu)); + assert(ty.isPtrLikeOptional(zcu)); return .byval; }, .pointer => { - std.debug.assert(!ty.isSlice(zcu)); + assert(!ty.isSlice(zcu)); return .byval; }, .error_union, @@ -138,13 +136,3 @@ pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type { else => return null, } } - -pub const callee_preserved_regs = [_]Register{ - .x19, .x20, .x21, .x22, .x23, - .x24, .x25, .x26, .x27, .x28, -}; - -pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; -pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; - -const allocatable_registers = callee_preserved_regs; diff --git a/src/codegen/aarch64/encoding.zig b/src/codegen/aarch64/encoding.zig new file mode 100644 index 0000000000..1aef2f40c2 --- /dev/null +++ b/src/codegen/aarch64/encoding.zig @@ -0,0 +1,11799 @@ +/// B1.2 Registers in AArch64 Execution state +pub const Register = struct { + alias: Alias, + format: Format, + + pub const Format = union(enum) { + alias, + integer: IntegerSize, + scalar: VectorSize, + vector: Arrangement, + element: struct { size: VectorSize, index: u4 }, + }; + + pub const IntegerSize = enum(u1) { + word = 0b0, + doubleword = 0b1, + + pub fn prefix(is: IntegerSize) u8 { + return (comptime std.enums.EnumArray(IntegerSize, u8).init(.{ + .word = 'w', + .doubleword = 'x', + })).get(is); + } + }; + + pub const VectorSize = enum(u3) { + byte = 0, + half = 1, + single = 2, + double = 3, + quad = 4, + scalable, + predicate, + + pub fn prefix(vs: VectorSize) u8 { + return (comptime std.enums.EnumArray(VectorSize, u8).init(.{ + .byte = 'b', + .half = 'h', + .single = 's', + .double = 'd', + .quad = 'q', + .scalable = 'z', + .predicate = 'p', + })).get(vs); + } + }; + + pub const Arrangement = enum { + @"2d", + @"4s", + @"8h", + @"16b", + + @"1d", + @"2s", + @"4h", + @"8b", + + pub fn len(arrangement: Arrangement) u5 { + return switch (arrangement) { + .@"1d" => 1, + .@"2d", .@"2s" => 2, + .@"4s", .@"4h" => 4, + .@"8h", .@"8b" => 8, + .@"16b" => 16, + }; + } + + pub fn size(arrangement: Arrangement) Instruction.DataProcessingVector.Q { + return switch (arrangement) { + .@"2d", .@"4s", .@"8h", .@"16b" => .quad, + .@"1d", .@"2s", .@"4h", .@"8b" => .double, + }; + } + + pub fn elemSize(arrangement: Arrangement) Instruction.DataProcessingVector.Size { + return switch (arrangement) { + .@"2d", .@"1d" => .double, + .@"4s", .@"2s" => .single, + .@"8h", .@"4h" => .half, + .@"16b", .@"8b" => .byte, + }; + } + }; + + pub const x0: Register = .{ .alias = .r0, .format = .{ .integer = .doubleword } }; + pub const x1: Register = .{ .alias = .r1, .format = .{ .integer = .doubleword } }; + pub const x2: Register = .{ .alias = .r2, .format = .{ .integer = .doubleword } }; + pub const x3: Register = .{ .alias = .r3, .format = .{ .integer = .doubleword } }; + pub const x4: Register = .{ .alias = .r4, .format = .{ .integer = .doubleword } }; + pub const x5: Register = .{ .alias = .r5, .format = .{ .integer = .doubleword } }; + pub const x6: Register = .{ .alias = .r6, .format = .{ .integer = .doubleword } }; + pub const x7: Register = .{ .alias = .r7, .format = .{ .integer = .doubleword } }; + pub const x8: Register = .{ .alias = .r8, .format = .{ .integer = .doubleword } }; + pub const x9: Register = .{ .alias = .r9, .format = .{ .integer = .doubleword } }; + pub const x10: Register = .{ .alias = .r10, .format = .{ .integer = .doubleword } }; + pub const x11: Register = .{ .alias = .r11, .format = .{ .integer = .doubleword } }; + pub const x12: Register = .{ .alias = .r12, .format = .{ .integer = .doubleword } }; + pub const x13: Register = .{ .alias = .r13, .format = .{ .integer = .doubleword } }; + pub const x14: Register = .{ .alias = .r14, .format = .{ .integer = .doubleword } }; + pub const x15: Register = .{ .alias = .r15, .format = .{ .integer = .doubleword } }; + pub const x16: Register = .{ .alias = .r16, .format = .{ .integer = .doubleword } }; + pub const x17: Register = .{ .alias = .r17, .format = .{ .integer = .doubleword } }; + pub const x18: Register = .{ .alias = .r18, .format = .{ .integer = .doubleword } }; + pub const x19: Register = .{ .alias = .r19, .format = .{ .integer = .doubleword } }; + pub const x20: Register = .{ .alias = .r20, .format = .{ .integer = .doubleword } }; + pub const x21: Register = .{ .alias = .r21, .format = .{ .integer = .doubleword } }; + pub const x22: Register = .{ .alias = .r22, .format = .{ .integer = .doubleword } }; + pub const x23: Register = .{ .alias = .r23, .format = .{ .integer = .doubleword } }; + pub const x24: Register = .{ .alias = .r24, .format = .{ .integer = .doubleword } }; + pub const x25: Register = .{ .alias = .r25, .format = .{ .integer = .doubleword } }; + pub const x26: Register = .{ .alias = .r26, .format = .{ .integer = .doubleword } }; + pub const x27: Register = .{ .alias = .r27, .format = .{ .integer = .doubleword } }; + pub const x28: Register = .{ .alias = .r28, .format = .{ .integer = .doubleword } }; + pub const x29: Register = .{ .alias = .r29, .format = .{ .integer = .doubleword } }; + pub const x30: Register = .{ .alias = .r30, .format = .{ .integer = .doubleword } }; + pub const xzr: Register = .{ .alias = .zr, .format = .{ .integer = .doubleword } }; + pub const sp: Register = .{ .alias = .sp, .format = .{ .integer = .doubleword } }; + + pub const w0: Register = .{ .alias = .r0, .format = .{ .integer = .word } }; + pub const w1: Register = .{ .alias = .r1, .format = .{ .integer = .word } }; + pub const w2: Register = .{ .alias = .r2, .format = .{ .integer = .word } }; + pub const w3: Register = .{ .alias = .r3, .format = .{ .integer = .word } }; + pub const w4: Register = .{ .alias = .r4, .format = .{ .integer = .word } }; + pub const w5: Register = .{ .alias = .r5, .format = .{ .integer = .word } }; + pub const w6: Register = .{ .alias = .r6, .format = .{ .integer = .word } }; + pub const w7: Register = .{ .alias = .r7, .format = .{ .integer = .word } }; + pub const w8: Register = .{ .alias = .r8, .format = .{ .integer = .word } }; + pub const w9: Register = .{ .alias = .r9, .format = .{ .integer = .word } }; + pub const w10: Register = .{ .alias = .r10, .format = .{ .integer = .word } }; + pub const w11: Register = .{ .alias = .r11, .format = .{ .integer = .word } }; + pub const w12: Register = .{ .alias = .r12, .format = .{ .integer = .word } }; + pub const w13: Register = .{ .alias = .r13, .format = .{ .integer = .word } }; + pub const w14: Register = .{ .alias = .r14, .format = .{ .integer = .word } }; + pub const w15: Register = .{ .alias = .r15, .format = .{ .integer = .word } }; + pub const w16: Register = .{ .alias = .r16, .format = .{ .integer = .word } }; + pub const w17: Register = .{ .alias = .r17, .format = .{ .integer = .word } }; + pub const w18: Register = .{ .alias = .r18, .format = .{ .integer = .word } }; + pub const w19: Register = .{ .alias = .r19, .format = .{ .integer = .word } }; + pub const w20: Register = .{ .alias = .r20, .format = .{ .integer = .word } }; + pub const w21: Register = .{ .alias = .r21, .format = .{ .integer = .word } }; + pub const w22: Register = .{ .alias = .r22, .format = .{ .integer = .word } }; + pub const w23: Register = .{ .alias = .r23, .format = .{ .integer = .word } }; + pub const w24: Register = .{ .alias = .r24, .format = .{ .integer = .word } }; + pub const w25: Register = .{ .alias = .r25, .format = .{ .integer = .word } }; + pub const w26: Register = .{ .alias = .r26, .format = .{ .integer = .word } }; + pub const w27: Register = .{ .alias = .r27, .format = .{ .integer = .word } }; + pub const w28: Register = .{ .alias = .r28, .format = .{ .integer = .word } }; + pub const w29: Register = .{ .alias = .r29, .format = .{ .integer = .word } }; + pub const w30: Register = .{ .alias = .r30, .format = .{ .integer = .word } }; + pub const wzr: Register = .{ .alias = .zr, .format = .{ .integer = .word } }; + pub const wsp: Register = .{ .alias = .sp, .format = .{ .integer = .word } }; + + pub const ip0 = x16; + pub const ip1 = x17; + pub const fp = x29; + pub const lr = x30; + pub const pc: Register = .{ .alias = .pc, .format = .{ .integer = .doubleword } }; + + pub const q0: Register = .{ .alias = .v0, .format = .{ .scalar = .quad } }; + pub const q1: Register = .{ .alias = .v1, .format = .{ .scalar = .quad } }; + pub const q2: Register = .{ .alias = .v2, .format = .{ .scalar = .quad } }; + pub const q3: Register = .{ .alias = .v3, .format = .{ .scalar = .quad } }; + pub const q4: Register = .{ .alias = .v4, .format = .{ .scalar = .quad } }; + pub const q5: Register = .{ .alias = .v5, .format = .{ .scalar = .quad } }; + pub const q6: Register = .{ .alias = .v6, .format = .{ .scalar = .quad } }; + pub const q7: Register = .{ .alias = .v7, .format = .{ .scalar = .quad } }; + pub const q8: Register = .{ .alias = .v8, .format = .{ .scalar = .quad } }; + pub const q9: Register = .{ .alias = .v9, .format = .{ .scalar = .quad } }; + pub const q10: Register = .{ .alias = .v10, .format = .{ .scalar = .quad } }; + pub const q11: Register = .{ .alias = .v11, .format = .{ .scalar = .quad } }; + pub const q12: Register = .{ .alias = .v12, .format = .{ .scalar = .quad } }; + pub const q13: Register = .{ .alias = .v13, .format = .{ .scalar = .quad } }; + pub const q14: Register = .{ .alias = .v14, .format = .{ .scalar = .quad } }; + pub const q15: Register = .{ .alias = .v15, .format = .{ .scalar = .quad } }; + pub const q16: Register = .{ .alias = .v16, .format = .{ .scalar = .quad } }; + pub const q17: Register = .{ .alias = .v17, .format = .{ .scalar = .quad } }; + pub const q18: Register = .{ .alias = .v18, .format = .{ .scalar = .quad } }; + pub const q19: Register = .{ .alias = .v19, .format = .{ .scalar = .quad } }; + pub const q20: Register = .{ .alias = .v20, .format = .{ .scalar = .quad } }; + pub const q21: Register = .{ .alias = .v21, .format = .{ .scalar = .quad } }; + pub const q22: Register = .{ .alias = .v22, .format = .{ .scalar = .quad } }; + pub const q23: Register = .{ .alias = .v23, .format = .{ .scalar = .quad } }; + pub const q24: Register = .{ .alias = .v24, .format = .{ .scalar = .quad } }; + pub const q25: Register = .{ .alias = .v25, .format = .{ .scalar = .quad } }; + pub const q26: Register = .{ .alias = .v26, .format = .{ .scalar = .quad } }; + pub const q27: Register = .{ .alias = .v27, .format = .{ .scalar = .quad } }; + pub const q28: Register = .{ .alias = .v28, .format = .{ .scalar = .quad } }; + pub const q29: Register = .{ .alias = .v29, .format = .{ .scalar = .quad } }; + pub const q30: Register = .{ .alias = .v30, .format = .{ .scalar = .quad } }; + pub const q31: Register = .{ .alias = .v31, .format = .{ .scalar = .quad } }; + + pub const d0: Register = .{ .alias = .v0, .format = .{ .scalar = .double } }; + pub const d1: Register = .{ .alias = .v1, .format = .{ .scalar = .double } }; + pub const d2: Register = .{ .alias = .v2, .format = .{ .scalar = .double } }; + pub const d3: Register = .{ .alias = .v3, .format = .{ .scalar = .double } }; + pub const d4: Register = .{ .alias = .v4, .format = .{ .scalar = .double } }; + pub const d5: Register = .{ .alias = .v5, .format = .{ .scalar = .double } }; + pub const d6: Register = .{ .alias = .v6, .format = .{ .scalar = .double } }; + pub const d7: Register = .{ .alias = .v7, .format = .{ .scalar = .double } }; + pub const d8: Register = .{ .alias = .v8, .format = .{ .scalar = .double } }; + pub const d9: Register = .{ .alias = .v9, .format = .{ .scalar = .double } }; + pub const d10: Register = .{ .alias = .v10, .format = .{ .scalar = .double } }; + pub const d11: Register = .{ .alias = .v11, .format = .{ .scalar = .double } }; + pub const d12: Register = .{ .alias = .v12, .format = .{ .scalar = .double } }; + pub const d13: Register = .{ .alias = .v13, .format = .{ .scalar = .double } }; + pub const d14: Register = .{ .alias = .v14, .format = .{ .scalar = .double } }; + pub const d15: Register = .{ .alias = .v15, .format = .{ .scalar = .double } }; + pub const d16: Register = .{ .alias = .v16, .format = .{ .scalar = .double } }; + pub const d17: Register = .{ .alias = .v17, .format = .{ .scalar = .double } }; + pub const d18: Register = .{ .alias = .v18, .format = .{ .scalar = .double } }; + pub const d19: Register = .{ .alias = .v19, .format = .{ .scalar = .double } }; + pub const d20: Register = .{ .alias = .v20, .format = .{ .scalar = .double } }; + pub const d21: Register = .{ .alias = .v21, .format = .{ .scalar = .double } }; + pub const d22: Register = .{ .alias = .v22, .format = .{ .scalar = .double } }; + pub const d23: Register = .{ .alias = .v23, .format = .{ .scalar = .double } }; + pub const d24: Register = .{ .alias = .v24, .format = .{ .scalar = .double } }; + pub const d25: Register = .{ .alias = .v25, .format = .{ .scalar = .double } }; + pub const d26: Register = .{ .alias = .v26, .format = .{ .scalar = .double } }; + pub const d27: Register = .{ .alias = .v27, .format = .{ .scalar = .double } }; + pub const d28: Register = .{ .alias = .v28, .format = .{ .scalar = .double } }; + pub const d29: Register = .{ .alias = .v29, .format = .{ .scalar = .double } }; + pub const d30: Register = .{ .alias = .v30, .format = .{ .scalar = .double } }; + pub const d31: Register = .{ .alias = .v31, .format = .{ .scalar = .double } }; + + pub const s0: Register = .{ .alias = .v0, .format = .{ .scalar = .single } }; + pub const s1: Register = .{ .alias = .v1, .format = .{ .scalar = .single } }; + pub const s2: Register = .{ .alias = .v2, .format = .{ .scalar = .single } }; + pub const s3: Register = .{ .alias = .v3, .format = .{ .scalar = .single } }; + pub const s4: Register = .{ .alias = .v4, .format = .{ .scalar = .single } }; + pub const s5: Register = .{ .alias = .v5, .format = .{ .scalar = .single } }; + pub const s6: Register = .{ .alias = .v6, .format = .{ .scalar = .single } }; + pub const s7: Register = .{ .alias = .v7, .format = .{ .scalar = .single } }; + pub const s8: Register = .{ .alias = .v8, .format = .{ .scalar = .single } }; + pub const s9: Register = .{ .alias = .v9, .format = .{ .scalar = .single } }; + pub const s10: Register = .{ .alias = .v10, .format = .{ .scalar = .single } }; + pub const s11: Register = .{ .alias = .v11, .format = .{ .scalar = .single } }; + pub const s12: Register = .{ .alias = .v12, .format = .{ .scalar = .single } }; + pub const s13: Register = .{ .alias = .v13, .format = .{ .scalar = .single } }; + pub const s14: Register = .{ .alias = .v14, .format = .{ .scalar = .single } }; + pub const s15: Register = .{ .alias = .v15, .format = .{ .scalar = .single } }; + pub const s16: Register = .{ .alias = .v16, .format = .{ .scalar = .single } }; + pub const s17: Register = .{ .alias = .v17, .format = .{ .scalar = .single } }; + pub const s18: Register = .{ .alias = .v18, .format = .{ .scalar = .single } }; + pub const s19: Register = .{ .alias = .v19, .format = .{ .scalar = .single } }; + pub const s20: Register = .{ .alias = .v20, .format = .{ .scalar = .single } }; + pub const s21: Register = .{ .alias = .v21, .format = .{ .scalar = .single } }; + pub const s22: Register = .{ .alias = .v22, .format = .{ .scalar = .single } }; + pub const s23: Register = .{ .alias = .v23, .format = .{ .scalar = .single } }; + pub const s24: Register = .{ .alias = .v24, .format = .{ .scalar = .single } }; + pub const s25: Register = .{ .alias = .v25, .format = .{ .scalar = .single } }; + pub const s26: Register = .{ .alias = .v26, .format = .{ .scalar = .single } }; + pub const s27: Register = .{ .alias = .v27, .format = .{ .scalar = .single } }; + pub const s28: Register = .{ .alias = .v28, .format = .{ .scalar = .single } }; + pub const s29: Register = .{ .alias = .v29, .format = .{ .scalar = .single } }; + pub const s30: Register = .{ .alias = .v30, .format = .{ .scalar = .single } }; + pub const s31: Register = .{ .alias = .v31, .format = .{ .scalar = .single } }; + + pub const h0: Register = .{ .alias = .v0, .format = .{ .scalar = .half } }; + pub const h1: Register = .{ .alias = .v1, .format = .{ .scalar = .half } }; + pub const h2: Register = .{ .alias = .v2, .format = .{ .scalar = .half } }; + pub const h3: Register = .{ .alias = .v3, .format = .{ .scalar = .half } }; + pub const h4: Register = .{ .alias = .v4, .format = .{ .scalar = .half } }; + pub const h5: Register = .{ .alias = .v5, .format = .{ .scalar = .half } }; + pub const h6: Register = .{ .alias = .v6, .format = .{ .scalar = .half } }; + pub const h7: Register = .{ .alias = .v7, .format = .{ .scalar = .half } }; + pub const h8: Register = .{ .alias = .v8, .format = .{ .scalar = .half } }; + pub const h9: Register = .{ .alias = .v9, .format = .{ .scalar = .half } }; + pub const h10: Register = .{ .alias = .v10, .format = .{ .scalar = .half } }; + pub const h11: Register = .{ .alias = .v11, .format = .{ .scalar = .half } }; + pub const h12: Register = .{ .alias = .v12, .format = .{ .scalar = .half } }; + pub const h13: Register = .{ .alias = .v13, .format = .{ .scalar = .half } }; + pub const h14: Register = .{ .alias = .v14, .format = .{ .scalar = .half } }; + pub const h15: Register = .{ .alias = .v15, .format = .{ .scalar = .half } }; + pub const h16: Register = .{ .alias = .v16, .format = .{ .scalar = .half } }; + pub const h17: Register = .{ .alias = .v17, .format = .{ .scalar = .half } }; + pub const h18: Register = .{ .alias = .v18, .format = .{ .scalar = .half } }; + pub const h19: Register = .{ .alias = .v19, .format = .{ .scalar = .half } }; + pub const h20: Register = .{ .alias = .v20, .format = .{ .scalar = .half } }; + pub const h21: Register = .{ .alias = .v21, .format = .{ .scalar = .half } }; + pub const h22: Register = .{ .alias = .v22, .format = .{ .scalar = .half } }; + pub const h23: Register = .{ .alias = .v23, .format = .{ .scalar = .half } }; + pub const h24: Register = .{ .alias = .v24, .format = .{ .scalar = .half } }; + pub const h25: Register = .{ .alias = .v25, .format = .{ .scalar = .half } }; + pub const h26: Register = .{ .alias = .v26, .format = .{ .scalar = .half } }; + pub const h27: Register = .{ .alias = .v27, .format = .{ .scalar = .half } }; + pub const h28: Register = .{ .alias = .v28, .format = .{ .scalar = .half } }; + pub const h29: Register = .{ .alias = .v29, .format = .{ .scalar = .half } }; + pub const h30: Register = .{ .alias = .v30, .format = .{ .scalar = .half } }; + pub const h31: Register = .{ .alias = .v31, .format = .{ .scalar = .half } }; + + pub const b0: Register = .{ .alias = .v0, .format = .{ .scalar = .byte } }; + pub const b1: Register = .{ .alias = .v1, .format = .{ .scalar = .byte } }; + pub const b2: Register = .{ .alias = .v2, .format = .{ .scalar = .byte } }; + pub const b3: Register = .{ .alias = .v3, .format = .{ .scalar = .byte } }; + pub const b4: Register = .{ .alias = .v4, .format = .{ .scalar = .byte } }; + pub const b5: Register = .{ .alias = .v5, .format = .{ .scalar = .byte } }; + pub const b6: Register = .{ .alias = .v6, .format = .{ .scalar = .byte } }; + pub const b7: Register = .{ .alias = .v7, .format = .{ .scalar = .byte } }; + pub const b8: Register = .{ .alias = .v8, .format = .{ .scalar = .byte } }; + pub const b9: Register = .{ .alias = .v9, .format = .{ .scalar = .byte } }; + pub const b10: Register = .{ .alias = .v10, .format = .{ .scalar = .byte } }; + pub const b11: Register = .{ .alias = .v11, .format = .{ .scalar = .byte } }; + pub const b12: Register = .{ .alias = .v12, .format = .{ .scalar = .byte } }; + pub const b13: Register = .{ .alias = .v13, .format = .{ .scalar = .byte } }; + pub const b14: Register = .{ .alias = .v14, .format = .{ .scalar = .byte } }; + pub const b15: Register = .{ .alias = .v15, .format = .{ .scalar = .byte } }; + pub const b16: Register = .{ .alias = .v16, .format = .{ .scalar = .byte } }; + pub const b17: Register = .{ .alias = .v17, .format = .{ .scalar = .byte } }; + pub const b18: Register = .{ .alias = .v18, .format = .{ .scalar = .byte } }; + pub const b19: Register = .{ .alias = .v19, .format = .{ .scalar = .byte } }; + pub const b20: Register = .{ .alias = .v20, .format = .{ .scalar = .byte } }; + pub const b21: Register = .{ .alias = .v21, .format = .{ .scalar = .byte } }; + pub const b22: Register = .{ .alias = .v22, .format = .{ .scalar = .byte } }; + pub const b23: Register = .{ .alias = .v23, .format = .{ .scalar = .byte } }; + pub const b24: Register = .{ .alias = .v24, .format = .{ .scalar = .byte } }; + pub const b25: Register = .{ .alias = .v25, .format = .{ .scalar = .byte } }; + pub const b26: Register = .{ .alias = .v26, .format = .{ .scalar = .byte } }; + pub const b27: Register = .{ .alias = .v27, .format = .{ .scalar = .byte } }; + pub const b28: Register = .{ .alias = .v28, .format = .{ .scalar = .byte } }; + pub const b29: Register = .{ .alias = .v29, .format = .{ .scalar = .byte } }; + pub const b30: Register = .{ .alias = .v30, .format = .{ .scalar = .byte } }; + pub const b31: Register = .{ .alias = .v31, .format = .{ .scalar = .byte } }; + + pub const fpcr: Register = .{ .alias = .fpcr, .format = .{ .integer = .doubleword } }; + pub const fpsr: Register = .{ .alias = .fpsr, .format = .{ .integer = .doubleword } }; + + pub const z0: Register = .{ .alias = .v0, .format = .{ .scalar = .scalable } }; + pub const z1: Register = .{ .alias = .v1, .format = .{ .scalar = .scalable } }; + pub const z2: Register = .{ .alias = .v2, .format = .{ .scalar = .scalable } }; + pub const z3: Register = .{ .alias = .v3, .format = .{ .scalar = .scalable } }; + pub const z4: Register = .{ .alias = .v4, .format = .{ .scalar = .scalable } }; + pub const z5: Register = .{ .alias = .v5, .format = .{ .scalar = .scalable } }; + pub const z6: Register = .{ .alias = .v6, .format = .{ .scalar = .scalable } }; + pub const z7: Register = .{ .alias = .v7, .format = .{ .scalar = .scalable } }; + pub const z8: Register = .{ .alias = .v8, .format = .{ .scalar = .scalable } }; + pub const z9: Register = .{ .alias = .v9, .format = .{ .scalar = .scalable } }; + pub const z10: Register = .{ .alias = .v10, .format = .{ .scalar = .scalable } }; + pub const z11: Register = .{ .alias = .v11, .format = .{ .scalar = .scalable } }; + pub const z12: Register = .{ .alias = .v12, .format = .{ .scalar = .scalable } }; + pub const z13: Register = .{ .alias = .v13, .format = .{ .scalar = .scalable } }; + pub const z14: Register = .{ .alias = .v14, .format = .{ .scalar = .scalable } }; + pub const z15: Register = .{ .alias = .v15, .format = .{ .scalar = .scalable } }; + pub const z16: Register = .{ .alias = .v16, .format = .{ .scalar = .scalable } }; + pub const z17: Register = .{ .alias = .v17, .format = .{ .scalar = .scalable } }; + pub const z18: Register = .{ .alias = .v18, .format = .{ .scalar = .scalable } }; + pub const z19: Register = .{ .alias = .v19, .format = .{ .scalar = .scalable } }; + pub const z20: Register = .{ .alias = .v20, .format = .{ .scalar = .scalable } }; + pub const z21: Register = .{ .alias = .v21, .format = .{ .scalar = .scalable } }; + pub const z22: Register = .{ .alias = .v22, .format = .{ .scalar = .scalable } }; + pub const z23: Register = .{ .alias = .v23, .format = .{ .scalar = .scalable } }; + pub const z24: Register = .{ .alias = .v24, .format = .{ .scalar = .scalable } }; + pub const z25: Register = .{ .alias = .v25, .format = .{ .scalar = .scalable } }; + pub const z26: Register = .{ .alias = .v26, .format = .{ .scalar = .scalable } }; + pub const z27: Register = .{ .alias = .v27, .format = .{ .scalar = .scalable } }; + pub const z28: Register = .{ .alias = .v28, .format = .{ .scalar = .scalable } }; + pub const z29: Register = .{ .alias = .v29, .format = .{ .scalar = .scalable } }; + pub const z30: Register = .{ .alias = .v30, .format = .{ .scalar = .scalable } }; + pub const z31: Register = .{ .alias = .v31, .format = .{ .scalar = .scalable } }; + + pub const p0: Register = .{ .alias = .v0, .format = .{ .scalar = .predicate } }; + pub const p1: Register = .{ .alias = .v1, .format = .{ .scalar = .predicate } }; + pub const p2: Register = .{ .alias = .v2, .format = .{ .scalar = .predicate } }; + pub const p3: Register = .{ .alias = .v3, .format = .{ .scalar = .predicate } }; + pub const p4: Register = .{ .alias = .v4, .format = .{ .scalar = .predicate } }; + pub const p5: Register = .{ .alias = .v5, .format = .{ .scalar = .predicate } }; + pub const p6: Register = .{ .alias = .v6, .format = .{ .scalar = .predicate } }; + pub const p7: Register = .{ .alias = .v7, .format = .{ .scalar = .predicate } }; + pub const p8: Register = .{ .alias = .v8, .format = .{ .scalar = .predicate } }; + pub const p9: Register = .{ .alias = .v9, .format = .{ .scalar = .predicate } }; + pub const p10: Register = .{ .alias = .v10, .format = .{ .scalar = .predicate } }; + pub const p11: Register = .{ .alias = .v11, .format = .{ .scalar = .predicate } }; + pub const p12: Register = .{ .alias = .v12, .format = .{ .scalar = .predicate } }; + pub const p13: Register = .{ .alias = .v13, .format = .{ .scalar = .predicate } }; + pub const p14: Register = .{ .alias = .v14, .format = .{ .scalar = .predicate } }; + pub const p15: Register = .{ .alias = .v15, .format = .{ .scalar = .predicate } }; + + pub const ffr: Register = .{ .alias = .ffr, .format = .{ .integer = .doubleword } }; + + pub const Encoded = enum(u5) { + _, + + pub fn decodeInteger(enc: Encoded, sf_enc: IntegerSize, opts: struct { sp: bool = false }) Register { + return switch (sf_enc) { + .word => switch (@intFromEnum(enc)) { + 0 => .w0, + 1 => .w1, + 2 => .w2, + 3 => .w3, + 4 => .w4, + 5 => .w5, + 6 => .w6, + 7 => .w7, + 8 => .w8, + 9 => .w9, + 10 => .w10, + 11 => .w11, + 12 => .w12, + 13 => .w13, + 14 => .w14, + 15 => .w15, + 16 => .w16, + 17 => .w17, + 18 => .w18, + 19 => .w19, + 20 => .w20, + 21 => .w21, + 22 => .w22, + 23 => .w23, + 24 => .w24, + 25 => .w25, + 26 => .w26, + 27 => .w27, + 28 => .w28, + 29 => .w29, + 30 => .w30, + 31 => if (opts.sp) .wsp else .wzr, + }, + .doubleword => switch (@intFromEnum(enc)) { + 0 => .x0, + 1 => .x1, + 2 => .x2, + 3 => .x3, + 4 => .x4, + 5 => .x5, + 6 => .x6, + 7 => .x7, + 8 => .x8, + 9 => .x9, + 10 => .x10, + 11 => .x11, + 12 => .x12, + 13 => .x13, + 14 => .x14, + 15 => .x15, + 16 => .x16, + 17 => .x17, + 18 => .x18, + 19 => .x19, + 20 => .x20, + 21 => .x21, + 22 => .x22, + 23 => .x23, + 24 => .x24, + 25 => .x25, + 26 => .x26, + 27 => .x27, + 28 => .x28, + 29 => .x29, + 30 => .x30, + 31 => if (opts.sp) .sp else .xzr, + }, + }; + } + + pub fn decodeVector(enc: Encoded, vs_enc: VectorSize) Register { + return switch (vs_enc) { + .byte => switch (@intFromEnum(enc)) { + 0 => .b0, + 1 => .b1, + 2 => .b2, + 3 => .b3, + 4 => .b4, + 5 => .b5, + 6 => .b6, + 7 => .b7, + 8 => .b8, + 9 => .b9, + 10 => .b10, + 11 => .b11, + 12 => .b12, + 13 => .b13, + 14 => .b14, + 15 => .b15, + 16 => .b16, + 17 => .b17, + 18 => .b18, + 19 => .b19, + 20 => .b20, + 21 => .b21, + 22 => .b22, + 23 => .b23, + 24 => .b24, + 25 => .b25, + 26 => .b26, + 27 => .b27, + 28 => .b28, + 29 => .b29, + 30 => .b30, + 31 => .b31, + }, + .half => switch (@intFromEnum(enc)) { + 0 => .h0, + 1 => .h1, + 2 => .h2, + 3 => .h3, + 4 => .h4, + 5 => .h5, + 6 => .h6, + 7 => .h7, + 8 => .h8, + 9 => .h9, + 10 => .h10, + 11 => .h11, + 12 => .h12, + 13 => .h13, + 14 => .h14, + 15 => .h15, + 16 => .h16, + 17 => .h17, + 18 => .h18, + 19 => .h19, + 20 => .h20, + 21 => .h21, + 22 => .h22, + 23 => .h23, + 24 => .h24, + 25 => .h25, + 26 => .h26, + 27 => .h27, + 28 => .h28, + 29 => .h29, + 30 => .h30, + 31 => .h31, + }, + .single => switch (@intFromEnum(enc)) { + 0 => .s0, + 1 => .s1, + 2 => .s2, + 3 => .s3, + 4 => .s4, + 5 => .s5, + 6 => .s6, + 7 => .s7, + 8 => .s8, + 9 => .s9, + 10 => .s10, + 11 => .s11, + 12 => .s12, + 13 => .s13, + 14 => .s14, + 15 => .s15, + 16 => .s16, + 17 => .s17, + 18 => .s18, + 19 => .s19, + 20 => .s20, + 21 => .s21, + 22 => .s22, + 23 => .s23, + 24 => .s24, + 25 => .s25, + 26 => .s26, + 27 => .s27, + 28 => .s28, + 29 => .s29, + 30 => .s30, + 31 => .s31, + }, + .double => switch (@intFromEnum(enc)) { + 0 => .d0, + 1 => .d1, + 2 => .d2, + 3 => .d3, + 4 => .d4, + 5 => .d5, + 6 => .d6, + 7 => .d7, + 8 => .d8, + 9 => .d9, + 10 => .d10, + 11 => .d11, + 12 => .d12, + 13 => .d13, + 14 => .d14, + 15 => .d15, + 16 => .d16, + 17 => .d17, + 18 => .d18, + 19 => .d19, + 20 => .d20, + 21 => .d21, + 22 => .d22, + 23 => .d23, + 24 => .d24, + 25 => .d25, + 26 => .d26, + 27 => .d27, + 28 => .d28, + 29 => .d29, + 30 => .d30, + 31 => .d31, + }, + .quad => switch (@intFromEnum(enc)) { + 0 => .q0, + 1 => .q1, + 2 => .q2, + 3 => .q3, + 4 => .q4, + 5 => .q5, + 6 => .q6, + 7 => .q7, + 8 => .q8, + 9 => .q9, + 10 => .q10, + 11 => .q11, + 12 => .q12, + 13 => .q13, + 14 => .q14, + 15 => .q15, + 16 => .q16, + 17 => .q17, + 18 => .q18, + 19 => .q19, + 20 => .q20, + 21 => .q21, + 22 => .q22, + 23 => .q23, + 24 => .q24, + 25 => .q25, + 26 => .q26, + 27 => .q27, + 28 => .q28, + 29 => .q29, + 30 => .q30, + 31 => .q31, + }, + .scalable => switch (@intFromEnum(enc)) { + 0 => .z0, + 1 => .z1, + 2 => .z2, + 3 => .z3, + 4 => .z4, + 5 => .z5, + 6 => .z6, + 7 => .z7, + 8 => .z8, + 9 => .z9, + 10 => .z10, + 11 => .z11, + 12 => .z12, + 13 => .z13, + 14 => .z14, + 15 => .z15, + 16 => .z16, + 17 => .z17, + 18 => .z18, + 19 => .z19, + 20 => .z20, + 21 => .z21, + 22 => .z22, + 23 => .z23, + 24 => .z24, + 25 => .z25, + 26 => .z26, + 27 => .z27, + 28 => .z28, + 29 => .z29, + 30 => .z30, + 31 => .z31, + }, + .predicate => switch (@as(u4, @intCast(@intFromEnum(enc)))) { + 0 => .p0, + 1 => .p1, + 2 => .p2, + 3 => .p3, + 4 => .p4, + 5 => .p5, + 6 => .p6, + 7 => .p7, + 8 => .p8, + 9 => .p9, + 10 => .p10, + 11 => .p11, + 12 => .p12, + 13 => .p13, + 14 => .p14, + 15 => .p15, + }, + }; + } + }; + + /// One tag per set of aliasing registers. + pub const Alias = enum(u7) { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + zr, + sp, + + pc, + + v0, + v1, + v2, + v3, + v4, + v5, + v6, + v7, + v8, + v9, + v10, + v11, + v12, + v13, + v14, + v15, + v16, + v17, + v18, + v19, + v20, + v21, + v22, + v23, + v24, + v25, + v26, + v27, + v28, + v29, + v30, + v31, + + fpcr, + fpsr, + + p0, + p1, + p2, + p3, + p4, + p5, + p6, + p7, + p8, + p9, + p10, + p11, + p12, + p13, + p14, + p15, + + ffr, + + pub const ip0: Alias = .r16; + pub const ip1: Alias = .r17; + pub const fp: Alias = .r29; + pub const lr: Alias = .r30; + + pub fn r(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.r0) and @intFromEnum(ra) <= @intFromEnum(Alias.pc)); + return .{ .alias = ra, .format = .alias }; + } + pub fn x(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.r0) and @intFromEnum(ra) <= @intFromEnum(Alias.sp)); + return .{ .alias = ra, .format = .{ .integer = .doubleword } }; + } + pub fn w(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.r0) and @intFromEnum(ra) <= @intFromEnum(Alias.sp)); + return .{ .alias = ra, .format = .{ .integer = .word } }; + } + pub fn v(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .alias }; + } + pub fn q(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .scalar = .quad } }; + } + pub fn d(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .scalar = .double } }; + } + pub fn s(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .scalar = .single } }; + } + pub fn h(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .scalar = .half } }; + } + pub fn b(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .scalar = .byte } }; + } + pub fn z(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .scalar = .scalable } }; + } + pub fn p(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.p0) and @intFromEnum(ra) <= @intFromEnum(Alias.p15)); + return .{ .alias = ra, .format = .{ .scalar = .predicate } }; + } + pub fn @"2d"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"2d" } }; + } + pub fn @"4s"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"4s" } }; + } + pub fn @"8h"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"8h" } }; + } + pub fn @"16b"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"16b" } }; + } + pub fn @"1d"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"1d" } }; + } + pub fn @"2s"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"2s" } }; + } + pub fn @"4h"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"4h" } }; + } + pub fn @"8b"(ra: Alias) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .vector = .@"8b" } }; + } + pub fn @"d[]"(ra: Alias, index: u1) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .element = .{ .size = .double, .index = index } } }; + } + pub fn @"s[]"(ra: Alias, index: u2) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .element = .{ .size = .single, .index = index } } }; + } + pub fn @"h[]"(ra: Alias, index: u3) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .element = .{ .size = .half, .index = index } } }; + } + pub fn @"b[]"(ra: Alias, index: u4) Register { + assert(@intFromEnum(ra) >= @intFromEnum(Alias.v0) and @intFromEnum(ra) <= @intFromEnum(Alias.v31)); + return .{ .alias = ra, .format = .{ .element = .{ .size = .byte, .index = index } } }; + } + + pub fn isVector(ra: Alias) bool { + return switch (ra) { + .r0, + .r1, + .r2, + .r3, + .r4, + .r5, + .r6, + .r7, + .r8, + .r9, + .r10, + .r11, + .r12, + .r13, + .r14, + .r15, + .r16, + .r17, + .r18, + .r19, + .r20, + .r21, + .r22, + .r23, + .r24, + .r25, + .r26, + .r27, + .r28, + .r29, + .r30, + .zr, + .sp, + + .pc, + + .fpcr, + .fpsr, + + .ffr, + => false, + + .v0, + .v1, + .v2, + .v3, + .v4, + .v5, + .v6, + .v7, + .v8, + .v9, + .v10, + .v11, + .v12, + .v13, + .v14, + .v15, + .v16, + .v17, + .v18, + .v19, + .v20, + .v21, + .v22, + .v23, + .v24, + .v25, + .v26, + .v27, + .v28, + .v29, + .v30, + .v31, + + .p0, + .p1, + .p2, + .p3, + .p4, + .p5, + .p6, + .p7, + .p8, + .p9, + .p10, + .p11, + .p12, + .p13, + .p14, + .p15, + => true, + }; + } + + pub fn encode(ra: Alias, comptime opts: struct { sp: bool = false, V: bool = false }) Encoded { + return @enumFromInt(@as(u5, switch (ra) { + .r0 => if (opts.V) unreachable else 0, + .r1 => if (opts.V) unreachable else 1, + .r2 => if (opts.V) unreachable else 2, + .r3 => if (opts.V) unreachable else 3, + .r4 => if (opts.V) unreachable else 4, + .r5 => if (opts.V) unreachable else 5, + .r6 => if (opts.V) unreachable else 6, + .r7 => if (opts.V) unreachable else 7, + .r8 => if (opts.V) unreachable else 8, + .r9 => if (opts.V) unreachable else 9, + .r10 => if (opts.V) unreachable else 10, + .r11 => if (opts.V) unreachable else 11, + .r12 => if (opts.V) unreachable else 12, + .r13 => if (opts.V) unreachable else 13, + .r14 => if (opts.V) unreachable else 14, + .r15 => if (opts.V) unreachable else 15, + .r16 => if (opts.V) unreachable else 16, + .r17 => if (opts.V) unreachable else 17, + .r18 => if (opts.V) unreachable else 18, + .r19 => if (opts.V) unreachable else 19, + .r20 => if (opts.V) unreachable else 20, + .r21 => if (opts.V) unreachable else 21, + .r22 => if (opts.V) unreachable else 22, + .r23 => if (opts.V) unreachable else 23, + .r24 => if (opts.V) unreachable else 24, + .r25 => if (opts.V) unreachable else 25, + .r26 => if (opts.V) unreachable else 26, + .r27 => if (opts.V) unreachable else 27, + .r28 => if (opts.V) unreachable else 28, + .r29 => if (opts.V) unreachable else 29, + .r30 => if (opts.V) unreachable else 30, + .zr => if (opts.sp or opts.V) unreachable else 31, + .sp => if (opts.sp and !opts.V) 31 else unreachable, + .pc => unreachable, + .v0 => if (opts.V) 0 else unreachable, + .v1 => if (opts.V) 1 else unreachable, + .v2 => if (opts.V) 2 else unreachable, + .v3 => if (opts.V) 3 else unreachable, + .v4 => if (opts.V) 4 else unreachable, + .v5 => if (opts.V) 5 else unreachable, + .v6 => if (opts.V) 6 else unreachable, + .v7 => if (opts.V) 7 else unreachable, + .v8 => if (opts.V) 8 else unreachable, + .v9 => if (opts.V) 9 else unreachable, + .v10 => if (opts.V) 10 else unreachable, + .v11 => if (opts.V) 11 else unreachable, + .v12 => if (opts.V) 12 else unreachable, + .v13 => if (opts.V) 13 else unreachable, + .v14 => if (opts.V) 14 else unreachable, + .v15 => if (opts.V) 15 else unreachable, + .v16 => if (opts.V) 16 else unreachable, + .v17 => if (opts.V) 17 else unreachable, + .v18 => if (opts.V) 18 else unreachable, + .v19 => if (opts.V) 19 else unreachable, + .v20 => if (opts.V) 20 else unreachable, + .v21 => if (opts.V) 21 else unreachable, + .v22 => if (opts.V) 22 else unreachable, + .v23 => if (opts.V) 23 else unreachable, + .v24 => if (opts.V) 24 else unreachable, + .v25 => if (opts.V) 25 else unreachable, + .v26 => if (opts.V) 26 else unreachable, + .v27 => if (opts.V) 27 else unreachable, + .v28 => if (opts.V) 28 else unreachable, + .v29 => if (opts.V) 29 else unreachable, + .v30 => if (opts.V) 30 else unreachable, + .v31 => if (opts.V) 31 else unreachable, + .fpcr, .fpsr => unreachable, + .p0, .p1, .p2, .p3, .p4, .p5, .p6, .p7, .p8, .p9, .p10, .p11, .p12, .p13, .p14, .p15 => unreachable, + .ffr => unreachable, + })); + } + }; + + pub fn isVector(reg: Register) bool { + return reg.alias.isVector(); + } + + pub fn size(reg: Register) ?u5 { + return format: switch (reg.format) { + .alias => unreachable, + .integer => |sf| switch (sf) { + .word => 4, + .doubleword => 8, + }, + .vector => |vs| switch (vs) { + .byte => 1, + .word => 2, + .single => 4, + .double => 8, + .quad => 16, + .scalable, .predicate => null, + }, + .arrangement => |arrangement| switch (arrangement) { + .@"2d", .@"4s", .@"8h", .@"16b" => 16, + .@"1d", .@"2s", .@"4h", .@"8b" => 8, + }, + .element => |element| continue :format .{ .vector = element.size }, + }; + } + + pub fn parse(reg: []const u8) ?Register { + return if (reg.len == 0) null else switch (reg[0]) { + else => null, + 'r' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| switch (n) { + 0...30 => .{ + .alias = @enumFromInt(@intFromEnum(Alias.r0) + n), + .format = .alias, + }, + 31 => null, + } else |_| null, + 'x' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| switch (n) { + 0...30 => .{ + .alias = @enumFromInt(@intFromEnum(Alias.r0) + n), + .format = .{ .integer = .doubleword }, + }, + 31 => null, + } else |_| if (std.mem.eql(u8, reg, "xzr")) .xzr else null, + 'w' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| switch (n) { + 0...30 => .{ + .alias = @enumFromInt(@intFromEnum(Alias.r0) + n), + .format = .{ .integer = .word }, + }, + 31 => null, + } else |_| if (std.mem.eql(u8, reg, "wzr")) + .wzr + else if (std.mem.eql(u8, reg, "wsp")) + .wsp + else + null, + 'i' => return if (std.mem.eql(u8, reg, "ip") or std.mem.eql(u8, reg, "ip0")) + .ip0 + else if (std.mem.eql(u8, reg, "ip1")) + .ip1 + else + null, + 'f' => return if (std.mem.eql(u8, reg, "fp")) .fp else null, + 'p' => return if (std.mem.eql(u8, reg, "pc")) .pc else null, + 'v' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| .{ + .alias = @enumFromInt(@intFromEnum(Alias.v0) + n), + .format = .alias, + } else |_| null, + 'q' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| .{ + .alias = @enumFromInt(@intFromEnum(Alias.v0) + n), + .format = .{ .scalar = .quad }, + } else |_| null, + 'd' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| .{ + .alias = @enumFromInt(@intFromEnum(Alias.v0) + n), + .format = .{ .scalar = .double }, + } else |_| null, + 's' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| .{ + .alias = @enumFromInt(@intFromEnum(Alias.v0) + n), + .format = .{ .scalar = .single }, + } else |_| if (std.mem.eql(u8, reg, "sp")) .sp else null, + 'h' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| .{ + .alias = @enumFromInt(@intFromEnum(Alias.v0) + n), + .format = .{ .scalar = .half }, + } else |_| null, + 'b' => if (std.fmt.parseInt(u5, reg[1..], 10)) |n| .{ + .alias = @enumFromInt(@intFromEnum(Alias.v0) + n), + .format = .{ .scalar = .byte }, + } else |_| null, + }; + } + + pub fn fmt(reg: Register) aarch64.Disassemble.RegisterFormatter { + return reg.fmtCase(.lower); + } + pub fn fmtCase(reg: Register, case: aarch64.Disassemble.Case) aarch64.Disassemble.RegisterFormatter { + return .{ .reg = reg, .case = case }; + } +}; + +/// C1.2.4 Condition code +pub const ConditionCode = enum(u4) { + /// integer: Equal + /// floating-point: Equal + /// Z == 1 + eq = 0b0000, + /// integer: Not equal + /// floating-point: Not equal or unordered + /// Z == 0 + ne = 0b0001, + /// integer: Unsigned higher or same + /// floating-point: Greater than, equal, or unordered + /// C == 1 + hs = 0b0010, + /// integer: Unsigned lower + /// floating-point: Less than + /// C == 0 + lo = 0b0011, + /// integer: Minus, negative + /// floating-point: Less than + /// N == 1 + mi = 0b0100, + /// integer: Plus, positive or zero + /// floating-point: Greater than, equal, or unordered + /// N == 0 + pl = 0b0101, + /// integer: Overflow + /// floating-point: Unordered + /// V == 1 + vs = 0b0110, + /// integer: No overflow + /// floating-point: Ordered + /// V == 0 + vc = 0b0111, + /// integer: Unsigned higher + /// floating-point: Greater than, or unordered + /// C == 1 and Z == 0 + hi = 0b1000, + /// integer: Unsigned lower or same + /// floating-point: Less than or equal + /// C == 0 or Z == 1 + ls = 0b1001, + /// integer: Signed greater than or equal + /// floating-point: Greater than or equal + /// N == V + ge = 0b1010, + /// integer: Signed less than + /// floating-point: Less than, or unordered + /// N != V + lt = 0b1011, + /// integer: Signed greater than + /// floating-point: Greater than + /// Z == 0 and N == V + gt = 0b1100, + /// integer: Signed less than or equal + /// floating-point: Less than, equal, or unordered + /// Z == 1 or N != V + le = 0b1101, + /// integer: Always + /// floating-point: Always + /// true + al = 0b1110, + /// integer: Always + /// floating-point: Always + /// true + nv = 0b1111, + /// Carry set + /// C == 1 + pub const cs: ConditionCode = .hs; + /// Carry clear + /// C == 0 + pub const cc: ConditionCode = .lo; + + pub fn invert(cond: ConditionCode) ConditionCode { + return @enumFromInt(@intFromEnum(cond) ^ 0b0001); + } +}; + +/// C4.1 A64 instruction set encoding +pub const Instruction = packed union { + group: Group, + reserved: Reserved, + sme: Sme, + sve: Sve, + data_processing_immediate: DataProcessingImmediate, + branch_exception_generating_system: BranchExceptionGeneratingSystem, + load_store: LoadStore, + data_processing_register: DataProcessingRegister, + data_processing_vector: DataProcessingVector, + + /// Table C4-1 Main encoding table for the A64 instruction set + pub const Group = packed struct { + encoded0: u25, + op1: u4, + encoded29: u2, + op0: u1, + }; + + /// C4.1.1 Reserved + pub const Reserved = packed union { + group: @This().Group, + udf: Udf, + + /// Table C4-2 Encoding table for the Reserved group + pub const Group = packed struct { + encoded0: u16, + op1: u9, + decoded25: u4 = 0b0000, + op0: u2, + decoded31: u1 = 0b0, + }; + + /// C6.2.387 UDF + pub const Udf = packed struct { + imm16: u16, + decoded16: u16 = 0b0000000000000000, + }; + + pub const Decoded = union(enum) { + unallocated, + udf: Udf, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op0) { + 0b00 => switch (inst.group.op1) { + 0b000000000 => .{ .udf = inst.udf }, + else => .unallocated, + }, + else => .unallocated, + }; + } + }; + + /// C4.1.2 SME encodings + pub const Sme = packed union { + group: @This().Group, + + /// Table C4-3 Encodings table for the SME encodings group + pub const Group = packed struct { + encoded0: u2, + op2: u3, + encoded5: u5, + op1: u15, + decoded25: u4 = 0b0000, + op0: u2, + decoded31: u1 = 0b1, + }; + }; + + /// C4.1.30 SVE encodings + pub const Sve = packed union { + group: @This().Group, + + /// Table C4-31 Encoding table for the SVE encodings group + pub const Group = packed struct { + encoded0: u4, + op2: u1, + encoded5: u5, + op1: u15, + decoded25: u4 = 0b0010, + op0: u3, + }; + }; + + /// C4.1.86 Data Processing -- Immediate + pub const DataProcessingImmediate = packed union { + group: @This().Group, + pc_relative_addressing: PcRelativeAddressing, + add_subtract_immediate: AddSubtractImmediate, + add_subtract_immediate_with_tags: AddSubtractImmediateWithTags, + logical_immediate: LogicalImmediate, + move_wide_immediate: MoveWideImmediate, + bitfield: Bitfield, + extract: Extract, + + /// Table C4-87 Encoding table for the Data Processing -- Immediate group + pub const Group = packed struct { + encoded0: u23, + op0: u3, + decoded26: u3 = 0b100, + encoded29: u3, + }; + + /// PC-rel. addressing + pub const PcRelativeAddressing = packed union { + group: @This().Group, + adr: Adr, + adrp: Adrp, + + pub const Group = packed struct { + Rd: Register.Encoded, + immhi: i19, + decoded24: u5 = 0b10000, + immlo: u2, + op: Op, + }; + + /// C6.2.10 ADR + pub const Adr = packed struct { + Rd: Register.Encoded, + immhi: i19, + decoded24: u5 = 0b10000, + immlo: u2, + op: Op = .adr, + }; + + /// C6.2.11 ADRP + pub const Adrp = packed struct { + Rd: Register.Encoded, + immhi: i19, + decoded24: u5 = 0b10000, + immlo: u2, + op: Op = .adrp, + }; + + pub const Op = enum(u1) { + adr = 0b0, + adrp = 0b1, + }; + }; + + /// Add/subtract (immediate) + pub const AddSubtractImmediate = packed union { + group: @This().Group, + add: Add, + adds: Adds, + sub: Sub, + subs: Subs, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + sh: Shift, + decoded23: u6 = 0b100010, + S: bool, + op: AddSubtractOp, + sf: Register.IntegerSize, + }; + + /// C6.2.4 ADD (immediate) + pub const Add = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + sh: Shift, + decoded23: u6 = 0b100010, + S: bool = false, + op: AddSubtractOp = .add, + sf: Register.IntegerSize, + }; + + /// C6.2.8 ADDS (immediate) + pub const Adds = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + sh: Shift, + decoded23: u6 = 0b100010, + S: bool = true, + op: AddSubtractOp = .add, + sf: Register.IntegerSize, + }; + + /// C6.2.357 SUB (immediate) + pub const Sub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + sh: Shift, + decoded23: u6 = 0b100010, + S: bool = false, + op: AddSubtractOp = .sub, + sf: Register.IntegerSize, + }; + + /// C6.2.363 SUBS (immediate) + pub const Subs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + sh: Shift, + decoded23: u6 = 0b100010, + S: bool = true, + op: AddSubtractOp = .sub, + sf: Register.IntegerSize, + }; + + pub const Shift = enum(u1) { + @"0" = 0b0, + @"12" = 0b1, + }; + }; + + /// Add/subtract (immediate, with tags) + pub const AddSubtractImmediateWithTags = packed union { + group: @This().Group, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + uimm4: u4, + op3: u2, + uimm6: u6, + o2: u1, + decoded23: u6 = 0b100011, + S: bool, + op: AddSubtractOp, + sf: Register.IntegerSize, + }; + }; + + /// Logical (immediate) + pub const LogicalImmediate = packed union { + group: @This().Group, + @"and": And, + orr: Orr, + eor: Eor, + ands: Ands, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100100, + opc: LogicalOpc, + sf: Register.IntegerSize, + }; + + /// C6.2.12 AND (immediate) + pub const And = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100100, + opc: LogicalOpc = .@"and", + sf: Register.IntegerSize, + }; + + /// C6.2.240 ORR (immediate) + pub const Orr = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100100, + opc: LogicalOpc = .orr, + sf: Register.IntegerSize, + }; + + /// C6.2.119 EOR (immediate) + pub const Eor = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100100, + opc: LogicalOpc = .eor, + sf: Register.IntegerSize, + }; + + /// C6.2.14 ANDS (immediate) + pub const Ands = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100100, + opc: LogicalOpc = .ands, + sf: Register.IntegerSize, + }; + + pub const Decoded = union(enum) { + unallocated, + @"and": And, + orr: Orr, + eor: Eor, + ands: Ands, + }; + pub fn decode(inst: @This()) @This().Decoded { + return if (!inst.group.imm.validImmediate(inst.group.sf)) + .unallocated + else switch (inst.group.opc) { + .@"and" => .{ .@"and" = inst.@"and" }, + .orr => .{ .orr = inst.orr }, + .eor => .{ .eor = inst.eor }, + .ands => .{ .ands = inst.ands }, + }; + } + }; + + /// Move wide (immediate) + pub const MoveWideImmediate = packed union { + group: @This().Group, + movn: Movn, + movz: Movz, + movk: Movk, + + pub const Group = packed struct { + Rd: Register.Encoded, + imm16: u16, + hw: Hw, + decoded23: u6 = 0b100101, + opc: Opc, + sf: Register.IntegerSize, + }; + + /// C6.2.226 MOVN + pub const Movn = packed struct { + Rd: Register.Encoded, + imm16: u16, + hw: Hw, + decoded23: u6 = 0b100101, + opc: Opc = .movn, + sf: Register.IntegerSize, + }; + + /// C6.2.227 MOVZ + pub const Movz = packed struct { + Rd: Register.Encoded, + imm16: u16, + hw: Hw, + decoded23: u6 = 0b100101, + opc: Opc = .movz, + sf: Register.IntegerSize, + }; + + /// C6.2.225 MOVK + pub const Movk = packed struct { + Rd: Register.Encoded, + imm16: u16, + hw: Hw, + decoded23: u6 = 0b100101, + opc: Opc = .movk, + sf: Register.IntegerSize, + }; + + pub const Hw = enum(u2) { + @"0" = 0b00, + @"16" = 0b01, + @"32" = 0b10, + @"48" = 0b11, + + pub fn int(hw: Hw) u6 { + return switch (hw) { + .@"0" => 0, + .@"16" => 16, + .@"32" => 32, + .@"48" => 48, + }; + } + + pub fn sf(hw: Hw) Register.IntegerSize { + return switch (hw) { + .@"0", .@"16" => .word, + .@"32", .@"48" => .doubleword, + }; + } + }; + + pub const Opc = enum(u2) { + movn = 0b00, + movz = 0b10, + movk = 0b11, + _, + }; + + pub const Decoded = union(enum) { + unallocated, + movn: Movn, + movz: Movz, + movk: Movk, + }; + pub fn decode(inst: @This()) @This().Decoded { + return if (inst.group.sf == .word and inst.group.hw.sf() == .doubleword) + .unallocated + else switch (inst.group.opc) { + _ => .unallocated, + .movn => .{ .movn = inst.movn }, + .movz => .{ .movz = inst.movz }, + .movk => .{ .movk = inst.movk }, + }; + } + }; + + /// Bitfield + pub const Bitfield = packed union { + group: @This().Group, + sbfm: Sbfm, + bfm: Bfm, + ubfm: Ubfm, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100110, + opc: Opc, + sf: Register.IntegerSize, + }; + + pub const Sbfm = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100110, + opc: Opc = .sbfm, + sf: Register.IntegerSize, + }; + + pub const Bfm = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100110, + opc: Opc = .bfm, + sf: Register.IntegerSize, + }; + + pub const Ubfm = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm: Bitmask, + decoded23: u6 = 0b100110, + opc: Opc = .ubfm, + sf: Register.IntegerSize, + }; + + pub const Opc = enum(u2) { + sbfm = 0b00, + bfm = 0b01, + ubfm = 0b10, + _, + }; + + pub const Decoded = union(enum) { + unallocated, + sbfm: Sbfm, + bfm: Bfm, + ubfm: Ubfm, + }; + pub fn decode(inst: @This()) @This().Decoded { + return if (!inst.group.imm.validBitfield(inst.group.sf)) + .unallocated + else switch (inst.group.opc) { + _ => .unallocated, + .sbfm => .{ .sbfm = inst.sbfm }, + .bfm => .{ .bfm = inst.bfm }, + .ubfm => .{ .ubfm = inst.ubfm }, + }; + } + }; + + /// Extract + pub const Extract = packed union { + group: @This().Group, + extr: Extr, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imms: u6, + Rm: Register.Encoded, + o0: u1, + N: Register.IntegerSize, + decoded23: u6 = 0b100111, + op21: u2, + sf: Register.IntegerSize, + }; + + pub const Extr = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imms: u6, + Rm: Register.Encoded, + o0: u1 = 0b0, + N: Register.IntegerSize, + decoded23: u6 = 0b100111, + op21: u2 = 0b00, + sf: Register.IntegerSize, + }; + + pub const Decoded = union(enum) { + unallocated, + extr: Extr, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op21) { + 0b01, 0b10...0b11 => .unallocated, + 0b00 => switch (inst.group.o0) { + 0b1 => .unallocated, + 0b0 => if ((inst.group.sf == .word and @as(u1, @truncate(inst.group.imms >> 5)) == 0b1) or + inst.group.sf != inst.group.N) + .unallocated + else + .{ .extr = inst.extr }, + }, + }; + } + }; + + pub const Bitmask = packed struct { + imms: u6, + immr: u6, + N: Register.IntegerSize, + + fn lenHsb(bitmask: Bitmask) u7 { + return @bitCast(packed struct { + not_imms: u6, + N: Register.IntegerSize, + }{ .not_imms = ~bitmask.imms, .N = bitmask.N }); + } + + fn validImmediate(bitmask: Bitmask, sf: Register.IntegerSize) bool { + if (sf == .word and bitmask.N == .doubleword) return false; + const len_hsb = bitmask.lenHsb(); + return (len_hsb -% 1) & len_hsb != 0b0_000000; + } + + fn validBitfield(bitmask: Bitmask, sf: Register.IntegerSize) bool { + if (sf != bitmask.N) return false; + if (sf == .word and (@as(u1, @truncate(bitmask.immr >> 5)) != 0b0 or + @as(u1, @truncate(bitmask.imms >> 5)) != 0b0)) return false; + const len_hsb = bitmask.lenHsb(); + return len_hsb >= 0b0_000010; + } + + fn decode(bitmask: Bitmask, sf: Register.IntegerSize) struct { u64, u64 } { + const esize = @as(u7, 1 << 6) >> @clz(bitmask.lenHsb()); + const levels: u6 = @intCast(esize - 1); + const s = bitmask.imms & levels; + const r = bitmask.immr & levels; + const d = (s -% r) & levels; + const welem = @as(u64, std.math.maxInt(u64)) >> (63 - s); + const telem = @as(u64, std.math.maxInt(u64)) >> (63 - d); + const emask = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - esize); + const rmask = @divExact(std.math.maxInt(u64), emask); + const wmask = std.math.rotr(u64, welem * rmask, r); + const tmask = telem * rmask; + return switch (sf) { + .word => .{ @as(u32, @truncate(wmask)), @as(u32, @truncate(tmask)) }, + .doubleword => .{ wmask, tmask }, + }; + } + + pub fn decodeImmediate(bitmask: Bitmask, sf: Register.IntegerSize) u64 { + assert(bitmask.validImmediate(sf)); + const imm, _ = bitmask.decode(sf); + return imm; + } + + pub fn decodeBitfield(bitmask: Bitmask, sf: Register.IntegerSize) struct { u64, u64 } { + assert(bitmask.validBitfield(sf)); + return bitmask.decode(sf); + } + + pub fn moveWidePreferred(bitmask: Bitmask, sf: Register.IntegerSize) bool { + const s = bitmask.imms; + const r = bitmask.immr; + const width: u7 = switch (sf) { + .word => 32, + .doubleword => 64, + }; + if (sf != bitmask.N) return false; + if (sf == .word and @as(u1, @truncate(s >> 5)) != 0b0) return false; + if (s < 16) return (-%r % 16) <= (15 - s); + if (s >= width - 15) return (r % 16) <= (s - (width - 15)); + return false; + } + }; + + pub const Decoded = union(enum) { + unallocated, + pc_relative_addressing: PcRelativeAddressing, + add_subtract_immediate: AddSubtractImmediate, + add_subtract_immediate_with_tags: AddSubtractImmediateWithTags, + logical_immediate: LogicalImmediate, + move_wide_immediate: MoveWideImmediate, + bitfield: Bitfield, + extract: Extract, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op0) { + 0b000, 0b001 => .{ .pc_relative_addressing = inst.pc_relative_addressing }, + 0b010 => .{ .add_subtract_immediate = inst.add_subtract_immediate }, + 0b011 => .{ .add_subtract_immediate_with_tags = inst.add_subtract_immediate_with_tags }, + 0b100 => .{ .logical_immediate = inst.logical_immediate }, + 0b101 => .{ .move_wide_immediate = inst.move_wide_immediate }, + 0b110 => .{ .bitfield = inst.bitfield }, + 0b111 => .{ .extract = inst.extract }, + }; + } + }; + + /// C4.1.87 Branches, Exception Generating and System instructions + pub const BranchExceptionGeneratingSystem = packed union { + group: @This().Group, + conditional_branch_immediate: ConditionalBranchImmediate, + exception_generating: ExceptionGenerating, + system_register_argument: SystemRegisterArgument, + hints: Hints, + barriers: Barriers, + pstate: Pstate, + system_result: SystemResult, + system: System, + system_register_move: SystemRegisterMove, + unconditional_branch_register: UnconditionalBranchRegister, + unconditional_branch_immediate: UnconditionalBranchImmediate, + compare_branch_immediate: CompareBranchImmediate, + test_branch_immediate: TestBranchImmediate, + + /// Table C4-88 Encoding table for the Branches, Exception Generating and System instructions group + pub const Group = packed struct { + op2: u5, + encoded5: u7, + op1: u14, + decoded26: u3 = 0b101, + op0: u3, + }; + + /// Conditional branch (immediate) + pub const ConditionalBranchImmediate = packed union { + group: @This().Group, + b: B, + bc: Bc, + + pub const Group = packed struct { + cond: ConditionCode, + o0: u1, + imm19: i19, + o1: u1, + decoded25: u7 = 0b0101010, + }; + + /// C6.2.26 B.cond + pub const B = packed struct { + cond: ConditionCode, + o0: u1 = 0b0, + imm19: i19, + o1: u1 = 0b0, + decoded25: u7 = 0b0101010, + }; + + /// C6.2.27 BC.cond + pub const Bc = packed struct { + cond: ConditionCode, + o0: u1 = 0b1, + imm19: i19, + o1: u1 = 0b0, + decoded25: u7 = 0b0101010, + }; + + pub const Decoded = union(enum) { + unallocated, + b: B, + bc: Bc, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.o1) { + 0b0 => switch (inst.group.o0) { + 0b0 => .{ .b = inst.b }, + 0b1 => .{ .bc = inst.bc }, + }, + 0b1 => .unallocated, + }; + } + }; + + /// Exception generating + pub const ExceptionGenerating = packed union { + group: @This().Group, + svc: Svc, + hvc: Hvc, + smc: Smc, + brk: Brk, + hlt: Hlt, + tcancel: Tcancel, + dcps1: Dcps1, + dcps2: Dcps2, + dcps3: Dcps3, + + pub const Group = packed struct { + LL: u2, + op2: u3, + imm16: u16, + opc: u3, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.365 SVC + pub const Svc = packed struct { + decoded0: u2 = 0b01, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b000, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.128 HVC + pub const Hvc = packed struct { + decoded0: u2 = 0b10, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b000, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.283 SMC + pub const Smc = packed struct { + decoded0: u2 = 0b11, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b000, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.40 BRK + pub const Brk = packed struct { + decoded0: u2 = 0b00, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b001, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.127 HLT + pub const Hlt = packed struct { + decoded0: u2 = 0b00, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b010, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.376 TCANCEL + pub const Tcancel = packed struct { + decoded0: u2 = 0b00, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b011, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.110 DCPS1 + pub const Dcps1 = packed struct { + LL: u2 = 0b01, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b101, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.110 DCPS2 + pub const Dcps2 = packed struct { + LL: u2 = 0b10, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b101, + decoded24: u8 = 0b11010100, + }; + + /// C6.2.110 DCPS3 + pub const Dcps3 = packed struct { + LL: u2 = 0b11, + decoded2: u3 = 0b000, + imm16: u16, + decoded21: u3 = 0b101, + decoded24: u8 = 0b11010100, + }; + + pub const Decoded = union(enum) { + unallocated, + svc: Svc, + hvc: Hvc, + smc: Smc, + brk: Brk, + hlt: Hlt, + tcancel: Tcancel, + dcps1: Dcps1, + dcps2: Dcps2, + dcps3: Dcps3, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op2) { + 0b001 => .unallocated, + 0b010...0b011 => .unallocated, + 0b100...0b111 => .unallocated, + 0b000 => switch (inst.group.opc) { + 0b000 => switch (inst.group.LL) { + 0b00 => .unallocated, + 0b01 => .{ .svc = inst.svc }, + 0b10 => .{ .hvc = inst.hvc }, + 0b11 => .{ .smc = inst.smc }, + }, + 0b001 => switch (inst.group.LL) { + 0b01 => .unallocated, + 0b00 => .{ .brk = inst.brk }, + 0b10...0b11 => .unallocated, + }, + 0b010 => switch (inst.group.LL) { + 0b01 => .unallocated, + 0b00 => .{ .hlt = inst.hlt }, + 0b10...0b11 => .unallocated, + }, + 0b011 => switch (inst.group.LL) { + 0b00 => .{ .tcancel = inst.tcancel }, + 0b01 => .unallocated, + 0b10...0b11 => .unallocated, + }, + 0b100 => .unallocated, + 0b101 => switch (inst.group.LL) { + 0b00 => .unallocated, + 0b01 => .{ .dcps1 = inst.dcps1 }, + 0b10 => .{ .dcps2 = inst.dcps2 }, + 0b11 => .{ .dcps3 = inst.dcps3 }, + }, + 0b110 => .unallocated, + 0b111 => .unallocated, + }, + }; + } + }; + + /// System instructions with register argument + pub const SystemRegisterArgument = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + decoded12: u20 = 0b11010101000000110001, + }; + + /// Hints + pub const Hints = packed union { + group: @This().Group, + hint: Hint, + nop: Nop, + yield: Yield, + wfe: Wfe, + wfi: Wfi, + sev: Sev, + sevl: Sevl, + + pub const Group = packed struct { + decoded0: u5 = 0b11111, + op2: u3, + CRm: u4, + decoded12: u20 = 0b11010101000000110010, + }; + + /// C6.2.126 HINT + pub const Hint = packed struct { + decoded0: u5 = 0b11111, + op2: u3, + CRm: u4, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.238 NOP + pub const Nop = packed struct { + decoded0: u5 = 0b11111, + op2: u3 = 0b000, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.402 YIELD + pub const Yield = packed struct { + decoded0: u5 = 0b11111, + op2: u3 = 0b001, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.396 WFE + pub const Wfe = packed struct { + decoded0: u5 = 0b11111, + op2: u3 = 0b010, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.398 WFI + pub const Wfi = packed struct { + decoded0: u5 = 0b11111, + op2: u3 = 0b011, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.280 SEV + pub const Sev = packed struct { + decoded0: u5 = 0b11111, + op2: u3 = 0b100, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.280 SEVL + pub const Sevl = packed struct { + decoded0: u5 = 0b11111, + op2: u3 = 0b101, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0010, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + pub const Decoded = union(enum) { + hint: Hint, + nop: Nop, + yield: Yield, + wfe: Wfe, + wfi: Wfi, + sev: Sev, + sevl: Sevl, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.CRm) { + else => .{ .hint = inst.hint }, + 0b0000 => switch (inst.group.op2) { + else => .{ .hint = inst.hint }, + 0b000 => .{ .nop = inst.nop }, + 0b001 => .{ .yield = inst.yield }, + 0b010 => .{ .wfe = inst.wfe }, + 0b011 => .{ .wfi = inst.wfi }, + 0b100 => .{ .sev = inst.sev }, + 0b101 => .{ .sevl = inst.sevl }, + }, + }; + } + }; + + /// Barriers + pub const Barriers = packed union { + group: @This().Group, + clrex: Clrex, + dsb: Dsb, + dmb: Dmb, + isb: Isb, + sb: Sb, + + pub const Group = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + decoded12: u4 = 0b0011, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.56 CLREX + pub const Clrex = packed struct { + Rt: Register.Encoded = @enumFromInt(0b11111), + op2: u3 = 0b010, + CRm: u4, + decoded12: u4 = 0b0011, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.116 DSB + pub const Dsb = packed struct { + Rt: Register.Encoded = @enumFromInt(0b11111), + opc: u2 = 0b00, + decoded7: u1 = 0b1, + CRm: Option, + decoded12: u4 = 0b0011, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.114 DMB + pub const Dmb = packed struct { + Rt: Register.Encoded = @enumFromInt(0b11111), + opc: u2 = 0b01, + decoded7: u1 = 0b1, + CRm: Option, + decoded12: u4 = 0b0011, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.131 ISB + pub const Isb = packed struct { + Rt: Register.Encoded = @enumFromInt(0b11111), + opc: u2 = 0b10, + decoded7: u1 = 0b1, + CRm: Option, + decoded12: u4 = 0b0011, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.264 SB + pub const Sb = packed struct { + Rt: Register.Encoded = @enumFromInt(0b11111), + opc: u2 = 0b11, + decoded7: u1 = 0b1, + CRm: u4 = 0b0000, + decoded12: u4 = 0b0011, + decoded16: u3 = 0b011, + decoded19: u2 = 0b00, + decoded21: u1 = 0b0, + decoded22: u10 = 0b1101010100, + }; + + pub const Option = enum(u4) { + oshld = 0b0001, + oshst = 0b0010, + osh = 0b0011, + nshld = 0b0101, + nshst = 0b0110, + nsh = 0b0111, + ishld = 0b1001, + ishst = 0b1010, + ish = 0b1011, + ld = 0b1101, + st = 0b1110, + sy = 0b1111, + _, + }; + }; + + /// PSTATE + pub const Pstate = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + decoded12: u4 = 0b0100, + op1: u3, + decoded19: u13 = 0b1101010100000, + }; + + /// System with result + pub const SystemResult = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + decoded19: u13 = 0b1101010100100, + }; + + /// System instructions + pub const System = packed union { + group: @This().Group, + sys: Sys, + sysl: Sysl, + + pub const Group = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + decoded19: u2 = 0b01, + L: L, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.372 SYS + pub const Sys = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + decoded19: u2 = 0b01, + L: L = .sys, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.373 SYSL + pub const Sysl = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + decoded19: u2 = 0b01, + L: L = .sysl, + decoded22: u10 = 0b1101010100, + }; + + const L = enum(u1) { + sys = 0b0, + sysl = 0b1, + }; + + pub const Decoded = union(enum) { + sys: Sys, + sysl: Sysl, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.L) { + .sys => .{ .sys = inst.sys }, + .sysl => .{ .sysl = inst.sysl }, + }; + } + }; + + /// System register move + pub const SystemRegisterMove = packed union { + group: @This().Group, + msr: Msr, + mrs: Mrs, + + pub const Group = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + o0: u1, + decoded20: u1 = 0b1, + L: L, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.230 MSR (register) + pub const Msr = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + o0: u1, + decoded20: u1 = 0b1, + L: L = .msr, + decoded22: u10 = 0b1101010100, + }; + + /// C6.2.228 MRS + pub const Mrs = packed struct { + Rt: Register.Encoded, + op2: u3, + CRm: u4, + CRn: u4, + op1: u3, + o0: u1, + decoded20: u1 = 0b1, + L: L = .mrs, + decoded22: u10 = 0b1101010100, + }; + + pub const L = enum(u1) { + msr = 0b0, + mrs = 0b1, + }; + + pub const Decoded = union(enum) { + msr: Msr, + mrs: Mrs, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.L) { + .msr => .{ .msr = inst.msr }, + .mrs => .{ .mrs = inst.mrs }, + }; + } + }; + + /// Unconditional branch (register) + pub const UnconditionalBranchRegister = packed union { + group: @This().Group, + br: Br, + blr: Blr, + ret: Ret, + + pub const Group = packed struct { + op4: u5, + Rn: Register.Encoded, + op3: u6, + op2: u5, + opc: u4, + decoded25: u7 = 0b1101011, + }; + + /// C6.2.37 BR + pub const Br = packed struct { + Rm: Register.Encoded = @enumFromInt(0), + Rn: Register.Encoded, + M: bool = false, + A: bool = false, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b11111, + op: u2 = 0b00, + decoded23: u1 = 0b0, + Z: bool = false, + decoded25: u7 = 0b1101011, + }; + + /// C6.2.35 BLR + pub const Blr = packed struct { + Rm: Register.Encoded = @enumFromInt(0), + Rn: Register.Encoded, + M: bool = false, + A: bool = false, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b11111, + op: u2 = 0b01, + decoded23: u1 = 0b0, + Z: bool = false, + decoded25: u7 = 0b1101011, + }; + + /// C6.2.254 RET + pub const Ret = packed struct { + Rm: Register.Encoded = @enumFromInt(0), + Rn: Register.Encoded, + M: bool = false, + A: bool = false, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b11111, + op: u2 = 0b10, + decoded23: u1 = 0b0, + Z: bool = false, + decoded25: u7 = 0b1101011, + }; + + pub const Decoded = union(enum) { + unallocated, + br: Br, + blr: Blr, + ret: Ret, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op2) { + else => .unallocated, + 0b11111 => switch (inst.group.opc) { + 0b0000 => switch (inst.group.op4) { + else => .unallocated, + 0b00000 => .{ .br = inst.br }, + }, + 0b0001 => switch (inst.group.op4) { + else => .unallocated, + 0b00000 => .{ .blr = inst.blr }, + }, + 0b0010 => switch (inst.group.op4) { + else => .unallocated, + 0b00000 => .{ .ret = inst.ret }, + }, + else => .unallocated, + }, + }; + } + }; + + /// Unconditional branch (immediate) + pub const UnconditionalBranchImmediate = packed union { + group: @This().Group, + b: B, + bl: Bl, + + pub const Group = packed struct { + imm26: i26, + decoded26: u5 = 0b00101, + op: Op, + }; + + /// C6.2.25 B + pub const B = packed struct { + imm26: i26, + decoded26: u5 = 0b00101, + op: Op = .b, + }; + + /// C6.2.34 BL + pub const Bl = packed struct { + imm26: i26, + decoded26: u5 = 0b00101, + op: Op = .bl, + }; + + pub const Op = enum(u1) { + b = 0b0, + bl = 0b1, + }; + }; + + /// Compare and branch (immediate) + pub const CompareBranchImmediate = packed union { + group: @This().Group, + cbz: Cbz, + cbnz: Cbnz, + + pub const Group = packed struct { + Rt: Register.Encoded, + imm19: i19, + op: Op, + decoded25: u6 = 0b011010, + sf: Register.IntegerSize, + }; + + /// C6.2.47 CBZ + pub const Cbz = packed struct { + Rt: Register.Encoded, + imm19: i19, + op: Op = .cbz, + decoded25: u6 = 0b011010, + sf: Register.IntegerSize, + }; + + /// C6.2.46 CBNZ + pub const Cbnz = packed struct { + Rt: Register.Encoded, + imm19: i19, + op: Op = .cbnz, + decoded25: u6 = 0b011010, + sf: Register.IntegerSize, + }; + + pub const Op = enum(u1) { + cbz = 0b0, + cbnz = 0b1, + }; + }; + + /// Test and branch (immediate) + pub const TestBranchImmediate = packed union { + group: @This().Group, + tbz: Tbz, + tbnz: Tbnz, + + pub const Group = packed struct { + Rt: Register.Encoded, + imm14: i14, + b40: u5, + op: Op, + decoded25: u6 = 0b011011, + b5: u1, + }; + + /// C6.2.375 TBZ + pub const Tbz = packed struct { + Rt: Register.Encoded, + imm14: i14, + b40: u5, + op: Op = .tbz, + decoded25: u6 = 0b011011, + b5: u1, + }; + + /// C6.2.374 TBNZ + pub const Tbnz = packed struct { + Rt: Register.Encoded, + imm14: i14, + b40: u5, + op: Op = .tbnz, + decoded25: u6 = 0b011011, + b5: u1, + }; + + pub const Op = enum(u1) { + tbz = 0b0, + tbnz = 0b1, + }; + }; + + pub const Decoded = union(enum) { + unallocated, + conditional_branch_immediate: ConditionalBranchImmediate, + exception_generating: ExceptionGenerating, + system_register_argument: SystemRegisterArgument, + hints: Hints, + barriers: Barriers, + pstate: Pstate, + system_result: SystemResult, + system: System, + system_register_move: SystemRegisterMove, + unconditional_branch_register: UnconditionalBranchRegister, + unconditional_branch_immediate: UnconditionalBranchImmediate, + compare_branch_immediate: CompareBranchImmediate, + test_branch_immediate: TestBranchImmediate, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op0) { + 0b010 => switch (inst.group.op1) { + 0b000000000000000...0b01111111111111 => .{ .conditional_branch_immediate = inst.conditional_branch_immediate }, + else => .unallocated, + }, + 0b110 => switch (inst.group.op1) { + 0b00000000000000...0b00111111111111 => .{ .exception_generating = inst.exception_generating }, + 0b01000000110001 => .{ .system_register_argument = inst.system_register_argument }, + 0b01000000110010 => switch (inst.group.op2) { + 0b11111 => .{ .hints = inst.hints }, + else => .unallocated, + }, + 0b01000000110011 => .{ .barriers = inst.barriers }, + 0b01000000000100, + 0b01000000010100, + 0b01000000100100, + 0b01000000110100, + 0b01000001000100, + 0b01000001010100, + 0b01000001100100, + 0b01000001110100, + => .{ .pstate = inst.pstate }, + 0b01001000000000...0b01001001111111 => .{ .system_result = inst.system_result }, + 0b01000010000000...0b01000011111111, 0b01001010000000...0b01001011111111 => .{ .system = inst.system }, + 0b01000100000000...0b01000111111111, 0b01001100000000...0b01001111111111 => .{ .system_register_move = inst.system_register_move }, + 0b10000000000000...0b11111111111111 => .{ .unconditional_branch_register = inst.unconditional_branch_register }, + else => .unallocated, + }, + 0b000, 0b100 => .{ .unconditional_branch_immediate = inst.unconditional_branch_immediate }, + 0b001, 0b101 => switch (inst.group.op1) { + 0b00000000000000...0b01111111111111 => .{ .compare_branch_immediate = inst.compare_branch_immediate }, + 0b10000000000000...0b11111111111111 => .{ .test_branch_immediate = inst.test_branch_immediate }, + }, + else => .unallocated, + }; + } + }; + + /// C4.1.88 Loads and Stores + pub const LoadStore = packed union { + group: @This().Group, + register_literal: RegisterLiteral, + memory: Memory, + no_allocate_pair_offset: NoAllocatePairOffset, + register_pair_post_indexed: RegisterPairPostIndexed, + register_pair_offset: RegisterPairOffset, + register_pair_pre_indexed: RegisterPairPreIndexed, + register_unscaled_immediate: RegisterUnscaledImmediate, + register_immediate_post_indexed: RegisterImmediatePostIndexed, + register_unprivileged: RegisterUnprivileged, + register_immediate_pre_indexed: RegisterImmediatePreIndexed, + register_register_offset: RegisterRegisterOffset, + register_unsigned_immediate: RegisterUnsignedImmediate, + + /// Table C4-89 Encoding table for the Loads and Stores group + pub const Group = packed struct { + encoded0: u10, + op4: u2, + encoded12: u4, + op3: u6, + encoded22: u1, + op2: u2, + decoded25: u1 = 0b0, + op1: bool, + decoded27: u1 = 0b1, + op0: u4, + }; + + /// Load register (literal) + pub const RegisterLiteral = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + imm19: i19, + decoded24: u2 = 0b00, + V: bool, + decoded27: u3 = 0b011, + opc: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + ldr: Ldr, + ldrsw: Ldrsw, + prfm: Prfm, + + pub const Group = packed struct { + Rt: Register.Encoded, + imm19: i19, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b011, + opc: u2, + }; + + /// C6.2.167 LDR (literal) + pub const Ldr = packed struct { + Rt: Register.Encoded, + imm19: i19, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b011, + sf: Register.IntegerSize, + opc1: u1 = 0b0, + }; + + /// C6.2.179 LDRSW (literal) + pub const Ldrsw = packed struct { + Rt: Register.Encoded, + imm19: i19, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b011, + opc: u2 = 0b10, + }; + + /// C6.2.248 PRFM (literal) + pub const Prfm = packed struct { + prfop: PrfOp, + imm19: i19, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b011, + opc: u2 = 0b11, + }; + }; + + pub const Vector = packed union { + group: @This().Group, + ldr: Ldr, + + pub const Group = packed struct { + Rt: Register.Encoded, + imm19: i19, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b011, + opc: VectorSize, + }; + + /// C7.2.192 LDR (literal, SIMD&FP) + pub const Ldr = packed struct { + Rt: Register.Encoded, + imm19: i19, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b011, + opc: VectorSize, + }; + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Memory Copy and Memory Set + pub const Memory = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + op2: u4, + Rs: Register.Encoded, + decoded21: u1 = 0b0, + op1: u2, + decoded24: u2 = 0b01, + o0: u1, + decoded27: u3 = 0b011, + size: IntegerSize, + }; + + /// Load/store no-allocate pair (offset) + pub const NoAllocatePairOffset = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b000, + V: bool, + decoded27: u3 = 0b101, + opc: u2, + }; + + /// Load/store register pair (post-indexed) + pub const RegisterPairPostIndexed = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b001, + V: bool, + decoded27: u3 = 0b101, + opc: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + stp: Stp, + ldp: Ldp, + ldpsw: Ldpsw, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b001, + V: bool = false, + decoded27: u3 = 0b101, + opc: u2, + }; + + /// C6.2.321 STP + pub const Stp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .store, + decoded23: u3 = 0b001, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.164 LDP + pub const Ldp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b001, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.165 LDPSW + pub const Ldpsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b001, + V: bool = false, + decoded27: u3 = 0b101, + opc: u2 = 0b01, + }; + + pub const Decoded = union(enum) { + unallocated, + stp: Stp, + ldp: Ldp, + ldpsw: Ldpsw, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.opc) { + 0b00, 0b10 => switch (inst.group.L) { + .store => .{ .stp = inst.stp }, + .load => .{ .ldp = inst.ldp }, + }, + 0b01 => switch (inst.group.L) { + else => .unallocated, + .load => .{ .ldpsw = inst.ldpsw }, + }, + else => .unallocated, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + stp: Stp, + ldp: Ldp, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b001, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + /// C7.2.330 STP (SIMD&FP) + pub const Stp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .store, + decoded23: u3 = 0b001, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + /// C7.2.190 LDP (SIMD&FP) + pub const Ldp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b001, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + pub const Decoded = union(enum) { + unallocated, + stp: Stp, + ldp: Ldp, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.opc) { + .single, .double, .quad => switch (inst.group.L) { + .store => .{ .stp = inst.stp }, + .load => .{ .ldp = inst.ldp }, + }, + _ => .unallocated, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register pair (offset) + pub const RegisterPairOffset = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b010, + V: bool, + decoded27: u3 = 0b101, + opc: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + stp: Stp, + ldp: Ldp, + ldpsw: Ldpsw, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b010, + V: bool = false, + decoded27: u3 = 0b101, + opc: u2, + }; + + /// C6.2.321 STP + pub const Stp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .store, + decoded23: u3 = 0b010, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.164 LDP + pub const Ldp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b010, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.165 LDPSW + pub const Ldpsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b010, + V: bool = false, + decoded27: u3 = 0b101, + opc: u2 = 0b01, + }; + + pub const Decoded = union(enum) { + unallocated, + stp: Stp, + ldp: Ldp, + ldpsw: Ldpsw, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.opc) { + 0b00, 0b10 => switch (inst.group.L) { + .store => .{ .stp = inst.stp }, + .load => .{ .ldp = inst.ldp }, + }, + 0b01 => switch (inst.group.L) { + else => .unallocated, + .load => .{ .ldpsw = inst.ldpsw }, + }, + else => .unallocated, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + stp: Stp, + ldp: Ldp, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b010, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + /// C7.2.330 STP (SIMD&FP) + pub const Stp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .store, + decoded23: u3 = 0b010, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + /// C7.2.190 LDP (SIMD&FP) + pub const Ldp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b010, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + pub const Decoded = union(enum) { + unallocated, + stp: Stp, + ldp: Ldp, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.opc) { + .single, .double, .quad => switch (inst.group.L) { + .store => .{ .stp = inst.stp }, + .load => .{ .ldp = inst.ldp }, + }, + _ => .unallocated, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register pair (pre-indexed) + pub const RegisterPairPreIndexed = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b011, + V: bool, + decoded27: u3 = 0b101, + opc: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + stp: Stp, + ldp: Ldp, + ldpsw: Ldpsw, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b011, + V: bool = false, + decoded27: u3 = 0b101, + opc: u2, + }; + + /// C6.2.321 STP + pub const Stp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .store, + decoded23: u3 = 0b011, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.164 LDP + pub const Ldp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b011, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.165 LDPSW + pub const Ldpsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b011, + V: bool = false, + decoded27: u3 = 0b101, + opc0: u2 = 0b01, + }; + + pub const Decoded = union(enum) { + unallocated, + stp: Stp, + ldp: Ldp, + ldpsw: Ldpsw, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.opc) { + 0b00, 0b10 => switch (inst.group.L) { + .store => .{ .stp = inst.stp }, + .load => .{ .ldp = inst.ldp }, + }, + 0b01 => switch (inst.group.L) { + else => .unallocated, + .load => .{ .ldpsw = inst.ldpsw }, + }, + else => .unallocated, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + stp: Stp, + ldp: Ldp, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L, + decoded23: u3 = 0b011, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + /// C7.2.330 STP (SIMD&FP) + pub const Stp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .store, + decoded23: u3 = 0b011, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + /// C7.2.190 LDP (SIMD&FP) + pub const Ldp = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + Rt2: Register.Encoded, + imm7: i7, + L: L = .load, + decoded23: u3 = 0b011, + V: bool = true, + decoded27: u3 = 0b101, + opc: VectorSize, + }; + + pub const Decoded = union(enum) { + unallocated, + stp: Stp, + ldp: Ldp, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.opc) { + .single, .double, .quad => switch (inst.group.L) { + .store => .{ .stp = inst.stp }, + .load => .{ .ldp = inst.ldp }, + }, + _ => .unallocated, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register (unscaled immediate) + pub const RegisterUnscaledImmediate = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool, + decoded27: u3 = 0b111, + size: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + sturb: Sturb, + ldurb: Ldurb, + ldursb: Ldursb, + sturh: Sturh, + ldurh: Ldurh, + ldursh: Ldursh, + stur: Stur, + ldur: Ldur, + ldursw: Ldursw, + prfum: Prfum, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize, + }; + + /// C6.2.347 STURB + pub const Sturb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.203 LDURB + pub const Ldurb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.205 LDURSB + pub const Ldursb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.348 STURH + pub const Sturh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.204 LDURH + pub const Ldurh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.206 LDURSH + pub const Ldursh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.346 STUR + pub const Stur = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.202 LDUR + pub const Ldur = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.207 LDURSW + pub const Ldursw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b10, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .word, + }; + + /// C6.2.250 PRFUM + pub const Prfum = packed struct { + prfop: PrfOp, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b10, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .doubleword, + }; + + pub const Decoded = union(enum) { + unallocated, + sturb: Sturb, + ldurb: Ldurb, + ldursb: Ldursb, + sturh: Sturh, + ldurh: Ldurh, + ldursh: Ldursh, + stur: Stur, + ldur: Ldur, + ldursw: Ldursw, + prfum: Prfum, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size) { + .byte => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .sturb = inst.sturb }, + 0b01 => .{ .ldurb = inst.ldurb }, + 0b10, 0b11 => .{ .ldursb = inst.ldursb }, + }, + true => .unallocated, + }, + .halfword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .sturh = inst.sturh }, + 0b01 => .{ .ldurh = inst.ldurh }, + 0b10, 0b11 => .{ .ldursh = inst.ldursh }, + }, + true => .unallocated, + }, + .word => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .stur = inst.stur }, + 0b01 => .{ .ldur = inst.ldur }, + 0b10 => .{ .ldursw = inst.ldursw }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + .doubleword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .stur = inst.stur }, + 0b01 => .{ .ldur = inst.ldur }, + 0b10 => .{ .prfum = inst.prfum }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + stur: Stur, + ldur: Ldur, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.333 STUR (SIMD&FP) + pub const Stur = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L = .store, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.194 LDUR (SIMD&FP) + pub const Ldur = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L = .load, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + pub const Opc1 = packed struct { + encoded: u1, + + pub fn encode(vs: Register.VectorSize) Opc1 { + return .{ .encoded = switch (vs) { + .byte, .half, .single, .double => 0b0, + .quad => 0b1, + else => unreachable, + } }; + } + + pub fn decode(enc_opc1: Opc1, enc_size: Size) Register.VectorSize { + return switch (enc_size.encoded) { + 0b00 => switch (enc_opc1.encoded) { + 0b0 => .byte, + 0b1 => .quad, + }, + 0b01 => switch (enc_opc1.encoded) { + 0b0 => .half, + 0b1 => unreachable, + }, + 0b10 => switch (enc_opc1.encoded) { + 0b0 => .single, + 0b1 => unreachable, + }, + 0b11 => switch (enc_opc1.encoded) { + 0b0 => .double, + 0b1 => unreachable, + }, + }; + } + }; + + pub const Size = packed struct { + encoded: u2, + + pub fn encode(vs: Register.VectorSize) Size { + return .{ .encoded = switch (vs) { + .byte, .quad => 0b00, + .half => 0b01, + .single => 0b10, + .double => 0b11, + else => unreachable, + } }; + } + }; + + pub const Decoded = union(enum) { + unallocated, + stur: Stur, + ldur: Ldur, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size.encoded) { + 0b00 => switch (inst.group.opc0) { + .store => .{ .stur = inst.stur }, + .load => .{ .ldur = inst.ldur }, + }, + 0b01, 0b10, 0b11 => switch (inst.group.opc1.encoded) { + 0b0 => switch (inst.group.opc0) { + .store => .{ .stur = inst.stur }, + .load => .{ .ldur = inst.ldur }, + }, + 0b1 => .unallocated, + }, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register (immediate post-indexed) + pub const RegisterImmediatePostIndexed = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool, + decoded27: u3 = 0b111, + size: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize, + }; + + /// C6.2.324 STRB (immediate) + pub const Strb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.170 LDRB (immediate) + pub const Ldrb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.174 LDRSB (immediate) + pub const Ldrsb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.326 STRH (immediate) + pub const Strh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.172 LDRH (immediate) + pub const Ldrh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.176 LDRSH (immediate) + pub const Ldrsh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.322 STR (immediate) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.166 LDR (immediate) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.178 LDRSW (immediate) + pub const Ldrsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b10, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .word, + }; + + pub const Decoded = union(enum) { + unallocated, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size) { + .byte => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .strb = inst.strb }, + 0b01 => .{ .ldrb = inst.ldrb }, + 0b10, 0b11 => .{ .ldrsb = inst.ldrsb }, + }, + true => .unallocated, + }, + .halfword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .strh = inst.strh }, + 0b01 => .{ .ldrh = inst.ldrh }, + 0b10, 0b11 => .{ .ldrsh = inst.ldrsh }, + }, + true => .unallocated, + }, + .word => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10 => .{ .ldrsw = inst.ldrsw }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + .doubleword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10, 0b11 => .unallocated, + }, + true => .unallocated, + }, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + str: Str, + ldr: Ldr, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.331 STR (immediate, SIMD&FP) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L = .store, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.191 LDR (immediate, SIMD&FP) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b01, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L = .load, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + pub const Opc1 = packed struct { + encoded: u1, + + pub fn encode(vs: Register.VectorSize) Opc1 { + return .{ .encoded = switch (vs) { + .byte, .half, .single, .double => 0b0, + .quad => 0b1, + else => unreachable, + } }; + } + + pub fn decode(enc_opc1: Opc1, enc_size: Size) Register.VectorSize { + return switch (enc_size.encoded) { + 0b00 => switch (enc_opc1.encoded) { + 0b0 => .byte, + 0b1 => .quad, + }, + 0b01 => switch (enc_opc1.encoded) { + 0b0 => .half, + 0b1 => unreachable, + }, + 0b10 => switch (enc_opc1.encoded) { + 0b0 => .single, + 0b1 => unreachable, + }, + 0b11 => switch (enc_opc1.encoded) { + 0b0 => .double, + 0b1 => unreachable, + }, + }; + } + }; + + pub const Size = packed struct { + encoded: u2, + + pub fn encode(vs: Register.VectorSize) Size { + return .{ .encoded = switch (vs) { + .byte, .quad => 0b00, + .half => 0b01, + .single => 0b10, + .double => 0b11, + else => unreachable, + } }; + } + }; + + pub const Decoded = union(enum) { + unallocated, + str: Str, + ldr: Ldr, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size.encoded) { + 0b00 => switch (inst.group.opc0) { + .store => .{ .str = inst.str }, + .load => .{ .ldr = inst.ldr }, + }, + 0b01, 0b10, 0b11 => switch (inst.group.opc1.encoded) { + 0b0 => switch (inst.group.opc0) { + .store => .{ .str = inst.str }, + .load => .{ .ldr = inst.ldr }, + }, + 0b1 => .unallocated, + }, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register (unprivileged) + pub const RegisterUnprivileged = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool, + decoded27: u3 = 0b111, + size: IntegerSize, + }; + + /// Load/store register (immediate pre-indexed) + pub const RegisterImmediatePreIndexed = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool, + decoded27: u3 = 0b111, + size: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize, + }; + + /// C6.2.324 STRB (immediate) + pub const Strb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.170 LDRB (immediate) + pub const Ldrb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.174 LDRSB (immediate) + pub const Ldrsb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.326 STRH (immediate) + pub const Strh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.172 LDRH (immediate) + pub const Ldrh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.176 LDRSH (immediate) + pub const Ldrsh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.322 STR (immediate) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.166 LDR (immediate) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.178 LDRSW (immediate) + pub const Ldrsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc: u2 = 0b10, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .word, + }; + + pub const Decoded = union(enum) { + unallocated, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size) { + .byte => switch (inst.group.opc) { + 0b00 => .{ .strb = inst.strb }, + 0b01 => .{ .ldrb = inst.ldrb }, + 0b10, 0b11 => .{ .ldrsb = inst.ldrsb }, + }, + .halfword => switch (inst.group.opc) { + 0b00 => .{ .strh = inst.strh }, + 0b01 => .{ .ldrh = inst.ldrh }, + 0b10, 0b11 => .{ .ldrsh = inst.ldrsh }, + }, + .word => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10 => .{ .ldrsw = inst.ldrsw }, + 0b11 => .unallocated, + }, + .doubleword => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10, 0b11 => .unallocated, + }, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + str: Str, + ldr: Ldr, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.331 STR (immediate, SIMD&FP) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L = .store, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.191 LDR (immediate, SIMD&FP) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b11, + imm9: i9, + decoded21: u1 = 0b0, + opc0: L = .load, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + pub const Opc1 = packed struct { + encoded: u1, + + pub fn encode(vs: Register.VectorSize) Opc1 { + return .{ .encoded = switch (vs) { + .byte, .half, .single, .double => 0b0, + .quad => 0b1, + else => unreachable, + } }; + } + + pub fn decode(enc_opc1: Opc1, enc_size: Size) Register.VectorSize { + return switch (enc_size.encoded) { + 0b00 => switch (enc_opc1.encoded) { + 0b0 => .byte, + 0b1 => .quad, + }, + 0b01 => switch (enc_opc1.encoded) { + 0b0 => .half, + 0b1 => unreachable, + }, + 0b10 => switch (enc_opc1.encoded) { + 0b0 => .single, + 0b1 => unreachable, + }, + 0b11 => switch (enc_opc1.encoded) { + 0b0 => .double, + 0b1 => unreachable, + }, + }; + } + }; + + pub const Size = packed struct { + encoded: u2, + + pub fn encode(vs: Register.VectorSize) Size { + return .{ .encoded = switch (vs) { + .byte, .quad => 0b00, + .half => 0b01, + .single => 0b10, + .double => 0b11, + else => unreachable, + } }; + } + }; + + pub const Decoded = union(enum) { + unallocated, + str: Str, + ldr: Ldr, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size.encoded) { + 0b00 => switch (inst.group.opc0) { + .store => .{ .str = inst.str }, + .load => .{ .ldr = inst.ldr }, + }, + 0b01, 0b10, 0b11 => switch (inst.group.opc1.encoded) { + 0b0 => switch (inst.group.opc0) { + .store => .{ .str = inst.str }, + .load => .{ .ldr = inst.ldr }, + }, + 0b1 => .unallocated, + }, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register (register offset) + pub const RegisterRegisterOffset = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2, + decoded24: u2 = 0b00, + V: bool, + decoded27: u3 = 0b111, + size: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + prfm: Prfm, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize, + }; + + /// C6.2.325 STRB (register) + pub const Strb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.171 LDRB (register) + pub const Ldrb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.175 LDRSB (register) + pub const Ldrsb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.327 STRH (register) + pub const Strh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.173 LDRH (register) + pub const Ldrh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.177 LDRSH (register) + pub const Ldrsh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.323 STR (register) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b00, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.168 LDR (register) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b01, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.180 LDRSW (register) + pub const Ldrsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b10, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .word, + }; + + /// C6.2.249 PRFM (register) + pub const Prfm = packed struct { + prfop: PrfOp, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2 = 0b10, + decoded24: u2 = 0b00, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .doubleword, + }; + + pub const Decoded = union(enum) { + unallocated, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + prfm: Prfm, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size) { + .byte => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .strb = inst.strb }, + 0b01 => .{ .ldrb = inst.ldrb }, + 0b10, 0b11 => .{ .ldrsb = inst.ldrsb }, + }, + true => .unallocated, + }, + .halfword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .strh = inst.strh }, + 0b01 => .{ .ldrh = inst.ldrh }, + 0b10, 0b11 => .{ .ldrsh = inst.ldrsh }, + }, + true => .unallocated, + }, + .word => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10 => .{ .ldrsw = inst.ldrsw }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + .doubleword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10 => .{ .prfm = inst.prfm }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + str: Str, + ldr: Ldr, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc: u2, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.332 STR (register, SIMD&FP) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc0: L = .store, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.193 LDR (register, SIMD&FP) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + S: bool, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opc0: L = .load, + opc1: Opc1, + decoded24: u2 = 0b00, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + pub const Opc1 = packed struct { + encoded: u1, + + pub fn encode(vs: Register.VectorSize) Opc1 { + return .{ .encoded = switch (vs) { + .byte, .half, .single, .double => 0b0, + .quad => 0b1, + else => unreachable, + } }; + } + + pub fn decode(enc_opc1: Opc1, enc_size: Size) Register.VectorSize { + return switch (enc_size.encoded) { + 0b00 => switch (enc_opc1.encoded) { + 0b0 => .byte, + 0b1 => .quad, + }, + 0b01 => switch (enc_opc1.encoded) { + 0b0 => .half, + 0b1 => unreachable, + }, + 0b10 => switch (enc_opc1.encoded) { + 0b0 => .single, + 0b1 => unreachable, + }, + 0b11 => switch (enc_opc1.encoded) { + 0b0 => .double, + 0b1 => unreachable, + }, + }; + } + }; + + pub const Size = packed struct { + encoded: u2, + + pub fn encode(vs: Register.VectorSize) Size { + return .{ .encoded = switch (vs) { + .byte, .quad => 0b00, + .half => 0b01, + .single => 0b10, + .double => 0b11, + else => unreachable, + } }; + } + }; + }; + + pub const Option = enum(u3) { + uxtw = 0b010, + lsl = 0b011, + sxtw = 0b110, + sxtx = 0b111, + _, + + pub fn sf(option: Option) Register.IntegerSize { + return switch (option) { + .uxtw, .sxtw => .word, + .lsl, .sxtx => .doubleword, + _ => unreachable, + }; + } + }; + + pub const Extend = union(Option) { + uxtw: Amount, + lsl: Amount, + sxtw: Amount, + sxtx: Amount, + + pub const Amount = u3; + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + /// Load/store register (unsigned immediate) + pub const RegisterUnsignedImmediate = packed union { + group: @This().Group, + integer: Integer, + vector: Vector, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2, + decoded24: u2 = 0b01, + V: bool, + decoded27: u3 = 0b111, + size: u2, + }; + + pub const Integer = packed union { + group: @This().Group, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + prfm: Prfm, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize, + }; + + /// C6.2.324 STRB (immediate) + pub const Strb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b00, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.170 LDRB (immediate) + pub const Ldrb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b01, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.174 LDRSB (immediate) + pub const Ldrsb = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .byte, + }; + + /// C6.2.326 STRH (immediate) + pub const Strh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b00, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.172 LDRH (immediate) + pub const Ldrh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b01, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.176 LDRSH (immediate) + pub const Ldrsh = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc0: u1, + opc1: u1 = 0b1, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .halfword, + }; + + /// C6.2.322 STR (immediate) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b00, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.166 LDR (immediate) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b01, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + sf: Register.IntegerSize, + size1: u1 = 0b1, + }; + + /// C6.2.178 LDRSW (immediate) + pub const Ldrsw = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b10, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .word, + }; + + /// C6.2.247 PRFM (immediate) + pub const Prfm = packed struct { + prfop: PrfOp, + Rn: Register.Encoded, + imm12: u12, + opc: u2 = 0b10, + decoded24: u2 = 0b01, + V: bool = false, + decoded27: u3 = 0b111, + size: IntegerSize = .doubleword, + }; + + pub const Decoded = union(enum) { + unallocated, + strb: Strb, + ldrb: Ldrb, + ldrsb: Ldrsb, + strh: Strh, + ldrh: Ldrh, + ldrsh: Ldrsh, + str: Str, + ldr: Ldr, + ldrsw: Ldrsw, + prfm: Prfm, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size) { + .byte => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .strb = inst.strb }, + 0b01 => .{ .ldrb = inst.ldrb }, + 0b10, 0b11 => .{ .ldrsb = inst.ldrsb }, + }, + true => .unallocated, + }, + .halfword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .strh = inst.strh }, + 0b01 => .{ .ldrh = inst.ldrh }, + 0b10, 0b11 => .{ .ldrsh = inst.ldrsh }, + }, + true => .unallocated, + }, + .word => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10 => .{ .ldrsw = inst.ldrsw }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + .doubleword => switch (inst.group.V) { + false => switch (inst.group.opc) { + 0b00 => .{ .str = inst.str }, + 0b01 => .{ .ldr = inst.ldr }, + 0b10 => .{ .prfm = inst.prfm }, + 0b11 => .unallocated, + }, + true => .unallocated, + }, + }; + } + }; + + pub const Vector = packed union { + group: @This().Group, + str: Str, + ldr: Ldr, + + pub const Group = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc0: L, + opc1: Opc1, + decoded24: u2 = 0b01, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.331 STR (immediate, SIMD&FP) + pub const Str = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc0: L = .store, + opc1: Opc1, + decoded24: u2 = 0b01, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + /// C7.2.191 LDR (immediate, SIMD&FP) + pub const Ldr = packed struct { + Rt: Register.Encoded, + Rn: Register.Encoded, + imm12: u12, + opc0: L = .load, + opc1: Opc1, + decoded24: u2 = 0b01, + V: bool = true, + decoded27: u3 = 0b111, + size: Size, + }; + + pub const Opc1 = packed struct { + encoded: u1, + + pub fn encode(vs: Register.VectorSize) Opc1 { + return .{ .encoded = switch (vs) { + .byte, .half, .single, .double => 0b0, + .quad => 0b1, + else => unreachable, + } }; + } + + pub fn decode(enc_opc1: Opc1, enc_size: Size) Register.VectorSize { + return switch (enc_size.encoded) { + 0b00 => switch (enc_opc1.encoded) { + 0b0 => .byte, + 0b1 => .quad, + }, + 0b01 => switch (enc_opc1.encoded) { + 0b0 => .half, + 0b1 => unreachable, + }, + 0b10 => switch (enc_opc1.encoded) { + 0b0 => .single, + 0b1 => unreachable, + }, + 0b11 => switch (enc_opc1.encoded) { + 0b0 => .double, + 0b1 => unreachable, + }, + }; + } + }; + + pub const Size = packed struct { + encoded: u2, + + pub fn encode(vs: Register.VectorSize) Size { + return .{ .encoded = switch (vs) { + .byte, .quad => 0b00, + .half => 0b01, + .single => 0b10, + .double => 0b11, + else => unreachable, + } }; + } + }; + + pub const Decoded = union(enum) { + unallocated, + str: Str, + ldr: Ldr, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.size.encoded) { + 0b00 => switch (inst.group.opc0) { + .store => .{ .str = inst.str }, + .load => .{ .ldr = inst.ldr }, + }, + 0b01, 0b10, 0b11 => switch (inst.group.opc1.encoded) { + 0b0 => switch (inst.group.opc0) { + .store => .{ .str = inst.str }, + .load => .{ .ldr = inst.ldr }, + }, + 0b1 => .unallocated, + }, + }; + } + }; + + pub const Decoded = union(enum) { + integer: Integer, + vector: Vector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.V) { + false => .{ .integer = inst.integer }, + true => .{ .vector = inst.vector }, + }; + } + }; + + pub const L = enum(u1) { + store = 0b0, + load = 0b1, + }; + + pub const IntegerSize = enum(u2) { + byte = 0b00, + halfword = 0b01, + word = 0b10, + doubleword = 0b11, + }; + + pub const VectorSize = enum(u2) { + single = 0b00, + double = 0b01, + quad = 0b10, + _, + + pub fn decode(vs: VectorSize) Register.VectorSize { + return switch (vs) { + .single => .single, + .double => .double, + .quad => .quad, + _ => unreachable, + }; + } + + pub fn encode(vs: Register.VectorSize) VectorSize { + return switch (vs) { + else => unreachable, + .single => .single, + .double => .double, + .quad => .quad, + }; + } + }; + + pub const PrfOp = packed struct { + policy: Policy, + target: Target, + type: Type, + + pub const Policy = enum(u1) { + keep = 0b0, + strm = 0b1, + }; + + pub const Target = enum(u2) { + l1 = 0b00, + l2 = 0b01, + l3 = 0b10, + _, + }; + + pub const Type = enum(u2) { + pld = 0b00, + pli = 0b01, + pst = 0b10, + _, + }; + + pub const pldl1keep: PrfOp = .{ .type = .pld, .target = .l1, .policy = .keep }; + pub const pldl1strm: PrfOp = .{ .type = .pld, .target = .l1, .policy = .strm }; + pub const pldl2keep: PrfOp = .{ .type = .pld, .target = .l2, .policy = .keep }; + pub const pldl2strm: PrfOp = .{ .type = .pld, .target = .l2, .policy = .strm }; + pub const pldl3keep: PrfOp = .{ .type = .pld, .target = .l3, .policy = .keep }; + pub const pldl3strm: PrfOp = .{ .type = .pld, .target = .l3, .policy = .strm }; + pub const plil1keep: PrfOp = .{ .type = .pli, .target = .l1, .policy = .keep }; + pub const plil1strm: PrfOp = .{ .type = .pli, .target = .l1, .policy = .strm }; + pub const plil2keep: PrfOp = .{ .type = .pli, .target = .l2, .policy = .keep }; + pub const plil2strm: PrfOp = .{ .type = .pli, .target = .l2, .policy = .strm }; + pub const plil3keep: PrfOp = .{ .type = .pli, .target = .l3, .policy = .keep }; + pub const plil3strm: PrfOp = .{ .type = .pli, .target = .l3, .policy = .strm }; + pub const pstl1keep: PrfOp = .{ .type = .pst, .target = .l1, .policy = .keep }; + pub const pstl1strm: PrfOp = .{ .type = .pst, .target = .l1, .policy = .strm }; + pub const pstl2keep: PrfOp = .{ .type = .pst, .target = .l2, .policy = .keep }; + pub const pstl2strm: PrfOp = .{ .type = .pst, .target = .l2, .policy = .strm }; + pub const pstl3keep: PrfOp = .{ .type = .pst, .target = .l3, .policy = .keep }; + pub const pstl3strm: PrfOp = .{ .type = .pst, .target = .l3, .policy = .strm_ }; + }; + + pub const Decoded = union(enum) { + unallocated, + register_literal: RegisterLiteral, + memory: Memory, + no_allocate_pair_offset: NoAllocatePairOffset, + register_pair_post_indexed: RegisterPairPostIndexed, + register_pair_offset: RegisterPairOffset, + register_pair_pre_indexed: RegisterPairPreIndexed, + register_unscaled_immediate: RegisterUnscaledImmediate, + register_immediate_post_indexed: RegisterImmediatePostIndexed, + register_unprivileged: RegisterUnprivileged, + register_immediate_pre_indexed: RegisterImmediatePreIndexed, + register_register_offset: RegisterRegisterOffset, + register_unsigned_immediate: RegisterUnsignedImmediate, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op0) { + else => .unallocated, + 0b0010, 0b0110, 0b1010, 0b1110 => switch (inst.group.op2) { + 0b00 => .{ .no_allocate_pair_offset = inst.no_allocate_pair_offset }, + 0b01 => .{ .register_pair_post_indexed = inst.register_pair_post_indexed }, + 0b10 => .{ .register_pair_offset = inst.register_pair_offset }, + 0b11 => .{ .register_pair_pre_indexed = inst.register_pair_pre_indexed }, + }, + 0b0011, 0b0111, 0b1011, 0b1111 => switch (inst.group.op2) { + 0b00...0b01 => switch (inst.group.op3) { + 0b000000...0b011111 => switch (inst.group.op4) { + 0b00 => .{ .register_unscaled_immediate = inst.register_unscaled_immediate }, + 0b01 => .{ .register_immediate_post_indexed = inst.register_immediate_post_indexed }, + 0b10 => .{ .register_unprivileged = inst.register_unprivileged }, + 0b11 => .{ .register_immediate_pre_indexed = inst.register_immediate_pre_indexed }, + }, + 0b100000...0b111111 => switch (inst.group.op4) { + 0b00 => .unallocated, + 0b10 => .{ .register_register_offset = inst.register_register_offset }, + 0b01, 0b11 => .unallocated, + }, + }, + 0b10...0b11 => .{ .register_unsigned_immediate = inst.register_unsigned_immediate }, + }, + }; + } + }; + + /// C4.1.89 Data Processing -- Register + pub const DataProcessingRegister = packed union { + group: @This().Group, + data_processing_two_source: DataProcessingTwoSource, + data_processing_one_source: DataProcessingOneSource, + logical_shifted_register: LogicalShiftedRegister, + add_subtract_shifted_register: AddSubtractShiftedRegister, + add_subtract_extended_register: AddSubtractExtendedRegister, + add_subtract_with_carry: AddSubtractWithCarry, + rotate_right_into_flags: RotateRightIntoFlags, + evaluate_into_flags: EvaluateIntoFlags, + conditional_compare_register: ConditionalCompareRegister, + conditional_compare_immediate: ConditionalCompareImmediate, + conditional_select: ConditionalSelect, + data_processing_three_source: DataProcessingThreeSource, + + /// Table C4-90 Encoding table for the Data Processing -- Register group + pub const Group = packed struct { + encoded0: u10, + op3: u6, + encoded16: u5, + op2: u4, + decoded25: u3 = 0b101, + op1: u1, + encoded29: u1, + op0: u1, + encoded31: u1, + }; + + /// Data-processing (2 source) + pub const DataProcessingTwoSource = packed union { + group: @This().Group, + udiv: Udiv, + sdiv: Sdiv, + lslv: Lslv, + lsrv: Lsrv, + asrv: Asrv, + rorv: Rorv, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + opcode: u6, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.388 UDIV + pub const Udiv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + o1: DivOp = .udiv, + decoded11: u5 = 0b00001, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.270 SDIV + pub const Sdiv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + o1: DivOp = .sdiv, + decoded11: u5 = 0b00001, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.214 LSLV + pub const Lslv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: ShiftOp = .lslv, + decoded12: u4 = 0b0010, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.217 LSRV + pub const Lsrv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: ShiftOp = .lsrv, + decoded12: u4 = 0b0010, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.18 ASRV + pub const Asrv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: ShiftOp = .asrv, + decoded12: u4 = 0b0010, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.263 RORV + pub const Rorv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: ShiftOp = .rorv, + decoded12: u4 = 0b0010, + Rm: Register.Encoded, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + pub const DivOp = enum(u1) { + udiv = 0b0, + sdiv = 0b1, + }; + + pub const ShiftOp = enum(u2) { + lslv = 0b00, + lsrv = 0b01, + asrv = 0b10, + rorv = 0b11, + }; + + pub const Decoded = union(enum) { + unallocated, + udiv: Udiv, + sdiv: Sdiv, + lslv: Lslv, + lsrv: Lsrv, + asrv: Asrv, + rorv: Rorv, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.S) { + false => switch (inst.group.opcode) { + else => .unallocated, + 0b000010 => .{ .udiv = inst.udiv }, + 0b000011 => .{ .sdiv = inst.sdiv }, + 0b001000 => .{ .lslv = inst.lslv }, + 0b001001 => .{ .lsrv = inst.lsrv }, + 0b001010 => .{ .asrv = inst.asrv }, + 0b001011 => .{ .rorv = inst.rorv }, + }, + true => .unallocated, + }; + } + }; + + /// Data-processing (1 source) + pub const DataProcessingOneSource = packed union { + group: @This().Group, + rbit: Rbit, + rev16: Rev16, + rev32: Rev32, + rev: Rev, + clz: Clz, + cls: Cls, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + opcode: u6, + opcode2: u5, + decoded21: u8 = 0b11010110, + S: bool, + decoded30: u1 = 0b1, + sf: Register.IntegerSize, + }; + + /// C6.2.253 RBIT + pub const Rbit = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b00, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b00000, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b1, + sf: Register.IntegerSize, + }; + + /// C6.2.257 REV16 + pub const Rev16 = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + opc: u2 = 0b01, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b00000, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b1, + sf: Register.IntegerSize, + }; + + /// C6.2.258 REV32 + pub const Rev32 = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + opc: u2 = 0b10, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b00000, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b1, + sf: Register.IntegerSize = .doubleword, + }; + + /// C6.2.256 REV + pub const Rev = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + opc0: Register.IntegerSize, + opc1: u1 = 0b1, + decoded12: u4 = 0b0000, + decoded16: u5 = 0b00000, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b1, + sf: Register.IntegerSize, + }; + + /// C6.2.58 CLZ + pub const Clz = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op: u1 = 0b0, + decoded11: u5 = 0b00010, + decoded16: u5 = 0b00000, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b1, + sf: Register.IntegerSize, + }; + + /// C6.2.57 CLS + pub const Cls = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op: u1 = 0b1, + decoded11: u5 = 0b00010, + decoded16: u5 = 0b00000, + decoded21: u8 = 0b11010110, + S: bool = false, + decoded30: u1 = 0b1, + sf: Register.IntegerSize, + }; + + pub const Decoded = union(enum) { + unallocated, + rbit: Rbit, + rev16: Rev16, + rev32: Rev32, + rev: Rev, + clz: Clz, + cls: Cls, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.S) { + true => .unallocated, + false => switch (inst.group.opcode2) { + else => .unallocated, + 0b00000 => switch (inst.group.opcode) { + else => .unallocated, + 0b000000 => .{ .rbit = inst.rbit }, + 0b000001 => .{ .rev16 = inst.rev16 }, + 0b000010 => switch (inst.group.sf) { + .word => .{ .rev = inst.rev }, + .doubleword => .{ .rev32 = inst.rev32 }, + }, + 0b000011 => switch (inst.group.sf) { + .word => .unallocated, + .doubleword => .{ .rev = inst.rev }, + }, + 0b000100 => .{ .clz = inst.clz }, + 0b000101 => .{ .cls = inst.cls }, + }, + }, + }; + } + }; + + /// Logical (shifted register) + pub const LogicalShiftedRegister = packed union { + group: @This().Group, + @"and": And, + bic: Bic, + orr: Orr, + orn: Orn, + eor: Eor, + eon: Eon, + ands: Ands, + bics: Bics, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc, + sf: Register.IntegerSize, + }; + + /// C6.2.13 AND (shifted register) + pub const And = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = false, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .@"and", + sf: Register.IntegerSize, + }; + + /// C6.2.32 BIC (shifted register) + pub const Bic = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = true, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .@"and", + sf: Register.IntegerSize, + }; + + /// C6.2.241 ORR (shifted register) + pub const Orr = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = false, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .orr, + sf: Register.IntegerSize, + }; + + /// C6.2.239 ORN (shifted register) + pub const Orn = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = true, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .orr, + sf: Register.IntegerSize, + }; + + /// C6.2.120 EOR (shifted register) + pub const Eor = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = false, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .eor, + sf: Register.IntegerSize, + }; + + /// C6.2.118 EON (shifted register) + pub const Eon = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = true, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .eor, + sf: Register.IntegerSize, + }; + + /// C6.2.15 ANDS (shifted register) + pub const Ands = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = false, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .ands, + sf: Register.IntegerSize, + }; + + /// C6.2.33 BICS (shifted register) + pub const Bics = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + N: bool = true, + shift: Shift.Op, + decoded24: u5 = 0b01010, + opc: LogicalOpc = .ands, + sf: Register.IntegerSize, + }; + + pub const Decoded = union(enum) { + unallocated, + @"and": And, + bic: Bic, + orr: Orr, + orn: Orn, + eor: Eor, + eon: Eon, + ands: Ands, + bics: Bics, + }; + pub fn decode(inst: @This()) @This().Decoded { + return if (inst.group.sf == .word and @as(u1, @truncate(inst.group.imm6 >> 5)) == 0b1) + .unallocated + else switch (inst.group.opc) { + .@"and" => switch (inst.group.N) { + false => .{ .@"and" = inst.@"and" }, + true => .{ .bic = inst.bic }, + }, + .orr => switch (inst.group.N) { + false => .{ .orr = inst.orr }, + true => .{ .orn = inst.orn }, + }, + .eor => switch (inst.group.N) { + false => .{ .eor = inst.eor }, + true => .{ .eon = inst.eon }, + }, + .ands => switch (inst.group.N) { + false => .{ .ands = inst.ands }, + true => .{ .bics = inst.bics }, + }, + }; + } + }; + + /// Add/subtract (shifted register) + pub const AddSubtractShiftedRegister = packed union { + group: @This().Group, + add: Add, + adds: Adds, + sub: Sub, + subs: Subs, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + decoded21: u1 = 0b0, + shift: Shift.Op, + decoded24: u5 = 0b01011, + S: bool, + op: AddSubtractOp, + sf: Register.IntegerSize, + }; + + /// C6.2.5 ADD (shifted register) + pub const Add = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + decoded21: u1 = 0b0, + shift: Shift.Op, + decoded24: u5 = 0b01011, + S: bool = false, + op: AddSubtractOp = .add, + sf: Register.IntegerSize, + }; + + /// C6.2.9 ADDS (shifted register) + pub const Adds = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + decoded21: u1 = 0b0, + shift: Shift.Op, + decoded24: u5 = 0b01011, + S: bool = true, + op: AddSubtractOp = .add, + sf: Register.IntegerSize, + }; + + /// C6.2.5 SUB (shifted register) + pub const Sub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + decoded21: u1 = 0b0, + shift: Shift.Op, + decoded24: u5 = 0b01011, + S: bool = false, + op: AddSubtractOp = .sub, + sf: Register.IntegerSize, + }; + + /// C6.2.9 SUBS (shifted register) + pub const Subs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm6: Shift.Amount, + Rm: Register.Encoded, + decoded21: u1 = 0b0, + shift: Shift.Op, + decoded24: u5 = 0b01011, + S: bool = true, + op: AddSubtractOp = .sub, + sf: Register.IntegerSize, + }; + + pub const Decoded = union(enum) { + unallocated, + add: Add, + adds: Adds, + sub: Sub, + subs: Subs, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.shift) { + .ror => .unallocated, + .lsl, .lsr, .asr => if (inst.group.sf == .word and @as(u1, @truncate(inst.group.imm6 >> 5)) == 0b1) + .unallocated + else switch (inst.group.op) { + .add => switch (inst.group.S) { + false => .{ .add = inst.add }, + true => .{ .adds = inst.adds }, + }, + .sub => switch (inst.group.S) { + false => .{ .sub = inst.sub }, + true => .{ .subs = inst.subs }, + }, + }, + }; + } + }; + + /// Add/subtract (extended register) + pub const AddSubtractExtendedRegister = packed union { + group: @This().Group, + add: Add, + adds: Adds, + sub: Sub, + subs: Subs, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm3: Extend.Amount, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opt: u2, + decoded24: u5 = 0b01011, + S: bool, + op: AddSubtractOp, + sf: Register.IntegerSize, + }; + + /// C6.2.3 ADD (extended register) + pub const Add = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm3: Extend.Amount, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opt: u2 = 0b00, + decoded24: u5 = 0b01011, + S: bool = false, + op: AddSubtractOp = .add, + sf: Register.IntegerSize, + }; + + /// C6.2.7 ADDS (extended register) + pub const Adds = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm3: Extend.Amount, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opt: u2 = 0b00, + decoded24: u5 = 0b01011, + S: bool = true, + op: AddSubtractOp = .add, + sf: Register.IntegerSize, + }; + + /// C6.2.356 SUB (extended register) + pub const Sub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm3: Extend.Amount, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opt: u2 = 0b00, + decoded24: u5 = 0b01011, + S: bool = false, + op: AddSubtractOp = .sub, + sf: Register.IntegerSize, + }; + + /// C6.2.362 SUBS (extended register) + pub const Subs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + imm3: Extend.Amount, + option: Option, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + opt: u2 = 0b00, + decoded24: u5 = 0b01011, + S: bool = true, + op: AddSubtractOp = .sub, + sf: Register.IntegerSize, + }; + + pub const Option = enum(u3) { + uxtb = 0b000, + uxth = 0b001, + uxtw = 0b010, + uxtx = 0b011, + sxtb = 0b100, + sxth = 0b101, + sxtw = 0b110, + sxtx = 0b111, + + pub fn sf(option: Option) Register.IntegerSize { + return switch (option) { + .uxtb, .uxth, .uxtw, .sxtb, .sxth, .sxtw => .word, + .uxtx, .sxtx => .doubleword, + }; + } + }; + + pub const Extend = union(Option) { + uxtb: Amount, + uxth: Amount, + uxtw: Amount, + uxtx: Amount, + sxtb: Amount, + sxth: Amount, + sxtw: Amount, + sxtx: Amount, + + pub const Amount = u3; + }; + + pub const Decoded = union(enum) { + unallocated, + add: Add, + adds: Adds, + sub: Sub, + subs: Subs, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.imm3) { + 0b101 => .unallocated, + 0b110...0b111 => .unallocated, + 0b000...0b100 => switch (inst.group.opt) { + 0b01 => .unallocated, + 0b10...0b11 => .unallocated, + 0b00 => switch (inst.group.op) { + .add => switch (inst.group.S) { + false => .{ .add = inst.add }, + true => .{ .adds = inst.adds }, + }, + .sub => switch (inst.group.S) { + false => .{ .sub = inst.sub }, + true => .{ .subs = inst.subs }, + }, + }, + }, + }; + } + }; + + /// Add/subtract (with carry) + pub const AddSubtractWithCarry = packed union { + group: @This().Group, + adc: Adc, + adcs: Adcs, + sbc: Sbc, + sbcs: Sbcs, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + Rm: Register.Encoded, + decoded21: u8 = 0b11010000, + S: bool, + op: Op, + sf: Register.IntegerSize, + }; + + /// C6.2.1 ADC + pub const Adc = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + Rm: Register.Encoded, + decoded21: u8 = 0b11010000, + S: bool = false, + op: Op = .adc, + sf: Register.IntegerSize, + }; + + /// C6.2.2 ADCS + pub const Adcs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + Rm: Register.Encoded, + decoded21: u8 = 0b11010000, + S: bool = true, + op: Op = .adc, + sf: Register.IntegerSize, + }; + + /// C6.2.265 SBC + pub const Sbc = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + Rm: Register.Encoded, + decoded21: u8 = 0b11010000, + S: bool = false, + op: Op = .sbc, + sf: Register.IntegerSize, + }; + + /// C6.2.266 SBCS + pub const Sbcs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + Rm: Register.Encoded, + decoded21: u8 = 0b11010000, + S: bool = true, + op: Op = .sbc, + sf: Register.IntegerSize, + }; + + pub const Op = enum(u1) { + adc = 0b0, + sbc = 0b1, + }; + + pub const Decoded = union(enum) { + adc: Adc, + adcs: Adcs, + sbc: Sbc, + sbcs: Sbcs, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op) { + .adc => switch (inst.group.S) { + false => .{ .adc = inst.adc }, + true => .{ .adcs = inst.adcs }, + }, + .sbc => switch (inst.group.S) { + false => .{ .sbc = inst.sbc }, + true => .{ .sbcs = inst.sbcs }, + }, + }; + } + }; + + /// Rotate right into flags + pub const RotateRightIntoFlags = packed union { + group: @This().Group, + + pub const Group = packed struct { + mask: Nzcv, + o2: u1, + Rn: Register.Encoded, + decoded10: u5 = 0b0001, + imm6: u6, + decoded21: u8 = 0b11010000, + S: bool, + op: u1, + sf: Register.IntegerSize, + }; + }; + + /// Evaluate into flags + pub const EvaluateIntoFlags = packed union { + group: @This().Group, + + pub const Group = packed struct { + mask: Nzcv, + o3: u1, + Rn: Register.Encoded, + decoded10: u4 = 0b0010, + sz: enum(u1) { + byte = 0b0, + word = 0b1, + }, + opcode2: u6, + decoded21: u8 = 0b11010000, + S: bool, + op: u1, + sf: Register.IntegerSize, + }; + }; + + /// Conditional compare (register) + pub const ConditionalCompareRegister = packed union { + group: @This().Group, + ccmn: Ccmn, + ccmp: Ccmp, + + pub const Group = packed struct { + nzcv: Nzcv, + o3: u1, + Rn: Register.Encoded, + o2: u1, + decoded11: u1 = 0b0, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010010, + S: bool, + op: Op, + sf: Register.IntegerSize, + }; + + /// C6.2.49 CCMN (register) + pub const Ccmn = packed struct { + nzcv: Nzcv, + o3: u1 = 0b0, + Rn: Register.Encoded, + o2: u1 = 0b0, + decoded11: u1 = 0b0, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010010, + S: bool = true, + op: Op = .ccmn, + sf: Register.IntegerSize, + }; + + /// C6.2.51 CCMP (register) + pub const Ccmp = packed struct { + nzcv: Nzcv, + o3: u1 = 0b0, + Rn: Register.Encoded, + o2: u1 = 0b0, + decoded11: u1 = 0b0, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010010, + S: bool = true, + op: Op = .ccmp, + sf: Register.IntegerSize, + }; + + pub const Op = enum(u1) { + ccmn = 0b0, + ccmp = 0b1, + }; + }; + + /// Conditional compare (immediate) + pub const ConditionalCompareImmediate = packed union { + group: @This().Group, + ccmn: Ccmn, + ccmp: Ccmp, + + pub const Group = packed struct { + nzcv: Nzcv, + o3: u1, + Rn: Register.Encoded, + o2: u1, + decoded11: u1 = 0b1, + cond: ConditionCode, + imm5: u5, + decoded21: u8 = 0b11010010, + S: bool, + op: Op, + sf: Register.IntegerSize, + }; + + /// C6.2.48 CCMN (immediate) + pub const Ccmn = packed struct { + nzcv: Nzcv, + o3: u1 = 0b0, + Rn: Register.Encoded, + o2: u1 = 0b0, + decoded11: u1 = 0b1, + cond: ConditionCode, + imm5: u5, + decoded21: u8 = 0b11010010, + S: bool = true, + op: Op = .ccmn, + sf: Register.IntegerSize, + }; + + /// C6.2.50 CCMP (immediate) + pub const Ccmp = packed struct { + nzcv: Nzcv, + o3: u1 = 0b0, + Rn: Register.Encoded, + o2: u1 = 0b0, + decoded11: u1 = 0b1, + cond: ConditionCode, + imm5: u5, + decoded21: u8 = 0b11010010, + S: bool = true, + op: Op = .ccmp, + sf: Register.IntegerSize, + }; + + pub const Op = enum(u1) { + ccmn = 0b0, + ccmp = 0b1, + }; + }; + + /// Conditional select + pub const ConditionalSelect = packed union { + group: @This().Group, + csel: Csel, + csinc: Csinc, + csinv: Csinv, + csneg: Csneg, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: u2, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010100, + S: bool, + op: u1, + sf: Register.IntegerSize, + }; + + /// C6.2.103 CSEL + pub const Csel = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: u2 = 0b00, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010100, + S: bool = false, + op: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.106 CSINC + pub const Csinc = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: u2 = 0b01, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010100, + S: bool = false, + op: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C6.2.107 CSINV + pub const Csinv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: u2 = 0b00, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010100, + S: bool = false, + op: u1 = 0b1, + sf: Register.IntegerSize, + }; + + /// C6.2.108 CSNEG + pub const Csneg = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + op2: u2 = 0b01, + cond: ConditionCode, + Rm: Register.Encoded, + decoded21: u8 = 0b11010100, + S: bool = false, + op: u1 = 0b1, + sf: Register.IntegerSize, + }; + + pub const Decoded = union(enum) { + unallocated, + csel: Csel, + csinc: Csinc, + csinv: Csinv, + csneg: Csneg, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.S) { + true => .unallocated, + false => switch (inst.group.op) { + 0b0 => switch (inst.group.op2) { + 0b10...0b11 => .unallocated, + 0b00 => .{ .csel = inst.csel }, + 0b01 => .{ .csinc = inst.csinc }, + }, + 0b1 => switch (inst.group.op2) { + 0b10...0b11 => .unallocated, + 0b00 => .{ .csinv = inst.csinv }, + 0b01 => .{ .csneg = inst.csneg }, + }, + }, + }; + } + }; + + /// Data-processing (3 source) + pub const DataProcessingThreeSource = packed union { + group: @This().Group, + madd: Madd, + msub: Msub, + smaddl: Smaddl, + smsubl: Smsubl, + smulh: Smulh, + umaddl: Umaddl, + umsubl: Umsubl, + umulh: Umulh, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp, + Rm: Register.Encoded, + op31: u3, + decoded24: u5 = 0b11011, + op54: u2, + sf: Register.IntegerSize, + }; + + /// C6.2.218 MADD + pub const Madd = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + op31: u3 = 0b000, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize, + }; + + /// C6.2.231 MSUB + pub const Msub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .sub, + Rm: Register.Encoded, + op31: u3 = 0b000, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize, + }; + + /// C6.2.282 SMADDL + pub const Smaddl = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + op21: u2 = 0b01, + U: bool = false, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize = .doubleword, + }; + + /// C6.2.287 SMSUBL + pub const Smsubl = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .sub, + Rm: Register.Encoded, + op21: u2 = 0b01, + U: bool = false, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize = .doubleword, + }; + + /// C6.2.288 SMULH + pub const Smulh = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded = @enumFromInt(0b11111), + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + op21: u2 = 0b10, + U: bool = false, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize = .doubleword, + }; + + /// C6.2.389 UMADDL + pub const Umaddl = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + op21: u2 = 0b01, + U: bool = true, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize = .doubleword, + }; + + /// C6.2.391 UMSUBL + pub const Umsubl = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .sub, + Rm: Register.Encoded, + op21: u2 = 0b01, + U: bool = true, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize = .doubleword, + }; + + /// C6.2.392 UMULH + pub const Umulh = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded = @enumFromInt(0b11111), + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + op21: u2 = 0b10, + U: bool = true, + decoded24: u5 = 0b11011, + op54: u2 = 0b00, + sf: Register.IntegerSize = .doubleword, + }; + + pub const Decoded = union(enum) { + unallocated, + madd: Madd, + msub: Msub, + smaddl: Smaddl, + smsubl: Smsubl, + smulh: Smulh, + umaddl: Umaddl, + umsubl: Umsubl, + umulh: Umulh, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op54) { + 0b01, 0b10...0b11 => .unallocated, + 0b00 => switch (inst.group.op31) { + 0b011, 0b100, 0b111 => .unallocated, + 0b000 => switch (inst.group.o0) { + .add => .{ .madd = inst.madd }, + .sub => .{ .msub = inst.msub }, + }, + 0b001 => switch (inst.group.sf) { + .word => .unallocated, + .doubleword => switch (inst.group.o0) { + .add => .{ .smaddl = inst.smaddl }, + .sub => .{ .smsubl = inst.smsubl }, + }, + }, + 0b010 => switch (inst.group.sf) { + .word => .unallocated, + .doubleword => switch (inst.group.o0) { + .add => .{ .smulh = inst.smulh }, + .sub => .unallocated, + }, + }, + 0b101 => switch (inst.group.sf) { + .word => .unallocated, + .doubleword => switch (inst.group.o0) { + .add => .{ .umaddl = inst.umaddl }, + .sub => .{ .umsubl = inst.umsubl }, + }, + }, + 0b110 => switch (inst.group.sf) { + .word => .unallocated, + .doubleword => switch (inst.group.o0) { + .add => .{ .umulh = inst.umulh }, + .sub => .unallocated, + }, + }, + }, + }; + } + }; + + pub const Shift = union(enum(u2)) { + lsl: Amount = 0b00, + lsr: Amount = 0b01, + asr: Amount = 0b10, + ror: Amount = 0b11, + + pub const Op = @typeInfo(Shift).@"union".tag_type.?; + pub const Amount = u6; + pub const none: Shift = .{ .lsl = 0 }; + }; + + pub const Nzcv = packed struct { v: bool, c: bool, z: bool, n: bool }; + + pub const Decoded = union(enum) { + unallocated, + data_processing_two_source: DataProcessingTwoSource, + data_processing_one_source: DataProcessingOneSource, + logical_shifted_register: LogicalShiftedRegister, + add_subtract_shifted_register: AddSubtractShiftedRegister, + add_subtract_extended_register: AddSubtractExtendedRegister, + add_subtract_with_carry: AddSubtractWithCarry, + rotate_right_into_flags: RotateRightIntoFlags, + evaluate_into_flags: EvaluateIntoFlags, + conditional_compare_register: ConditionalCompareRegister, + conditional_compare_immediate: ConditionalCompareImmediate, + conditional_select: ConditionalSelect, + data_processing_three_source: DataProcessingThreeSource, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op1) { + 0b0 => switch (@as(u1, @truncate(inst.group.op2 >> 3))) { + 0b0 => .{ .logical_shifted_register = inst.logical_shifted_register }, + 0b1 => switch (@as(u1, @truncate(inst.group.op2 >> 0))) { + 0b0 => .{ .add_subtract_shifted_register = inst.add_subtract_shifted_register }, + 0b1 => .{ .add_subtract_extended_register = inst.add_subtract_extended_register }, + }, + }, + 0b1 => switch (inst.group.op2) { + 0b0000 => switch (inst.group.op3) { + 0b000000 => .{ .add_subtract_with_carry = inst.add_subtract_with_carry }, + 0b000001, 0b100001 => .{ .rotate_right_into_flags = inst.rotate_right_into_flags }, + 0b000010, 0b010010, 0b100010, 0b110010 => .{ .evaluate_into_flags = inst.evaluate_into_flags }, + else => .unallocated, + }, + 0b0010 => switch (@as(u1, @truncate(inst.group.op3 >> 1))) { + 0b0 => .{ .conditional_compare_register = inst.conditional_compare_register }, + 0b1 => .{ .conditional_compare_immediate = inst.conditional_compare_immediate }, + }, + 0b0100 => .{ .conditional_select = inst.conditional_select }, + 0b0110 => switch (inst.group.op0) { + 0b0 => .{ .data_processing_two_source = inst.data_processing_two_source }, + 0b1 => .{ .data_processing_one_source = inst.data_processing_one_source }, + }, + 0b1000...0b1111 => .{ .data_processing_three_source = inst.data_processing_three_source }, + else => .unallocated, + }, + }; + } + }; + + /// C4.1.90 Data Processing -- Scalar Floating-Point and Advanced SIMD + pub const DataProcessingVector = packed union { + group: @This().Group, + simd_scalar_pairwise: SimdScalarPairwise, + simd_copy: SimdCopy, + simd_two_register_miscellaneous: SimdTwoRegisterMiscellaneous, + simd_across_lanes: SimdAcrossLanes, + simd_three_same: SimdThreeSame, + simd_modified_immediate: SimdModifiedImmediate, + convert_float_integer: ConvertFloatInteger, + float_data_processing_one_source: FloatDataProcessingOneSource, + float_compare: FloatCompare, + float_immediate: FloatImmediate, + float_data_processing_two_source: FloatDataProcessingTwoSource, + float_data_processing_three_source: FloatDataProcessingThreeSource, + + /// Table C4-91 Encoding table for the Data Processing -- Scalar Floating-Point and Advanced SIMD group + pub const Group = packed struct { + encoded0: u10, + op3: u9, + op2: u4, + op1: u2, + decoded25: u3 = 0b111, + op0: u4, + }; + + /// Advanced SIMD scalar pairwise + pub const SimdScalarPairwise = packed union { + group: @This().Group, + addp: Addp, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: u5, + decoded17: u5 = 0b11000, + size: Size, + decoded24: u5 = 0b11110, + U: u1, + decoded30: u2 = 0b01, + }; + + /// C7.2.4 ADDP (scalar) + pub const Addp = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: u5 = 0b11011, + decoded17: u5 = 0b11000, + size: Size, + decoded24: u5 = 0b11110, + U: u1 = 0b0, + decoded30: u2 = 0b01, + }; + }; + + /// Advanced SIMD copy + pub const SimdCopy = packed union { + group: @This().Group, + smov: Smov, + umov: Umov, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u1 = 0b1, + imm4: u4, + decoded15: u1 = 0b0, + imm5: u5, + decoded21: u8 = 0b01110000, + op: u1, + Q: Register.IntegerSize, + decoded31: u1 = 0b0, + }; + + /// 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, + decoded15: u1 = 0b0, + imm5: u5, + decoded21: u8 = 0b01110000, + decoded29: u1 = 0b0, + Q: Register.IntegerSize, + decoded31: u1 = 0b0, + }; + + /// C7.2.371 UMOV + pub const Umov = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u1 = 0b1, + decoded11: u1 = 0b1, + decoded12: u1 = 0b1, + decoded13: u2 = 0b01, + decoded15: u1 = 0b0, + imm5: u5, + decoded21: u8 = 0b01110000, + decoded29: u1 = 0b0, + Q: Register.IntegerSize, + decoded31: u1 = 0b0, + }; + }; + + /// Advanced SIMD two-register miscellaneous + pub const SimdTwoRegisterMiscellaneous = packed union { + group: @This().Group, + cnt: Cnt, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: u5, + decoded17: u5 = 0b10000, + size: Size, + decoded24: u5 = 0b01110, + U: u1, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.38 CNT + pub const Cnt = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: u5 = 0b00101, + decoded17: u5 = 0b10000, + size: Size, + decoded24: u5 = 0b01110, + U: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + }; + + /// Advanced SIMD across lanes + pub const SimdAcrossLanes = packed union { + group: @This().Group, + addv: Addv, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: u5, + decoded17: u5 = 0b11000, + size: Size, + decoded24: u5 = 0b01110, + U: u1, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.6 ADDV + pub const Addv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: u5 = 0b11011, + decoded17: u5 = 0b11000, + size: Size, + decoded24: u5 = 0b01110, + U: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + }; + + /// Advanced SIMD three same + pub const SimdThreeSame = packed union { + group: @This().Group, + addp: Addp, + @"and": And, + bic: Bic, + orr: Orr, + orn: Orn, + eor: Eor, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u1 = 0b1, + opcode: u5, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + size: Size, + decoded24: u5 = 0b01110, + U: u1, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.5 ADDP (vector) + pub const Addp = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u1 = 0b1, + opcode: u5 = 0b10111, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + size: Size, + decoded24: u5 = 0b01110, + U: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.11 AND (vector) + pub const And = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u1 = 0b1, + opcode: u5 = 0b00011, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + size: Size = .byte, + decoded24: u5 = 0b01110, + U: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.21 BIC (vector, register) + pub const Bic = 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: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.213 ORR (vector, register) + pub const Orr = 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: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.211 ORN (vector) + pub const Orn = 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: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.41 EOR (vector) + pub const Eor = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u1 = 0b1, + opcode: u5 = 0b00011, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + size: Size = .byte, + decoded24: u5 = 0b01110, + U: u1 = 0b1, + Q: Q, + decoded31: u1 = 0b0, + }; + }; + + /// Advanced SIMD modified immediate + pub const SimdModifiedImmediate = packed union { + group: @This().Group, + movi: Movi, + orr: Orr, + fmov: Fmov, + mvni: Mvni, + bic: Bic, + + pub const Group = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u1 = 0b1, + o2: u1, + cmode: u4, + imm3: u3, + decoded19: u10 = 0b0111100000, + op: u1, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.204 MOVI + pub const Movi = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u1 = 0b1, + o2: u1 = 0b0, + cmode: u4, + imm3: u3, + decoded19: u10 = 0b0111100000, + op: u1, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.212 ORR (vector, immediate) + pub const Orr = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u1 = 0b1, + o2: u1 = 0b0, + cmode0: u1 = 0b1, + cmode: u3, + imm3: u3, + decoded19: u10 = 0b0111100000, + op: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.129 FMOV (vector, immediate) + pub const Fmov = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u1 = 0b1, + o2: u1 = 0b1, + cmode: u4 = 0b1111, + imm3: u3, + decoded19: u10 = 0b0111100000, + op: u1 = 0b0, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.208 MVNI + pub const Mvni = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u1 = 0b1, + o2: u1 = 0b0, + cmode: u4, + imm3: u3, + decoded19: u10 = 0b0111100000, + op: u1 = 0b1, + Q: Q, + decoded31: u1 = 0b0, + }; + + /// C7.2.20 BIC (vector, immediate) + pub const Bic = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u1 = 0b1, + o2: u1 = 0b0, + cmode0: u1 = 0b1, + cmode: u3, + imm3: u3, + decoded19: u10 = 0b0111100000, + op: u1 = 0b1, + Q: Q, + decoded31: u1 = 0b0, + }; + }; + + /// Conversion between floating-point and integer + pub const ConvertFloatInteger = packed union { + group: @This().Group, + fcvtns: Fcvtns, + fcvtnu: Fcvtnu, + scvtf: Scvtf, + ucvtf: Ucvtf, + fcvtas: Fcvtas, + fcvtau: Fcvtau, + fmov: Fmov, + fcvtps: Fcvtps, + fcvtpu: Fcvtpu, + fcvtms: Fcvtms, + fcvtmu: Fcvtmu, + fcvtzs: Fcvtzs, + fcvtzu: Fcvtzu, + fjcvtzs: Fjcvtzs, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3, + rmode: u2, + decoded21: u1 = 0b1, + ptype: Ftype, + decoded24: u5 = 0b11110, + S: bool, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.81 FCVTNS (scalar) + pub const Fcvtns = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b000, + rmode: Rmode = .n, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.83 FCVTNU (scalar) + pub const Fcvtnu = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b001, + rmode: Rmode = .n, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.236 SCVTF (scalar, integer) + pub const Scvtf = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b010, + rmode: Rmode = .n, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.355 UCVTF (scalar, integer) + pub const Ucvtf = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b011, + rmode: Rmode = .n, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.71 FCVTAS (scalar) + pub const Fcvtas = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b100, + rmode: Rmode = .n, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.73 FCVTAU (scalar) + pub const Fcvtau = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b101, + rmode: Rmode = .n, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.131 FMOV (general) + pub const Fmov = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: Opcode, + rmode: Fmov.Rmode, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + + pub const Opcode = enum(u3) { + float_to_integer = 0b110, + integer_to_float = 0b111, + _, + }; + + pub const Rmode = enum(u2) { + @"0" = 0b00, + @"1" = 0b01, + _, + }; + }; + + /// C7.2.85 FCVTPS (scalar) + pub const Fcvtps = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b000, + rmode: Rmode = .p, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.87 FCVTPU (scalar) + pub const Fcvtpu = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b001, + rmode: Rmode = .p, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.76 FCVTMS (scalar) + pub const Fcvtms = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b000, + rmode: Rmode = .m, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.78 FCVTMU (scalar) + pub const Fcvtmu = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b001, + rmode: Rmode = .m, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.92 FCVTZS (scalar, integer) + pub const Fcvtzs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b000, + rmode: Rmode = .z, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.96 FCVTZU (scalar, integer) + pub const Fcvtzu = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b001, + rmode: Rmode = .z, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize, + }; + + /// C7.2.99 FJCVTZS + pub const Fjcvtzs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u6 = 0b000000, + opcode: u3 = 0b110, + rmode: Rmode = .z, + decoded21: u1 = 0b1, + ftype: Ftype = .double, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + sf: Register.IntegerSize = .word, + }; + + pub const Rmode = enum(u2) { + /// to nearest + n = 0b00, + /// toward plus infinity + p = 0b01, + /// toward minus infinity + m = 0b10, + /// toward zero + z = 0b11, + }; + }; + + /// Floating-point data-processing (1 source) + pub const FloatDataProcessingOneSource = packed union { + group: @This().Group, + fmov: Fmov, + fabs: Fabs, + fneg: Fneg, + fsqrt: Fsqrt, + fcvt: Fcvt, + frintn: Frintn, + frintp: Frintp, + frintm: Frintm, + frintz: Frintz, + frinta: Frinta, + frintx: Frintx, + frinti: Frinti, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + opcode: u6, + decoded21: u1 = 0b1, + ptype: Ftype, + decoded24: u5 = 0b11110, + S: bool, + decoded30: u1 = 0b0, + M: u1, + }; + + /// C7.2.130 FMOV (register) + pub const Fmov = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + opc: u2 = 0b00, + decoded17: u4 = 0b0000, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.46 FABS (scalar) + pub const Fabs = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + opc: u2 = 0b01, + decoded17: u4 = 0b0000, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.140 FNEG (scalar) + pub const Fneg = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + opc: u2 = 0b10, + decoded17: u4 = 0b0000, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.172 FSQRT (scalar) + pub const Fsqrt = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + opc: u2 = 0b11, + decoded17: u4 = 0b0000, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.69 FCVT + pub const Fcvt = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + opc: Ftype, + decoded17: u4 = 0b0001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.162 FRINTN (scalar) + pub const Frintn = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .n, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.164 FRINTP (scalar) + pub const Frintp = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .p, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.160 FRINTM (scalar) + pub const Frintm = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .m, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.168 FRINTZ (scalar) + pub const Frintz = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .z, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.156 FRINTA (scalar) + pub const Frinta = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .a, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.166 FRINTX (scalar) + pub const Frintx = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .x, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.158 FRINTI (scalar) + pub const Frinti = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u5 = 0b10000, + rmode: Rmode = .i, + decoded18: u3 = 0b001, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + pub const Rmode = enum(u3) { + /// to nearest with ties to even + n = 0b000, + /// toward plus infinity + p = 0b001, + /// toward minus infinity + m = 0b010, + /// toward zero + z = 0b011, + /// to nearest with ties to away + a = 0b100, + /// exact, using current rounding mode + x = 0b110, + /// using current rounding mode + i = 0b111, + _, + }; + }; + + /// Floating-point compare + pub const FloatCompare = packed union { + group: @This().Group, + fcmp: Fcmp, + fcmpe: Fcmpe, + + pub const Group = packed struct { + opcode2: u5, + Rn: Register.Encoded, + decoded10: u4 = 0b1000, + op: u2, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ptype: Ftype, + decoded24: u5 = 0b11110, + S: bool, + decoded30: u1 = 0b0, + M: u1, + }; + + /// C7.2.66 FCMP + pub const Fcmp = packed struct { + decoded0: u3 = 0b000, + opc0: Opc0, + opc1: u1 = 0b0, + Rn: Register.Encoded, + decoded10: u4 = 0b1000, + op: u2 = 0b00, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.67 FCMPE + pub const Fcmpe = packed struct { + decoded0: u3 = 0b000, + opc0: Opc0, + opc1: u1 = 0b1, + Rn: Register.Encoded, + decoded10: u4 = 0b1000, + op: u2 = 0b00, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + pub const Opc0 = enum(u1) { + register = 0b00, + zero = 0b01, + }; + }; + + /// Floating-point immediate + pub const FloatImmediate = packed union { + group: @This().Group, + fmov: Fmov, + + pub const Group = packed struct { + Rd: Register.Encoded, + imm5: u5, + decoded10: u3 = 0b100, + imm8: u8, + decoded21: u1 = 0b1, + ptype: Ftype, + decoded24: u5 = 0b11110, + S: bool, + decoded30: u1 = 0b0, + M: u1, + }; + + /// C7.2.132 FMOV (scalar, immediate) + pub const Fmov = packed struct { + Rd: Register.Encoded, + imm5: u5 = 0b00000, + decoded10: u3 = 0b100, + imm8: u8, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + }; + + /// Floating-point data-processing (2 source) + pub const FloatDataProcessingTwoSource = packed union { + group: @This().Group, + fmul: Fmul, + fdiv: Fdiv, + fadd: Fadd, + fsub: Fsub, + fmax: Fmax, + fmin: Fmin, + fmaxnm: Fmaxnm, + fminnm: Fminnm, + fnmul: Fnmul, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ptype: Ftype, + decoded24: u5 = 0b11110, + S: bool, + decoded30: u1 = 0b0, + M: u1, + }; + + /// C7.2.136 FMUL (scalar) + pub const Fmul = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fmul, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.98 FDIV (scalar) + pub const Fdiv = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fdiv, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.50 FADD (scalar) + pub const Fadd = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fadd, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.174 FSUB (scalar) + pub const Fsub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fsub, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.102 FMAX (scalar) + pub const Fmax = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fmax, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.112 FMIN (scalar) + pub const Fmin = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fmin, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.104 FMAXNM (scalar) + pub const Fmaxnm = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fmaxnm, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.114 FMINNM (scalar) + pub const Fminnm = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fminnm, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.143 FNMUL (scalar) + pub const Fnmul = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + decoded10: u2 = 0b10, + opcode: Opcode = .fnmul, + Rm: Register.Encoded, + decoded21: u1 = 0b1, + ftype: Ftype, + decoded24: u5 = 0b11110, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + pub const Opcode = enum(u4) { + fmul = 0b0000, + fdiv = 0b0001, + fadd = 0b0010, + fsub = 0b0011, + fmax = 0b0100, + fmin = 0b0101, + fmaxnm = 0b0110, + fminnm = 0b0111, + fnmul = 0b1000, + _, + }; + }; + + /// Floating-point data-processing (3 source) + pub const FloatDataProcessingThreeSource = packed union { + group: @This().Group, + fmadd: Fmadd, + fmsub: Fmsub, + fnmadd: Fnmadd, + fnmsub: Fnmsub, + + pub const Group = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp, + Rm: Register.Encoded, + o1: u1, + ptype: Ftype, + decoded24: u5 = 0b11111, + S: bool, + decoded30: u1 = 0b0, + M: u1, + }; + + /// C7.2.100 FMADD + pub const Fmadd = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + o1: O1 = .fm, + ftype: Ftype, + decoded24: u5 = 0b11111, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.133 FMSUB + pub const Fmsub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .sub, + Rm: Register.Encoded, + o1: O1 = .fm, + ftype: Ftype, + decoded24: u5 = 0b11111, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.141 FNMADD + pub const Fnmadd = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .add, + Rm: Register.Encoded, + o1: O1 = .fnm, + ftype: Ftype, + decoded24: u5 = 0b11111, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + /// C7.2.142 FNMSUB + pub const Fnmsub = packed struct { + Rd: Register.Encoded, + Rn: Register.Encoded, + Ra: Register.Encoded, + o0: AddSubtractOp = .sub, + Rm: Register.Encoded, + o1: O1 = .fnm, + ftype: Ftype, + decoded24: u5 = 0b11111, + S: bool = false, + decoded30: u1 = 0b0, + M: u1 = 0b0, + }; + + pub const O1 = enum(u1) { + fm = 0b0, + fnm = 0b1, + }; + }; + + pub const Q = enum(u1) { + double = 0b0, + quad = 0b1, + }; + + pub const Size = enum(u2) { + byte = 0b00, + half = 0b01, + single = 0b10, + double = 0b11, + + pub fn toVectorSize(s: Size) Register.VectorSize { + return switch (s) { + .byte => .byte, + .half => .half, + .single => .single, + .double => .double, + }; + } + + pub fn fromVectorSize(vs: Register.VectorSize) Size { + return switch (vs) { + .byte => .byte, + .half => .half, + .single => .single, + .double => .double, + }; + } + }; + + pub const Ftype = enum(u2) { + single = 0b00, + double = 0b01, + quad = 0b10, + half = 0b11, + }; + }; + + pub const AddSubtractOp = enum(u1) { + add = 0b0, + sub = 0b1, + }; + + pub const LogicalOpc = enum(u2) { + @"and" = 0b00, + orr = 0b01, + eor = 0b10, + ands = 0b11, + }; + + pub const Decoded = union(enum) { + unallocated, + reserved: Reserved, + sme: Sme, + sve: Sve, + data_processing_immediate: DataProcessingImmediate, + branch_exception_generating_system: BranchExceptionGeneratingSystem, + load_store: LoadStore, + data_processing_register: DataProcessingRegister, + data_processing_vector: DataProcessingVector, + }; + pub fn decode(inst: @This()) @This().Decoded { + return switch (inst.group.op1) { + 0b0000 => switch (inst.group.op0) { + 0b0 => .{ .reserved = inst.reserved }, + 0b1 => .{ .sme = inst.sme }, + }, + 0b0001 => .unallocated, + 0b0010 => .{ .sve = inst.sve }, + 0b0011 => .unallocated, + 0b1000, 0b1001 => .{ .data_processing_immediate = inst.data_processing_immediate }, + 0b1010, 0b1011 => .{ .branch_exception_generating_system = inst.branch_exception_generating_system }, + 0b0100, 0b0110, 0b1100, 0b1110 => .{ .load_store = inst.load_store }, + 0b0101, 0b1101 => .{ .data_processing_register = inst.data_processing_register }, + 0b0111, 0b1111 => .{ .data_processing_vector = inst.data_processing_vector }, + }; + } + + /// C6.2.1 ADC + pub fn adc(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_with_carry = .{ + .adc = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.2 ADCS + pub fn adcs(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_with_carry = .{ + .adcs = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.3 ADD (extended register) + /// C6.2.4 ADD (immediate) + /// C6.2.5 ADD (shifted register) + pub fn add(d: Register, n: Register, form: union(enum) { + extended_register_explicit: struct { + register: Register, + option: DataProcessingRegister.AddSubtractExtendedRegister.Option, + amount: DataProcessingRegister.AddSubtractExtendedRegister.Extend.Amount, + }, + extended_register: struct { register: Register, extend: DataProcessingRegister.AddSubtractExtendedRegister.Extend }, + immediate: u12, + shifted_immediate: struct { immediate: u12, lsl: DataProcessingImmediate.AddSubtractImmediate.Shift = .@"0" }, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.register.format.integer == extended_register_explicit.option.sf()); + return .{ .data_processing_register = .{ .add_subtract_extended_register = .{ + .add = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{ .sp = true }), + .imm3 = switch (extended_register_explicit.amount) { + 0...4 => |amount| amount, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.register.alias.encode(.{}), + .sf = sf, + }, + } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .register = extended_register.register, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtb, .uxth, .uxtw, .uxtx, .sxtb, .sxth, .sxtw, .sxtx => |amount| amount, + }, + } }, + .immediate => |immediate| continue :form .{ .shifted_immediate = .{ .immediate = immediate } }, + .shifted_immediate => |shifted_immediate| { + return .{ .data_processing_immediate = .{ .add_subtract_immediate = .{ + .add = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{ .sp = true }), + .imm12 = shifted_immediate.immediate, + .sh = shifted_immediate.lsl, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form if (d.alias == .sp or n.alias == .sp or register.alias == .sp) + .{ .extended_register = .{ .register = register, .extend = switch (sf) { + .word => .{ .uxtw = 0 }, + .doubleword => .{ .uxtx = 0 }, + } } } + else + .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_shifted_register = .{ + .add = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = switch (shifted_register_explicit.shift) { + .lsl, .lsr, .asr => |shift| shift, + .ror => unreachable, + }, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr => |amount| amount, + .ror => unreachable, + }, + } }, + } + } + /// C7.2.4 ADDP (scalar) + /// C7.2.5 ADDP (vector) + pub fn addp(d: Register, n: Register, form: union(enum) { + scalar, + vector: Register, + }) Instruction { + switch (form) { + .scalar => { + assert(d.format.scalar == .double and n.format.vector == .@"2d"); + return .{ .data_processing_vector = .{ .simd_scalar_pairwise = .{ + .addp = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .size = .double, + }, + } } }; + }, + .vector => |m| { + const arrangement = d.format.vector; + assert(arrangement != .@"1d" and n.format.vector == arrangement and m.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_three_same = .{ + .addp = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .Rm = m.alias.encode(.{ .V = true }), + .size = arrangement.elemSize(), + .Q = arrangement.size(), + }, + } } }; + }, + } + } + /// C6.2.7 ADDS (extended register) + /// C6.2.8 ADDS (immediate) + /// C6.2.9 ADDS (shifted register) + pub fn adds(d: Register, n: Register, form: union(enum) { + extended_register_explicit: struct { + register: Register, + option: DataProcessingRegister.AddSubtractExtendedRegister.Option, + amount: DataProcessingRegister.AddSubtractExtendedRegister.Extend.Amount, + }, + extended_register: struct { register: Register, extend: DataProcessingRegister.AddSubtractExtendedRegister.Extend }, + immediate: u12, + shifted_immediate: struct { immediate: u12, lsl: DataProcessingImmediate.AddSubtractImmediate.Shift = .@"0" }, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.register.format.integer == extended_register_explicit.option.sf()); + return .{ .data_processing_register = .{ .add_subtract_extended_register = .{ + .adds = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm3 = switch (extended_register_explicit.amount) { + 0...4 => |amount| amount, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.register.alias.encode(.{}), + .sf = sf, + }, + } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .register = extended_register.register, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtb, .uxth, .uxtw, .uxtx, .sxtb, .sxth, .sxtw, .sxtx => |amount| amount, + }, + } }, + .immediate => |immediate| continue :form .{ .shifted_immediate = .{ .immediate = immediate } }, + .shifted_immediate => |shifted_immediate| { + return .{ .data_processing_immediate = .{ .add_subtract_immediate = .{ + .adds = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm12 = shifted_immediate.immediate, + .sh = shifted_immediate.lsl, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form if (d.alias == .sp or n.alias == .sp or register.alias == .sp) + .{ .extended_register = .{ .register = register, .extend = switch (sf) { + .word => .{ .uxtw = 0 }, + .doubleword => .{ .uxtx = 0 }, + } } } + else + .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_shifted_register = .{ + .adds = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = switch (shifted_register_explicit.shift) { + .lsl, .lsr, .asr => |shift| shift, + .ror => unreachable, + }, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr => |amount| amount, + .ror => unreachable, + }, + } }, + } + } + /// C7.2.6 ADDV + pub fn addv(d: Register, n: Register) Instruction { + const arrangement = n.format.vector; + assert(arrangement.len() > 2 and d.format.scalar == arrangement.elemSize().toVectorSize()); + return .{ .data_processing_vector = .{ .simd_across_lanes = .{ + .addv = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .size = arrangement.elemSize(), + .Q = arrangement.size(), + }, + } } }; + } + /// C6.2.10 ADR + pub fn adr(d: Register, label: i21) Instruction { + assert(d.format.integer == .doubleword); + return .{ .data_processing_immediate = .{ .pc_relative_addressing = .{ + .adr = .{ + .Rd = d.alias.encode(.{}), + .immhi = @intCast(label >> 2), + .immlo = @truncate(@as(u21, @bitCast(label))), + }, + } } }; + } + /// C6.2.11 ADRP + pub fn adrp(d: Register, label: i33) Instruction { + assert(d.format.integer == .doubleword); + const imm: i21 = @intCast(@shrExact(label, 12)); + return .{ .data_processing_immediate = .{ .pc_relative_addressing = .{ + .adrp = .{ + .Rd = d.alias.encode(.{}), + .immhi = @intCast(imm >> 2), + .immlo = @truncate(@as(u21, @bitCast(imm))), + }, + } } }; + } + /// C6.2.12 AND (immediate) + /// C6.2.13 AND (shifted register) + /// C7.2.11 AND (vector) + pub fn @"and"(d: Register, n: Register, form: union(enum) { + immediate: DataProcessingImmediate.Bitmask, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + switch (d.format) { + else => unreachable, + .integer => |sf| { + assert(n.format.integer == sf); + form: switch (form) { + .immediate => |bitmask| { + assert(bitmask.validImmediate(sf)); + return .{ .data_processing_immediate = .{ .logical_immediate = .{ + .@"and" = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .@"and" = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + }, + .vector => |arrangement| { + const m = form.register; + assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_three_same = .{ + .@"and" = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .Rm = m.alias.encode(.{ .V = true }), + .Q = arrangement.size(), + }, + } } }; + }, + } + } + /// C6.2.14 ANDS (immediate) + /// C6.2.15 ANDS (shifted register) + pub fn ands(d: Register, n: Register, form: union(enum) { + immediate: DataProcessingImmediate.Bitmask, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .immediate => |bitmask| { + assert(bitmask.validImmediate(sf)); + return .{ .data_processing_immediate = .{ .logical_immediate = .{ + .ands = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .ands = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + } + /// C6.2.18 ASRV + pub fn asrv(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_two_source = .{ + .asrv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.25 B + pub fn b(label: i28) Instruction { + return .{ .branch_exception_generating_system = .{ .unconditional_branch_immediate = .{ + .b = .{ .imm26 = @intCast(@shrExact(label, 2)) }, + } } }; + } + /// C6.2.26 B.cond + pub fn @"b."(cond: ConditionCode, label: i21) Instruction { + return .{ .branch_exception_generating_system = .{ .conditional_branch_immediate = .{ + .b = .{ + .cond = cond, + .imm19 = @intCast(@shrExact(label, 2)), + }, + } } }; + } + /// C6.2.27 BC.cond + pub fn @"bc."(cond: ConditionCode, label: i21) Instruction { + return .{ .branch_exception_generating_system = .{ .conditional_branch_immediate = .{ + .bc = .{ + .cond = cond, + .imm19 = @intCast(@shrExact(label, 2)), + }, + } } }; + } + /// C6.2.30 BFM + pub fn bfm(d: Register, n: Register, bitmask: DataProcessingImmediate.Bitmask) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and bitmask.validBitfield(sf)); + return .{ .data_processing_immediate = .{ .bitfield = .{ + .bfm = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + } + /// C6.2.32 BIC (shifted register) + /// C7.2.20 BIC (vector, immediate) + /// C7.2.21 BIC (vector, register) + pub fn bic(d: Register, n: Register, form: union(enum) { + shifted_immediate: struct { immediate: u8, lsl: u5 = 0 }, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + switch (d.format) { + else => unreachable, + .integer => |sf| { + assert(n.format.integer == sf); + form: switch (form) { + else => unreachable, + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .bic = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + }, + .vector => |arrangement| switch (form) { + else => unreachable, + .shifted_immediate => |shifted_immediate| { + assert(n.alias == d.alias and n.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_modified_immediate = .{ + .bic = .{ + .Rd = d.alias.encode(.{ .V = true }), + .imm5 = @truncate(shifted_immediate.immediate >> 0), + .cmode = switch (arrangement) { + else => unreachable, + .@"4h", .@"8h" => @as(u3, 0b100) | + @as(u3, @as(u1, @intCast(@shrExact(shifted_immediate.lsl, 3)))) << 0, + .@"2s", .@"4s" => @as(u3, 0b000) | + @as(u3, @as(u2, @intCast(@shrExact(shifted_immediate.lsl, 3)))) << 0, + }, + .imm3 = @intCast(shifted_immediate.immediate >> 5), + .Q = arrangement.size(), + }, + } } }; + }, + .register => |m| { + assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_three_same = .{ + .bic = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .Rm = m.alias.encode(.{ .V = true }), + .Q = arrangement.size(), + }, + } } }; + }, + }, + } + } + /// C6.2.33 BICS (shifted register) + pub fn bics(d: Register, n: Register, form: union(enum) { + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .bics = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + } + /// C6.2.34 BL + pub fn bl(label: i28) Instruction { + return .{ .branch_exception_generating_system = .{ .unconditional_branch_immediate = .{ + .bl = .{ .imm26 = @intCast(@shrExact(label, 2)) }, + } } }; + } + /// C6.2.35 BLR + pub fn blr(n: Register) Instruction { + assert(n.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .unconditional_branch_register = .{ + .blr = .{ .Rn = n.alias.encode(.{}) }, + } } }; + } + /// C6.2.37 BR + pub fn br(n: Register) Instruction { + assert(n.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .unconditional_branch_register = .{ + .br = .{ .Rn = n.alias.encode(.{}) }, + } } }; + } + /// C6.2.40 BRK + pub fn brk(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .brk = .{ .imm16 = imm }, + } } }; + } + /// C6.2.46 CBNZ + pub fn cbnz(t: Register, label: i21) Instruction { + return .{ .branch_exception_generating_system = .{ .compare_branch_immediate = .{ + .cbnz = .{ + .Rt = t.alias.encode(.{}), + .imm19 = @intCast(@shrExact(label, 2)), + .sf = t.format.integer, + }, + } } }; + } + /// C6.2.47 CBZ + pub fn cbz(t: Register, label: i21) Instruction { + return .{ .branch_exception_generating_system = .{ .compare_branch_immediate = .{ + .cbz = .{ + .Rt = t.alias.encode(.{}), + .imm19 = @intCast(@shrExact(label, 2)), + .sf = t.format.integer, + }, + } } }; + } + /// C6.2.48 CCMN (immediate) + /// C6.2.49 CCMN (register) + pub fn ccmn( + n: Register, + form: union(enum) { register: Register, immediate: u5 }, + nzcv: DataProcessingRegister.Nzcv, + cond: ConditionCode, + ) Instruction { + const sf = n.format.integer; + switch (form) { + .register => |m| { + assert(m.format.integer == sf); + return .{ .data_processing_register = .{ .conditional_compare_register = .{ + .ccmn = .{ + .nzcv = nzcv, + .Rn = n.alias.encode(.{}), + .cond = cond, + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + }, + .immediate => |imm| return .{ .data_processing_register = .{ .conditional_compare_immediate = .{ + .ccmn = .{ + .nzcv = nzcv, + .Rn = n.alias.encode(.{}), + .cond = cond, + .imm5 = imm, + .sf = sf, + }, + } } }, + } + } + /// C6.2.50 CCMP (immediate) + /// C6.2.51 CCMP (register) + pub fn ccmp( + n: Register, + form: union(enum) { register: Register, immediate: u5 }, + nzcv: DataProcessingRegister.Nzcv, + cond: ConditionCode, + ) Instruction { + const sf = n.format.integer; + switch (form) { + .register => |m| { + assert(m.format.integer == sf); + return .{ .data_processing_register = .{ .conditional_compare_register = .{ + .ccmp = .{ + .nzcv = nzcv, + .Rn = n.alias.encode(.{}), + .cond = cond, + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + }, + .immediate => |imm| return .{ .data_processing_register = .{ .conditional_compare_immediate = .{ + .ccmp = .{ + .nzcv = nzcv, + .Rn = n.alias.encode(.{}), + .cond = cond, + .imm5 = imm, + .sf = sf, + }, + } } }, + } + } + /// C6.2.56 CLREX + pub fn clrex(imm: u4) Instruction { + return .{ .branch_exception_generating_system = .{ .barriers = .{ + .clrex = .{ + .CRm = imm, + }, + } } }; + } + /// C6.2.58 CLZ + pub fn clz(d: Register, n: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_one_source = .{ + .clz = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C7.2.38 CNT + pub fn cnt(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 = .{ + .cnt = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .size = arrangement.elemSize(), + .Q = arrangement.size(), + }, + } } }; + } + /// C6.2.103 CSEL + pub fn csel(d: Register, n: Register, m: Register, cond: ConditionCode) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .conditional_select = .{ + .csel = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .cond = cond, + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.106 CSINC + pub fn csinc(d: Register, n: Register, m: Register, cond: ConditionCode) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .conditional_select = .{ + .csinc = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .cond = cond, + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.107 CSINV + pub fn csinv(d: Register, n: Register, m: Register, cond: ConditionCode) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .conditional_select = .{ + .csinv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .cond = cond, + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.108 CSNEG + pub fn csneg(d: Register, n: Register, m: Register, cond: ConditionCode) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .conditional_select = .{ + .csneg = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .cond = cond, + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.110 DCPS1 + pub fn dcps1(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .dcps1 = .{ .imm16 = imm }, + } } }; + } + /// C6.2.111 DCPS2 + pub fn dcps2(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .dcps2 = .{ .imm16 = imm }, + } } }; + } + /// C6.2.112 DCPS3 + pub fn dcps3(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .dcps3 = .{ .imm16 = imm }, + } } }; + } + /// C6.2.116 DSB + pub fn dsb(option: BranchExceptionGeneratingSystem.Barriers.Option) Instruction { + return .{ .branch_exception_generating_system = .{ .barriers = .{ + .dsb = .{ + .CRm = option, + }, + } } }; + } + /// C6.2.118 EON (shifted register) + pub fn eon(d: Register, n: Register, form: union(enum) { + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .eon = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + } + /// C6.2.119 EOR (immediate) + /// C6.2.120 EOR (shifted register) + /// C7.2.41 EOR (vector) + pub fn eor(d: Register, n: Register, form: union(enum) { + immediate: DataProcessingImmediate.Bitmask, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + switch (d.format) { + else => unreachable, + .integer => |sf| { + assert(n.format.integer == sf); + form: switch (form) { + .immediate => |bitmask| { + assert(bitmask.validImmediate(sf)); + return .{ .data_processing_immediate = .{ .logical_immediate = .{ + .eor = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .eor = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + }, + .vector => |arrangement| { + const m = form.register; + assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_three_same = .{ + .eor = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .Rm = m.alias.encode(.{ .V = true }), + .Q = arrangement.size(), + }, + } } }; + }, + } + } + /// C6.2.124 EXTR + pub fn extr(d: Register, n: Register, m: Register, lsb: u6) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_immediate = .{ .extract = .{ + .extr = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imms = switch (sf) { + .word => @as(u5, @intCast(lsb)), + .doubleword => @as(u6, @intCast(lsb)), + }, + .Rm = m.alias.encode(.{}), + .N = sf, + .sf = sf, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// C7.2.50 FADD (scalar) + pub fn fadd(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fadd = .{ + .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, + }, + }, + } } }; + } + /// C7.2.66 FCMP + pub fn fcmp(n: Register, form: union(enum) { register: Register, zero }) Instruction { + const ftype = n.format.scalar; + switch (form) { + .register => |m| { + assert(m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_compare = .{ + .fcmp = .{ + .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, + }, + }, + } } }; + }, + .zero => return .{ .data_processing_vector = .{ .float_compare = .{ + .fcmp = .{ + .opc0 = .register, + .Rn = n.alias.encode(.{ .V = true }), + .Rm = @enumFromInt(0b00000), + .ftype = switch (ftype) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }, + } + } + /// C7.2.67 FCMPE + pub fn fcmpe(n: Register, form: union(enum) { register: Register, zero }) Instruction { + const ftype = n.format.scalar; + switch (form) { + .register => |m| { + assert(m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_compare = .{ + .fcmpe = .{ + .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, + }, + }, + } } }; + }, + .zero => return .{ .data_processing_vector = .{ .float_compare = .{ + .fcmpe = .{ + .opc0 = .zero, + .Rn = n.alias.encode(.{ .V = true }), + .Rm = @enumFromInt(0b00000), + .ftype = switch (ftype) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }, + } + } + /// C7.2.69 FCVT + pub fn fcvt(d: Register, n: Register) Instruction { + assert(d.format.scalar != n.format.scalar); + return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{ + .fcvt = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .opc = switch (d.format.scalar) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .ftype = switch (n.format.scalar) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = d.format.integer, + }, + } } }; + } + /// C7.2.98 FDIV (scalar) + pub fn fdiv(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fdiv = .{ + .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, + }, + }, + } } }; + } + /// C7.2.99 FJCVTZS + pub fn fjcvtzs(d: Register, n: Register) Instruction { + assert(d.format.integer == .word); + assert(n.format.scalar == .double); + return .{ .data_processing_vector = .{ .convert_float_integer = .{ + .fjcvtzs = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .V = true }), + }, + } } }; + } + /// C7.2.100 FMADD + pub fn fmadd(d: Register, n: Register, m: Register, a: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype and a.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_three_source = .{ + .fmadd = .{ + .Rd = d.alias.encode(.{ .V = true }), + .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, + }, + }, + } } }; + } + /// C7.2.102 FMAX (scalar) + pub fn fmax(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fmax = .{ + .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, + }, + }, + } } }; + } + /// C7.2.104 FMAXNM (scalar) + pub fn fmaxnm(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fmaxnm = .{ + .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, + }, + }, + } } }; + } + /// C7.2.112 FMIN (scalar) + pub fn fmin(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fmin = .{ + .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, + }, + }, + } } }; + } + /// C7.2.114 FMINNM (scalar) + pub fn fminnm(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fminnm = .{ + .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, + }, + }, + } } }; + } + /// C7.2.129 FMOV (vector, immediate) + /// C7.2.130 FMOV (register) + /// C7.2.131 FMOV (general) + /// C7.2.132 FMOV (scalar, immediate) + pub fn fmov(d: Register, form: union(enum) { immediate: f16, register: Register }) Instruction { + switch (form) { + .immediate => |immediate| { + const repr: std.math.FloatRepr(f16) = @bitCast(immediate); + const imm: u8 = @bitCast(@as(packed struct(u8) { + mantissa: u4, + exponent: i3, + sign: std.math.Sign, + }, .{ + .mantissa = @intCast(@shrExact(repr.mantissa, 6)), + .exponent = @intCast(repr.exponent.unbias() - 1), + .sign = repr.sign, + })); + switch (d.format) { + else => unreachable, + .scalar => |ftype| return .{ .data_processing_vector = .{ .float_immediate = .{ + .fmov = .{ + .Rd = d.alias.encode(.{ .V = true }), + .imm8 = imm, + .ftype = switch (ftype) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }, + .vector => |arrangement| { + assert(arrangement.len() > 1 and arrangement.elemSize() != .byte); + return .{ .data_processing_vector = .{ .simd_modified_immediate = .{ + .fmov = .{ + .Rd = d.alias.encode(.{ .V = true }), + .imm5 = @truncate(imm >> 0), + .imm3 = @intCast(imm >> 5), + .Q = arrangement.size(), + }, + } } }; + }, + } + }, + .register => |n| switch (d.format) { + else => unreachable, + .integer => |sf| switch (n.format) { + else => unreachable, + .scalar => |ftype| { + switch (ftype) { + else => unreachable, + .half => {}, + .single => assert(sf == .word), + .double => assert(sf == .doubleword), + } + return .{ .data_processing_vector = .{ .convert_float_integer = .{ + .fmov = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .V = true }), + .opcode = .float_to_integer, + .rmode = .@"0", + .ftype = switch (ftype) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = sf, + }, + } } }; + }, + .element => |element| return .{ .data_processing_vector = .{ .convert_float_integer = .{ + .fmov = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .V = true }), + .opcode = .float_to_integer, + .rmode = switch (element.index) { + else => unreachable, + 1 => .@"1", + }, + .ftype = switch (element.size) { + else => unreachable, + .double => .quad, + }, + .sf = sf, + }, + } } }, + }, + .scalar => |ftype| switch (n.format) { + else => unreachable, + .integer => { + const sf = n.format.integer; + switch (ftype) { + else => unreachable, + .half => {}, + .single => assert(sf == .word), + .double => assert(sf == .doubleword), + } + return .{ .data_processing_vector = .{ .convert_float_integer = .{ + .fmov = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{}), + .opcode = .integer_to_float, + .rmode = .@"0", + .ftype = switch (ftype) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + .sf = sf, + }, + } } }; + }, + .scalar => { + assert(n.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_one_source = .{ + .fmov = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .ftype = switch (ftype) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + }, + }, + .element => |element| switch (n.format) { + else => unreachable, + .integer => |sf| return .{ .data_processing_vector = .{ .convert_float_integer = .{ + .fmov = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{}), + .opcode = .integer_to_float, + .rmode = switch (element.index) { + else => unreachable, + 1 => .@"1", + }, + .ftype = switch (element.size) { + else => unreachable, + .double => .quad, + }, + .sf = sf, + }, + } } }, + }, + }, + } + } + /// C7.2.133 FMSUB + pub fn fmsub(d: Register, n: Register, m: Register, a: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype and a.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_three_source = .{ + .fmsub = .{ + .Rd = d.alias.encode(.{ .V = true }), + .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, + }, + }, + } } }; + } + /// C7.2.136 FMUL (scalar) + pub fn fmul(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fmul = .{ + .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, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// C7.2.141 FNMADD + pub fn fnmadd(d: Register, n: Register, m: Register, a: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype and a.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_three_source = .{ + .fnmadd = .{ + .Rd = d.alias.encode(.{ .V = true }), + .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, + }, + }, + } } }; + } + /// C7.2.142 FNMSUB + pub fn fnmsub(d: Register, n: Register, m: Register, a: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype and a.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_three_source = .{ + .fnmsub = .{ + .Rd = d.alias.encode(.{ .V = true }), + .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, + }, + }, + } } }; + } + /// C7.2.143 FNMUL (scalar) + pub fn fnmul(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fnmul = .{ + .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, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// 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) { + else => unreachable, + .single => .single, + .double => .double, + .half => .half, + }, + }, + } } }; + } + /// C7.2.174 FSUB (scalar) + pub fn fsub(d: Register, n: Register, m: Register) Instruction { + const ftype = d.format.scalar; + assert(n.format.scalar == ftype and m.format.scalar == ftype); + return .{ .data_processing_vector = .{ .float_data_processing_two_source = .{ + .fsub = .{ + .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, + }, + }, + } } }; + } + /// C6.2.126 HINT + pub fn hint(imm: u7) Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .group = .{ + .op2 = @truncate(imm >> 0), + .CRm = @intCast(imm >> 3), + }, + } } }; + } + /// C6.2.127 HLT + pub fn hlt(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .hlt = .{ .imm16 = imm }, + } } }; + } + /// C6.2.128 HVC + pub fn hvc(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .hvc = .{ .imm16 = imm }, + } } }; + } + /// C6.2.131 ISB + pub fn isb(option: BranchExceptionGeneratingSystem.Barriers.Option) Instruction { + return .{ .branch_exception_generating_system = .{ .barriers = .{ + .isb = .{ + .CRm = option, + }, + } } }; + } + /// C6.2.164 LDP + /// C7.2.190 LDP (SIMD&FP) + pub fn ldp(t1: Register, t2: Register, form: union(enum) { + post_index: struct { base: Register, index: i10 }, + pre_index: struct { base: Register, index: i10 }, + signed_offset: struct { base: Register, offset: i10 = 0 }, + base: Register, + }) Instruction { + switch (t1.format) { + else => unreachable, + .integer => |sf| { + assert(t2.format.integer == sf); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_post_indexed = .{ .integer = .{ + .ldp = .{ + .Rt = t1.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{}), + .imm7 = @intCast(@shrExact(post_index.index, @as(u2, 2) + @intFromEnum(sf))), + .sf = sf, + }, + } } } }; + }, + .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 = .{ + .ldp = .{ + .Rt = t1.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{}), + .imm7 = @intCast(@shrExact(pre_index.index, @as(u2, 2) + @intFromEnum(sf))), + .sf = sf, + }, + } } } }; + }, + .base => |base| continue :form .{ .signed_offset = .{ .base = base } }, + } + }, + .scalar => |vs| { + assert(t2.format.scalar == vs); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_post_indexed = .{ .vector = .{ + .ldp = .{ + .Rt = t1.alias.encode(.{ .V = true }), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{ .V = true }), + .imm7 = @intCast(@shrExact(post_index.index, @intFromEnum(vs))), + .opc = .encode(vs), + }, + } } } }; + }, + .signed_offset => |signed_offset| { + assert(signed_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_offset = .{ .vector = .{ + .ldp = .{ + .Rt = t1.alias.encode(.{ .V = true }), + .Rn = signed_offset.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{ .V = true }), + .imm7 = @intCast(@shrExact(signed_offset.offset, @intFromEnum(vs))), + .opc = .encode(vs), + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_pre_indexed = .{ .vector = .{ + .ldp = .{ + .Rt = t1.alias.encode(.{ .V = true }), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{ .V = true }), + .imm7 = @intCast(@shrExact(pre_index.index, @intFromEnum(vs))), + .opc = .encode(vs), + }, + } } } }; + }, + .base => |base| continue :form .{ .signed_offset = .{ .base = base } }, + } + }, + } + } + /// C6.2.166 LDR (immediate) + /// C6.2.167 LDR (literal) + /// C6.2.168 LDR (register) + /// C7.2.191 LDR (immediate, SIMD&FP) + /// C7.2.192 LDR (literal, SIMD&FP) + /// C7.2.193 LDR (register, SIMD&FP) + pub fn ldr(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u16 = 0 }, + base: Register, + literal: i21, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Option, + amount: LoadStore.RegisterRegisterOffset.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Extend, + }, + }) Instruction { + switch (t.format) { + else => unreachable, + .integer => |sf| form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .ldr = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + .sf = sf, + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .ldr = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + .sf = sf, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .ldr = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, @as(u2, 2) + @intFromEnum(sf))), + .sf = sf, + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .literal => |offset| return .{ .load_store = .{ .register_literal = .{ .integer = .{ + .ldr = .{ + .Rt = t.alias.encode(.{}), + .imm19 = @intCast(@shrExact(offset, 2)), + .sf = sf, + }, + } } } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .ldr = .{ + .Rt = t.alias.encode(.{}), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (sf) { + .word => switch (extended_register_explicit.amount) { + 0 => false, + 2 => true, + else => unreachable, + }, + .doubleword => switch (extended_register_explicit.amount) { + 0 => false, + 3 => true, + else => unreachable, + }, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + .sf = sf, + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + }, + .scalar => |vs| form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .vector = .{ + .ldr = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .vector = .{ + .ldr = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .vector = .{ + .ldr = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, @intFromEnum(vs))), + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .literal => |offset| return .{ .load_store = .{ .register_literal = .{ .vector = .{ + .ldr = .{ + .Rt = t.alias.encode(.{ .V = true }), + .imm19 = @intCast(@shrExact(offset, 2)), + .opc = .encode(vs), + }, + } } } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .vector = .{ + .ldr = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (vs) { + else => unreachable, + .byte => switch (extended_register_explicit.amount) { + 0 => false, + else => unreachable, + }, + .half => switch (extended_register_explicit.amount) { + 0 => false, + 1 => true, + else => unreachable, + }, + .single => switch (extended_register_explicit.amount) { + 0 => false, + 2 => true, + else => unreachable, + }, + .double => switch (extended_register_explicit.amount) { + 0 => false, + 3 => true, + else => unreachable, + }, + .quad => switch (extended_register_explicit.amount) { + 0 => false, + 4 => true, + else => unreachable, + }, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + }, + } + } + /// C6.2.170 LDRB (immediate) + /// C6.2.171 LDRB (register) + pub fn ldrb(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u12 = 0 }, + base: Register, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Option, + amount: LoadStore.RegisterRegisterOffset.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Extend, + }, + }) Instruction { + assert(t.format.integer == .word); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .ldrb = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .ldrb = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .ldrb = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = unsigned_offset.offset, + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .ldrb = .{ + .Rt = t.alias.encode(.{}), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (extended_register_explicit.amount) { + 0 => false, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + } + } + /// C6.2.172 LDRH (immediate) + /// C6.2.173 LDRH (register) + pub fn ldrh(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u13 = 0 }, + base: Register, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Option, + amount: LoadStore.RegisterRegisterOffset.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Extend, + }, + }) Instruction { + assert(t.format.integer == .word); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .ldrh = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .ldrh = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .ldrh = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, 1)), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .ldrh = .{ + .Rt = t.alias.encode(.{}), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (extended_register_explicit.amount) { + 0 => false, + 1 => true, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + } + } + /// C6.2.174 LDRSB (immediate) + /// C6.2.175 LDRSB (register) + pub fn ldrsb(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u12 = 0 }, + base: Register, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Option, + amount: LoadStore.RegisterRegisterOffset.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Extend, + }, + }) Instruction { + const sf = t.format.integer; + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .ldrsb = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .ldrsb = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .ldrsb = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = unsigned_offset.offset, + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .ldrsb = .{ + .Rt = t.alias.encode(.{}), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (extended_register_explicit.amount) { + 0 => false, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + } + } + /// C6.2.176 LDRSH (immediate) + /// C6.2.177 LDRSH (register) + pub fn ldrsh(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u13 = 0 }, + base: Register, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Option, + amount: LoadStore.RegisterRegisterOffset.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Extend, + }, + }) Instruction { + const sf = t.format.integer; + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .ldrsh = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .ldrsh = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .ldrsh = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, 1)), + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .ldrsh = .{ + .Rt = t.alias.encode(.{}), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (extended_register_explicit.amount) { + 0 => false, + 1 => true, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + .opc0 = ~@intFromEnum(sf), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + } + } + /// C6.2.178 LDRSW (immediate) + /// C6.2.179 LDRSW (literal) + /// C6.2.180 LDRSW (register) + pub fn ldrsw(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u14 = 0 }, + base: Register, + literal: i21, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Integer.Option, + amount: LoadStore.RegisterRegisterOffset.Integer.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Integer.Extend, + }, + }) Instruction { + assert(t.format.integer == .doubleword); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ + .ldrsw = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + }, + } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .ldrsw = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ + .ldrsw = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, 2)), + }, + } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .literal => |offset| return .{ .load_store = .{ .register_literal = .{ + .ldrsw = .{ + .Rt = t.alias.encode(.{}), + .imm19 = @intCast(@shrExact(offset, 2)), + }, + } } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .ldrsw = .{ + .Rt = t.alias.encode(.{}), + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (extended_register_explicit.amount) { + 0 => 0b0, + 2 => 0b1, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + } + } + /// C6.2.202 LDUR + /// C7.2.194 LDUR (SIMD&FP) + pub fn ldur(t: Register, n: Register, simm: i9) Instruction { + assert(n.format.integer == .doubleword); + switch (t.format) { + else => unreachable, + .integer => |sf| return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .ldur = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + .sf = sf, + }, + } } } }, + .scalar => |vs| return .{ .load_store = .{ .register_unscaled_immediate = .{ .vector = .{ + .ldur = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }, + } + } + /// C6.2.203 LDURB + pub fn ldurb(t: Register, n: Register, simm: i9) Instruction { + assert(t.format.integer == .word and n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .ldurb = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + }, + } } } }; + } + /// C6.2.204 LDURH + pub fn ldurh(t: Register, n: Register, simm: i9) Instruction { + assert(t.format.integer == .word and n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .ldurh = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + }, + } } } }; + } + /// C6.2.205 LDURSB + pub fn ldursb(t: Register, n: Register, simm: i9) Instruction { + assert(n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .ldursb = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + .opc0 = ~@intFromEnum(t.format.integer), + }, + } } } }; + } + /// C6.2.206 LDURSH + pub fn ldursh(t: Register, n: Register, simm: i9) Instruction { + assert(n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .ldursh = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + .opc0 = ~@intFromEnum(t.format.integer), + }, + } } } }; + } + /// C6.2.207 LDURSW + pub fn ldursw(t: Register, n: Register, simm: i9) Instruction { + assert(t.format.integer == .doubleword and n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .ldursw = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + }, + } } } }; + } + /// C6.2.214 LSLV + pub fn lslv(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_two_source = .{ + .lslv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.217 LSRV + pub fn lsrv(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_two_source = .{ + .lsrv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.218 MADD + pub fn madd(d: Register, n: Register, m: Register, a: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf and a.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .madd = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Ra = a.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C7.2.204 MOVI + pub fn movi(d: Register, imm8: u8, shift: union(enum) { lsl: u5, msl: u5, replicate }) Instruction { + const arrangement = switch (d.format) { + else => unreachable, + .scalar => |vs| switch (vs) { + else => unreachable, + .double => .@"1d", + }, + .vector => |arrangement| switch (arrangement) { + .@"1d" => unreachable, + else => arrangement, + }, + }; + return .{ .data_processing_vector = .{ .simd_modified_immediate = .{ + .movi = .{ + .Rd = d.alias.encode(.{ .V = true }), + .imm5 = @truncate(imm8 >> 0), + .cmode = switch (shift) { + .lsl => |amount| switch (arrangement) { + else => unreachable, + .@"8b", .@"16b" => @as(u4, 0b1110) | + @as(u4, @as(u0, @intCast(@shrExact(amount, 3)))) << 1, + .@"4h", .@"8h" => @as(u4, 0b1000) | + @as(u4, @as(u1, @intCast(@shrExact(amount, 3)))) << 1, + .@"2s", .@"4s" => @as(u4, 0b0000) | + @as(u4, @as(u2, @intCast(@shrExact(amount, 3)))) << 1, + }, + .msl => |amount| switch (arrangement) { + else => unreachable, + .@"2s", .@"4s" => @as(u4, 0b1100) | + @as(u4, @as(u1, @intCast(@shrExact(amount, 3) - 1))) << 0, + }, + .replicate => switch (arrangement) { + else => unreachable, + .@"1d", .@"2d" => 0b1110, + }, + }, + .imm3 = @intCast(imm8 >> 5), + .op = switch (shift) { + .lsl, .msl => 0b0, + .replicate => 0b1, + }, + .Q = arrangement.size(), + }, + } } }; + } + /// C6.2.225 MOVK + pub fn movk( + d: Register, + imm: u16, + shift: struct { lsl: DataProcessingImmediate.MoveWideImmediate.Hw = .@"0" }, + ) Instruction { + const sf = d.format.integer; + assert(sf == .doubleword or shift.lsl.sf() == .word); + return .{ .data_processing_immediate = .{ .move_wide_immediate = .{ + .movk = .{ + .Rd = d.alias.encode(.{}), + .imm16 = imm, + .hw = shift.lsl, + .sf = sf, + }, + } } }; + } + /// C6.2.226 MOVN + pub fn movn( + d: Register, + imm: u16, + shift: struct { lsl: DataProcessingImmediate.MoveWideImmediate.Hw = .@"0" }, + ) Instruction { + const sf = d.format.integer; + assert(sf == .doubleword or shift.lsl.sf() == .word); + return .{ .data_processing_immediate = .{ .move_wide_immediate = .{ + .movn = .{ + .Rd = d.alias.encode(.{}), + .imm16 = imm, + .hw = shift.lsl, + .sf = sf, + }, + } } }; + } + /// C6.2.227 MOVZ + pub fn movz( + d: Register, + imm: u16, + shift: struct { lsl: DataProcessingImmediate.MoveWideImmediate.Hw = .@"0" }, + ) Instruction { + const sf = d.format.integer; + assert(sf == .doubleword or shift.lsl.sf() == .word); + return .{ .data_processing_immediate = .{ .move_wide_immediate = .{ + .movz = .{ + .Rd = d.alias.encode(.{}), + .imm16 = imm, + .hw = shift.lsl, + .sf = sf, + }, + } } }; + } + /// C6.2.228 MRS + pub fn mrs(t: Register, op0: u2, op1: u3, n: u4, m: u4, op2: u3) Instruction { + assert(t.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .system_register_move = .{ + .mrs = .{ + .Rt = t.alias.encode(.{}), + .op2 = op2, + .CRm = m, + .CRn = n, + .op1 = op1, + .o0 = @intCast(op0 - 0b10), + }, + } } }; + } + /// C6.2.230 MSR (register) + pub fn msr(op0: u2, op1: u3, n: u4, m: u4, op2: u3, t: Register) Instruction { + assert(t.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .system_register_move = .{ + .msr = .{ + .Rt = t.alias.encode(.{}), + .op2 = op2, + .CRm = m, + .CRn = n, + .op1 = op1, + .o0 = @intCast(op0 - 0b10), + }, + } } }; + } + /// C6.2.231 MSUB + pub fn msub(d: Register, n: Register, m: Register, a: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf and a.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .msub = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Ra = a.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.238 NOP + pub fn nop() Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .nop = .{}, + } } }; + } + /// C6.2.239 ORN (shifted register) + /// C7.2.211 ORN (vector) + pub fn orn(d: Register, n: Register, form: union(enum) { + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + switch (d.format) { + else => unreachable, + .integer => |sf| { + assert(n.format.integer == sf); + form: switch (form) { + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .orn = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + }, + .vector => |arrangement| { + const m = form.register; + assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_three_same = .{ + .orn = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .Rm = m.alias.encode(.{ .V = true }), + .Q = arrangement.size(), + }, + } } }; + }, + } + } + /// C6.2.240 ORR (immediate) + /// C6.2.241 ORR (shifted register) + /// C7.2.212 ORR (vector, immediate) + /// C7.2.213 ORR (vector, register) + pub fn orr(d: Register, n: Register, form: union(enum) { + immediate: DataProcessingImmediate.Bitmask, + shifted_immediate: struct { immediate: u8, lsl: u5 = 0 }, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + switch (d.format) { + else => unreachable, + .integer => |sf| { + assert(n.format.integer == sf); + form: switch (form) { + .immediate => |bitmask| { + assert(bitmask.validImmediate(sf)); + return .{ .data_processing_immediate = .{ .logical_immediate = .{ + .orr = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + }, + .shifted_immediate => unreachable, + .register => |register| continue :form .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .logical_shifted_register = .{ + .orr = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = shifted_register_explicit.shift, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr, .ror => |amount| amount, + }, + } }, + } + }, + .vector => |arrangement| switch (form) { + else => unreachable, + .shifted_immediate => |shifted_immediate| { + assert(n.alias == d.alias and n.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_modified_immediate = .{ + .orr = .{ + .Rd = d.alias.encode(.{ .V = true }), + .imm5 = @truncate(shifted_immediate.immediate >> 0), + .cmode = switch (arrangement) { + else => unreachable, + .@"4h", .@"8h" => @as(u3, 0b100) | + @as(u3, @as(u1, @intCast(@shrExact(shifted_immediate.lsl, 3)))) << 0, + .@"2s", .@"4s" => @as(u3, 0b000) | + @as(u3, @as(u2, @intCast(@shrExact(shifted_immediate.lsl, 3)))) << 0, + }, + .imm3 = @intCast(shifted_immediate.immediate >> 5), + .Q = arrangement.size(), + }, + } } }; + }, + .register => |m| { + assert(arrangement.elemSize() == .byte and n.format.vector == arrangement and m.format.vector == arrangement); + return .{ .data_processing_vector = .{ .simd_three_same = .{ + .orr = .{ + .Rd = d.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .V = true }), + .Rm = m.alias.encode(.{ .V = true }), + .Q = arrangement.size(), + }, + } } }; + }, + }, + } + } + /// C6.2.247 PRFM (immediate) + /// C6.2.248 PRFM (literal) + /// C6.2.249 PRFM (register) + pub fn prfm(prfop: LoadStore.PrfOp, form: union(enum) { + unsigned_offset: struct { base: Register, offset: u15 = 0 }, + base: Register, + literal: i21, + extended_register_explicit: struct { + base: Register, + index: Register, + option: LoadStore.RegisterRegisterOffset.Option, + amount: LoadStore.RegisterRegisterOffset.Extend.Amount, + }, + extended_register: struct { + base: Register, + index: Register, + extend: LoadStore.RegisterRegisterOffset.Extend, + }, + }) Instruction { + form: switch (form) { + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .prfm = .{ + .prfop = prfop, + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, 3)), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + .literal => |offset| return .{ .load_store = .{ .register_literal = .{ .integer = .{ + .prfm = .{ + .prfop = prfop, + .imm19 = @intCast(@shrExact(offset, 2)), + }, + } } } }, + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.base.format.integer == .doubleword and + extended_register_explicit.index.format.integer == extended_register_explicit.option.sf()); + return .{ .load_store = .{ .register_register_offset = .{ .integer = .{ + .prfm = .{ + .prfop = prfop, + .Rn = extended_register_explicit.base.alias.encode(.{ .sp = true }), + .S = switch (extended_register_explicit.amount) { + 0 => false, + 3 => true, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.index.alias.encode(.{}), + }, + } } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .base = extended_register.base, + .index = extended_register.index, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtw, .lsl, .sxtw, .sxtx => |amount| amount, + }, + } }, + } + } + /// C6.2.253 RBIT + pub fn rbit(d: Register, n: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_one_source = .{ + .rbit = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.254 RET + pub fn ret(n: Register) Instruction { + assert(n.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .unconditional_branch_register = .{ + .ret = .{ .Rn = n.alias.encode(.{}) }, + } } }; + } + /// C6.2.256 REV + pub fn rev(d: Register, n: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_one_source = .{ + .rev = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .opc0 = sf, + .sf = sf, + }, + } } }; + } + /// C6.2.257 REV16 + pub fn rev16(d: Register, n: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_one_source = .{ + .rev16 = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.258 REV32 + pub fn rev32(d: Register, n: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_one_source = .{ + .rev32 = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + }, + } } }; + } + /// C6.2.263 RORV + pub fn rorv(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_two_source = .{ + .rorv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.264 SB + pub fn sb() Instruction { + return .{ .branch_exception_generating_system = .{ .barriers = .{ + .sb = .{}, + } } }; + } + /// C6.2.265 SBC + pub fn sbc(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_with_carry = .{ + .sbc = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.266 SBCS + pub fn sbcs(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_with_carry = .{ + .sbcs = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.268 SBFM + pub fn sbfm(d: Register, n: Register, bitmask: DataProcessingImmediate.Bitmask) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and bitmask.validBitfield(sf)); + return .{ .data_processing_immediate = .{ .bitfield = .{ + .sbfm = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + } + /// 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, + }, + .sf = n.format.integer, + }, + } } }; + } + /// C6.2.270 SDIV + pub fn sdiv(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_two_source = .{ + .sdiv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.280 SEV + pub fn sev() Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .sev = .{}, + } } }; + } + /// C6.2.281 SEVL + pub fn sevl() Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .sevl = .{}, + } } }; + } + /// C6.2.282 SMADDL + pub fn smaddl(d: Register, n: Register, m: Register, a: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .word and m.format.integer == .word and a.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .smaddl = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Ra = a.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + }, + } } }; + } + /// C6.2.283 SMC + pub fn smc(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .smc = .{ .imm16 = imm }, + } } }; + } + /// C7.2.279 SMOV + pub fn smov(d: Register, n: Register) Instruction { + const sf = d.format.integer; + const vs = n.format.element.size; + switch (vs) { + else => unreachable, + .byte, .half => {}, + .single => assert(sf == .doubleword), + } + return .{ .data_processing_vector = .{ .simd_copy = .{ + .smov = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .V = true }), + .imm5 = switch (vs) { + else => unreachable, + .byte => @as(u5, @as(u4, @intCast(n.format.element.index))) << 1 | @as(u5, 0b1) << 0, + .half => @as(u5, @as(u3, @intCast(n.format.element.index))) << 2 | @as(u5, 0b10) << 0, + .single => @as(u5, @as(u2, @intCast(n.format.element.index))) << 3 | @as(u5, 0b100) << 0, + }, + .Q = sf, + }, + } } }; + } + /// C6.2.287 SMSUBL + pub fn smsubl(d: Register, n: Register, m: Register, a: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .word and m.format.integer == .word and a.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .smsubl = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Ra = a.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + }, + } } }; + } + /// C6.2.288 SMULH + pub fn smulh(d: Register, n: Register, m: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .doubleword and m.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .smulh = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + }, + } } }; + } + /// C6.2.321 STP + /// C7.2.330 STP (SIMD&FP) + pub fn stp(t1: Register, t2: Register, form: union(enum) { + post_index: struct { base: Register, index: i10 }, + pre_index: struct { base: Register, index: i10 }, + signed_offset: struct { base: Register, offset: i10 = 0 }, + base: Register, + }) Instruction { + switch (t1.format) { + else => unreachable, + .integer => |sf| { + assert(t2.format.integer == sf); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_post_indexed = .{ .integer = .{ + .stp = .{ + .Rt = t1.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{}), + .imm7 = @intCast(@shrExact(post_index.index, @as(u2, 2) + @intFromEnum(sf))), + .sf = sf, + }, + } } } }; + }, + .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 = .{ + .stp = .{ + .Rt = t1.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{}), + .imm7 = @intCast(@shrExact(pre_index.index, @as(u2, 2) + @intFromEnum(sf))), + .sf = sf, + }, + } } } }; + }, + .base => |base| continue :form .{ .signed_offset = .{ .base = base } }, + } + }, + .scalar => |vs| { + assert(t2.format.scalar == vs); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_post_indexed = .{ .vector = .{ + .stp = .{ + .Rt = t1.alias.encode(.{ .V = true }), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{ .V = true }), + .imm7 = @intCast(@shrExact(post_index.index, @intFromEnum(vs))), + .opc = .encode(vs), + }, + } } } }; + }, + .signed_offset => |signed_offset| { + assert(signed_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_offset = .{ .vector = .{ + .stp = .{ + .Rt = t1.alias.encode(.{ .V = true }), + .Rn = signed_offset.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{ .V = true }), + .imm7 = @intCast(@shrExact(signed_offset.offset, @intFromEnum(vs))), + .opc = .encode(vs), + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_pair_pre_indexed = .{ .vector = .{ + .stp = .{ + .Rt = t1.alias.encode(.{ .V = true }), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .Rt2 = t2.alias.encode(.{ .V = true }), + .imm7 = @intCast(@shrExact(pre_index.index, @intFromEnum(vs))), + .opc = .encode(vs), + }, + } } } }; + }, + .base => |base| continue :form .{ .signed_offset = .{ .base = base } }, + } + }, + } + } + /// C6.2.322 STR (immediate) + /// C7.2.331 STR (immediate, SIMD&FP) + pub fn str(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u16 = 0 }, + base: Register, + }) Instruction { + switch (t.format) { + else => unreachable, + .integer => |sf| form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .str = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + .sf = sf, + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .str = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + .sf = sf, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .str = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, @as(u2, 2) + @intFromEnum(sf))), + .sf = sf, + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + }, + .scalar => |vs| form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .vector = .{ + .str = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .vector = .{ + .str = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .vector = .{ + .str = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, @intFromEnum(vs))), + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + }, + } + } + /// C6.2.324 STRB (immediate) + pub fn strb(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u12 = 0 }, + base: Register, + }) Instruction { + assert(t.format.integer == .word); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .strb = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .strb = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .strb = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = unsigned_offset.offset, + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + } + } + /// C6.2.326 STRH (immediate) + pub fn strh(t: Register, form: union(enum) { + post_index: struct { base: Register, index: i9 }, + pre_index: struct { base: Register, index: i9 }, + unsigned_offset: struct { base: Register, offset: u13 = 0 }, + base: Register, + }) Instruction { + assert(t.format.integer == .word); + form: switch (form) { + .post_index => |post_index| { + assert(post_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_post_indexed = .{ .integer = .{ + .strh = .{ + .Rt = t.alias.encode(.{}), + .Rn = post_index.base.alias.encode(.{ .sp = true }), + .imm9 = post_index.index, + }, + } } } }; + }, + .pre_index => |pre_index| { + assert(pre_index.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_immediate_pre_indexed = .{ .integer = .{ + .strh = .{ + .Rt = t.alias.encode(.{}), + .Rn = pre_index.base.alias.encode(.{ .sp = true }), + .imm9 = pre_index.index, + }, + } } } }; + }, + .unsigned_offset => |unsigned_offset| { + assert(unsigned_offset.base.format.integer == .doubleword); + return .{ .load_store = .{ .register_unsigned_immediate = .{ .integer = .{ + .strh = .{ + .Rt = t.alias.encode(.{}), + .Rn = unsigned_offset.base.alias.encode(.{ .sp = true }), + .imm12 = @intCast(@shrExact(unsigned_offset.offset, 1)), + }, + } } } }; + }, + .base => |base| continue :form .{ .unsigned_offset = .{ .base = base } }, + } + } + /// C6.2.346 STUR + /// C7.2.333 STUR (SIMD&FP) + pub fn stur(t: Register, n: Register, simm: i9) Instruction { + assert(n.format.integer == .doubleword); + switch (t.format) { + else => unreachable, + .integer => |sf| return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .stur = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + .sf = sf, + }, + } } } }, + .scalar => |vs| return .{ .load_store = .{ .register_unscaled_immediate = .{ .vector = .{ + .stur = .{ + .Rt = t.alias.encode(.{ .V = true }), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + .opc1 = .encode(vs), + .size = .encode(vs), + }, + } } } }, + } + } + /// C6.2.347 STURB + pub fn sturb(t: Register, n: Register, simm: i9) Instruction { + assert(t.format.integer == .word and n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .sturb = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + }, + } } } }; + } + /// C6.2.348 STURH + pub fn sturh(t: Register, n: Register, simm: i9) Instruction { + assert(t.format.integer == .word and n.format.integer == .doubleword); + return .{ .load_store = .{ .register_unscaled_immediate = .{ .integer = .{ + .sturh = .{ + .Rt = t.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm9 = simm, + }, + } } } }; + } + /// C6.2.356 SUB (extended register) + /// C6.2.357 SUB (immediate) + /// C6.2.358 SUB (shifted register) + pub fn sub(d: Register, n: Register, form: union(enum) { + extended_register_explicit: struct { + register: Register, + option: DataProcessingRegister.AddSubtractExtendedRegister.Option, + amount: DataProcessingRegister.AddSubtractExtendedRegister.Extend.Amount, + }, + extended_register: struct { register: Register, extend: DataProcessingRegister.AddSubtractExtendedRegister.Extend }, + immediate: u12, + shifted_immediate: struct { immediate: u12, lsl: DataProcessingImmediate.AddSubtractImmediate.Shift = .@"0" }, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.register.format.integer == extended_register_explicit.option.sf()); + return .{ .data_processing_register = .{ .add_subtract_extended_register = .{ + .sub = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{ .sp = true }), + .imm3 = switch (extended_register_explicit.amount) { + 0...4 => |amount| amount, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.register.alias.encode(.{}), + .sf = sf, + }, + } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .register = extended_register.register, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtb, .uxth, .uxtw, .uxtx, .sxtb, .sxth, .sxtw, .sxtx => |amount| amount, + }, + } }, + .immediate => |immediate| continue :form .{ .shifted_immediate = .{ .immediate = immediate } }, + .shifted_immediate => |shifted_immediate| { + return .{ .data_processing_immediate = .{ .add_subtract_immediate = .{ + .sub = .{ + .Rd = d.alias.encode(.{ .sp = true }), + .Rn = n.alias.encode(.{ .sp = true }), + .imm12 = shifted_immediate.immediate, + .sh = shifted_immediate.lsl, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form if (d.alias == .sp or n.alias == .sp or register.alias == .sp) + .{ .extended_register = .{ .register = register, .extend = switch (sf) { + .word => .{ .uxtw = 0 }, + .doubleword => .{ .uxtx = 0 }, + } } } + else + .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_shifted_register = .{ + .sub = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = switch (shifted_register_explicit.shift) { + .lsl, .lsr, .asr => |shift| shift, + .ror => unreachable, + }, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr => |amount| amount, + .ror => unreachable, + }, + } }, + } + } + /// C6.2.362 SUBS (extended register) + /// C6.2.363 SUBS (immediate) + /// C6.2.364 SUBS (shifted register) + pub fn subs(d: Register, n: Register, form: union(enum) { + extended_register_explicit: struct { + register: Register, + option: DataProcessingRegister.AddSubtractExtendedRegister.Option, + amount: DataProcessingRegister.AddSubtractExtendedRegister.Extend.Amount, + }, + extended_register: struct { register: Register, extend: DataProcessingRegister.AddSubtractExtendedRegister.Extend }, + immediate: u12, + shifted_immediate: struct { immediate: u12, lsl: DataProcessingImmediate.AddSubtractImmediate.Shift = .@"0" }, + register: Register, + shifted_register_explicit: struct { register: Register, shift: DataProcessingRegister.Shift.Op, amount: u6 }, + shifted_register: struct { register: Register, shift: DataProcessingRegister.Shift = .none }, + }) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf); + form: switch (form) { + .extended_register_explicit => |extended_register_explicit| { + assert(extended_register_explicit.register.format.integer == extended_register_explicit.option.sf()); + return .{ .data_processing_register = .{ .add_subtract_extended_register = .{ + .subs = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm3 = switch (extended_register_explicit.amount) { + 0...4 => |amount| amount, + else => unreachable, + }, + .option = extended_register_explicit.option, + .Rm = extended_register_explicit.register.alias.encode(.{}), + .sf = sf, + }, + } } }; + }, + .extended_register => |extended_register| continue :form .{ .extended_register_explicit = .{ + .register = extended_register.register, + .option = extended_register.extend, + .amount = switch (extended_register.extend) { + .uxtb, .uxth, .uxtw, .uxtx, .sxtb, .sxth, .sxtw, .sxtx => |amount| amount, + }, + } }, + .immediate => |immediate| continue :form .{ .shifted_immediate = .{ .immediate = immediate } }, + .shifted_immediate => |shifted_immediate| { + return .{ .data_processing_immediate = .{ .add_subtract_immediate = .{ + .subs = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .sp = true }), + .imm12 = shifted_immediate.immediate, + .sh = shifted_immediate.lsl, + .sf = sf, + }, + } } }; + }, + .register => |register| continue :form if (d.alias == .sp or n.alias == .sp or register.alias == .sp) + .{ .extended_register = .{ .register = register, .extend = switch (sf) { + .word => .{ .uxtw = 0 }, + .doubleword => .{ .uxtx = 0 }, + } } } + else + .{ .shifted_register = .{ .register = register } }, + .shifted_register_explicit => |shifted_register_explicit| { + assert(shifted_register_explicit.register.format.integer == sf); + return .{ .data_processing_register = .{ .add_subtract_shifted_register = .{ + .subs = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm6 = switch (sf) { + .word => @as(u5, @intCast(shifted_register_explicit.amount)), + .doubleword => @as(u6, @intCast(shifted_register_explicit.amount)), + }, + .Rm = shifted_register_explicit.register.alias.encode(.{}), + .shift = switch (shifted_register_explicit.shift) { + .lsl, .lsr, .asr => |shift| shift, + .ror => unreachable, + }, + .sf = sf, + }, + } } }; + }, + .shifted_register => |shifted_register| continue :form .{ .shifted_register_explicit = .{ + .register = shifted_register.register, + .shift = shifted_register.shift, + .amount = switch (shifted_register.shift) { + .lsl, .lsr, .asr => |amount| amount, + .ror => unreachable, + }, + } }, + } + } + /// C6.2.365 SVC + pub fn svc(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .svc = .{ .imm16 = imm }, + } } }; + } + /// C6.2.372 SYS + pub fn sys(op1: u3, n: u4, m: u4, op2: u3, t: Register) Instruction { + assert(t.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .system = .{ + .sys = .{ + .Rt = t.alias.encode(.{}), + .op2 = op2, + .CRm = m, + .CRn = n, + .op1 = op1, + }, + } } }; + } + /// C6.2.373 SYSL + pub fn sysl(t: Register, op1: u3, n: u4, m: u4, op2: u3) Instruction { + assert(t.format.integer == .doubleword); + return .{ .branch_exception_generating_system = .{ .system = .{ + .sysl = .{ + .Rt = t.alias.encode(.{}), + .op2 = op2, + .CRm = m, + .CRn = n, + .op1 = op1, + }, + } } }; + } + /// C6.2.374 TBNZ + pub fn tbnz(t: Register, imm: u6, label: i16) Instruction { + return .{ .branch_exception_generating_system = .{ .test_branch_immediate = .{ + .tbnz = .{ + .Rt = t.alias.encode(.{}), + .imm14 = @intCast(@shrExact(label, 2)), + .b40 = @truncate(switch (t.format.integer) { + .word => @as(u5, @intCast(imm)), + .doubleword => imm, + }), + .b5 = @intCast(imm >> 5), + }, + } } }; + } + /// C6.2.375 TBZ + pub fn tbz(t: Register, imm: u6, label: i16) Instruction { + return .{ .branch_exception_generating_system = .{ .test_branch_immediate = .{ + .tbz = .{ + .Rt = t.alias.encode(.{}), + .imm14 = @intCast(@shrExact(label, 2)), + .b40 = @truncate(switch (t.format.integer) { + .word => @as(u5, @intCast(imm)), + .doubleword => imm, + }), + .b5 = @intCast(imm >> 5), + }, + } } }; + } + /// C6.2.376 TCANCEL + pub fn tcancel(imm: u16) Instruction { + return .{ .branch_exception_generating_system = .{ .exception_generating = .{ + .tcancel = .{ .imm16 = imm }, + } } }; + } + /// C6.2.385 UBFM + pub fn ubfm(d: Register, n: Register, bitmask: DataProcessingImmediate.Bitmask) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and bitmask.validBitfield(sf)); + return .{ .data_processing_immediate = .{ .bitfield = .{ + .ubfm = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .imm = bitmask, + .sf = sf, + }, + } } }; + } + /// 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, + }, + .sf = n.format.integer, + }, + } } }; + } + /// C6.2.387 UDF + pub fn udf(imm: u16) Instruction { + return .{ .reserved = .{ + .udf = .{ .imm16 = imm }, + } }; + } + /// C6.2.388 UDIV + pub fn udiv(d: Register, n: Register, m: Register) Instruction { + const sf = d.format.integer; + assert(n.format.integer == sf and m.format.integer == sf); + return .{ .data_processing_register = .{ .data_processing_two_source = .{ + .udiv = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + .sf = sf, + }, + } } }; + } + /// C6.2.389 UMADDL + pub fn umaddl(d: Register, n: Register, m: Register, a: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .word and m.format.integer == .word and a.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .umaddl = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Ra = a.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + }, + } } }; + } + /// C6.2.391 UMSUBL + pub fn umsubl(d: Register, n: Register, m: Register, a: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .word and m.format.integer == .word and a.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .umsubl = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Ra = a.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + }, + } } }; + } + /// C7.2.371 UMOV + pub fn umov(d: Register, n: Register) Instruction { + const sf = d.format.integer; + const vs = n.format.element.size; + switch (vs) { + else => unreachable, + .byte, .half, .single => assert(sf == .word), + .double => assert(sf == .doubleword), + } + return .{ .data_processing_vector = .{ .simd_copy = .{ + .umov = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{ .V = true }), + .imm5 = switch (vs) { + else => unreachable, + .byte => @as(u5, @as(u4, @intCast(n.format.element.index))) << 1 | @as(u5, 0b1) << 0, + .half => @as(u5, @as(u3, @intCast(n.format.element.index))) << 2 | @as(u5, 0b10) << 0, + .single => @as(u5, @as(u2, @intCast(n.format.element.index))) << 3 | @as(u5, 0b100) << 0, + .double => @as(u5, @as(u1, @intCast(n.format.element.index))) << 4 | @as(u5, 0b1000) << 0, + }, + .Q = sf, + }, + } } }; + } + /// C6.2.392 UMULH + pub fn umulh(d: Register, n: Register, m: Register) Instruction { + assert(d.format.integer == .doubleword and n.format.integer == .doubleword and m.format.integer == .doubleword); + return .{ .data_processing_register = .{ .data_processing_three_source = .{ + .umulh = .{ + .Rd = d.alias.encode(.{}), + .Rn = n.alias.encode(.{}), + .Rm = m.alias.encode(.{}), + }, + } } }; + } + /// C6.2.396 WFE + pub fn wfe() Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .wfe = .{}, + } } }; + } + /// C6.2.398 WFI + pub fn wfi() Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .wfi = .{}, + } } }; + } + /// C6.2.402 YIELD + pub fn yield() Instruction { + return .{ .branch_exception_generating_system = .{ .hints = .{ + .yield = .{}, + } } }; + } + + pub const size = @divExact(@bitSizeOf(Backing), 8); + pub const Backing = u32; + pub fn read(mem: *const [size]u8) Instruction { + return @bitCast(std.mem.readInt(Backing, mem, .little)); + } + pub fn write(inst: Instruction, mem: *[size]u8) void { + std.mem.writeInt(Backing, mem, @bitCast(inst), .little); + } + + pub fn format(inst: Instruction, writer: *std.Io.Writer) std.Io.Writer.Error!void { + const dis: aarch64.Disassemble = .{}; + try dis.printInstruction(inst, writer); + } + + comptime { + @setEvalBranchQuota(68_000); + verify(@typeName(Instruction), Instruction); + } + fn verify(name: []const u8, Type: type) void { + switch (@typeInfo(Type)) { + .@"union" => |info| { + if (info.layout != .@"packed" or @bitSizeOf(Type) != @bitSizeOf(Backing)) { + @compileLog(name ++ " should have u32 abi"); + } + for (info.fields) |field| verify(name ++ "." ++ field.name, field.type); + }, + .@"struct" => |info| { + if (info.layout != .@"packed" or info.backing_integer != Backing) { + @compileLog(name ++ " should have u32 abi"); + } + var bit_offset = 0; + for (info.fields) |field| { + if (std.mem.startsWith(u8, field.name, "encoded")) { + if (if (std.fmt.parseInt(u5, field.name["encoded".len..], 10)) |encoded_bit_offset| encoded_bit_offset != bit_offset else |_| true) { + @compileError(std.fmt.comptimePrint("{s}.{s} should be named encoded{d}", .{ name, field.name, bit_offset })); + } + if (field.default_value_ptr != null) { + @compileError(std.fmt.comptimePrint("{s}.{s} should be named decoded{d}", .{ name, field.name, bit_offset })); + } + } else if (std.mem.startsWith(u8, field.name, "decoded")) { + if (if (std.fmt.parseInt(u5, field.name["decoded".len..], 10)) |decoded_bit_offset| decoded_bit_offset != bit_offset else |_| true) { + @compileError(std.fmt.comptimePrint("{s}.{s} should be named decoded{d}", .{ name, field.name, bit_offset })); + } + if (field.default_value_ptr == null) { + @compileError(std.fmt.comptimePrint("{s}.{s} should be named encoded{d}", .{ name, field.name, bit_offset })); + } + } + bit_offset += @bitSizeOf(field.type); + } + }, + else => @compileError(name ++ " has an unexpected field type"), + } + } +}; + +const aarch64 = @import("../aarch64.zig"); +const assert = std.debug.assert; +const std = @import("std"); diff --git a/src/codegen/aarch64/instructions.zon b/src/codegen/aarch64/instructions.zon new file mode 100644 index 0000000000..4aacf85e01 --- /dev/null +++ b/src/codegen/aarch64/instructions.zon @@ -0,0 +1,1343 @@ +.{ + // C6.2.3 ADD (extended register) + .{ + .pattern = "ADD , , ", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + }, + .encode = .{ .add, .Wd, .Wn, .{ .register = .Wm } }, + }, + .{ + .pattern = "ADD , , , #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + .extend = .{ .extend = .{ .size = .word } }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 3 }, .max_valid = 4 } }, + }, + .encode = .{ .add, .Wd, .Wn, .{ .extended_register_explicit = .{ .register = .Wm, .option = .extend, .amount = .amount } } }, + }, + .{ + .pattern = "ADD , , ", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .register = .Xm } }, + }, + .{ + .pattern = "ADD , , , #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + .extend = .{ .extend = .{ .size = .word } }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 3 }, .max_valid = 4 } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .extended_register_explicit = .{ .register = .Wm, .option = .extend, .amount = .amount } } }, + }, + .{ + .pattern = "ADD , , , #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .extend = .{ .extend = .{ .size = .doubleword } }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 3 }, .max_valid = 4 } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .extended_register_explicit = .{ .register = .Xm, .option = .extend, .amount = .amount } } }, + }, + // C6.2.4 ADD (immediate) + .{ + .pattern = "ADD , , #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 12 } } }, + }, + .encode = .{ .add, .Wd, .Wn, .{ .immediate = .imm } }, + }, + .{ + .pattern = "ADD , , #, LSL #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } }, + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 12 } } }, + .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 4 }, .multiple_of = 12 } }, + }, + .encode = .{ .add, .Wd, .Wn, .{ .shifted_immediate = .{ .immediate = .imm, .lsl = .shift } } }, + }, + .{ + .pattern = "ADD , , #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 12 } } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .immediate = .imm } }, + }, + .{ + .pattern = "ADD , , #, LSL #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } }, + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 12 } } }, + .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 4 }, .multiple_of = 12 } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .shifted_immediate = .{ .immediate = .imm, .lsl = .shift } } }, + }, + // C6.2.5 ADD (shifted register) + .{ + .pattern = "ADD , , ", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + }, + .encode = .{ .add, .Wd, .Wn, .{ .register = .Wm } }, + }, + .{ + .pattern = "ADD , , , #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + .shift = .{ .shift = .{ .allow_ror = false } }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } }, + }, + .encode = .{ .add, .Wd, .Wn, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } }, + }, + .{ + .pattern = "ADD , , ", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .register = .Xm } }, + }, + .{ + .pattern = "ADD , , , #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .shift = .{ .shift = .{ .allow_ror = false } }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } }, + }, + .encode = .{ .add, .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } }, + }, + // C6.2.13 AND (shifted register) + .{ + .pattern = "AND , , ", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + }, + .encode = .{ .@"and", .Wd, .Wn, .{ .register = .Wm } }, + }, + .{ + .pattern = "AND , , , #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + .shift = .{ .shift = .{} }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } }, + }, + .encode = .{ .@"and", .Wd, .Wn, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } }, + }, + .{ + .pattern = "AND , , ", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .@"and", .Xd, .Xn, .{ .register = .Xm } }, + }, + .{ + .pattern = "AND , , , #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .shift = .{ .shift = .{} }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } }, + }, + .encode = .{ .@"and", .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } }, + }, + // C6.2.15 ANDS (shifted register) + .{ + .pattern = "ANDS , , ", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + }, + .encode = .{ .ands, .Wd, .Wn, .{ .register = .Wm } }, + }, + .{ + .pattern = "ANDS , , , #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wm = .{ .reg = .{ .format = .{ .integer = .word } } }, + .shift = .{ .shift = .{} }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } }, + }, + .encode = .{ .ands, .Wd, .Wn, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } }, + }, + .{ + .pattern = "ANDS , , ", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .ands, .Xd, .Xn, .{ .register = .Xm } }, + }, + .{ + .pattern = "ANDS , , , #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .shift = .{ .shift = .{} }, + .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } }, + }, + .encode = .{ .ands, .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } }, + }, + // C6.2.35 BLR + .{ + .pattern = "BLR ", + .symbols = .{ + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .blr, .Xn }, + }, + // C6.2.30 BFM + .{ + .pattern = "BFM , , #, #", + .symbols = .{ + .Wd = .{ .reg = .{ .format = .{ .integer = .word } } }, + .Wn = .{ .reg = .{ .format = .{ .integer = .word } } }, + .immr = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } }, + .imms = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } }, + }, + .encode = .{ .bfm, .Wd, .Wn, .{ .N = .word, .immr = .immr, .imms = .imms } }, + }, + .{ + .pattern = "BFM , , #, #", + .symbols = .{ + .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + .immr = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } }, + .imms = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } }, + }, + .encode = .{ .bfm, .Xd, .Xn, .{ .N = .doubleword, .immr = .immr, .imms = .imms } }, + }, + // C6.2.37 BR + .{ + .pattern = "BR ", + .symbols = .{ + .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .br, .Xn }, + }, + // C6.2.40 BRK + .{ + .pattern = "BRK #", + .symbols = .{ + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } }, + }, + .encode = .{ .brk, .imm }, + }, + // C6.2.56 CLREX + .{ + .pattern = "CLREX", + .symbols = .{}, + .encode = .{ .clrex, 0b1111 }, + }, + .{ + .pattern = "CLREX #", + .symbols = .{ + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 4 } } }, + }, + .encode = .{ .clrex, .imm }, + }, + // C6.2.109 DC + .{ + .pattern = "DC IVAC, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b000, 0b0111, 0b0110, 0b001, .Xt }, + }, + .{ + .pattern = "DC ISW, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b000, 0b0111, 0b0110, 0b010, .Xt }, + }, + .{ + .pattern = "DC CSW, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b000, 0b0111, 0b1010, 0b010, .Xt }, + }, + .{ + .pattern = "DC CISW, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b000, 0b0111, 0b1110, 0b010, .Xt }, + }, + .{ + .pattern = "DC ZVA, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b011, 0b0111, 0b0100, 0b001, .Xt }, + }, + .{ + .pattern = "DC CVAC, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b011, 0b0111, 0b1010, 0b001, .Xt }, + }, + .{ + .pattern = "DC CVAU, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b011, 0b0111, 0b1011, 0b001, .Xt }, + }, + .{ + .pattern = "DC CIVAC, ", + .symbols = .{ + .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } }, + }, + .encode = .{ .sys, 0b011, 0b0111, 0b1110, 0b001, .Xt }, + }, + // C6.2.110 DCPS1 + .{ + .pattern = "DCPS1", + .symbols = .{}, + .encode = .{ .dcps1, 0 }, + }, + .{ + .pattern = "DCPS1 #", + .symbols = .{ + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } }, + }, + .encode = .{ .dcps1, .imm }, + }, + // C6.2.111 DCPS2 + .{ + .pattern = "DCPS2", + .symbols = .{}, + .encode = .{ .dcps2, 0 }, + }, + .{ + .pattern = "DCPS2 #", + .symbols = .{ + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } }, + }, + .encode = .{ .dcps2, .imm }, + }, + // C6.2.112 DCPS3 + .{ + .pattern = "DCPS3", + .symbols = .{}, + .encode = .{ .dcps3, 0 }, + }, + .{ + .pattern = "DCPS3 #", + .symbols = .{ + .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } }, + }, + .encode = .{ .dcps3, .imm }, + }, + // C6.2.116 DSB + .{ + .pattern = "DSB

rsgh^RcYX1=+98zRhanM$Z`ios9m95xD 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 Jsy3U`;6HI-@df|@ delta 1005190 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 z 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%0D$5Y>2wod78qNm^ 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 z z6Y#+zF!oFiaM0tdegPx#l8Yf*2`hdoe7s;43jvS8>$c#bgE&UY^`$;|WU>wf9@1mn zV;}H{sF)XcL%Q+dGt$yk$KVK__P>LIY~cSH6nq#+x3cI~LC2&d#ws3G=zWoPKO80PuhluQE@d&PD3a-eyJ1~?$kIcv~ z1vcm`+g)n9nMDB%7t#;@0O5BJW zbPdP?`5=@f8?ul!7i5=7gWwn@6>c(VsY1t=202Z>v%^YIj^w-WMUV>rJxyo4gZMC& zX^!OS0q@wyS`X>?JMaX#`hLFjTSte@sUzE27m_&2&BZVRea!ZN+;vJ5g zJJcQPfzx4eBq-mu`_F1o0YUFxi?Dp& zU5G-K=N?*g+yPE)Mo<DCqt?^nfN<=ma&Z`k5MA-E;VGp}5Eu(lXfOO{WZG+NKV62F`H z9f_lBZj>L$5=-TYMt-GvkBGwo3ldv2@9rYg@GSb)j<~^q8J%-68=XZN(P9e2jEFpWqru= zae0^ZFi(DX>tT)nnDsDAp8k5Mk}DR9Bwr*%R#WFnR$mwBo~9=OK`hh`*#)t z#_{ri!-0Gr*~E3NyU-%=Dj*3o^K7;}f|qZsG&DgD5fKNyL};vcq6p;+ji(DUX%I1zFI!}2y)3)+LA~kn%;NZ>4R@Eg zt8S}Fz3Hm%Jxi#IDud zI43!$rKY>0NHoXKwgQEqNR8bea{g*W+Z{n8O|_}RXuw+O-|nEk{zH3s21osg0}(_% zTKA#drME}HV0D5KUyI@zT#vxYx|0KZQQ_%ocnZCl$y40#s(5zhxeLisLVPvz%wg() z(nW$`#*)if7fkHTcqyY{=aBUnnFNyGo7e)FGDC89dg23AfFO#j#hs|uo%_VhE;0vX z#<@RH1mrfMc?H(ZLJDP{HHI$S+%OIunAZ2aS>m8y- z6WYS>?Fhm?ZG*-M@j;830Jab)%fOxeNZ5D77vKpF%`B2|6wyq<>(Ho(IZ_9UsW15i z{}bUX`vo2@7I-lnm^f)nXgcIqpzm$r>`eTI0dy87fZ%OLxXTv}3pSF|@SCC#yBXL9oV5u`VyJ7CsjsJiN%5?8zQ+ zmBO(i_Hb#39J`7VpjF~z?+IT9Y^Ik!*i26WoAE>x&O?xy{-%HqH44o8LmAPyCE)ge zDBv-;M%S1`zL?!PGg9Q#B?mxHk>^csrWcFEXJ*K3CDK$#Dy2P{%6(`z^jmDTJy+D}4!g z+%vP#S|OD@LM2J|Q>uw?FBSx3p%tSEDZI$+A|`?6%_&` zq&aja8PH`s>D%lK9!#2XP?xeh#rq~l3I$C2Euulnk%9OdI!oM0WpE)z2xF-mq0G~~ z#l*?n7uQH(4H(Ic%~=9XiP}Eohp*ZQu+21x1QBxPXBdd!_ERMMGv^;h&r6IS4JunTAkRr;Y zA%lj@K_-ES5{RT{4I>#d;*O9x8SRN=p8SRoA4=*UM2usjX-&{)?Zm9LM_;+q{$<3l z)k2bm!DaWU=C_evMfnB&)}QXQ+s8#9kV#%d$7DM(0rHG=K#@Os9IZ(+!mLQ%ejXzL z8_M8|3Evk_V*C~eBzPoDtUR~#6d_D_S9f~#o#-jxXA`1+DBdFJ2yoZS#<9~Z3}HT% zbDyWsA0rzf*Gv_=avE3mw*&GR!pR7u6FTr`<0k^x+W=b-GcEu((P+GbsEtN^Hp{@R zVS{De7q+ss;?!G6=o1@q=Zo^*I^F71jx7+5q%;6Y6tS(9`$LhNd@*XM%q*dFKCXkk z(wv8!!fN0ZX#OtcG!wIf_S|U2`H{`4Ef#D~;Zur}+b-B{gz`3IhDzS2k~U-pe3!Fv zhRj&(IOb5eNuc?Vunh+wGl`x#JE%?&eyQ@B6Yx67hmaqmuccl%z69OjZI{oL;vK_4 z4N-V}avBsOBZVo=AZ87SrF8GZ(^3l_Rvv zvpWbTPnIi^8ag9i5~N;PIZ~f2rmjgzuovn=3M5vYfT_-sP>kEPwTlQJIsS-?88K=& z*`CR=P_9SfS!+c*$#b5?IT4Agoh59o5if-#Fe==%jQD9hKN*!cd&+3m%ac(w$rCvq zA6BGIMkmisj82~EL*pK8L048s0ty#oZuue@hb${UWsf6rr2G`RoA5X_Ki5jC^ef>E zE4&{X0FvE1i&9CB5H|w^U+*In3z8ETnj;j=S1*PP$%Ev~H{T)DhKjivkQ`#q0*by+ z=>t+32t{%M3)N_SfxskR+{KosV)jz>U0>M)4{Fr~!$yfwN(?2&z(w;aw zAVap>V!7-LM&oTv4)`fK;}f#x9Jfn8T$gW^%Nmp;j|GC7J5|Yjz z26CgQkNzJbGgKZN&IrTq4%|cv#A?iMv#1KVUXFfWA!{lqRwK-y`hULVL~fd1`>lN) zP6|hU$L$8bm*gei+2wdZ-u0cm)tarZ++)uQav&gmuiYi1RpByVFFrT-=nMAR)2uoA zlfCv}>p@-iJx+GB^)=rUvwpU2{@$J$oNntYf8gVF`jH>-%zj7zeie`E@&PIrkq_<_N-%{7z*(##u=#mC3U_~qz0UMDZ> zRl|!)tXK4zg-RSbuPs#P($?zxgE2kpzTltqHATuvB-9s42j$}f`t4%X8Nc}5#p)pI zTzp2=MC&&V(@NAxE3^@>*VUyeX06maO4YAw33iUtAR9J6o zuQKhGVYi!hC0l(LRjdbeQE9rYgBtD^&OsJ}G&y8L6w(7Cs-$672UTYI-_sM{^@fh> zj9xqC{ucyb(EYQp=g&-F9Z4EB*-8DVPwb>BQ|f@yaF^ziPU<&)q8`v$koQ@vuEg63o?2%^e$IXS?5RhQg6w# zM<4a1taIKwH0PtH$}9mAbD%YlCj}-^e*EB_lQO2oXh!vkzi}kvyv;&ycoxTK38F zPsvl^=g}JuR$1}XD(>65e>2r~+h*R9JSoTFv1U8>rF7$S`CgXVjodchZ+?gGU6bFr zf`YLiGPfB^y<{4c%w+N`+$Ya_@95bhRPjlv1K2C&RNHbZ_bE4Z8+kgn&BOVWHtjFi zruU9eS6h?xRU_31>mvQsNOd&R7d%AuwXV<=ho~{U%^ue}nZ1`wwZq$1yM3Q(zmz=f z_2RMWxB7xZ)h#|I#L6?=C*)H`YjJS~+uWvjhTFJJk2*|MhpO3qUOG%o5B{rOpK!Q3 zd7rMtZ+e%y4r;9nYoPNlU#BUU(j?zYQom=n`Tpj9-?Q3$|A%~UgJ~J5-$x&yf!x## z?*0wrwE2GDc74Ve^?Nzs>je zX1kNOJ$LB0$E#C9FTGK9 z`^De1*R`XwY<=Pc#g!$5uZ{*$taC12xpz)AZ=7~RvU6XN13y6ma+_~+Z>Gke98{8= zuYE~tK{==-Z-2kfeB;23y#4cKQw}wQyj@>y>I`~y)=DoWLjADRWrnIh;Clys=~&g_g8Srm zd1~IaTh@#6y-Vu%wp-rE^1Tf<8 z-`n&ht1`Z7TWuN3zir>rTh0KcqGMl?X&n zyr652fyLctcHWnY_Sy6>a3@+bjipp}vVQ4Ul^=ZjalP$WRc=kxX~(Jl)-ihMaq7># z-n8Lsg`0r~#sAtteTd81NNbd^!onofafcf!fD827$Em?Y#>zcj^~|SKW}8xa?D6XK zvKHb-`>dl9A<45xDVGE9gbn8+B{gYXPb18vvkC#s6VHf2pouBe@Sq8c0dw{34aQ5B5D z+V1rjoq%}2{QEu&^uq#uJ@7=;Nss)6at3mh3|*Z^SDB;#q!NAgFR&WDXX`1w(+i4m zP!>rfWp2{l=IeCxFOaj}*Wrn#o7Y$cwy{U@-srIdUd=r&pni$NBFetuqB=DC8fXyTIDaqR&d8#1JUJv63R1-W$=-o~ z>JTHRTaSGfZ7DVgME1ur5bMycVh6zVTo@zP8CDjAM=>&(_BmB!)=NY*dm-Z7N-&|M zCY6~pn$#+Aj0h$XTgDY|sFWEox>i${e&r<9J;)0A>Lk@?K;mkrTpV6-H1u6ETDX4H zYf|Dm5XWEMS~!Fg*2kZ$it|#!UW9O&(SJBu4bH2_|HNOAtmMyh!^vnRa`m?-t1>*| zE$7)pP-6os79eSlbBTdBKxGC;$he3Q1AvR0A-9(baJxBAWcW9Xv@L4-v@d*GtC%UC zil&Ml!EQuRb~u*q`9}RnHTs!TREOZa+x1(gs581OWjy`Fc}VBW_)%&*RDz`xaJX}B z*S|bf9eP9?NK}JTDXvQt!c!qH1}-&Y%59Ag(jspOaull4+fGGw^4l4RYTTui4%YC< z&H@p54o_&mK{WGWNKBN=;snD*dl?eT#Qh;4@*$b!u)QsZYP`E7&}fnsU4>+dO~$7+ z=NM4UisW43-rE$o)WoHd8=BQ*ch5rP;z=BB6~}a;AR$rzNwE8mKuG)-SUvbJp`VD_ zg3F?umf9n>bU}{h2gb7aGbdce->k`UQr|6_aG96{M7vDCWI@>80TGs({{A#o9G_J{ zZa#~30S6Tn@zkx%^whC7!l~HJ-4)pDYFUB!{}hO^!7Lz@Mrp}a&AJWl^44ohJLalI z)@v2(Ric1|+*;}cX*|~!P{DXovs}$YwKETj+30q7-SrCRy zIg4q%S7=LNEH5>>DInr|b36R+9&$DjL)Y8?g%Gj0ceR5Q7(j*%AX!AHNBK6Zfcf{F zY!XDU6SdltW6_J40F91(cM{1$+N3iSns68H$3 z+!j)R9%vn{L1zIvhID!fB6|j0b`Jrz$3%f5v7j(4W=_#feT|7q>Ng0(+U7$|zEIyA zizQQ%5|bYziCizR`q${NKRp9I-F>>?OmuNi>IrA6-rY^5`{!qrvONj~cN;&`HD{`x z!-VkKa_2nutW|C3uHW{LH{EBvqNp7E0gu)j*ESNwSZLm53c2OUiH4ySoTZX>zUYJu!nH2@*O#>Jj6I(qhVr=E?Gn{vQ|15RH0NH`@HiEU-(#}%?Cy?MrCSuXq zs=dDCY?ZB;N&! zQ4@na&pSsQY|Yp6&%xyKsQ&&O)sNT}ea}^Eh%C}_t{T+`yG-J&DIG_1z84+=gOLOn zv78SqP>_AgBFxBMefoK-qU7OVEW^WmwiQB<@DZPpNA$htscWoFy36_Mcx$7+Urm{`j)64tT|sTsSwXDvJZ zx6F;PmdRPTkn@SY;kRlndhrF5v6uaRlChVW-yJ3^F-P(Hdmh#$n8#LLq-Kc@efImo zf`+qxuWET|;LDg``cpXY$qH>a_A+(5MT~*>E=R||ydmcbm2O!p8jAm*76t99?3ktN z$9L&^=Y)J)AHOc&ZusJV)WufXSj^OpGfrRlC-s0eR_9!$F3s2>9#C{~T*D1lsaq_R z>BU!LxEQMszFOUDy{!LsHE?)Z=U#(2_p~17JLVo5Q>Uv(+ONbhF&%9?E(yZY@*Dmx%b60?ovu?|trdFIb*iZ2Gmu7aDS3Q{ z*$8b+q~yEgI@PU&D`4cB5aGLj6-o0iU~!(&k6x#aw4TvlU#AZ5l9FdSX6R%ljv+?U zI6gh`detlZWg4}9si$49{$xF?kGnyY5`3fh2F!il+nBlLj!)aIg(}J2CGhcVKoWY% zv@a*gOcf!Ww3o;YH77PzZS7sWEmBCl`;v*H~mGT*PL122n2Z(xUm2B)R7Gaxv`0=K+y0;YR-cQ_yG ziMOf~JG5y)iq(r)8nLq;(oMI5@9XrZx2p1jrFAI*NmwmzQ=RTHOAYF^hz~|(aWrKc z6ECOmA|4E8WCWd`QYjUIlxx)amotG9#GJ9YEzFaliU zK1cQGn7m*pWNw@WW05-8<2h%ne=!HTu|!`v2P^1f4R6j-JFVdQIz4bM)b!muRjzfH z{>PoLv0hpy{p?(o+p{m|*_WN(zjP@l#x%1u#XANLK#&FY(b=WX)~YaB+iYHX-N?o- z8EsA>rc@C1N=DT~!j-dJoAoPN^|4m#Z*@xJ!)sJ=ws+A}PJ|X#>(gpf=VagboPEN1 z)Gbg?c20Z`S7#UrsY`a52w2eS{v*j_xIY+L;0?A=Bk)q`rzUIuu4<2m$sHH z%`Ty262{QWz<4|ECc4G6lpf|+QPOrcSSn^VLIwmSa;JC-2#T}unlwGsg?A^rQ!)5z zy16HH9EGVJgBec1v@pBCuaHoM`%FejmGF6}Tx7Ul9J`IUN3Yhq?@ofWr#^C?D(&Wf z&QEOx5kAq1`Khm;2k=+x2j{8&`DRLm9yp2SKbV*7e`np}9@V9fUumuZfR6*jAWeaz z*^_8ym%ij4Rb)MtFi&kgv8nC`!Kk2;KfBRU^L`&xa)d}#PC zy?MUsM#*pHtG+$`wup2@15p9L*IhYUmPH4MBw-podI3azwf_AA)t(xv7pPuC{i5C#6FSjVe%W-U%ftM#~Ato^(61+}V6Z|`FI93@U|tq3GqfRt+B zjhU7wYE`fHeyebRi4MrM5c2s8J{SGPEG2fRx_lW*lxXkVWPxvws%t^ z;{hzSyY<-*KumY*D<4qjt1rV3IaBra2UH~2)Goo7@ZF>O+y_Cxsk;AzfOnZb=0SB~ z8D6mP=Ua(tkt3uFc@#$z_A#`@Ho^Q~dyvsA(O*BP&WKKhnZ}>P&uY&8p@9%a10le< z(s@!}@erKI5`E@U9C}`R2u$>U$Lb*Ji}lDl6=}aD2RCYW77i&!hG?QqB(9LNR9{i2 z&QGVu90YdW)_<>4Ckb^q_+iyo#vv||glBOTA#g5#7`CBF-}|r{kiH~`-8C>iru#gi zdgvb>Ry}C7$0KTjH+WHeGpP*znnx(~g?{uAEbzN^`J?Kj5e7W6=b9Wc6FWuWNYBz8 z^UaV#3HKD^(k$l4b^tC{B2Cqek3!$4>K%`&?tn6Lkvh++(-$vNhtv8Ki&$7obnr1X zI%mr+jl=Ik5qS7{;$wKBJ*8iH3^+Zl(;i36ZP154u8zXlXU60BcP-I7A6JJ0tKLr- zz#sbr&P`MGq9<_lcv8Rcgn@6c9!D7;w128+R7>yAq>4=mRwFQM9%`nn~! zB-H7bmZ+=9GWJO|kG9@_k~h=*aXcPhYMOk0DMHk$JD-Aa-`z0yDHRTeSLDFm^wQQc zgt}82RHVctl(YV2_sx-QoJd>F8hvX6__#tp-k^%D=k;G2=+6rM0VQPq(;9J(xKR&j zgjHUl{R+VHjs9*0ApS-dtyDdurf0ar zdL0Nj&kC?``1an>TKu4w~9sDZpl!nxsD4Nl%a`xvdxdl_!SND)@G_P>YXCjbd(3_o)_R40?OAYtmHz%&z_m*E zTg4;*lT~nRE3{q(XYq}G+j|Q(so7nd67V+0g>)h0#(D<4Rx=1t;5q$tlX{ub(^sp9 zS=jxbQv-RN^_;3mPiE22LFrcL&!1Dv`1bG`)u~5H%}isK^IVRw*UH@!l*vez{B#Y= zW<^7{wStvvayXONOMkHrteo&XSZNZ#N;B?u$4FtsmhqNBJlSqec{MefUejYrX#EOW??QJ>?}#duAz|VtuPKHvo%o^|gNm3FqodHmLT& z>ZyA1UvassnyQ=ss!qXc(cP?y)r&c>TYB~uRaAlA+3SRdJ`DKApt-%+`~s9_+1c(! zIv~Kf>SeSiFX$IuR%J&f%Nmgshy!3$M(N%IodIP4xi~jUarr4cwt0)C^TdS<-~BMY ztpi_CMTb!q{@CO+Ka=I9M#>8RE$0F`M!=JEi?~wzwPew?`q$N&L(xon<4N`@OZw#1 z>;IH`DUj%wlr4P~d*(}e_S<%CeCu`e>- z-8RR~4v0F|i~5w!s>`6{)cas;5UBGlXyy$Rs%Ws5jNwDKmjO?EizA$oOB+1X9X> zI}@wk`rpq)#{X(2djC!J>}b|sy4+7^yArB0!y~|~?2Y=Ozp2x@Zc30~gWu-Zs1e=S zAzl;mAiL_ve^W2CH#ISFw(x+4xs_8cih5q!s;)p8H|iaB+fDkici3%{MIU~Lg9&T& z_wT3)sG`Sj<870^XB(StO2a9euI;Wh98j`?cek zu!F-2oArhr>e~EiDYfNB3Z3ct^bY{nD|+Jxs_5wHg;_3Af?Qu2Hky{Os8)X{z&ykO z86_5G3BbYDA?e>2U?6uUM>9MlOKP(j&UJd=hiX~;`b2wXKBX9C2TXF_MW+Cuoa!(= z4vu2Gg}X3u+>oD~8xo~Bjm&@~sXCD)W~u|$1CO*{dZyO^yH2&f{3CUN^{#&VBlY{^ zW}rCX`ajN!n+EnPM==!45-6I+p>3^=2lj7-x^=Va+_4%}YuNR)lxbPHqJ!@3uA6gL ziBqRvZASi?qH{i0y?&KAcrzh~>LnH)+HVXXgj6pz$A!|+N}@a^q`N30_^>DxYLlpFOoAH!|UY#8#1%CLAI_Ngij@;v!7^*QgF z_YMtbe-77YP1DoAP`&!K=~RZu?zVIat4w+<9dks6L2$9kNSyb#zd(C^wf^o4Fze=q zQD3S*S}44i{Zp;ub;>SvgT)h1%ir_Vycagi{1;M|wN3Z>Ms+INhU%hUpis0X;_4%( zRRnJ4u%EL{pZg75)+hSbZ#Wob9z%7-w`f=W-*M-ibc9h3ommtc0S2W{>?| zE%CCAk{m`=otooaebNtVh1GmVyYSGGC+WNB@|UJDC89?*Z(rZ>>u_1Sn~YY?tjjuD zC~GM z_qJ&7Ykiazj-K(g99~-FL2M}jkenHb;Jla^h>U<(ES-~z2|NM08At3OGa$dvMk^Lb zDI}WgU8Y6i9P1aXaDQ6bZH3EV97=-WtE^k}ZNcy#5%RM_;fj9JJoSd6*bTUeM`f9o z-0g5~Vl1Fpq+EA>NXGuOzAzLXkL+I`3U4V$p@)PIN}J^F{94~_hs&IV6HjmtmIimn z*ZKuJd|pA*&em8&a@()FVW%FY!XuqVJ{aZxT7mxBo%$vf?qS`km#A=09&f8~RnG>g zNp9*B;{lz48^mwPH?)YwV_}A2x2A-#Q^2V&3x~gexqmb*Tros0UF0->fknqM&hzq|;Uzi!*CZfW)tgwg*4cBIcqgJ>{KF-k(WQSu&gzK}Jgw^_^?C`ND zLWkyr`*&U=m$VuAMLMyE+dA<>;@2*2M{Ckka>At)n41&s7QgW^+)<0Xd%9=k#zMx& zbPl(fW^*QS`okQFXW|J3oNHcy&sv6EVbyq9lbK|EUKYre6m!21IDcu=i+NNkVdd|g zi{Gj8TC6=G(d5GYnw+;!lXd$yxzNw@S)jE|eM>H*dtN`3%WSXHujGaY_l5@z<=BGV zVzb%J&DA#sMPT-4T?SZ-dKBjg-6t=+i5cFT7cR|siUsU|pEGrt6CMkGp6`UqtuOU0 zPPjK%zsw1D0^n~r;Uh_m=F{x$dPIJ>Pmo>w!hBq)aQwS1Kin;k_A{J0(soUFl5>at zOMdw1Q@HV+4zb59&y=I6);J>B?Re>K(VIf+21wu`A>_Wnhh2svdg=c`PJM2>@UPHM zHMOHdU+K5ng}ZmXku5SPv@E3;(w%Bj`uk&y7KA$$7y^_N=n#>BO6Mzv9ks8vhY=yRuDco_EAtGy3$Fv5pvefNz%7~EA)*7y{#aXzy%C} z%jL^QI-`(9u}60=3@32 zr|d0+p6$?OMd5?PpAz2I`9Pmh6fQ|$^eIQy)0}Jd)kWd4@%wSv>lZjgJnBph!{L9R zj45r7nhcuSLZLFGiHTxVBCBC`0uD8YoDb5HR(Pf`n?ZQ?TJHXWLMF0)h_DGrGK|A? z?HA^@#r_gXCd1u~M|8cH;w5Yq7wOc59V9e;p)W2D|2nTG@!=AR)_tMhDGv8aC6gKM zbgo^1OtvoxAKgkOS26-2lYbyJl}u*1HBdUpJ@*>h!T!Uhu7ZqoBh!yP+TO~sZfx5uofo=8To;6=lNwc;PSXj8-H(r_;; zz3%2IoS9)FR0o#k&AM-gaDQQRak2p3FX{6-g!{0I%<2%n8YL}W(MJ!ODRY;#&-p#e zy{!oXlXAMta=p6As4wjuE<%rcZ^v-^M4l~i9TK)Y~nx9+Z3``JS%e}ivZ#p?# zvQKxcGfa18pF-omE5l9wOMPj1xFkn#N657tLcUcmC<`B+zOC-M5;q%=eOLwrZ`Mwi z@E{Ll=x6}h(Otp=0NFKN!q@z4ka>0gG{}ZU!`;Ik$n-hUaPJg2t&fI>|7YuI!Pw>N~rJ$EDx@FldnvSU%`#z>?Q30TvIFj_PK>a&@==0xVwLKMj_l zJzBwXc8@k-S=Zyg086sXp8*y@pS&Hy%Iz>d2pr-uc+@rhR)gI;3HW(VxIjNq9`2dm z@H)GE7698;9`2QXv&q2i(zE-5f?a!sOH!BN5xv3#(r+$eA zN?*DvT+|NRH=dJXdlp?sD-j+!fA zp$6Zs9@-Y)8iywFZE5(w!#A&qp9bFj!&~v~mEmphZOFm@1)P#?{#-b{IwA?Dob~^Y zwl9H?s!0CNdy`D}WD+t7IUpA^Lx6B590m{&9;+Z83m&U@f;T33u)5wlK~YWx6lhSy zpa|j8h-eVQrN|jDC?YCqP*za6MFj-?f2(@t%?wC>OFo~!?B-41UDef9)z#hA(CUcZ z@Wwsp&Fltrz4m*M$z0scW-_%-&15EYizc|Yy4k2J7xhD(Zmw?BP45v+cNJ(0GQZ&h zL4>y$K5wE3ud8Zzp0o2=OEtSEXN%BvJ#B=l{XLoR{^$uze)tTjJs`aEdokhN-7A{# z=Jn#~`c__nDqAb112k8fsAzllSLOX z=?%Upn)Ie$#H6?BqI!^?uDfoujl3ipZR6s#mcHo{XHtar+33=GAj@w5ERe-&)+Crw ztx54wWT?LQCu8l0e*$ZZ%CWRb0@e!p+w@lvuj%jp{?YU|xxY<+M*&=Y^ef$5-H0r` zEIfzjUluiolP|9a^6WOx5_xLD6*iMg9Ou?kcLYo@bi zR2v5}wQU~=l+6mE%y$jX(TlH%o}(kLu_>%Jp?(ymo2VOG|Gq9vVJ}}7MPZKX>wzM> z&9g&Ms+w^_n8J==lZ}MK-UOnsEjIvNFK&dvk(LdaH`)|7E>Tn1pc|tptn@~vu(xik z2ZiZ&>c&~g;Ar-?BT-9*?TI86YH=p!6sd4xa6K?-_vCCbscsHovXNSTv(pVtc*D&= z;f5n4$nE13s^Kk6aXoH{rnrZ1VYJP=r5I38hzu2F#47a38Qa0VcY|(MECG?%a^XZZuC7^98GoWl6d*0mKQr4LJXB|B|Ma1<+ds>pQn%m7R6lWz zvypoCKBqfqVyf||n_9!f^!9zub~fpg--qqkIS7u{($Cn-$P-iUXUcDNe>COae!nfM zDw3l_)fouzbgb$Y>c;A24}|&0V-JM*2itqUdmxO-Gqia+Cha!Q7L$)Z6y_hRaW9xe zRaG+akBWz!7uraqZOIxW2a&kpN4;gHG9G3uS`SBK@$rY5DBpNEOq6E;<#hDvy6Z;Y zkVm75vLZzjUa`4P)Pwe)dj9cv%QR`Ocq zdBT}4KiLh71a1Aad%~9d+f($!zVC_Xi9P8Fn-a?!){hc(b9JLKP#UJhd8JX5=owxQ zO0?TNOB|~CPlhRRFS&)dODiIuYTGdovEo&qqB+{X>?j6rxUvFdflVb zdt`WY?iv{}I!)E`kcK&DJ9Q)WnsLz_WNMn`Ag_#z;2>SpPvhz_1?={t z$*fKd1U3+RX*K^jW*keNW5!{O2ji$9CtXf3jwa)6)U8a@sJj&hbt3mmo*r+L=BV^2 z(mVqrJe@Rkb9H0z-icw-d}(3?X?9V@^Yy@*-R4FX-un?MJede?ox#-k+>B`I+%SWw^N$(zpiW(P z-PnAmEIheu%c3TCpPBW*o89JF;w`C^o)s-^97U0dBM_!pegda)$E(!WBJfBj-zFeA zgvaWj_4v`(Y(gz-tO<4HYte+d^EI1L_rQs7eF*hEZmw?BR=*xis0Yy&+2Qv#CPF>Z z7+sGh)Ej2k2jQWfoF&55(K%s4%`Im_ZCxILU|J-P%Cr%@49-E>5c7OF6KYj?G@%|X zw~<<#SwE!eChA7&_w&N^+F*VZz1}~+9_X{%JX`c7Ews_s@CeXEWV$yKWXk6kj;i+M zSf5_E&{<$Ft?+)ERhnfZbUn>{Vy$x*+7vq>OH=Irh0zq7^agW*UT@Tc3+Q(0#_I7m zqe-9fRQ(sp{vb$d>fSW=HvF*S~?VnwN@3X0ON8$o@valXIGp3O&d z^la|9EP6Iyxy+`-mAUn!MBQB7sO-5SOo_!SqA0OuMLj6dZu2a0s7h9aDRFDAro`%8 zqQn`iA}EnxW=C?663Z5X5@}~M?gOU8f)Ao8vE&1$#OWW@gA#S!b))a9)nW7vUmbqm*Yxw=t#*ZSy5y##HMCbZZ~noyM&LmsUO{kgs# z2)282mIzkUHrn*5R&3;{UA-}aF!9))Rf7ZJLBgE<5d=3)^^Vm{m^V~M6XvvP8>!{K z`XN;}Q8!XYY!1`so0}u(v#Uz~IJ`4;hPBtJ79+#5+dNzJRn*w%ORVzg>Al!T)BF1x zoFx8~o?;(a9 z`@eAh(^hTCny|X54aCf;w1tQv-}wat;r=oNggzzR_e%!ip)czJgs!`8AUf;_198I+ z2I83=48$s=1W))95Sd>`OmjT+YgOmjK+F%FTps>412O;WXdrffZBO%A%~aq^+aBHF ze;yFsMBT7_@@*KFlixBd&1)H!n`$*IqiT6TmexkVLW}9G&1_gEz7I1Fu`J(ChUMa& z(Xfo%X~VLhdHrC~P1Fs`zrPQ|^78i#%bM>Q7Gsx&C4U#ga_z1NSnwLDwWGNW%g*)C zv>F1IRl69LpLa#W(&E21EQ?#z4;I}--LRzW4#U!8H^XxKZiZzhQi#UZ?Pgex?~Z^a z3$W~JVZ$fUI06=Was5Oq8VoehGo&mSl`gx zAAX!+nSVSQmfgo~Sa!9oA1u0wx?%a`L>QKnCm5FICmEKTPHI?2on%;+o{WIS2UsSx zvtijsXLm{9`~GBDF8(tbmXUwjuP#0n4mH8*ZlZ2jE_a7vdECvg%yu&@JCVY|C4pf%Hz5L+W`JdJ2OE|jX zkOGqqSavpKSlp@6u=Gu}VVQnT{b12e)D26A^e`+pq%$nfq%$n5kix6au#ELb!?MV{=eUKp0I^B5LqGls=#rePVqWJDS_D ztUkAXu;?c0hUN17Ff5PfGc2?78J3+$VfoUMVL7*D1T1X<%aLVY#ID0=3 zcAL@0(mIdqZRmnayNA`jE`t0(BzFZ`mP`U#=up|fu8fu^yGEmBc~?fufv)vHi>|wF zw9M@mM$6W2QD`~0dp*!%w|N$5S=EElva1K9MV=@477AWI(`R>&^t35|*-YHDBW#tN zCqf=p{^r!~89Gttsof^M(E79LAH76Jy|G1)1#MKNy+j9)<=ef&_#aL~mI|~+gLt}` zT=%wa{NL4Ev3xPKPv29pn~d&Tom4scd_aC!Ej(W|166){ zz8Dyo=mD< zY6KkfM(5yHF?2_C9cg&~GCE7@sS*6}MP!+jQ*$Jr8o`fML>5UpHAjO`M8N6MJxL73 zgd*T3H@au=)*2lFhriJga9W?(Glc`JVOFBFaOV8PrAMUOn&kyso%67vK-i_xQ*aTYnD z@W4G=d_R$6u5e6Qn3JZ?>nB{vcs7V%$)hJ0ym0xtP7UlQI$rxV+`pp0H~RA}Ucz9n zUUBrKGvaFz(KhBGTsT9qdyxnS15Xns$X6LPgePV=7Qf%6!sM|DSN-rxk*b*}Q`ND4 zqBR`DHM>X*!$(~t?lIO^ii^ck1CL*PaET~3cB;0QiZ#Y}>d2*H0Nll0@K3SK`Mu~J z#o!p{J~Eh@aiVVByEtV+K^6efN`*OOr3qL$uQS6m@lhW%OUVM>F)x96@95e_XGhyDiMH28aa z?+TH1&WSkkB-b|PO3UmM+Y6r39OTE9-(12AC)&g1SNuW+yz^mG5S~rp;nH=bXpurb z!>lUudIZ0H^qksy)%!~E1|-d)E5+PM&uGy6V%am=!heZK&uC=GhG#U_RpO#Z&uBOk z9LJu~Zof)IIFKAc*E!)C?X9as^R)3qv_!A;L?8|&p#c0d?!8L~LzgpbVaufA$ zhbXpc53=a-^Eaxa z8WBfB*(qW*A`V7I>^Z0g4HPY1<+G<|_=L4!wwgFl_&eFXhEG@^1-Qd=XCX*9?jSr> z!iz2qDfu3Dcs^CT2Z|PE)m-JdM)ZXbvw_#(`S9n}q-(?=?*?=guWU`^_h6OG^x zbKrF-Rjp=RCmw|p#?0%*ow*a?NVLGHibqTAQglhYrjrVI=A3Xy&AeV@n4=D z#_e%&KD@oA-zZx9zTnEnxi!FTW1e%9dtDrUhDQUP`{o-(PpBNN28%w8{}_$ysM*2m zs6;4uA)D4vW0f&Tq?>S4H*>H^1x|b3Eb#mUSRgL@Tz|9hs**vXmAU$7)#N6Tm$KyN zpbOIkFq>331pHZjbF=Wlm)(Iutlw~BY7837t#@NvgN>~_rLh{ju?KI$zy~3FhZ?H~ zWEYvcg6(WSrJdSPJ5z2EccPi%Tg3V1meH!>mcPf4dF%g#p=WLtLozlAJk79~{(MY- zY6M1DdbX&z+r(h_IKKThaR=yZ^KBy2ctZVno3K*9vS|XW)U+xb#duloyxT!sJJg`t zMb9udjy?TRws&tA{;uH0K?acs!Al1_N0}rUL)Eb#(CMfb5)x_n(axI;zBo=L-ys?Y z!cx+-u&tX$Qd0YJrB|3ui+Q34?l>XD!y&#$1}PqH4vEzHziAD5*@@=}NH07l>{$=5 zn#768*L94VdxvO&mngT~A=3JVgfBdP+kBlJ7bhB|yRrO5$4M-vd04onY2ljYfpEof zEwtc4QmrZ&BGS@GmXZYE0zv5BRo4s={xmpb(m*m(4Z^4<3=yg65%AMA4W%tbvAh+b zR}sfq*^B-mH*^Poyf@jy14nZ`mR*~=DM}?2i_YontyuvV81M*h z&DRu*CR&z}UuC3hr%Qk9)tF+j2(qk435af!y0JvO87Z;onk7XEu5}01_jifX_Hg|fat;dd z21yj*9F#qSSrP6-v5~YzO}bm;Ln3t3_7wc4-xjH&@?LLX_N?@k5dco6`5W2G>U~xVwECU zE1N906A5-64oo9&3`QP0v>l`Vx>sZvFRP4U;$h++P(|nu_y=ANAWzi_77-Hs?Np{lEJ_rKWlnzf;IP za2#|{Z;Tg`1`_EpakBF;{k5rpS#X0!g{3xXR8a44z zQG(CON5$><-1?Zf&RA2q@-ea8M4muEOh+)g6i)FGyks~!xu$aGa8Q&^ZJ~NSB_2fF zlBYy-1CKj=I|9S7K%Mur$Zq&2Iq)DVi4AxSEO@NrN7Y?Vi+)BxRXz>Pe6z}Y2HM$< zpVSr4h)*yE%}0vC>YI_mrN)nh3E(^R&PbS|zE|Il6djt5FyVlo-S^|Eh1GzF-hnje ztxY(2w%QCW_N-N%Mv3vdo`EWRw2>yX(Djq{DAR#;>O!d}i z(K2maeCTyAj5D5cgEL+{9(!Okdi`tV_Ohz7sX#xrgpm6 zhOa_6vvSJ}QDbDhLGKHW19uEQ(_4)N0lkT_q)d!zSRIe~hu0=d5Vl_S%u-j(6rD02 zBR|wtTxymBKiBckO=`wWk?C6v)L69~Ga8Dnew5=G`gM>a$Eh!7!tnZ-!rQuq?pNUZ zUr&u;st+O(?P-ArwTG(GS4EDoR?T=7C4N&iuL^pK@`qQkcH6CrUK4GywNN1Rg4575 zenjDW@LF=QXO4Q}HPJi+>OzF8_6g_%9l|bG@4qJcIHzKWth+*v~4*@ot` zK>T~u^Rt9M`jy{lv3M7aIaz}JTCd>=zShf$nCIkcSw8_x_n_dW!@x6{C`YgS+DUW; zfW;ywv7RxGRt(TmS0NXkMJK=HUQ=~^U1X<~y$!{-ou?)16u44J+1^sOzApaRf#31O zTc6-3!RLPQBxLxxU#=dgF)cjGi*k7GSM7Qon%OjU!EA7vMe4rUVo0;GSl;3_V^k7D ziVE>I>NP)0N- zh2BrWcO|d*z9ce-s03)Wl|$xW&Trm>9`xF}S6%v!$V!he3$rc50cnAi(PeKmPn9M)MKBQ>w4iwl zwdx&ljZvnu-W8p|!Uw!7Mj8jy_IE)w2b8lCvge@cSqU@DLG@Op_zylUmY@R%)k{mn zb9has)qA3kF;@+H54e0^J^vmL{|E13yWo)e{H&5I#O0pDrY(>#Prj;9#Vdf{BkH9UB0sn6xzTi1 zt)Rm$ri^!Y|bITl_-|Q+e6Rd6H)om+9UX$lT@I#@O zRyWVSh+Iz31XaFLWan+e11FfA@fu}g(U?_6Gr_81WaCHmWHsnR(8F<6`k^T9c?d`V z+rpxXcq6)Y)bNzyar;mOtR`6VjXtET)Lj?I=)4a^POCpq55IXBi;=P``X5Rse~7uQ zOOJ~4pH*o;C;EYhZGnJRg*QMsi?rKU#91KJa#FX1P1FSu186FA6)Q@^N_UGt}%E5 zwLh=rP}xww2_h0y%Md?=)vd@$Z{Z4ho7{6bwYSpnn`kb^BQlBGs8 z>Qzfex#6Q%q_OnJ-`bb}SxcSxSqfXgy8yeuSYVz~58i;#C9FJ}{?vyZhEJNk901O$ zcoJ11(gIjkDH@6QRB}CwpF~BVL1afB)EPjgC6pUTq}*VdO9Gs=inF4Bm}gI#c#7Q3 zU4S07(BI%OUp(H3X>d7a2|&r)bFfEQy+y0Lx{i?f7Mh`{hf&L#NZ z74{49!9bpmPeNz2J3av*rVG9p>`wS#unX~VcXs6CW3I9s_rqwi_1KFUq#4t7v?~!G zW7ULWFcbrVu&<2q6qvoN1=TNQSRc$(8CIU{_KhQv4FdH2;7hkLI%D7m%IQ2UXZO=` zuGxdf9UP}*#0o5W%Tm`m`kRUT5JaZi(}9#6nrB4qJPq9L7>t*JL*Mfnxz(}t;-kbe z?0Q1wfM~O(FHjpdh(a<2p4=eXw)^bu5gEluBjyz}_khSu)^AZwtd%s(3g=M+H;N`M zmX^Oe-7C~(ea$q71MdqN=ec|5kb2-bA#E$Q!@}`8+ow>?`+i^Fr3SDI#}}nPP7OWO z4W(xfp71d7ga_n{!|GK6HEBtznHh+sNnC1__#JsM)~K@S3)Mvdtd*p$o@P;odml;kyw`OR>zS%RsI9L2sE za4&1bh%z`(JeO($N<9tq60MiDdK`9>3!0JgNpq15Sm=3Sv9A$6jf#DZ@$nS<=;3WF zV6z;w&tR&)n?!!VVU0j{2wX=DCYWh?X~UerMytf|i}Pdn0dw*(7F=UETAICl+2Ew+ zg}xm64DjXBXP~c%w~>CKvk@B7zZ~8soy~kmwcO6wgK~ItI)l$SyxE<>(;U5%7JWHt zHVN!`*@* zu-KnQ9j0!no*#=h?Q9L{t+N!)mg2?ZK3IVSvnAC;WIJR%G7KL0}TU2Xk$JTRa$`MV^Cf%S2jKI}xXg z>yGs%pGvkQHmF+|3{)FeWIlu-q$LWG0H#hNi2xix)0Zdf%9x1RipqJuVw(-`{lyfw zAs!N*wpK4)i1?{D1GQSKnKkLTu3q{~^h+>_O=v@cLmm7~qz4SE*3qBjJhLkVSSC{} zvnG6rH$hfv2F9#{`Hyh{*q~j|YiI^YQMx8DFq`jrA?cXeVka5gw#qOZ0q|N+iC;av zO{AxfTr>v8I8S~i6lgr~f!hyQTh35#ZNm|<7t7W5Z6ZHu+*lQ6;UmVXq|e2e*lH*a z>dgys@>FraA1^SD5CGMcpL`AjYvRi@4YOU4t8Z4vzYrabWvbnm!e z;0(Z~*pQA7?M3fl)K){)NkO@_up$%~yQ+0=w#p-|$XvJ4faz16^Ob0bl~}*8gf|OO z#h&${y1?k6rEnKKm1@LSqBQvpePp9Hc77Bo48|cZlnf&?$Sr zfg*q2A+GcSG*UCmO&@_lmjoGDP#0bwGd;hmL0^ls0A4A!4K}nBk0)ob(*dmL?hv|S z;K2`|VX(;7M;Y=>C}TI5&JFio%FzsfgIuz7eehQ^`wUIhszi zR$_0w4U@HLtt0>vwP^<|iCV@|PFpJZ=|Bv&XEURCH3ruK6eClG%MV?$5nlVoC)EdU zQ2FKpmmiQc!b{-zq(Z4g9BQXGFrmvgag-VU!p@Ef7-7n6_>wfHF}zlD;CSe_A}6qC zR7ex?W|5k55Mf}4=QZh&JD3z}k50@eGPl!jFRBEF@uNd;@955s+D(Spi)hh8GWe6V z`Fp5!jcm0JV-xQ^)iq_T^#kP+orEy13J5n7|(K>=1`$kL%7;HeyoiWs~Q1TWoY7<*1`25 zYP81w1q#UvQpl0@7&-7E%8qPPUHlLtb;ts4_f}JHWs$ijK))<>88cIN7B#{>IY*am z2vv?eG?P6Wm@H;(IAi5BcWKo}EkeowJE+9-eMr*<(c(D_dXo`dAPih?J|fD&hUj+4QT53#Q55(A*a}*U zz~G!F%{yX5n$4tvA?-yU+r})$6Pt8u##&d7vnkT5rjLi`191(_Cd*y?SZb|RWC#Ass9-u|1`C?j!>S2Rk8Q-zg173S<>3OOVtyYa zXC5!ACO9xf#Ke%rI5&n_C^1a#Drg!|VD+Jz{U92rZsSf&hoE8<5ZkO&m;WHz1&HZ_ zW-)iMQVX&tm=j#VM_0kJD1C{mST9PvG(~aBB^W|KT7oHSHb+TicoC;YuX4F1us_&J zHIG4WG%)y3Jb9Eo2h@ag61bpo-ejX(+`%|6giJTXlYmonG8q z_X3SXw8iK{qnL%L1`0elJ$eo*BgSu$@CdU{Fd!!>YZxIl^CywfLhFMI!u6jru7m}s zChFUt#1(FmJ+$Ka0kv6B}R-5!v}YAXTD-^tMU^t4SxAU!N*90Ija(r7V&EK4Y7B*9hbJTzDE z(&9UNMREXK&)1X+p#@(v`hiluR#abDovDVq``?smv}&cTJY7 zxe4Z>$mN4E-DFjR2@ojS!#{v&s7s)i07wyh5Whf*P@3?gX-LCsR%CAFm{ypj^ut;g z2Au|k8K9=n>JaqQ*w)Z6AU}%o5a0&{KdJ|bkeo$nsw|J{AyFF?0$W4%fWzRWdjJnb zUhUZ@dO!njzF&0q5+xk<>qRa?RhL^v^ zIDm3riBZw~Xc7Yk`LCG^=}O=gk>nAG;3-)CfE;|38xz>alMva7Qmisv3`soqA<6B* zxA{aAPeOqZPr@K;o&P10_u0aW4-x8Of7uoyGmTd6Aw+w6Byflq3pICtAq4*bXsaqx$in zXmi>ClOh=$;DSS<8;`8S$PPP%ZF?3VA=aTK9`St4QLWQpG?>G?{hr{+>Ul@ZAd(AU zI(Bww>V?(P7aDq==OK=OnjnB+$O}$L%=tiQ^+r?M2Q&odZ&(uPSqo%A71(I#@kV2s zGtfM47GwxCRfq-%j}Z)kCngXK0Yq~v-<-ZN#1JquseK+DWIGc_0RA*b0EuXZfML@N z0ip>SDFzbspcw)hpa)UiHUIai33|J{#L`-dC=BZ7;iO1u^4XG=C7p0ed zhQtD62NrBBsuq)3g>-Pw`-CV4)mDCS$p;A89_uaid;ygeYZ~AqQD?(@^JIG~kDJ^} zrl+AW%q&!pLBVApa1J%#D9#a=tCFK43;UuYkK&|5m0EsO6iBRYoT#;`^4Fu{1;Yu9 z(Xp-PjtiB&jQUZEgF%{ZnX6-wm2bAeDLU&9(a4F$Tv$U4`U6Kbms4&pWj?44M5E$7 z%hZd1h|DIM&KAV^#p#feiR;rp#1-dqYZZ2Da2{n}uF&Nd$N3XZFQ1L_gHMQ|IBvJ; zgzges%}$6*y;uwjz10Mp96A<-V#+n}B#c9_EIxTsbO(A?oD_JO9rj%$eRC40_mT8_ zI4N5v*|^1}DUgYE1P4Ts@yvi1EpeHw$J25Qwxsp6Mc6XZmgMyOu8$3}dFU{uhxZymICqFphOo70 z31nT!SX71I3y7y-I2Pk~&{Razk)rC(YJ=-pJ~>ggH#SY~DRHTQNp$k{GP{Y^sz*43 zOVX$T0vS(MvPA>}vEgjj2q^!5An+(5sNtCrIQGYgbjH{<{{tS_x^~tmQ=1IgxrsJm zO+OQgR8v#7xd13Vm1W?L-zgM-Y7U~`$T^`Vnez6Kl>k;7o@|=sHgL%F7C+!P9!g-T z4b!9GMJvuo8_a3xz={jo`gCLFVu$n=E}8_(h(9P|a6%^zywIh&_!5Wa%Bb4HIMZn61c8hG_5>nrv~zlX1H+qW>JlCkl1{a%1Ir!M1id{B-7mtn5Q$ z%l$49Fq?f>RK;}lZ6@IEXJ+;)L`Ya^e~Vi`-M39 zH-_vl;$*XoY0sia$SPD7E|TVyMP%Q%O(Rs#wN9BGlz>~qS)Xyr{EUfHaDK642rj4J z3AhM?kDRi5a2aqU9Gfg;L8Dot;Olb(@X3fg5l$T_>@t&tUDGTfTPCf*xt-v)HOUUO zL&)p^?eF*+V8Afvu&cf#9@eoKn8u>wA(GC$+a^><0h$YT}KZUbBVtPva3jCk1}CHBY5 zf8liT`EGfmQK4RQ%eKZ6wZ$!O>KbeThAVGe3C`PM%>$i^eg%aT>ZDbf^d{(CHdnAa zsw6>P=HucRO(NpDB4|fex$2cDGYZiT#uDveP;$H;Iy=v`;`m>vJ07nH0z;)nCd%us3MFB6vd~}X z_0Z*$5zy_)%Hu6Wz}p_dl?W0n_>u;IRQeDsP)tLI-ZUP0c z7f21Wbq5(}>l{a+tilo-UJ|Wno2o<0G+LoP67yrKR2L`7uFYz>U((x{c8F8^)1d{N z8GytsGIxdgzDLbXl73^as>a8I68^$DP*Le(EcNrxB$<)@K@b514z}W{chGp^yg9|l z(cUA|FC$1`6W0StC`Coozdd1;@(_AHj7x~2Ln<*Ky)@72IOfd%(+#o${8%9Y-@4L|)SP750mmo5OqTzI0aCR}k^e^U zZUip-&c$5Le19<++ZD9|Mix~ai)3>mS6 z8pkFMbYv8MWCy!~-{G#TQTH^Iw@_7E8_N50s0jzoNuoITODV0FA$&F%IEYueLEV-r zTVAL82aC-a`Uhen<0N*4OMo+{kH!KR_7-xKKV=~Zc`7P)`Wm_&q*WzYChg@zqg6kq zO3TEOx=otQZN8d24=Bit)+*R}GM%9!M@bNsYt+4IU^^eEQEBqLB3e-~CV1XOV&Vi> zhQ%Z-ZKz656+JXZ)!;Ol>c;+cJZ2Gi^9L#`U7iyUDE#n)10$=tHeEJr2Q>IvPE`Ztod4d!KNFt33+pgdg6DmPIJDZtL)TXIAr z2oqhC7L2Kh4Eib*uLeoKz})bxjl-%YsRTR@d=Fr!HkLhz$zRb}UUk(3GC$#v6ZJgA z5J4QMETO+{6(f!6nGpngV7KU@F6b7yo)G1)Q-3vJG;%zOJ+4(&-0t8oi@EN2f;?K%aZq?)W$5? zBB7L)yg?AvpINeFFK#Idr+JAM>8xU`H_@{`j(!q+JgW&+L>R;rklr--i)>sqYFM_+ zS6MkSzbUZ}n>PT*Dc*)mzTPG%hR^d$jQ$v?2>fkg;KgQj8#8jZ*^f}9K% z7peoM6jX*fVAbI|@FnaU2T7SlD?81&qiPm^wIxZmN+%`^*(OEL!f{wR0fw zH*Fb_`C=m5`X71$b9h8AsB@=)6>G(Xdl3_egv$|?1dQ0dJ*DfBHAMC=s^>T@6|Nu( zWg*OhE(8BZZ=)KHv*;d75P?y+i0XN4bhZdEqF^^ETOeFrRBz7&p&5yyQ-F%7z=ja> zCSo{Fr!wF_5`j@2bXs&l^zXopYB&nqxX3ag(nJ@W)+xvfc9zcdS&ryJ5wJ6kLS2X& zT}LF6qlP+aEJEYQDvu^bubcL{$oK^rkQdzNXSFbbRYFEE(`4i+UsIU|PTaXEbG3o^ z1U5x2oWmxwl~|2L9__J0=YAYZ$gsEsGu(k~a5r6L+#I=nLLzKRN9Uxa+Yfot z-{Lr0hLslUtqT&NkYMobjByctQ3cIp;f0Vb=%?nREN2{^kAV|h0&wA+94lKm_z=e$ z953K-_%o!Qn!L?H($Q0+<}{Nx#D^hO4Vuf2=ppfJ!h$w!Zc>*wNB?8h@aD20pcjT) zh2LN;ix=IrFf?evk9&W7G@6EPJ5>OiI2Nwn_MUm@U)z{c$xCBz*B} zoWzFa1pICrb5x-3W=qF*;VVSS1f1BK|0Yf@u8Q$&3B-kN)RK-D-8xwTyg0&DMm4sF zUPvvVr3LiGb-0zZbijcKt701*sW|9lIwH^oSc`$qP$XOIaKD^7RPKUG2`-9d2%&tc z3>14liPQE;z;9SH$hMRov^{BS93R5qvzvd{L8{AVIH0PcX4GU1Lp@|8-fjATHtPQp z>ObUHMX>+X^i8w);9g*u-;qU2tko1*HVc*mTHSjQ39W1;CP^3&3<{lg`&(#(=s+FS z8cyV0Ot2({>}ZCUAg+MZq9DMD6a%IqqV6sr(h6N%(8t^i@OEqDCorerZUx`t)W=e^ zZeLbH(c`=|+2cJFWD+r_r0wUa<#RtF~eZUzFt5qNJ31Ag+T4Dl59JGuJMFby|znBCVQDb_S;=jPkSWNK*m`ru} zpo4-?*>$j#gYc22gS955VjC$k2n(hT)^Js@Ug}^O2XO;h2P-%TVh*Odsj67`pNkAb zJqra@o9)TD?c+ea!1-L$#(C`D;hfi7u+rj&^ucH2G6E2l8qI{{ln{;^mNbYpqzqxP zC$JdjWGq(4Xv784n4|I6QR5H96v7`UjPYlqpaS>%V#tWAyFGz};(}24AS2AifySP?08E&a-49Z6Ux1n3HsZ{&U{|2YA0I;_?k1{Z6D`we`lZ}uR;6fCVtr}1||h}HbVc1u~yFwo;yxOVs^1r zjj+w9LWgXjO~Ck59B~2{(}3BSK?ijWJ;GR*#t?77n!vL{Jz60B?T(b%M|OdkR4=SZ zpo^oY$3h02C{-H^qz{VJz5;nW%%fKp%5LT?=L?rDZ)E&a%`248-!V4cObQsG%UPKI zUfj#i^WkzPU4`UZy9u!Rf~83u=p@btiwqr@!ZA-|$@Y67z&yT0E!${*dj)Xo#28X0 zj1pbbj8`+;%dEB%DG9+tc1c;V6vKrF&JD3;Owz&hWUt$^2m2IOz>6!}Za-vlE1f0B%7g;$MS{v+$ShVjzytliDZCmq_9>#urb2^yya* zNlv6TqA&4S9OSqPKOpHm(D7LQ%PgFBf{GyFQHrmidgBU=gv1yO0{Eiz^C)I>EhV@G zxX|-~-aepNgqxdmX%^ROip)H3TyHx17*F@gNDdRBXIsOi&q?Eo&cd>VQ+Cu5It>|T z<mG4a@I{g5DT!0;#Gfi){Iz>xe`!9^!_m0df9*lb>iJ{Ts)x~APxaf zzFA0mN1WA#+Yb+i9*tnZ?xz&Qesz&K5b!3_btQnEZ+0o7v4Zws00CFx7utobbpVeU zW)_dq^*RSG$B;Vc75%g578U1BRreLi9hsk?ih<0UCq`9bC(k0^~_ZV zd&|!Y~{S1?(ml?%aZBpFb;#5rNdRcB@2yDRX0nH%O1CDGL{5Y zv}pyM3kUlh?286n!*<&DmRy{HB1UvM#_fapMS8(i>b>5wH{I$w*;`&~yru^J zLv{)LWg1RW?{LkMG*)1RTnOP$fWJJXF%i@hjLyKH&bU0ZOHs3;=~P zxKoGg^*j$qK}-Ngb`UEm#D&x#5AjmWxiOt|LI4*!tx33PO>{_$9VgqZaRrvf7wnSQ z5d5VO!@?n+Hu`t0A6EN({01r0|1Sp3W3S1J`{)$xhafJW!Z!)JMXOIp4AemXOOB1< zUIWA3{c*9q00YSWb7-TcNxG3qQmt)FCkP3QE>(jXz&ogZXysst8VkI$!$v+#Q#ywQ z4@by~ALLR1J{?deCDScKuqE2!p}O7HcCZryvoD+#JFH$tiMsuK`5*P)KC(01jO6r{ zonzNJJXUo}2y>5gB;3;*_jk3SL z`swFGe%kob_twZT^tByNtJ)i7KV)w;NOr{M#zC?hJ}(YJDAK7&iJcm7{Qg4>|z(bFlp#*YA)0DayM_`^( z>u;7#{ujXGZ-I{Rq$;>YUhuzUpK=SDe@cCSi!8z?^H$j%pQ~?0FGi@+TV<}sOj!3Z#7P+4{wt%p}|47V~b>@dh&L8A@H>EcA1?$1}<5!IODmbd3kKh;A!z4>x^;*Q zxSk?29f=WjV0Uu5IaVm+@rKPE(lbW2y%UtWOO@Oy-+`kaU$GnlHGNF6%)&gHU5wG& zrnVK!+-5lMLJ}RS5o|hi+y#;~!3Vv7NtsxN6Q^AomjK}(s{tiI_($s55=jo=j+MwK z;L_%yq4HuJQCK}xZgadcf7A>29J@gsoG71EkKHXtnLmzG&U<8jVBY#?GH}E9PIB_- zu#Q)AXo%Z_5y$#mhwjj!F*@h%I&@5@-KNVO9zB|BxmAb8jN;HOI`ru#uJdLc`fMDh z-K1;zt%0s)uul9#y?>9)3OxThm$*Sk9*1oRbziSTBqR{JPM4oPpF`K`(Be59y2fq2 zt$z*FW%j+oHC(N8)#xU#3f8A<_?J$bMxlV2aHS6J(pj(2p=#ar%iY!?9empQkk=vT7E9<8#o2C1C zq0Y8j7wW4+Wjd{o4t=bvzQAq$TCH-2$@IWnT~lwJK23)#UGHalbb9H~%;y<{J$24+ zbSvlSv^O<4J#=WTZl$}<`IFAsO{YDrTR2yTP6l*hSDp1M9qOV(Cv>Q@+xk-fDso#Z zG(OL9Td^R*>AJE`y0RB_sG}~uRhQ|YLnC!sd$;wejxW?@0tL z+roKWYx(}CDAZP`{WY3HZ6FBokSpR_yDhlau}c3KL7`S|OCYpjFNa#Xt(CuU{(N26 z)bSkOLYJ97o5`ZNJHQRCz#~4WqM1%CJIM(?@>iv^0&yA1WDlsk+UFoN~ zi4>ixt8JSRiwqb ztc_=db9*~9R?@y^K9+1t9|Eh-{ zklDswHRS<$X*YX0K&D4p3nXcwMe2WOKMCu-Q`Z7cS_|YpC<{}fZ+F8><8C$hL75uq zj1CKunBc8%b>l;_y?Xya*)pPK=sFQCL(Tj@TW<1@%x@UcGOdY(_4*!l?L+b=W4Wq& zNOsNA$eV@r4($=y*ek=`Y2|qsOOEBrdRTUCuJct7IMG`u28T`YBkVJMgOX5qVAI;;B1a)3$k3u1VRC zk$^Bipg#v=J%?2OPT91w#beOnjESoFaakj)wDY7FRPQHbE4hq9CVXw*|AbuXTM}*AMEJ z@v_voMYYR_YphO=m)F4a?9~&XXqyKzDj%OHpEJyFPO8KgzyP+XPA^Dr8Y-lPGAg9b zphB$b5>$UB{c7wB(t~W%UqJbNYSjz!9E5&HDE0SP_;*7|vcZtKm-I(fFbR6+Hg(q| znV0iBwomAgNU(M|C#B;by7p{cd(9-Sedi>cHrl5WCUfn*C(Cy62Vu%Zod$^a5glDI znKON*L&;NQVWa)AmZJo<8bs{$IT#CKMUiW!a82W<$TlfQ8Js01?BF3CyJn%xRJ*5O z{P(FiCC^C;cF`N`qUxux>e;5ARN#(B)mut7OF72X>>_h|R5gXuq*KYM3`gUsod3e9 zcK%7U|9X1*XCZ}hgN`8@%)AvPm_PBcBML8e?OoGP6`Vj??9TtgvvlDz| zfBmAo3^f+KB%gxY(6?Vg!Q<-3mjGU{1Zt$bvDVM8DDQL}Jvgq4rpxihpK8r?>22^Q zmOoe>x9NiepBR=S)4>9dtKKim7QlS*%Vfe*6)(#h+wB9%la0|~mFLsmvD4dtucPw; zH;1((6xrK53h}D@4B0iXd@W>V9Ld>u*u6ho0eKpS?H4Nnp&F92ad5~B0WGX|$8s9W zS}bm#hZK#&<~wAnuu4e|$C0cx5t{lkhahjM&L5xT5acb@vS`uc*L2)u4<& zRv8b@gJrV4SsJJ2m0>kHTzy`KW&b{P6p6%2h<-8W;f-{k>OYeUJUvqvSTYloJ6s)_ zDX$RBO0i4x)|)be$UQZFWiwjn5o+N)8Ed*HRpqPlJklS?VoGQ` zz>@tS7#-GP7_C7^)&hJD&r>wx*XbzSj>(Ipt#4jqD3WJ!b$w^aPR23y*ep2+OWqx` zz#E=anXhw}OJB#v$|1G*b$M>)e()bcs~4&Xrw?{-POGmP462 z`-B`DQ#dNXFmR|w*f^PqU`Q?uqV20x$2858F-XDFHv;}Is+ zd`^3LKBs-E(@yBL!Uddm%K}cDwt#!Qd4X)3LS2Pt?5tBbN(ifM7jlVP7IK%KTPV}p zJVPj)mBq&$9oCUdf)W=EDRxwrzONs}oru5xp{r^ChRp8-BKrlKAVg#anMh&i-3-x121y?!)IdrA9Ea=en)s4n2y=M_b9s_qUJxHdz(m~$~b@KO%V9DR7 zir(hTL*GW`v4n|nq0A#F^E{ngi)8raPF#F?YZol$3Lae?s-P+@*xG_LRPdfoKCoDJ z$bsNw^52yj)&N=at~g87n(fr4`c)D2xt2m*_^~`!9*HXa^8NGvO^}xajO(tDcq^rhG#(d zVqN4X6agd2S#mlG{M8mhQY%*B7||hhY!xcormp`$w#j;y2=)YCe&P|VrkfYGZu8m)T-6sJFsF+B z5C+TVl=Y#^XgnUJ;GHI(kD;T2BY0?vwB2Xbun&Qn{c7TeGL;C%34)pbA$RPX54pC~ z)tCnpl(jl6eM7TfArAf8gd8aos9$iDrTaC)g?Zrqh?A|4Salt$L$7{>dv9R>*oLr` z<6gwDpd726AMt$1T!SW$tG;XGh3&^-w~+c?Eg=+X`-pOKdD^}rJ{Q!&v86+y-D>3; zuH@Jn*&zj`9ds;B*HI?Xs~&5)SNE;upP^VK@24*ON+dfC}r`Hi}9zigud>*Wb!l6q`|+~=Hx`Ium;;TvRUHG89cQ%4wf z#4zR9gc>HP-kapV%n{#KPTK@M!I)fmY%_?_n5?pE! zP$NG@FQ%$BpUUUV?K{=Z-*MUCiLLSgo}sz>Gl285D%plxI;(f8nr(6wy72txxGSeB z4}A{hI3D7PtvI{BQK<6p>@hJiWtC5-P%-SCy%Y`jw0W(TM&?G?reU4fYj zx11O)tnx;y!C%X_bO3m0Qzd#os`cC46+MnvN@1Z~bsb2mb1OKP`^m~|b{#3`l zmy_^$ZkJ5Q=k;CSEq|&_yX59H@KYF+d;&a`HqpR%O)%by|H>Ov4q(*>p-QJGaYh!s z|5JH?kgMXWznhprjvLK@dSWNk&CQ3UOSyNW>Y0`P-PrRr$|^7XQI0imO$*@h#p@<9BkB8() z()E3f%gHZY9ol|z0b zVw&8Sx~lcKI>0{HFxk}y8&}sRyXgGF!erDlTYZ)68eq&(ZBkt4`?L)ZBprNp z$Yd)@=okx5?p97raZNMeP@;9JtE?e<9m^iaaQKhhI#b8LNp812ZtLxo7IMFSALh*BB~7XPvnEwC4YxrLz*&PyF7K<7$^vXGc`WT&B23a$Wrdj#L4$)v7et)kuAp>$(~=cy1G< zzfn27i7VAGW~nJ&S5FBilE_yf)RsI~L*q?#$O}6CO*QekG7&o0=jwo(bN&gT{AQl(f?&_4hT?9? zbJ1I28}g`?%KzrM?&PO$1~dnp73zuRptrZw^5(8bK$2Zs;L)ee>e?2rY&?GTU<=oy zCh`nx>uRAgTe$+pTa_=i0u>={QES%>1TSmjYC=I|DMv7~oogTk?<;V1!Dm5%>s(a6 zyTJ7@J~tJ*?#E|aA$stZ>eb%W1E4+8-Zh_2{g!}B7BA`8QT;1?XVuQ8sfO=8;#so(s`}m`BM{zwVHNv zT?WJZe>%BtG}rA_pLKE-1lSi0+=$~`53w8kN$`w^lqSU`h7XhAh&k~H4|$y-ISkS@ zJSfH`pv%DQMkP2n!tlI+)1?$SD*ST-{e-@VetPJxh=nTwc`Ks>Ru8oYh zTzd?B*=#t+)u$yIbkNMAXlk7;!)zfa)a~={0m$C9$h8iv)a(qRo}>D8c8!MH(@#5t zV1HAhi>nPjow~RRQ+CbQCwUEaauj3L&Q}j~ag8w6SN_q(wbTgUY670npd-RYc<%+EdqEi#L`_9elYnVAQBhG*NHIr4MI)V(l#GmY^oNLviipbN|M~87?qgms(C=q4`<#9E z*?aA^*Is+=wbx#&_-w=}HW7B(nFu?0=3z6Jw-Z)f9IX_2>Wu~KB2T?EWcllc@Ll%j zWYE~gIp&08fg4ZA$3k5zOyW2e#*fWe$H6amn9GmzP8*{z+g4YuJ4beuU4`C`s9`cT zfbIlQ1j5l{CFZ#wd4o)!E?t?{w@oW_{XwgsK0|30}su)Tb}6i1s-)>-O9)D<*g=ut1p>cq2Ii`eopK1!K7P z1MeY1|K2&#JE$)_A@4KWm8GVR37$5aCVJ;kWb%njzh}*?6TM4``Bid~Hw&}p*(Z53 z`+tb_BSga$UA{nnY2TgXhvuM@J+AvM|JBJ}twZ78Px4O2M%tU~{gAgyCVPRYnCw-X zrzd;ol5xN(Oxxz=x1A!E*K8g>)q5VJ#H;?p`&JL`KqDheWrJTjl{*|C|A#luU0rH+ z{$*HybJ}U%NVmDvOn7nF(0CcX18z9YyM)WgRi}HWC0}4iG$uA08$?B#z_jmsr+dS+ zrd)ZtcgA2serf}NEjCiV=$p4nJT$OKeqgqq?oG|k?4PK%)0X~mfiC%Wzr-|Rw^(y- z{J+Ps_QN7hhZ{1COUuwY7N6j1T-{058;ISB*_lyBrd2b=rlv}jfIn-+3X*FO+Xfp08o+RB#Aw2 zj)=08kHLsNA3Gi#DtZqdP#HIQDCd6Ng(=$^P%o*Tib7lPz78;v*8wJ# zD~3sBieR$W+{7%kXE`aXkxL=T@DBl2QUDBDgCDu+ zRlpTl9pF;2Vz^W$f-5UzA*{3=K%@~n1|d66@oy< z!F8PFX31xuvoW#L?FtqNT$BTwctMQxG#Mw$4GxM+JhnuNCo~l}MP{tHKDM><$X7br zPmPPq4ts(6sGwyL%{sYZxFdJTQD&yWl6Iq|Vx>hSt1>@aszOC{twP0UP@y6;(Ct|8 zpuXYmVy=BV(TghPz33YuiWOY5+T8m&@2858fMG4ybl?IUbs-KP{dsS!yLE>7{^z|x z{dQe7PX?#fVjL@R>r5ipb(OjC^WN>1va;b?vTV&?GN1W^ci_PiAH>vRx?8{#Dz`8y z(kn~3`q?MHJ0R2C`~~k;mV^mk^!oR1#`+X@B2nQK+OF+p>KEC;d}OZtBKyckP16^> zv1!G3VuMCN%;d+|zJ^;3HW6C5&D=$ZV;{OU{IC|X_hC2#bZC&Ff}41+y0{jwcGtE-NJ_S_m478(<)%@rZY zQ$B7}^i_~zhHp0$&hidE+X7&Vg#8fy#T3F$>4N6(If+OX0Z*PHXyG4@5MH3*lYFOo+VfY_CSn z5$UyT97v!87vGhEqs3OONVbhG(&oO7uy*Au=J~U|5og#^<-uCUYeycM2x)CbmpqN` zdW|k|)`~_KZdVxHiC^(PQ~B5`SYPNi;3ae8SG*GoIb}@6O<(a29iB%OGZ7)O2tiT! zGh)OX|5fjERfy6G9GQZ-FoACTDzolU^V_d_sgN*kyqe&XMOZ5dJC1nKuH)ekL7mF8 z?&k0jm6zDuMy*j5mER4A;qpoQ@*sKQ6tS^ru=ZTqmk`yVFk|qmuX%&AA5~zs#vEY- z6VgWeK80gO{|i+_!+48&Go}Dtt|YOwIu>4|!o(AaU>1#XWWS_LQ=#>0GyWWJmRnb5 zet(Yl-%2N*>z)5yORH~+ho_3UBcvq}Pk+$V5F##lSfp49dyu=VttfL~R&m)t;+JEI zJ{s+G@P~f^%HZH$lH|Sp6Qj+v;luhK9Zy~^v9JU=Pq|eyREF`QF!P;m-4I3$LE<{dWm1ee?U?T8H$=sov|PhWCNXkNhE` zIw$<+{>XdIUGTaYbD=lDUD0YzxsVlQzWMHj-icqb=)Y+ZJ*wRo^gp)_^g`qv&<{Ik zczyZq3%w^?=R4+!AA4)?)HLT}Z)`6cw$NV0<8}-^OYSr+7kfjp&tJvue>eFDX51bAosT!nPX#tWAR(qsj_(HFv1%x=#Qq-_J5;q{J73KnB-+ zmx5Erm*I>GyL)@Zws z71q7NFIPPaUh-i2g&&sQnFoX~5``aD-Xmt58^CJ&gEmMOrw?OO-4<6j?#crz@CnQq zJbw*UmlcoBE!%OtK%J)S67TS%PlRBQaNHyh2)7v*&Z4Ig(REH)fj**!+$1-wq@`4J z$K;9T!b?zPooH^p#Opi6ew)AgEA@$Pn~eU|ofzw`?G^yA=SJ7g6f zV;jvbbmsEJ&%A24J;O?Hfk$G*8guy1y_Y7#uqwr{JbFp(3}=m{^D*8hN+-tchQpZyPb;(&eOiMQzsB;w+AO6wP!BqZhTFJePj#pnRrIec!Y zD>_w-`jULYw9fPnJ;UCX#>^5koyl0N1~} zC>PS1?J;+YFAViWtcjziWDZYm60)5KBLj{lM<1$mY>Yn4*!Z6=LENjLX}0S9Yd=d_o78`~*JnB)f;-@*&KuZ1s# z7XHmOQn*(TDQFgsIO1btxE2WNT$u(idpXul3fkCrXc(+61frmUTRmvwGRKUQk8nbB z1}ETCXngDz{kQ?f9)}f`2TM5f9Ikf1nI5U+{Pc+Ugq-#cC!`~EL3AV@56VJ3R}hCZ zd$l*9H)Wfga3i?T$#`fqJ#*L9-ta*dajO)SyL=Q)kBe^@NIUd4Rc_wA+8dTd_0Onf zh%L&U^eMrm(~=_;*?M)~jBk5O;6ENi4>Ufv)+;E&4P?dM;|hEDLciIED^4mn<+?FI zR>J<`J9H-(jNd)6CH9Mnt+BernGCl0KtnVVCsTu`-z;-pS5HQzf1BLf9_9UX%ZPvOJSz(p@`gxgcpcSwpPhCk1!s{um>;~ry) zGnVNo;w;JZPEJaV?V0MGoSZ&7<-_@8qi}HQ=*830aoUM1^6R1bCgSW_QU{wOukl7y z9E|HjX2HScylcF{+459IUq)R6DUblv1=bU(8vI(wwuphP2=6EZ5(b0iIz^Em3ScN7 z6I(TqPLzx87Y@vIU07-!s40aShf3@x$$ISwe4OvdM@)GNg330Fh@d#`tC#2QfD;&^ zJXi)G72!lkiSPGI|0BF{u>^>>nI zo#X`~e;E!Iz_LhUM_K^uY=LFfC+8G_b#ND8Wm6U|+Gz_{4lJiruyBwB?|}xQ;PeCy zV8}STc9N%R0&XG3houPyv2CyH(CBkb>5a~~tYwB8?4ok=siboSxEb^~HLcj%YH`9; z+{FG^^A*3JG$?l7qLg^|6X1{{o7L2GwXW)80S~6~92?GHr|R(JDT>-rrkTkhC}@GJ zi64mt1X*0*9KBe}gNUg%D=O6RDR&yz?8vT|iZ2`eiW~H!CmvJ3I9-#f&>CdtHOwx< zyk?YBiIXp)T6aRK2A0JwgURAtpH-gP4|!w8auc{4-AVDMBwPy+4DF1~vW&p}#G_J~ zXs%YsRZR?82QGqVb2Lzc(#+RzD5#Zj_uw*iP@v8!sSL&R$*CD{P^t!;QFxwR zq2SHDBONs29VjOq%;p&?bf?Q>5dGor^n|2>k!dfIj4);YDeEN3rJ3rSV2~xUa){Tm z;70m@OKJpzRN0u=0&z+9Gvi|)NgyWY#)KArZ+z?~HAcFA@r{9sNOk zHUF5hWeOt^v>=!StMgvlliFVb9%PgpG*W?|>m;M1+ z`fwq;8ydF2JHccjT@Em$J+xbAKC8cnURt?5Ri>B5p(4-H4 z0J|jl9!-W_qAxMJL~*XhV2T2ADlFcz1&K-*_+2T3a) zjQq?X5hYRWA@ZR4N`A7zyO0;`fl-A#FL;*R@nALIUW}6l8saC~k$T9=&urwX?XBu& zZ~Wlears2!f+e>a)AR@j=Oc9yY<|p?O$HgE`f`ivB20fAmHDJ9?5Z$5++uHI6)=MZ z5JoyCwxtRRpA@nJ>rb8aw~9%NL=gNMXmYeQCiZa^00i#T9tXzwuSWmpPyWA}m=mkv zKV^h}G_tVP74o$uHv;&k<}o*7OEhg1iN;PJScQrKW)q#xTGn0`EFws}m%Ibw3|101 z0kF^1pD!lwBx|g*u18gGeyS%vJRgm6SN4!1vO+qGvS4kG-0@q5cG6|RnjSW`nJr;w z>8gB&ZFmuNSed{}X4Rww05;etH6#F_LgwDDEr|2YvS2;%Aml}mY!4y9L8!1h%3W_E zvD%ZeU~3){sp|N4bG~6Mf@HMjGBn*l8lmYLgpLqvvw6&6ZMv`p*6Nf(xI0AW5!S4z zUKZ@Ym3Kvmw8ju=-&7aQPu^vbHophqxumIa$H&xV{npq65B}P|p%3NE#PXWvS}%vDMIMnU?ivcB{|?Rm=4!4ohvd%;cq% z%Jr~`XG|XO*~){uxQJA)e9D4_VtD%SI0UfDslg?uR%u9Z7HYZ3R#LDZY!>mV*wN{p z!4NdROxP{gMH9A~{mGDU!Vc$=3Aom`a+XKYofXE_piGtKGQf*1QPYD7Rs?4%5C!GBic}V? zU@fT1)9=~@BBf3>s)OEBf?-+a`5Bf-)`i7T>;0`X#|nscmgrFub6p3y-%`ptIO!JF zd3F&T2*%-IJ=t7oiQr!a|8Nc%+=N+2Nm*${5?2I=Py`D=Ln$?Ni^yN~cdfuGNusawQ8EWCooj-PA6ryU8$!=F0j1oe#s9sCc7*-M(aLZ|R2OYm1ww22 zl=Mixk_Rej6QZ@mVtpfy_@bp>Q(Z;0@xdfS8+cn}DPEb1ia;9?-(TND%L3T(l1nVs z2W!=V5G2ZYh>kraX7<#UMT-qM*8&6?&`PRW%MX8Y(xF8bTl|%XAX?g2lqW*r^9(*8 z#V%g^OVRMb$c7^TQ~QvC+Id=hbb%kZf<#+nN@k=T_9{sy)Lfk!K8aN@frCc75=Lud zUT@*SBmgy%?z9^i!OI48a`B&tyGvLPoK*G27qMo8y4(`ZPPZl;1JoK><~swSL6BIo z;LW^PqAUqoTz$#yLAiXzohy5WvV%uUQ)7@HEr&)JOEYgJ7TOtCnd>=2RE^$L_yrm$ zQ%^2WTI#cS8?~6mriu|#1iho&={!sHYr#^h*b#G#7KBzAhdSkU&t7C;pvEq=hhFCHo#?ATz7 zoFAK(!q}|W*l0|~#Ig*IC|{y-I687K${^LGt1VqtGdgzLk2Q`)MP*ww2KY%MiyL%g z?Bh_h>icjgzTU}L>^qVH$Re4Qji~h&$(Yn5teW$BCi;aqeqS2ZHU=(xJHjo(F+nD! z^8hts`O#h}m0@{+o@#=!slm@B$@)CAFm7FYLnz5=Ve~(fwW6#(Wo2y8Z*4@`n!G4m zlgdP*EbInSX7btw?Yf94OOG@MH*Rh}2j@dkxjD#nvfLb8pvSTx+aS_kB&u7D@BSi3 z<@xzhm_*HX|753@zFic~8lJS5YCm%vX331JIq+#aE0kR{E3)xv)^v$VShIKg0974q z6duG3tF2n}H9D7qckc4FV%iC0=MUdl=$iz6NM_yb1s^n7=(9|EhN<%|FvWsf>WG8qUl;!u892!&g?AqjEct%ZdQ&K3vPX1=p-!aW2>qU)dsB>=I!s_6x2R-GM6` zys4AZ()}99mHP$P%3`>(oz7hm{$vjL)01oya4iyN9h+$$r7Lbzk7h=6TxIwt#g3 z*TN6LSI5O^FFeiSYkOyKWwoJYhtM@o+ukj>%$)sM1J;#hA+a5u0k&7Tc61A_dDbea z>v7EHZpO5gek{W3Gzs>Z!0O~^TicR768>+5FM{)A_f0CZa82JYxTbdsSC&lxR=N4T zX|U*lSr)MUfQuvMea073ZGESJwTJ7;J23O|yB^3rjN|M*z(xBgi?q4#AYISy7F_fG zfRWk$O~?k|!j2)_C3Ta>0%t?F;97KfZsF{7-Hg->PL`#hcrmvG*0=|Rb&apaZozd^ zTSk%PVj4#rZFYK{N!J#x`TGUe{5`>C=f4Hu@kXSwE~THYgdYpng8hPPf#AySl~X*A zb5?PR{g{vo_YRj7m+iNe3-^RD%`=Oy-v0&%@odMP@xR&l0 zzLplj#rSt6`v2Bi1-D07x8PdVZMZ`DC)!pnD*~)*xR%_0+5R$$6&>Kp<^mb+Nd#Nb zEx7(w2jTClD2)-0vPZ{lrG=|`zu;=_7F@r-0(@Zv zwm+P8)qcUXsv}(4j+*PBc_!9yT_J4G_*%WMaKZoKtgAZ$)(KpH-Tw)>rYpEG!lM1I zM6fm8f~%gu>HDrvvGmoV^Ynce!CJZn*Gu>8U6jstLHKL;3$C@@g6mJ(+eQ*aHh15w zn*5~ZwFZCaMis6zV=TvoZTs0_Ec?8BG~M1!L>?z$o+v!_LQ9hwl}qOL$~01 z?RAtCd(zHH|I`KHH|`f)jopIl@dqP(W%ub&-ZF~$`vuqh&fzMW|8^ys_r|_xPueI? z7VHmP=LHKw!1k~T-EA)ecPSRyBS0`?j{q^4TLc_3kXk5s78W0~tOWN}nX7EO~bs%N}nR%CAljY^rlN2<&MBKm5Sng(W0r%;6BPN3fP~kyv+* zi7hOV33xfb;GIiXGq6Nn9hVghfjHt*FdN4b$eu}ZwIo=N$4l8>=Q6_5d{tp{*2KeT z7<{;|-(jR-8*KMoc5e;Jn3MiB;-AR&{NnaKZ{Y4S*MJLM=cYy1U8e`O>sz88WOF?Y zws!(atFC&7UEdgWJ@?^0d}q7f#doQ;xNg&+?YjBvn_Q~w5zLo0C#IyB$>|D2-1-wO z9;%=ohoQxT83A4geS*N1jx6m&J1cN1w!jkYbdP&ERJEvNN*Eb7G8d+Jc(;0Lk9f>= z;#_XP^I~}0j;Gb}Dam_u%K>vM>@X{ImoWD`^>dprd2jL0Aecf=HO<+uQvtH)kg z=_Eg2emQh zuTKxMKW1iOYq+{(8YNfjeo~K7xV1cbvC`3*V-~9jHgTieCbQu!uOF92$IDILw2SVZ zn3_4AQ~T(fWc1Au(KnUmZ*O^r)lf_S=$GT7UwWF&1gTBm{tq`@GUJ-ZI$L9oAHU#bKcwDL5F8@WvC2nKN(N^ z;mmnb15eH>C+%T{%bf_iDD;?~CBqIhm%r^DKd8borC{KsK~Rv3`LKuCXQ0UN73Sr) zy#r8&iE9M|PpjhUS*qgVU}fE*)2gub4+c)Is>B|g-6qzF%Aig)l_*M`-&GZr*+lU3sP^C>3mzA5`T-^GeWS_;ofZMsuyjLg8kfMa zYWFEYF7jY@kM z<#W}mB1G`hx>gB-pOP&-pZHMd8Si+XnyiU|WqpNeEhV8=#}*0kkE2i!LCvLSVDs*V zAJ^Y@AU(p!^SEh0(FH%)k9p=D?`u_In~M{{6FcYi)6gAmzP-hhnzC#XU!?GNK-BV2 z<|)Km1xTu@s3e-C&A^;HYS9i3*MUmCBab}Ni;2PTDK`GR^U6z4RW@pB;GTLB4+B+e z9QO zH&9szwaKdCCT-AH`3Dx|`@!(ZsT2I*pvlv!m^s0&zlDq+u}=IEriNd-rJ>v5pgA@v z;}c_g`q%mC76N^S_X2(M&v(6=?0sdGhZfZ=oK{su$O8>vv#N491%h%6Ous?;(T-XD zYGbjZVppiXZPA-sr?|WR=VTE zuw1-QjYkYlY;}ie^5N;i2`+nR79$*#RTAhj8g2coABW~@&jDwbiOLA#cXyb_cY3`` zhBC@#%X?nGfw}KS@!kEs?V2fzdO!JNlYZYDF#P_qN;n`Rzc>N@wzqF5 zz?Lt}{pa)j%);x_=W$coy!5_zAYq+$y$>tY?Pry*IHT&@5%E--6SsQDW*@!JI%}|v z_hQj5D=p9Wkn1@Wfh1tt!!tA&eLGHE^vr+YRWRx8tt=odlps%5jxv~PLx(CrW=L^LNCS=rTk{^;i39FZf22*Lm zPzQ*5rjrF8AnOaofWLFPQUBbL$7*Sf|4L=>u>0 z=;E;i4X03_rJdP3XSJi9nLEw2kG-pJGcj7KPY zM;xsCK{)7wtT}BVv-cF3EU6^-;*|Q_Ur>s}2RlW7muB&{Qz^O3-29=}HzZe3LtvTU ziQyr+aQQe(Jghn(Y>iqQRC~fyZubUcZ)(l;*@Gp7?TZoj zhDQX>#UUucGdc#cm=WWs=lkFZ0+mHjJycftS#@ZU2b&iU!t>XH+g7mK#LBnO~K} zEiO!xa1xe8vpl$AC!y*5l$X`iL|`vJ%;UfE3OoyFwu;3~s_I!_yIjKUO)K-oG3UHB zEA&Guj@h7`S>%YTS{`b->h4WNIUnX;>b@Qf;2LrfSZ5aC>Qh4uUpwJ}OJK1$y8Q=(M0@a@Fu| zeK)-5I}PvTs&wwVB@qv^BbAKSqeVL{whq^P4W$-~f4&=*`|i)VA=H@XzI!&r@*#wM zZui{~O7)0pzcI%)XKJF{5Vmy^;h@`nGl*X;HQh*C_}w|SFsyTnTkjRsdv)0QNyLtB zSJQx8P51DfL<76}TDrWwxTcz@rkkUhh&J7>CVZ;w2TVhArk;)X3KuNCL~ zE%(9{A)G5)5X*4oS_L5OW(|bA5|F>)aqEN2i(DjB9^L`!Uio3$6M30AyS(zDZz;64 zof@rAhB8~nxiS4!R;sGKt)kbxs#Ua)>ABm^jnTWk;o&@nj>Z7@8&7v$|IiXYnD?r= zZnxJvQwayO%&%4)aCEyR&yQB>KIU(qiJvBGm#7`j(=dp@!NN9 zC_?KZb+5e8JwIsvOMX!jm&g4^6-zKl+a)*;&kc+HfuC*<4zb&xd0q08*ukbhvYSF_D*}<7j20pbB1!vCL*b^6F^(^Z;C=`H5FwKn zW2g-_Y9)wP8rXic)*$@Ne<-)+Rs_+?f-u5XOk1IFRy5)0u@p920x@3j(Y?mim}`T)+GI01K2jP!5?2iG9*1h3sUs~?VOPM8)9o0O>(X;h|2+L8uH1266J@-wWl z#FERsC4O3Rafv^O2ltiwy{j3V_M@GueNij(z#`hG;r7RhIT^l>Ike2eeOj5HI;RN48nw83 zR9*y?2x%QbC`I_Q0xyEE6?mT-C8D&$x2z0*BEc4p<`5&|awNc@4WSzIbeTW24}vat za&6~VX#IM4koxj5KYiY3GtUOy{|q82H@7biE~}h22)(HpsyNNxM98K(MD>>w5_O30aSA~BNBYk{@p91D-NJjPob4=;dz8W;V_}I2D zqayQzL#BwYm7Gx(vsKxWlMbk0!yq?2=>9GwA^Im8DCwB-Nl*p@hKLBK@c%0P{=HEZ zw*4Pl(O*{j$B5n6y4%0PS9IbunE4Wg2bVh7)|R{^@^Ol)Rf_2yU(378;qY>OEO#7W z{pAoZmz?NeLMH)KGCiMz!n1v~ld7^d=0LmF@cN)*o`1W+Zqy+aeH*CFhAO|mT?CiD z%u1H)z9S?r;{+juYS}qOXjsz$4Xg5-3)fHung zH(F5}M~6++l}-s}ht~nC0VjHd)#IgeL=1PQoT4m=r_x+n4}X(W4Cp0=-}B!Cj8c&o zNh8kDrP`N=Qyv7W(v{B1QWf{2b_cz5Q=CU!K<+Nn-^dG-@rNK z!jk0dG9c5n0?pOr?$Q+J3v$2+UAguoXjE|XgY)~1bz=B)!$DwQc{H$}rYn^$z`vyY z+%?KyTzPOa9!{!J17zZJFZ<|I*;FOf;$D>&lXNaThhw23wgy>JJme4Cv|bF8Hv#KH zkC*VoG`7u*1DPI47w_L4flKS{t7+2 zsUFA1-i%A00aTEHpn)&e3c1qLu@H{+=(crnSUJwct4fL;vJ=9l--=i2oE(=+_pe5?Q)=D20GD#EeH<&V zM3^(c=KM4CHD3$xRbkFwr?YJdo!Le?FYKV6c=1=eAHpgtDihXx+q^7-em<)guI(JL z*a4HP`lYPjkKsyU>5)MN-iT&l-4Z(d4Y_EF4+LHz$012TLsNJ#pp>j^sKvch6PMBBOh*YiwYkicC+D2o%B0!CUYE z8}bF$I(!QC_GLxN-;iOa22~V0zbKdSvULp13>d)oxLx${r86RwjdJ$2QZhgL7NIlq zp0mk8W8BvKT&3Rn3#IZ`@>EJ=R4hxY)QhT%a2=s^uA{j^50(rlfS&Cx>`QX62QRDY zM6PNp%C`ekd!n{rs#JS0RqDlTF`OrJhRBvR6D~YTzR@-qB3D> ziIth0QW1JnNqk`)-{CN~5+JpJ;Q$fEZG)Ue zcYqk4!|Z6nVH=B!omXS6^ip~n8T6)EF^p2KxwMbp$LVFR@8ciJT*xLBkmfDTbaC};UTs!>*ZX#S|~J{81j0&OGl3z3y)h1k9Ga>1*Y>UFlO-n zYQ1v<%v$+7<$Xh#x|^f-?MmfNDvPIBo^_;y1Ey`ihokjMP9$TymK3oZ4N>!eOeOX; zrQp9j*uXDJwdholrdY(~2^gNzL@H0ZD+Y{nalVXG-hu2wKw!CC;^8KEAf8{73-Qd4 zyF2TzPqwypr8*AL^hQO)$U7G+mwU8aY5am}4AdUWcKFHY%O34-O9pyA9dgmo3J0$LnsL<$yb!*Iui7H1^ ziaceBLS%cn2xm+InKFqg55C{T2G`C=imZxRTc`4%m1QDcc->wlJZ{$7kvvc|U56J> zv4i%^8&w`_PCM5kTf{WBR9V*|$JhWDe_4!1CpT-m72tw!TJH?<`(>H?+pEU8cy@7v zwS;m8gj#nx4G`tGD#@zQ<;sjk%ndf=K54L@H172it@_lx1J$I~ecZ!Gn}YT|r+Uh5 zx1RsB#yjno__>`e@k|p|rp=|9PibK+52p8+k|uBp>t1PQoOOkX2k&$(|3>p~OfV>x zK7@yA{*C3|!TcM=zeD-g(+nHp4<2+D<2GR`MCYVF1*K(^U(i$>>)@Q$oHfKBkCj9^ z2Cq6hc zy|rX;7-B;zqA+roDzs%(>^O;fGHQIL*_EyZxPW@?(# zL&auD)l5m_p;FO{a7`)hc2KbNB{mAt5`}3?EPWVDgHKvb4a7MhESMt5^OuEUWNP&A z3?VR(lllb-C~OkH zD z#8dHl8co;yg)STLMHmn=gamV6+V5-Tq&Rm02WttuA{;PdEe`M%WpRL$EsF!3fLR>i zJk8=@X(>2BVzH7>MIH-yZ^OxS#upa6h+KnpP+4$?`E#-bTYJDjQ-yk z+KRpPca{3P_gGME`kNl%+o4^8u0>tNjNf*3p;CsHYY(=oT+6UsSsRto(G5x&)AdTB;k8N` z)0WOV+OpprZ8Keu*xQ({+z%SQ?!a(NS8A(3SC=WJt4o#ARXobuuHx<7c6EVLx;kHO zn>%u*`6zk!k24)#%$c;R9Aaf;?T0zfN{$UDdY#obf#G;omrujaa*R8ag(*6I@81fawnZ>()q<6*eu5bU9eeUd2kV_=T zX|Gw4w8$MCBDa61$ZZjFnS?nDlrFIQS=vUBg>oB| zLb=3vvDlri6ztZ?(6A$es>3J$p0F$P7Kv^hMW^cGIZb>jvt_j3FI;^ZE^jk&Lmd3- zj*d68!(VcDRJ#xsr;G`EyjeY=$E`}~@dl;zc%4#uyjCgnv_>gCUfp?*@%_KY9(R+A z6?0oFW`m|9LP=Y4_SmD`(IjYCYe^11${j&cdz70Z8KvkqWvS6bzacqPUgyhFV~%o@ zB)Cf_W>M;pqhM}jsl#;CVtwlC!t0?Z_YajeslX~j1imHV97fssD)8DNA^xU|hk-wW zL|FV0Dahha!4`zS%}T*vt5Wc{q4One!(Ns&j_kXjm^Q1xh}1zrC{Ksl9;T$SLb=ms zarZ=y3fZciJHeMUu+{Uo$jaRK}`W}i(8CkqRnk+C;uM|p}t`thz z!NyLQ*sc^zY*7j(HoHL3e(SXvF!pKd)!S~pthrf!&n#56x|u;~?unqNb~A(1W3=@W zDngO84R%5au7I6`!OoGuRvTi6ZrICq?Xc5OlBo^1U|yTu&CG?p#tCg78M&iL&e4-R8)mq^r}5>(JQ;4lMN-AJ_LFM_Vp#1zNo4>MwEt! zt6QX_!+vWOTNdxo@0IcHOc1FF#60`ZRnz<_e}pEBz4-|e)<=_t!*Pp*7NyX`8l}*~ zDy7gva~x>ePnKpt+9!IjlclsHJ*I~IpG}(+L%8){=JV0l-rbjgoe}8ZWQN>nUgAL;xg*rJ5w=AR_x-Up+BhKWzC}=ph z>|yn{B}kX$!G``*f-Uv)Wqy)7P~3t98XYLsWd}?N)@`{iXPpw60K{AR^XW!Diw+al z@vcL}jmmGKygdrr&YKKO*giNvfhqFRP>Cwv8gXEZX%9{;e`AoeMSl6?(-OX5vHUyMfytYAun<^AT-ejo0^B1IB-$ySjFPg*_2Y>iQHyppoi zu}X9x1w2yH+5>HY!br%U4Vr?(&Cg%`cAekK`y?=Oo&C=k}+w?|?MgMdA+LlG(}Am(T1+PcTJy2eIKo zY2VJ?IF#FdtqOX!qb;I=ced%uKA^%mL)YA~h~N1R?a9u{IzhvS#KnybO2q<9mO6!~ zK)Z#3LO~u+9{xEEk~Idh=fv^kYn**(fS2j~5IJ|LQWW|2w0R|`ozVdh0}s7I*J+5X zG>QI`Bchc9GYR7!>mT?zTgS$zj@GCSPH)SD9i7&}w9y%AzNQ`2q(fuO$_Cm{w&rKY z`a{CG`};RJlPlb12-y?IuO*=kO0+q<^|f#m?DiuTvLq}usPQpsv{nCZpgoY}NG%P< zEyaieGnN>G)c`8p1oPOM1%Q>TyEf`lOVlMDx5`iwgR*AzZhaTM;VaB$jQUrX-j62@1&4y^~p{a~c zNrx)s!5ST6lcLSA)vmLLzakzTLe}$Z8qWi<&x4_PmX6j8u%lnV&~pQbeJLUF=reVH zwzJ_{NHy3D7L8XpESOQ12a5_25IkF51BOf=QlQAl76X&1Og~bf$@Fgz&!P|>Xy^6; zmKuX5TcN7zlj@u5m(nLm;LnB~XlmbqI|nenCHAl%11H*P5e_i4u?&5E|1f6qGJPgY zVfnXOC7#kmZhR0w#mFcq;1F}<+CO2T5Of$P#gOh=)b0u&` zXtj2@qmU}F$m@W+EvkG+0e3mfk*m?;gxB^Z9KoI%KHP}G2X?gY{;u5k(ebXkzb@B( z;!bW>7wP^6rBK>>rF6fA?zieW?Ec$4=DUxvM7DkR`MHj{*9F4sH5HMp)KWt?j=6Dye?Vn<@G?O&k4nx5gJwYBAKte-m1uXW-Xh8FydWppy`=RZXPl5* zJNC!}3JU?6z~4M@6Owe4W1R@N0WSN=QB2Y(r++}y1F{sA zG|Y<5#!b(OexGtHV(q-cjG5?<2w(2I)0~}udFxJdMgC>(UFM#N{sCdJ7wn79u7Zb`m_zd~Z!a-lY_n7jNasV&9$9(Ff9E|JlF<;NWe0-0&GGA=%z2?FE zFLRfg4f&UqOHJbBTx~P%GviLq)po~y=3Dufmix>#`IoEjH!Jerti9i~=6|{F0aHCG zSHbEB%=rAvjAiCKlXC4mv&>wVf0^^3c{KmB_CfPz{^h2JOrOcQ+SWW|Cgfi}e#lJa zC0vT`Tn-9Hx52+`LPA66(5exhu&!^$nn;AJN=otFqukIa1-^iR%vrMh!f23nsFu|z z`J-s@QJ7tf@dNH?i(j!KvsAfR_9Xg+Q1@h7t65kUA~{#wRBGR>W?^P_$;BF-nOUqM zCu(Z&x(rDBmbL|*4ix0pl_c+3!PG@n+Apg>!N`$Wcs;q zAR3UOFR{jGZ&(|xy-_FY#DnNvc3+HwVT0-(>wy>SP-hA5Fr|GUnm*vS0u3h}X!Hvci5h1LU&2Ia#%>raMO`@^2 z>|$_BsHE-SlEdJ(!H3pThK1hQ#e_a;K)JXPj_B%WL{~*4+T7WQUcb3mZf!fFTHf}R zUe~kQ@8gKJ0im-I-OGTo-h~4i4rI2Id0IS@mK1_F?wa2ZV+^D>E9HlySv(&$M%ZhO zu(v_jvjk)-NJKR-DP@S284=iMG%+M;OUj5Yb)H^$pMId#sssc=2Hr z5)y-&{D-WFL-SQhpU9*|_RpHL9ah~H>_^RyPWK0dE5>WL!|smHF@kWnv1fe>hHQ_Y z?)S@}CuGB{?Q3MYja;*e%5~|YnKWoN(OiDK(roGg#71SXjcrvb zn{3ixGoPn}=J0Y9HlMwxgwl@PzFF2~bC7oOIV)@q!cHzHRl;7lHgZKySBXalKPc!odVTl?9{iWRh?)2O0Gw4YILHb;$Y zRBtx^d%bx_?XK)+G$5_5|K1O3!--BtDX7(UOOMEonzYeM+?2$SroOi zQ0*)X+XtB8%+V9;_;1Uw zE6%h+`U))Z9rl*Ejx0epK$yhxv0rvp)?^ZNDwax!$qUsOr`AXyw$X|$8lpM{H#8T}={{|I{rYMDmOM{}u^QCt#@Vv|O(DICRc zJO>@^KH4V zRitCV3+Cys`jbN9+bL42=txQcK^*!v+s{a&J>yZn7}D{Ju8Hs0`l2m!gTBT?Z>dtL zN1&19*{M?PwCvRmo+{JKRd9$C=(Bh@S{hp}UNo`}Q(zEa6sD?V@^KbKT^$KdcOZd z=W?^@eE&g`h2Qa~P(k7Xf7M~TO0nKfB!*JCon9vd8|ts`7i^!wpV2|Xm0;-p{)vQs zj=>v~r_B4VpLQmh%y<2(YAofXg5PWRFeqpuq07zR^(Q#fjq^Q!VnrBvKDol2_C0@c z_VVZK9p)9fJY7}60YTu{HaGo|{EB3U{AKkD2G%iu)@Am3Ux)IKZNzFg(Xsq3kC9)I z%y#&oLH?r3I(~5TbDig3D?mFbe?u4fuRMz(S-xZ0LXUs<2|aK+Hax3~iniJq6CKMx z{Y#lic9MTDfO_rnxgr|8_X%X1ho}J1(~CiM`wuAJB-Hh8R}uNEKcRt68vY#XmFK2D_znlG(1M<2BvIccX8M z^4oN#DF6N4j8cyNit_KjoqBR)P?Z0iyj{9UXN75=>K~PQL(P>&&BY5~tk^(4)tbIP zz}#(z?yBZ0a|m%~x(bq-!Q6nLSi}K%B0Q(%UMfq)zQIx-Jr!5~%ilQHG-3=}~ zsi+N>ipsx(BeJ3nXvyRLk45+s8pN8Y503g4<}Z4ewtLz9F&q*We%SIG$Zc@P58j;x zW~sa@_*qori?T>C0K@8f!vXm1n}qL7IW@}{^&F3-i0gb9J*raa7bl4?^LM2 zh?t7<%?N z4+@29Hbx`D9SH&ratc?IQsv1;Gx1{ob55gayx9LtS?g7-%V(JXx!C`2=LYkg|MZW_ zW!?Ip{#j&gCvjUoGH3pTA+=CFe;w_05X;&_)s;vkkymSWU*dn(nPWct zQ~x{&W$91-Q))N2liDjeJa2ackB!}`mrUwse&3c>Of94^OAxPr^T?ffr1_}ysw|J)xsU@jVGB!1jF=)PyVhbA^}!HwpO zpZm4xhw-=;W|oopJYnS1ggD@mEw-1K(j_fMC!2r#+%G++?dGUcq}nU1MddhFQ*9tL z=PF?jX#OS`zV%Xn3>bd?Qvcgbnos?=|8wU6M8J;x2U+;Q zZ_V$roe-$g34uDn&cB#L>T(2XVv8UE+pHSZZ`ra!>>z8N&rALRb)J#+L-|7iFA_svB! z{WIN$_sy!Ae!~Pg-J*e%GbVO(Tt2}#?~Mg(-)G~Wu@`|+I5=r^9mqIs%f%_T?S0cY z%fG3A=fBS1YEG^95AWjFhqo@DTMxOr>$jRGuJpg-?%HZbUFCn-UHE~y{3`!a_lXbY zT}=mDKQNiA{ju)MZRQJC`&YY7+suZm{ei#_K$ZrZ!?=_`$N;$ewhCljR}6fSS(}7?_vMLc~UH( z*r;p#W8I}6nhUO>vOj-l>PdV33maDrOx$`6j0IaiG!I|n&l)I?NeaZ{YWQ$|WL4Q!IsPQ~ciWfGnd4vTxEp_Ks;H;(BI)B8#ThRK1{yd#QX-C2- zUVg^Js*^XF?_TGBI@J7393qJzd0T0CWQPK*qk^Yz=L#Nk-1Yv%p4Nfx&TBE;bTIUe zCAOQ-O?LalE8v?bmOke;E-@=7yZt-pm3GSUN5KYqADaEiT>sPVM~lrvbNvG+J@*d$ z+L5|!?rZi5RoUf#w@*;*tDoRZU3Ou?j3yLfj?PHkAcI51MDXtIwiHjV%rTipe{_hD z&vk%wCK0mt4CxV_BdDVW>~C=?0?X1kgWNLr|9v#x>^fkuXTEcTpUFN zgM}ZkD7lyq2IaghdH^41F8;IZZ}a{@r`$>$-K*p}#our%`)Eyz2ah+>O&8N}``zoc}s+iDfBRJL;+IuiWAEl3Qv7^_hS1U3QxVQB=l(7{i=pguZs9d zQLFJJz?k%yt~8!5zbRNFZw#lvvRX}fW3hNp06>0Y?x+?;x8se$z59Cek9q!3gn?c2 z{PEd;y$>qmf-;eIgPHGH?IBQB@V4b=-0I9c@q4HK|^|F<`{xQ_j0;PU@cGnDZ-i`fxn~lc5 zqTlTDR0)&SD*8i5KirW>eq=76594}ywt0NM-}i)%6B#L=@pdCVl}WzsrkUp2dvcw@ zN$@8YT&)O~TUTSkjEZYo1_S1joE@|2`%C|a?v5riFqCM%HU%75(KT)2v%5S{Fv!y%YXTHcYRHyQd+b9pssr-T~>#dJqkLUR>kZ&pVmtqBLQ6DPyP+)R@)HCO;a#%Qjun zPc)|d`gt?@Nu{pm8P}2zdFgp~1D%Ap^!!JzR*JOre09Tc3PD+Vd0`%vGGuz*+DJuE zm7d==yMD!T@vEkKt~_u+B86-vJ289^C4U`H+avW@aQ|!O_#6HHeO6&0o&2yYlQWxU zEC?ktdE7CwEP7_lor~Z$wHKP~!LwYW|o;hIk^&-Tk>m$1O;R1h>(_)q_ z@MmUE)S=N}Ru_`~X|A_WRjMR;q6-5jU{0n?oLrZkb$dk{ztE{mWq3cgBCU;hlozw7 z9#~PXn{&Or1!48~s4rTI-_h@{F1(k-g628q(VP6i&b8*{oBZ1HYiVe#bCTI`lV4+o z-0W8+Zz3uaivqhsq_jcFYt8tZ{iDb3LnF40TW>}%YMf*KakD?Pdj0I{Sy^LHI1#m$ zTx%+CVP?-a2jAi!?9C{%r(+x=e&ZJZf|7MF+|bYbDcfi8@eU0dO^%af!6pEg&Bb?S5_7ygorU^QHGgJ6cxYk6Bg_eA>&t*eQRi|iZ3zWz4*1gUF6 zH{NdFAokaUl&y>Hs}r`i;05!uTm3t-&9k{QyVm-&svIm{17Xa+rjUoqaKuz3sTbj;iMOd$`ak+xj(von&%4T@X2n=73U>!t!6V9JzpqT9TnVBl*bOzmRs9WD2L}xo44do zg*>cCY~J)g7xFlGwt4G|^03dadCRgd6bfQbVKZm1E#zVPVDnmw@;KkIc`N@?C}(w6 z3|-@kg*=Y`ZMn@wc@Rk~?F8Fb6Ns)fJ|T6U6Vx3usftr~WY1K9S2G78PuaGNctP2?+B^q3v@_c9DO4nHo%9VxCyUwx(%~IplVn zZntR(o@|Qn@RKM9kXI7iDFu8%QL`csnJYW}svzvT2qv-w=k$yH^d zAF?&0Qy#plYhR_oW|HLp^vmU!DhLWEHo+_sS6zH(T%X7dTg%>+VM- zHGlbC_xsD-9;;=ueY6&JG{X(%hY$M4IqxjL^FcpZI^gYoiT-3C7OR*Tze0L%UNjz? z%nJ|ui=8LU&mTc2{iIp+2!;hum6_za@_y#MNBkc+|fJX zxJ9+_8p+>BI!<(8A_Phu^13GWiLIn-<()MDlI(_s6h9*kpz_`=gxCn^T%mzr0{(HTxIFTRyqY zeB4Yw)|%mu`-cw_SShaZYV#`l3~&jVts-{9>*m78(eC`k%zhjM?liYQ?k}!dBJ%?; zSosMjjf>4!pYT)E|MMsOE1kp4=N@%?%XK^zO4whHRxee`Ege&{${#bFh9k_gtaFIY zCc%aBEmDHI3O-nC&R^xHv#;C9(o&&4WgKA#>C%>E!x2jYF4f+BDVrbNrT<-t>t&r^ zgtsY6^Z_2E(}^IPE_Elk6KDx#Jn9!fD#y|t;IMnk(O+ezaw{>g#Y&}i3wru zgjBX( zbA|b6mH!nCg}(44{#NVEwNLs#AoG(aebe6H$lUsr{~wgx{FHwLCDTv)|Jk9)ZBP3@ zqDY_B{u<{`=GE2cpf@c~J|jb=zb&tM*2iM+Z_79S!C$KMmw)vC$4S0mwWotiWVuo? zg~`$IJV9nQnKjSD)wY}OxA;do+n3+c;{VZgR+=Ba;I9soQRW+K{ac-t%bmYq(r=co z^9P%+zvzGBw3@HJ#P;=7bKg3DkZF9$-&xXHmRM=deTiBA+H&POHRQbLIHlrGV54kA z42`bY@K^tfF6mJ(qm5p+*qr^ce=y8>`pf=7|M7Miv)qw!*xO}^PuoR@VN1^Kscdo2 z+*3vVsZu{AlZ{c48ol=C+;;oR{^y6Rq&`iNS}Y>zx$YHk>j3rmN_6k$N3RY&)R@0n zmHMW?`Grio|>1p~pwlP{RL zt^ODLKB}(~QXz9nF+I_fB%d@pTcM&)%u%m%RCK$^zV43>OX{T8mSkQsc_{h#>;5;x z-`E_4zrhEXPy9CS4gbH&pDIZ)C)b$c-|+jH=il%Taqcm@-het+n6v-K|4r}JO_+p; z*;>w&%EXd&rpHEqjJvwY9KX>&$lX&nNIlbpo} zjCHI(dKny*B;PleZSrd>cQHHJzpKbrbL%Dur_TIwlRv!XpL{yj`GKm^T{$N>Iyu9X zyya({f0~hR`3DZU3MH~`6^aQvTlRoOuh&&VTC~GlFKe9I0(Y}z{EE*m##3!Z1zVF-TpsC)}LE=33oDL z?M`y0xni?_(s23&$)?~xnwnJeD71o34%b{FKnlKXHf;unZ<*xV{y{au!I)Sr%ZVm4 zwi8bBpXRe~`v+)*{_|}X=X$g3ZU2N!eYuE!RQ#K;C&`SG31M#zp>p`+CLb#?EuT%* zm{I@ozt1;w|K(p>^A7qhMS_t9G3F3ZL2`>Z@*U)wEz2)@2j3XZgC@JhUqlB-ylZ*c zH{SKnVH8%r>;FiDKjuAaOL^{ltnwSpCGSD7xD|TOKbvI0`*5o1X7c;M_@)eh5ZD<$jA*S7lKCHehU{~NttE{U{{EQnH9CjV}Z_<)m+I&V>Kh0$%okf5f0G%A@-9L8^r4s!>r2#J7cmwhf;4ra5~X zs=-&xe{S=K4SCZN>FI^LN8witw!>iGx6SX@D?AmoZCk{(a!+}if1*UD5B-$G>D`C^ z*k02a{-CjyKVyPrJSXorbszfu-Cy@NhfQ;8N?F*F=a~mSMCO}kj^57H+HTI>?vFZU zDMvVw*{Yk0MLRAutGVn7tieS(nHWAu5ZiO+2y~M<(O*NaaeQR?bKCtIw`>!~U4bzh zKgJ$>jv4lee+xZ+>=VE5;LyA!MI7)mGmftQJ5qr-V0Xh^=G{;Hp+gEK)|RLUlQX<@ zYm0D|>$HIdUnnsIgfAj3IMF?IbKQ=damC9sfOByBvuo&6B5tSV%~wCgX}9103$10p z2`6ZD!#%Xj&Y_^_+WTNtoeKK2**}CXS-63B>Q3g*&I)tkPXDm%F2cJV;#^7xOD4FM zUB@z9>8*8t%7MibkAuDv8@EDqsh#mvORti7+XcC)gUb+km6XSBZltApC$DEFN7chcx6mO^56w0Yj#)h|_zU+0q;?x0yGwB}N`sQ0 zkY|RKCPtk9qHH|!1IY58>XG~~Iw;~T*nJgBzoN)PS{{mjzxAI?v7+C(1dM|M;>DIt z$>fbL7MCW5x@#{tPnRZ6aMxUJD#{Y$oy*LLWr<0cbzfDM(8BirnR^rPsEX`;xVy7- zl3ShJKv=RtC+tW76;Tm|ivsGnFSze}Tt=K3$8pq-5;ZDHP@tlNV^EZ+AaO*4iV_ty zB4Sj;h$ul(qoM`}ov5h)_dQj&yAyC3=l4C&_wbP0b?>=#YdLkcI_K2lbf*_?zL@U( z>FD<%_X0o3wlSVSQfylQ_scz0;@g{o9%}MQJa}5Bha}~2U#paH!<0c~$bcMgT9n~l zt!IRte)z&8VW$L4@y)O^3Z<+JXD3`n!#0kamt{C3ptROyIQ@H@iXtIsBAFhug3A}& z>-DA#=jdR&9+T-5O}ZvCn#nW&@I7gBMY=mJlPQr>Cc`xX4~L}GBQz1ovHX@rQd5ZM zz!DiKG#XXT&2*;pY0vOUFeLOWsB}P@ILvk0&2oy+wz4c|K=1>7B+MV@Zo(&^o1`?Q zVUr6~rz#U>8N>sFMWR2C!xKX!{b<_xPDzTl5?F^N1Z9MqK;L6y5+_d$29`zOr^s73 zJc3)@{d|aC1K)>TNY66aXrFu2Budq2ql$Kls)xS+=zV#cUeHn7FwG3Xa^s2 z6D=L8!OV7ShNg0Nfu$Qw33eEkR^BJ?!4$*NbEbq1hA>Jqo3WcKUuS9YO9F}g1(xoC zS8WN@6ijd`bVA)zVO}5CW0Z5~;J1m-edCmi+AI*h6B$BBJF4}=$|*Ra>;2BF0V-+I zYM_GJc{T946JHH{j!nDbh*LBe&PJ*5;UY5-^y5)9nO++xKvLifkV>Bx!DQa0ua7wW zaxr;8W0^#A)lWv8UCP9la2*M9B1wSpeA~|5vi${JknJ2m&!w}nodKu({Hxo!*8Luz zNc~B+Gu*WAx4DBp`-0vt$Fy%oj^Dn8JL=m1P5ZjL=3^(j{>NtXWAV%paW}5t$kl$y z&rrYbI#UWe^Wzp$fH*@wGS{(=b^kp>e&9v4J2Q^f*7K1Jxl5|8pPR>DX2=MM&(qOv zP9HeHjqB#*_Y!=qPu~t-|JcpP*QkE9n==8@V+U?^e+mMMFdku*&7pGl4n4BFQLIotw^Lsd# zXV{~Z&eosza0-hKU_X{%9ApUE;KZ4DOZV^TTx}p${0zi1J)N(!lHO?S}&4&5{)NX1$=$xf>$>fkn<9xJ)Z@_QGX(k#mtV9qOCB1XJrC#d!Rwo?7hm z#bsu(GZawEik-9hh5mhO{IHFCP|?@hA1;#>pspR zR57uyvkxwn=JH%$=ZL}88Q6B15XC4f_fu{>Kwqbk3(2=46X++mEK&}E@Ke2a)G62< z5IiXmaykRe>LU}7mBgzDLg!K%RH3GRzEi~0qHBfxdwvU z7V&zkBNO`IQMIX8Z@|kKhVO7G!dKh`U`1pCRptr{II=_l|A5OV08;O^Ep1m5pkjIL zNXIWNRn7!F@?5ss!KyO9_3b5?k5BcRCC)!M{H4wz9PnpKokC0xWL+?~NtncsOR*@Y z>qtLm;N+*M$F|~NWcphgV>vVnD*RQ-odIKG6~dO;M&#%Yy47}GSmLCig?xf3zr3Gw z_P$9LcXS_F;sbzzIK)V3bAuW>Cp#5}SQr#i5)sYv92NEJ@0?Q7siF=s3D+Z8Ezz+W z=m@ZT`a9+9T+RT9W7G9s1Dw%C#8lQx&Ki%DhKB^GUbVh?fHMq(^27k=j9oib-wsJl z+JsSbZw$hV1oIgxf;|If>u_#5$oDIQ>Im6}NICHsbxHCRj-LQPPb6zSO&WG&k z+|=)6NNK~M^HQCJWhF2P<()(ahK4;m6Q!ZLWR%mRVe_t#_Tg~4>u%0^=-lpT)TQ;| zqn#eT=7_``;;ZlpfukZC7IC${aa;P=<+!ZJC4DZ%-iY2f#_6m7zLzt_+{iLFzS+we4!{9por7?>WGtp)uC5u2 zM$FZZj&)8w2@5bqLJR!mht)=7P)$&l9}nU3YK0WT=eIwh5R#ieLtWCBdyl(j*$|LdcE4-5e(mcd-Ug_FCZI=Jj(w(f5u6HqO%< z$3fJXH)lMiw^kR7cZxDP2vG!xea1UegY~+8ymLG{+W6qW0eYn86b^lup+wo1hf{Os zKqllLv7)8m({STH{Gow)8FA%vrsw=KRPnLid4e-F{Xyh^!NKfx6PzP#_`{rio#!V# z$t@QQ3U+SX)xr_R(%=#ajW_?K8$}Fezu&m%l?Jm@bAvfN@-gT=7(8FnJL=OWJH7VC zPBuuy5>TNO_oGk&JQPuP5SB|XIaV9A5+ehXg*(sGh*~B^B2tCEbU&v@zi$XI9aak% zTL*{EEe*SDCA!RFH5H82`l0=t1B(Q(GQ?n(R@%22;z5{JS`+goI;H9L?DfNX>_jI& zy#d=mc=Lpb&cMB2LRZTId(cLJGb}UGf?1)gl&mZOKfpH@%3$%&V*HT?qiS~kc}<~%)Rva=gj|MbaD|H1!S2UlU)!0%Rqj0e-i6B7C= zJt1#F5UXt7dtY4J&@$P%J{W3Wt4}+?>5=~mSip#&Dh&f|(`fU>LM#hpV^C%sfa$zK z*B{{A7kpPAb09d(d%E&K5Sk5o(SgoI!6$UlADk^PwbmZ$^l2D&kh44(Y-`wju=5D7 zxcO1A*JE%)GDd6?e6cA56B_iLhl8=dp<54k?oVCV+gYh+p4t6q{r(ZoD?Qp_T%@zd z!88SjWoOlWMK9l@N3Y;&z5Yl@U03LBM>=QV!>1nQJZLVZdic>!rT_bwo@p2BiBp_G z!LRj&Q=DV*r5C1Ps6N%(rZ`h*tUl@(5Zssa>|?M3x9Hc7fog=ijom^;dY==WY(42% z=e6KV4ZV+ZGE)2}4R|s>*yFpbs5q>qd7HQ!zyWhB^=&6Pm%wC_ezNnY?lWXeal{>^ zc*#R>t)6}|1k!1G+$kX2*EXDV3dT0{eU=`73W(*kdhV&tp0Gr(J=J-2Lj7hKCO{*5 zyFb8!>vbxoltG@vYVUNH26LG9^5j#cAsA3TJ`Fq=cFxnC(^IDfOVjn$r$YF;wxQ;9 z=kQ?a_gV2fI^#?Xz&5?#na)#A(wDS4%jtJ`(v2(zhVawrT|SN5rP5276ul*EdI;^t z`X~$Zjo)9$`;PmAt~krd8<|`!_ci%u^1bc4v*Il2&epTQotEhtXFI3p1J7}?bjR6P zUGM9@=RivOK>y(!=eFS24I9pZ=#cLt(E&uF3?T&(?6d-rMxN*N1|l7B9TLO1@#WC|t{@9GZDAg5Y}C0M8jiT!`7wmr z4!_cA3F105=6sH;M!BG2m#dvqLWx=)oaU6cNwpx?Ib{F!)12FZ`1Q9qr8;)Aa|3?2 z&%v6NTeJ1a*EttrIj*|SSV?^-J!o^@w zy0`=rgaaUgr3k?ef`h5;zyI9ypZR_RniR6%+Pk5B1{MfDj0TKpxO1j+W#?8Le_Nsz zn4Z7Q-ceT{nEf-KV((|`VKwY3yS8`3TQy0atGpxexdwFl#t_%^!hbr?1D`Ir%lQPm z%qP}5DcFE+IQ4Gl?I3QWT+ndTeCO+6aG^f%K1jtk=-clD)p|;=zt5SNc0+}(r~xo=ml6+sAFcGbDjRfgHC2c^nS+)2H$TO_5kP?HmMJK5Zpd{ zQ@WT_W0O*29vbwgh0a7a?1_cWVMIo?h}Ar*_kRe&>m3d6KIDAMci*aas#8MEtMqwG zoW-NouhQ@^rG>H0{DO6HtN8`X;tKN%R>gWPaIh%i*Fldu6G|Y^i?`Nz_W`r@MwNvd zz$qTQxkBzHz4%es5;y5L9)+lVlkWAHb0P-e&yT_2`?6m7774m5zQ&D8reIMeeUVdWqNbPvNHBnSE;Tff%;>;02@>{3kVOucrgvn=?bzU^`6 zD3sbBhs^!59`FP{aGRd^gtI#!uYAHe0?(SCa85;O=O@vIY5Mvn0a>G0JP8Z>m-?qC zK}4U_doIJXnR>=DAizu=ddfMg06k6d+8`{jhdd(>Wn?!^cR$isKZTcW(oa5xzCEdX zKke+C=nc=WV{eXoT6%No)6UhXvEylH3P2BCo(#Gb1d^cD%LR1NaseID=rr+-MyEWu zT%Ylbb9mnLO}E3jANx7G!KGlc`2I)r&I@|>)~(Mt5p39f@r*Mu{H#0}tG7H;nx_wW z*2xb&7uLDY_RP~)KI=p><{`Tgl2TlyZ++GoTu^y`O)vM~Ry<=c!BHl)pEpgfc@`M7 zSi8?TBeHK;(JkeYSn5^5E8r7Rs}Fw8DFh2Y?>XnS*f!+bItJ{y9oJ)VZN_yXt~EW% zOD@E9MR9q_1YEZvRaZZj@to{@R!(`zSX}4fItkaUczzfKUrCHv76_DFgu8Q5%V=Dy zaP2R_kRyVDl0V|k8r+#21@d=vD=!&>8|~d-lDMSgIR4C&pU2~86F#@Ex9I_iqAWQa zcb+dSFF66%uX~r5oQUgFD4&Guy}9KjC*xYxy}V?9To>X4d*fP*>pr+v;JOp8v(TYa zaBV@&7sFG?TY?9B;zkF0Fap=x(W6sweHZ|z;kpeC^l)8+il^eb1lI#_or~*!xOM>U zbX+&%dIqj*a6J>(R$R}*b!%pM$v9l=0Dm^FmAIaRYc;Ota!9x01LxtoDO_H1Ag;^c z4|IgL<|m1iEZIFpM-Rw27|yGOy7tzR)A?t01O7}5HZ(LjbwTXFYlV*>4P*5I|7<;a*oRW6_ay|o*zfNRH<_ba;N+ExhL0hUUt z*9d7p>{dXO9}(PKSeh330&+7Q650ys)`evI@YKRlP5B|nCS@@YLP(SFA4f)Yj6HJg z(oTT0Mi5u}Ho~(;1efNck}pw7S>Op|V8?3^y(>b-FNKNNk?PpR@ty!}CMZtE0_f|= zagTbX!SprTb4;KX9pJU12kuLEM~7nVFl9=+fkW;a5m3i=*yB>^jWahVnmU15$6mc_ zJhG^x#D-V%irj5&=!jR<$K)Ytlh#)wbLN@|8#29*ZL?Q_;Rt&Dqv`H5(!|Ol`vht4 zib~T5dRH9;2Z>a&gjvO?IUN%4Pd|mH;zKBU)y3s$B=^HsIi*K87Do?_BH1$THx`$> zZ$?UuZottwL>`3QRsPM1kld`7A+l;Qm5}(65L ztosNC7I_1+OM55vT9B`Iyw`$#z@fAhcKfPVoc?7sNCPXu4+FWMEDIlV4pAe3{9a^C zICZ=m{8BLM72Wl#uQ(3{-_lpDcKR3c$eP`v*aXJM-xajj+M8A21^v`&=dV}^i{As)8rnpd51C%+Ymj-|E;nFd%8h>qnrwH4AmW4`4r_IrzV&ryx3kf*@obs0LxM51 zLlPZtzY4a9(x6~Q$E!9OmR2Id;LsYTnH_ckQLz=7s0-W{LB&>G_6Dp%OZC)U z+#-GIH%@j87?ebuu=)f&NSxqv)`>WgtN>bw6W}@#C#+J7|0GW7mN;RJh9jFWKE#Kr zJ|u+AtOeJ$eE6y$Qa?7AdlFy;MYLolFCow!vxL$hsE0a{B$7f9M4+td3agJWislwOf`x@v1l%{nrSLJL@4|h{9#AtVe?q*{+-G#^o6bQo8D7IF{6WTK z2&KYRH~~0(QbS z!5U72;L5CG>QcgAF&8o_=uNTvZbW@(C=G`25AY_I50ih|1x6azjf)Jgp;v%1%WOmt z=fH$nrwkH)1MSHen=z1Sa96zOEvHWmcxykG8UK{3iZs8K4$qnn1x;HW1fl;=JYcg8%7@qN%$9g}A%6dkU6(=XO4*bF}-Qf7nDb;3L`ovpdZ7UUyzT{b6f zA$y7Mc93qt=8f}aq|60pc{F&kNGio+0`>eNp@`=wckmh>6IjAp`iu!I&4wUCfQ4k< zd=UV=3c(-%j)wJOlOklLUZb>2a1-;I?zRf%Hf3WKw+ZU5$et*W#Ir`+29^+e7G`sr zv)qkb0|LR)`w_T&X2pPkZ6QQ5f=!7(e1_L^a)b<&BQ+PVlbO5sB9-tqD~)y#cdNTW zT*P&*;8sU&x%+{%W>z<}<`cHI7HM|V)VrKeRyE7D6~R3Ui%amU+ATi@E!l)OiR#tz zfknV+{y-nvay^VsSzvQ690uXX4J6JrpPeP21(v$A1f{p;+ClJYmtES@8 zbdoJ3H7+i>6tDP{Miw7vzhn8}taB}-5K2%U2Z`c?njYQilvePA=Qh4zYswH z*TgFbBDu04wiWmcjU_1+YHo*RmcDSkvrjHH0Iv+QoU@wQx;3&^Pt7T=P?LW zKH_{G_l2+HWwM(1;P5^N36rxl-wqI)V3#(U5#GT7)qv=m0jka^xs)<^LWu!+N*W=k zF#;)AP*@5w13|%54w?pT)^&)Cz@2FpurGZ;K!r~v`a`w{-n3Hm0Z1WIcg%7ga00?r z_3O2T3nJEz(gLCD$-T7tDpEo30Tdv*YO z;qJ#WyZY?+oP7=x4vtzntd2*_^s<4doQBSPA9n>4ktnx=gWr|k%Uk;DCv);*qOb^G zCAlENQh4{aU5AaSI`%S-v$mfg7)7!&JL4={YZObrE8C$gl}8M?+m?i~qXbt)f8)BQ z{$|Fif|Fq=zrj@?jinqb^phJn9^`MfXC$;;2)yZx!33ZTS??&2$k3mGJ?J6q)k4VO z=J^Sz%8-R`qzD^KGWZGXfEW^vvjRw#zEDS0 zGa_A@`>7FNK;MW>hOBy_^RrWey4OahTWWQ2XuO^}CA(A~y3q+k4=%^KBmLaRgCg4i z>8g5^W^#Ra6*w3Ie|waR<1O#N1Oqn-v4HsZ&^;_d$VT1d{S@!J8c?Lp{)8k!?iX1; zDM|4@|G@Ww-3`=5b|Pg+O%^wCvG6B|YsSxr4J;D+mZ|H5ImMYPis7t^UN-8H?>po6 z=0{qyN;Cb>aC$=Sx^M}cG%~>ij7;Iv2aB8TXEH(6dg1%dq5IczJ%PSaBbR9>f*bV5 zbcwhN^sWLXQQEGsmH`~gCE7y=`6-^a=gs*rJO6^XWrWDg@GdXl-MAD+xeRx+aH&lo zDUmnHG+XIWMkFWE6(eT58fP)v>qPN%C#8;6O{EE*X&7D~;-Io8I(+GyH5Z z;s|P@A~pcyi<419?&tF9t$Fw~bVN)lYzN=EY?Cu`7ZD0Y#fyInn!o+hrfhs^JvxalA`y>$Z`~%RC{{rt!!~kN@=P|E zH4Gwho`HrL{{tG<-myIzQie>R;W5d0xC1nllz6{{hSEG7^i>QIh7(gvTZC7q$9?Sd zyznP7~*1MG#4Gf*)qe%(9&^=rnc| zXAxE2kx{Hh>JlzB^Usdwp&wy9MUUw+o_(>f zbYfNSa5M#t4AtGyqRyah@-w5kQ(`osm)pS<|2e+05DpcL36h`RH?@BZxC`DHbvWjt0CgF$(&Q2Q{bX<*N32ih5reAbo|Zts|1a@xx5S81 zk?e|p6o_TG{+qFze3b8{By`~&)FBiH`Ep3L7DWZ0F|1-7O!wgL1(6=-UE#!GssXcT zGkjzdDBwfLord#=N%5+6+b2%{ShJ8QGh4L1!Jsd9YKr6Bhnxn}DngHtja=`gKxe-7 zy9q`NJ2UsZ2C*cs^NdlXNS?3`&T2gP13o)2sBP~o|2?7n)R$eknx2e5iD?t@#OE0uQ1e8T1kd;)!86`Y<{7bMrXf$_Oyg~-+$V6d z*r-o=#%u!#LaL|gWGbJZ3j&8wAS~zFTwxPnWug({(Co>Wovg`I4X?E1l7_%oSME_F z=n$uOzkl^E_y=Y%krr9#9Su0ixbXQ0NBUCC4(Xkkd47rB3G>|{_Xyk7t55sf$vXo4 zqGboeNi|CBKDgT9gw@dwXFi7WEi;_?UHAoM1n`SqF=0D6mT)^JT%1e-e?QY$!9h%% zPAY>)!>Ap^hAtNbKD2jO&3_V#}3% zRUrt>?cuh=%2q&+3bVt?Tc1Y2tUzCv=7Rui_{!-81EJoyI;Vf^0~up8q6rQnd+K#p*3 z@m8?bSyp)zlSpUQy5;W?u-2lkthI=eWP-J_VPLIxC-(g$)|#f?mjN}_AS6tCy@pYQ zLdUH22MZ=`Vq|qlZ4^jgpf!OLjMj+M&mwAd+~S;?L4lhXq_e+vhFPV(v5WNcOIkaz z8h#7O6KS+|Y(Uh^#jv9?ld4;A2DG-bE-tH$6gU%k!6VowG15v?QE8@b?XG(2^gaxY$8_ zOS`mGVLNS$HK1-L?+io_6J#%<8gUr6KxH{_{N#pk1gscA32fIHOL0ET`;>AJI80?s z4rZq%2`!&)cSvekhP`MH+KVvj=Z_B6*jv_w1bV?6qF}-J-Gdav#SwR6ffakF3rj7l z0|BE=KYFAKZ)9V(mwi3Iixjr90Ec-4{vnD3LAJCAQ^pgrhhBdrvK{czYO@eEvG?bG zqkDBYeW_By;TLfSdqeE+W1)RtmLJ6|IbE2_2+4fA{IAXxmu?X#g zIRR$p0-TYUO5I>ErmKlmgl9k#De7?r1egzVxLn1quPDI&g>3Tl1Oju{1s|5ZC6r~a zNt$bn%`kWrl*F+6LTug)2V(SrxZfySS&1#!Mr?|}pp{E|1+^t>kwj7@PE!+>DNQ5a zL||9~e#Cz>9Gt{lUb#1ww4)8x(>m9hi<@DoXaA{Qx^WI^!)I+Z2!ZE|fb77D$|ck~ z(Wnx74z%JLQ7_1I1ly(400V`!zL1QtI2L(GMKz29{J5X#Z&MT5ZLdNhdpvckAs%U-TuQNhevklp{ zjF1FCY{{KXsCBu95#F-R2^Y@KH998LuFg7URUbWXgz6td=Q}Hz7@tm@kS$AaLM(4C z0I|HLDOuVwU1kosZO55uC#ozJYZsRx_ni^93helgtfXahAbU6D1xxzSEXHZ$^kL%- zCyfAMmll>}sulXQR%ei&^CKKa#D@y?ij6kSY*EC0z*vrNWY|3;ga~T!LXCgD!=#%yCkwZ0{NGcL1(F1?*TCGrP#j+;O8ULJJ6H z$q3M7AVLozyWwVnf3=ASjon$;$@;|!l+5zp5Hd)5!wEY{MbYdHY8NYn4M|$||KrX{ z4dg^`K<8D@72VnB;M*L2=g!H)NA~U2{TUKKv9*E}VGdxq)>mh%9{QD6v%}g6siGK_ zqBQq=vsu5KWU-T-GzB>UxMjp8@hjctQrfZTndW_P7YLo7T|ma$N}gkaSm0fh*EAkU zljcjF#b%dEdSNh0?C@T-ZpHZx6O9E0d%c+xBLj?`F3JAhC=y3P3;>n_9Zjy60HmUA zvOcTzUMXt-@$o%p?pIJ4jnfj5XmK~bRp?sELkh=jJd{C3u^HMsWzKJ{vge7gwxW5Xw?GI zQS|YtYHXihUar#6{Y@K^8VdB_9R9*eW)ew$pMD|%_eV?I^3wkQICyJ+Y4C!!@tuY) zWvbpaU5z}>3_$Y06v+%yyzXJ9EZzMHogWe0N(U5f?a8X)jM4$09=zh7#X@4B@^o#E zD(KE#FjF7IbMuNGIZzdyOdmJnWz&&o(r? zkGoBbARto=8Tgp*Hsrux2>M@m@>If6%DgwVBwZQf5tgio>|DCktgtx6KP{3vn9i}C8XuoI!Vhi)#Azu%OAGTq0*WqGcj_1NHxb1fqbW4@2u417<}_&F6p}` z(h}K`(JCQBiQ5romd^Z~`D$D_^K|N`|EBEJapuX|=xAvWpO!0flDapsS*NVK!hFi) zuUcwTE1P#q3n!xy!{uwCaZtJmBqhD zLr;4*kgaol{j1_`;8Y@cl-d4aP}&ZCSQ02jGAHLfT9l#?CB&~YO10~EyQ_i{nQiFK zg$ASA3cFxbg%(RBI^S9U!uWL3zi9kOU{rG_j1rvjjPnQ;E#oPK$#JKFjv#WzpHypU zcHjWb`z?&9c{LP@g<{m(!E4s!%dCMG(4ICr)WRu;!5nf^*saWlA&1YsR@{LehkI{k z5x{t{da&uk9GB@{3)(Ou2p^WT$QsObS>V}hd5u&DGmP<~KPMDJ_c}lhffFW~W8a~3 zN#O8R>USjICT3<4#B$N#L$$mQ1c!|`I54{~7Md7Y`1^5b&;J#dVla_gU49O6$>~g= zAPc(JX8X(#SKMFU44PCEIp2V{ z3lZhkLcu@0Ch|8Jka*LQ>zXYbzFdI?NW_7j&Uh;$fq{ns^~lAbd0z*#H)2FkEkt`S zVpAU^W%U*a1GsU&LX-M2fLkeJ0`tivfq6B&rj7_y748+dm%o99j1;5egHOjqY62G4 z<_H$*=58t@_WQ`$!2cBmeGH5tpo-{~-US7*$&id(TfT)z+GVpA@lKu4bVm+8ze@7L zW~V?9u;hQmJyDmE@DD<468^m*)5xeZp-hGbiOk)cZFx`Dq=@hsV*xOf=1G8z2)2{r z#a=@13HQ*PHJnA za%*ovXgsNc1tN&>er`oTCFt!|2|12UXDe*4Y>*CwuYBZjxgjn-zY#JrA-z3NRl*!6 zdv&-&i(L|UBPFobOP8AXeX&4Gj<`BvTJKgNz}StXWW`eC7*S;UE-wjd+yi&c+}J z2Z0iHtWm;Na!$-HCdB7DeQJR!jR_;NhGedo1}#nFSG>nJUFzUQHIg! zv`7=E2ii9LzZ!B2_lI zYo`(m*SFVE*%ie6g&>A%y|GB;pK8{1lF z*|LBUri@Wb#Yofg52SFT5wC+WBzMS&S9bH?F}=r0NytR}4|!IfW1eAy3Bh2fwno(?NPpH>^@-WAQ|Uokya%%iY-Fh(BVV+v+j_8%C14-CzcfVG9~J$^z4KVG;9Y2TPRthcG*`AK9y_ z%5`56ki|Wi6Ff9IayzS_RR-xEaGZqO0Qz1QsP1kaaEJ3ki$d|?hcl^)+=2J_+1gwP zY1CDrnW6!orO;bhJQ&ikYkTcYNS7Zp#0k=wdCm6|F@{Khi-a~;kf~h z{FDgYDd}z@Su}{ zg^S%_hiJQ*zPekZd-qpEcZa`->=a@&V1#-xg<`xhH(-dv?k3;7X7>G|>{Zd4mad0C z7wV~J^jE4MSUj<-0(#|)RNsCLGRd1T zGNVwwe?G*BQm7X?cYw+(_*s)8?C5Ys8`RX*0EAh#^UF1FmG2ew78_}#R-aezNai!7 zLa31F<+4kP{y{oIr$l~Dvh)^7{JdTL=tTkkCYr8K`=O z8RJZ`{Fs4iVt=x7WXq>H9~eI(i2VX%5a(71NfVSO-8fJUDwB9ZUxq{Y{r_s+`XX}Ny)CkWA{em`Ey9hVT^cFw+aw* zNM7zDm#REDclQ(@=u`sX^xt$flYp6K3)QQu(<29~(cyV^lr9{sdZ&>B_Mk<-sCQWa zLjH(gs(X!~3}Ut5E4ZI}ddCyI-A)Y$EUq+Rw&N3vjR-0xMjpoZB!KnqxN`TDCA0!S z755&~K5Xg4cM-7_cHt1^ZKoTFB>eNb5gNfH2FcjPsiN2frnfdLghU#^WSL(yH`7I{ zjD&8MtLc&nW1)-4F2tiwt`<+pOc2%L70Ks{{9fo&hW?+Zh;~qPJF44mvHkwJz%a~@ z&%_hSGl7%9+7U4C`N3d)@btr)K(UF z4f&vg-gg1-ThxTC9QlPm|*4u(}IXLGJKFF;b_P_?_V1jPML0NP!s$A2HLcD1qr zL|n@|eA$2^Zj!Z#B5tE>tAHZqdIOT}kUdNw`OgFqQs>*TqX(3uclJzjZV?`g`8T{= zv~)9T=l~?WbXu;{t)oX(k-qrk?0g*?0n>G*4T!4PJ|ODXytak7BtlJ7ypg}XXH*(? z+%ulS0%2yP5qV-}52S0)NWM(ZI=isMqu${+Et3UP4VW9H4Qv5GsO!%^I7$uD5%Q86 zmlmX@p@$g&xG)nTr8feX%S2X%n`&1bVm@z55o<7A0X8;KTt(Qd*5k?MXu&EBzUFA-7(9Z4j)tVEVC zF+Y-|bBd{Q%6=`zPJ|2wBt*z}_+lLjuq?4{X-S(oAkw`sE>@vqJh}UTFebP-HSMbU z94x-?LHI0F-Rf&>QZzbm<76Ylfyj5*z!4i2rJM0T`D_Wvl}ne0H7MS9m!YUm(# zom`bX00f&DhDLH5J_*OGbM(gRa(d@Ainl&GCi1aR%qCqwN*$j4YXM5T{6e(XeE*wW zD*44Y<3k2ww5;kb>4WZ<1=NJ3gI^c#6#fF@#ou;pKpzLTknMp%;YYqfH)(S3=VCJfcs7oqF*Wd z!8Fn$q(H3AP&y|O^j|Wsl{DJc%UDgfpgi^#+jp+JI*T8N@z&G}BLFfDokwh#&bJmD z`Pk1K<5L=L%21h8rrs zhgqYT<6^78GR}my3C6P%Hc_+eJVDvyx8+>s3|~)*2C%7*riuEwrz)Jn1;~nV(6PHn z=9er6vy~<@hV?s>Vo(^N9guZ|`V^Fx1bIjx5^_!p(Uh|mIY{M}WFuu!7mtUDuLu)Q z!ebhq={_Cy<$9z!gH}US#0Tb)CYle_l6HVTnGbXd&OSv4UF5CAn`1qdUqnTU5i<|L zP&p!aO$ZzuOOEDB7aE(Vy6X*NFcKheLJn*slJHMZSII<2RhRDV;4}OSQiul>1hAFN zls`I?6by zRrEjrlDk~b*-H&SuA+zWqX33RGhv~nj0%cPQJ2d^5~5H@=Y&C@a2E@rND?C&Lx8>2 zNFvKmR3Lp!Io<>${jGKJSXDRx93ayiC=PVo3!@b6heQ}RhSa05CdOytj}ufeGEe?} ztQv%4NA4M`E{?TgSDy`7!`=PuC}XpIx|vV0iaS`wDsHpoO)7ac`;?KI&Nk$kPNs~N zLdekKZ8+wDtMh0!zcL0&*LnXAj)F&9ucazqmu^F4n74>5$~5(`kgr*mGy;VcRj#mI zlrX12pM(aWORw&jIuy|WMbr8wqYUPdI)b>V@~(Mx_Tege_U6RDiN!3b;u{F z&2eh>8OexlFuWGgo6@H2f0t^7nK3h zHd-f0RiK4isv=3g5>!QGk4{vjS`VL~iaHUMv}B^Pq$^Q@aX{ZPK@9{|d31t0a^RXU zgdLI-k~>hBCKB6D)CG-0NW_ylJ@vR5*=c0#UJG8zG=%;L>Q5`e3%~GyqxYb!Q+VF`pnXUl3o%asx0sxqk_>*HXnaigv*VP z_AopRlIyphqDz$dz)1|$9X`10+82&sEqz@1@%`Tg`>DR?VwHix&s~Q-s(;1JS&Kr^ zbG^n6|L9p1a4u~V%2v%B;w=L&rVRq)IfM^apllzW<5gf^6+2W=uJHIAKCZe(51)w5 zw^fNc&*uF#i8^H##NPp07SzdpNSBA#`vHs-&UpuO6-5Mlz`i!>>nP~oU>lb!OHe{` zCr2;v#*&FeM@NUVwfg0WYRXRc_C#HHVLs}aCBJb}AS8SUOY?29!4wzhgC?ooXExI# zg0718#S_8g>6VTbPAV zHlWJvVv@n*14fAhXOD)k1<)Ncm*ze2<^y{1WR-WIIHiDQm&&FILv2O)m32?Tjng1d z0q7hO&8!Rqzk$j&&7XM;ZU9SViuWKn#gzgb_Y;{H!}ofa zm4V1ipqZy6WgeHV^Hgq=Z(X!di~#ek*YtA8pkCAe*k27qdSA4S=OKfdH2|Ft6+s@y zLUL9hjiM2Iz+RdHacyZfTMnH&BuoXrR)nB{B2r;=Xb%&}#CbFCrMVA)K0uGkgM2O; z_trdWt565>uqsjQSTFB_Qa%0vm6yp6;T!H<`lJKYu6^OC4D1aVQw?_M>@o=vv0F_sNPBJqqb1Z*M%*nJ$<9^d#Z{m;cjyq}V|8ApgByd-i3h7)hF98mI%r|-!lt&` z?9kp;9DI!|y?AHY!4RXL)awpbhXmX8kVDj7yR_jU5yCh0daZIJfe;oETJQyAx=k-h zVT#D^`sMvyCpMeHDzP`$>k_x|doxb7EP^q3sNE(8pOZN@#$Z3{5A$TGLS}^4;CR9b zJ^oNNfQcrI!Bk&#sM>o-3wl!)O)11)=rW(}pjw-3?*Z^MAtmlQz2;Cg45#x14}&f5 zDIYX4pGVRJK?Bu278V&5+^6Hu?}-L3erl{dw`{wejbHSQ0>O z9N?}vK+vU|yg~50;?MsXe;!DB{?GXHJLAuT?lbZC?~Fg68-Jb>Kz8^3hvUrpXZ1u} zV$a6G;X7f+<8~k1eJ=hK9vSv&4W7cWg6D^VHn`phcZCq-O=-|^YJfIK`j`f>a*n{Y z#Nmcpxb=k70@soj1EASfZjJ+%S->>}oFjnEtWb7qy{0%EH_4utPp={*F?MjMiI8R= zw=oWx7KdCx$XNo}NXVGk&0QJ?g#>3ST}IH`1av7uWjoiak3)h(SjZ)Wyj39U30W;Y zuZu(C7zPWukdQM4vW}25Vggzl2Za;81)WdOY5}b!=q&Qts>}6QfWmTk`bo{vXYR~1+s#W z+>-$FfvjTiBj~;$le7)46p@<*u7lg-vb%t6ZgIeX1>8zN$~0h&1e6^HuPqK2bYGMT zHxup#fomh&_447?I9$NOZ6aKiz_k+YI)Q5uxEL`w=r&1(>j`+h0JaeDS^;cMs?fl# zA>4HW*G#xdfoqDxjdWi!Xi2zh1+Iy3(*&*&zF8Swpd<_bBl^9gFkcmpzVK+ZC)VVG zqt$2}`}*F|swnu8{{Cn+BsfD4n4P(cLJx)ytuF?6&tHHtfdcyIFr)AX~uZCpE*;MH`SoNjj)seVae1h89ukol8)L1lb z<_W5IuwFlKf-1q6SDhdY`2GYn5;YDx5xrTf4>(Z`>puN%hQz`|n&JM4(`|;NG-c>T zr>b81t`os8*XpNFR55&EpOet#X?og8YA;l`{3NwYZ}XW!Pa=sI_$o_Y$Jw?N;0 zit3M#ym*S5j7!d`()4{!1OEL?^FUd*CH?C)Lp zPREy9bnEG=hY~Lvyj!1*cY|lhOZ%K5BlPqc@>0r~^3u37@zTfof-~X4y;k3Pri?2UV)vyfdb_CxZdOq4UOJ9G!An4}v z1wr?{KoInT3j{%*y+94g@evgJqeD_^q6Q^$FT_V@>d6-ZyhdMqq3Q`-xc)+!-p9<% z4Hrsd(=Jjo06F_2Y3#a-)BzZp{(qFAIr5J(H1&U!p((o9ADUt`@8FANXy#q4F2m~u zQ_;$2b!;jY?Q{D6scNUx7ej8nZkY-!T&ufXB9n6CCGwG1FOf-c{-lQUP&cfI%{uo_ zs{5gBJ3%Wj!x_e@%_gs$STu z7)iS`pWdqP{*!te$8KNoXUzU;z39)AQf#Y($$wEcSP)ODsz~(bvRc;1jj`nJtvn5(E?I_ z`*3J6I8$OXuHNS1<%kl~t(U5Odv8g{8XOb2egy7UjUf7L8_t!Y_xdX^WTl?+SGCZ# zZ`%m%{ay9erI)GkAsi5R=4I-1^ykIPKqy!1A1_lQ)n{%=uV^9qtK3KQn7^r=fHG(Q zO`U@|S^YP4sqMflJ^t^ik3R2m<(du>xv#!loeZ$n%W>Gzvio(`-(`7@`MWyNS(Fh4 zrQ#I4qUZfx?OSXqNO3RDVS2 z!7J5iAb^pp)UH^1-c{=8EIXQ`OVf18Rp{5!t5o*{fqmmDb+QF%+}#H`GN$?%RQgLy z?GwC3FNvvfAnaRXsPGzD5ntnwCSfsE^+~_ZqzStiBV^a9MQ?u={2G%{5p!&*-9Q zn6AZo_i5@LOvXFY1i7**-9nmZn)mZUUIAFua9GItDbQ^CV&4b zRhar&3Kr!#RWg$IRe`?G(HpC1?wF2d&eHAEg`ORKvnmBzTz0dx zef7;gX<^%cx>=Uvk+&$Gd|7u38nza0SvZKRmr%DkGgKLxFnosE8xwHB3}FrT&rr>P zzPwsU%<^h=JW2&KW#SH)sgCGoqLOk+C9)>aVWCwonhC{mj(&M2l1@AwEHM%G>Cr;F z1LD%fD3gYUUheYl_|<{~0_dB9)a}vC!69sU4Gt;zQfI;#*}b%zK-WTV%=BoGz{8gA zP-@?Cy{GlWTOk~*)#u!*_6l8-qaS|3`Inw@o4Tm1wHNFg>NvPPP)nZ6lq`5LT8gt* zws!N-sx&!_th5`OrMKLsdI9Y-XQ@*%USEx1_1-w}^5R*lZ}4VaJxle=#ol?Q*MWBC zz`sMe-{PkD3hHjj6?+dRI&mj7wXEw-F>kJ=+x zq4&KFeHkb$qY<=1UlEjG{hS!|wPX3G!Q@+MpE zS#O>nR&U_1vE^m9{DmzKe%L%e{$T?@%a&g-@}ma6`cd<|$(FyfG!93ri!92g%mhZOZb+)XQs+}_CY_05t?3`EWaZ82Fp0X62YpK3wsp_9^ z$a8d1f?x*+(pp8mKx0sN`$aW$E%Zs#Y| z2bRaxhjo-n-L6kUR+*`;+8`P1`4pT7mg>`i^tvVfexT{+&^+)9rQ!Ui41m07SBXSomR_UF{VU+ zXZ@z8=f0q#lT8zvqzQDl{UwY_nm}m+%w=#QPnXqj*$WCMIbPGS_yvgGFpvE7qAKnO zEKWn)1RFBC;4~}4Jw2{T^&9oJ3`9*vX$}T3f|-yvv&@@P>N114r{^?*oXpTIO|T!_ zqJuA~5oN#OgB-vy9Kck)VU_Cno7y<%CG{*!Ajhp#^FtN)>z`JtUb(Aau%n<&`{emB zQ7+PhUIsN>uK)J3ng@};aFx0}xL!ZHN{#L-`?en)hha% z+Wm*sFdjan&t3gnXp+Bb9!2fy-?^n(#w3Bmr8llt2krvh8|$Gy?Y~$@C%p>IaE8{e z!bW(DUWUuTzeaq-LMx|b{RU*$Wz918?agX_aG{?4n%XV)Yad8QillUEOkh#Jc|~Pl zPPv|HGt2wK69Qy`oRse7@J^+5`f9OG*Qb`pghJpk4dutEUq;^yvv zgH>#Ii&O|qEn$8Xn3{N9!`xLom~=#NE&+cMf5I99 z{vd$WaloAbC|(1E`%&Pk3Aat)s^V|~_Z5MgPPiY8e*Q;EEL-1^=AKdY%aYf6|7+eTcmcAbFs^Cc=G@^r6r3o3VwG zo^i8O&}b^qFRW2LVz0}WRfv6nt&e)U{xG2VBw|{?jwP# zBHSi{tBAu5wfj$%g!@q7DhT(1z;&2#1n`w12JYnGwh#az;G%^cjDvk&0NZ^)_f2Wu zRziIsQ0;`;NGL{};CCy2bMtS5xrcli>FPbHrA4;^>vA|!wrG{p`A3+)kSb*I3^}rSwi{IBLwWxdh!Z;L(q6PTMGMJ_Vc(oB* zH3PwE)7&q!b;;Z6gwWMNedXJ*p53Th-d5*_{+**Iu2XySTDea40ovEW&!S3?dk3QR zQhnn)swjka>)ufh!g_twdTarIovS}uk3FfCI_+Kc7jUq@zpILOs>P|j*n|2hIJK~J zkoQf%y)!48g>$hnQ;ee-}GzH|q5F)QiDtz4|>Vef%DT zk{h+V0V2vueZ&Se0T*Pb9vNJuU)X@&F48}2P(wpoa`cc^NK7mB-&(Olv`F9A3VZ2F zz0Q=v8`aU>zRo4PhmX5#aN$&t`}IW|)xH?8e{F>6_kI2GM)gqeaXsgKwf}@qW+U<~ z>A043XnOw%`W`vyUez2tPfH{NNRJNf^!(;II{Ja?)kiWWN-RT`w+TTXSzZ&*inc+3 z`sfeTb-{Id{RgmX&eHjt(DpVxX_Fe5XH1ysFkyPF7|fDFZ{uoR0jKZaDm@REs0Co? zNtU-B2TU5tHH&+7K(R%7<0e&zx_{WD_V1mj4ZxL1_)QsaZK=t&d^sSp!X#torR>Cr_!$x?Q260`6YnpqfUcFfj$&}r-9Ar73^Erf} z1$xZqz`DiyiqCS38uz8CKm-W%@*s58r zuWeW7gno?ZFWOeR2Wop7h#=_^&3RNtXr!Jxic zU-^}(@1w7zzLI~d8t)8)Vbl@?ix zzIn?GC@d6!TcX6%*;}STXT_(v(1xZp+7~FrrF#mwrO-{hu}pb|2vjRgE0iv0;VxKN zAzVBe&GVL?*`a#$SYf}xr+!S@D*a@Kn$Sm1C$gQCkX3Y&UXAQ55}6i$%QP_(Hb(bK zv|svX!?(;aQD}v@tLLg)&-Y-PSmFuP@e zS=4bQ_6f`u*K0z0*|$j8BLNF?EGM-Fxa}6=JtSjr-^zxkEKhvLdl~pIPV8G4zL_O~ z5x%c1!WR6X0}w-rI*?oOSRMq5@^g4eZj}WB_&KQ%K+rsn<-jA56LAvo=lqrLKzr&M zy_J%P8S4BUJ?}e}kE09FUWDRG8Y=iL;=C0J&w}@RkMarPWe!wH{G5-?DR?QFgurwS z_ZG^Xgm-N!-&&IFw{21e^K>Q|qI&w$V(wNU@*xJ_emZp9vo0|&AfGL=o@;YUQ)E=P zi8m@jVqksXh+wYn&MhHg)6Ev)NoY0QWSNj5Q`1{!0?-D+&|^FA3|* z^tNRN4#3VET)Fz_XTDd5#RSPr@HAS8lsGArk`Rah%>(B)fgdouj~KoV$Nfm?ONH^i zMAo&NxU3kLmck^DXa$4Ir3;KCPDfizhBn(3xFbkR3OJ{68O0Grva~7~cL}5hL3+mw z1(5oe>hW;=5C$bb5-ebyCfd@kGi=8bp_IQ+2{05@jC~^COZl?{ayAO}a6p0Y>^$}) zy%dD0q$W^af{Wt43PbSv!w+EB6+QIXKd4drHZk5I%LI`|5n?YPrt$a{ge94%IC!uB zY^(IUKPZnCMt@Y%y$}|cV#4A&e_7gzS%zC!iln(H3p6Q=7Rc4;(1~pP3jOyVvFY8a z-~CY)7q*dAGXgP{!6x^h-}rTp14OLWJ$_OL^`FmM40OqoNAvk;dX~%*-(?Kh)B4(< z)TGX7tl(?RMdTB1iQ6&9OCf`Yn84w&oCrP6096Cw*%=>4SQgT8J?=ZKfI97L4CKqg z_*KmS5&SwUVYd#TfDn#-lHx-4P1vym(S7+e$u@pw=eUqF`MBzu*;SD~qm#lyPUr0; z!?}coNZ1OZ1ZDb!&~or*uaA@>!9Tt055QZnb;i?X$t|3o&h^=xlv@jKElnWrvhcgZ zf0&}vD!EY>n4c#hAk3YKx8QaT<1-$O2;7@jf_Z}e*S!RB?L)AuUGi7r>4LnHZu-h# zq#P^)@BDRl6PX&drNF6Ff*kMAL7gdqbGBFexLy4ZmDOpZ>qu_`j{ zwTvl2jS_N#Kp2TJ$wnSy-p9Aw@@=pRem9RsVL{LM3eI4tNm58#L{1;)ml2bN5Z*ut zYyxqUdTuDPbF8k2s}-vZ?@L|=Va08Q<|sHoM?E}e&>r7GhgglfG%X$$EDVCdz!*-< zk;MM*hJaM&0Em+%{01+y%MBDM9rz{n79;4Mp_Z>@*b4Dr6gMnPsOxztk<+;NR;5Hr z&cxT#{l&*cOs3@1UtmRaJZRN=M%74rvGoCqFUT(q#U3swA<_nsDaqN9BCCTY8|5m9 zSD6rg4o{6-(ElEsdPjoT=9sUz?z=(*H#_ljE$-19QzPdVO5g+qsO>{j^l3*$@-n6{ zn?%HYPgkTx@?sm}KH00~dk{_Fj-5#o2QdTz-I6R3Kiz2h`5a~IPX0>JkP@CX$H)sH zHtE_{&Rv)If_C1kv@#FqFS-Da3~w1s>S`Rv2RxyQ@)YXCWLDDSku$K0>xV%DQ=$>~ zUnrQj*J?<(7sf=gO}ze zUPAD$yz|F!q_BG(`6t#VkKD5%NIDlXnU>^$CoRay({F_%kh(}pL4+%Dl?HXnOduQ* zc}d$iDF_d1#g)+OdBsX-;eG$8hNNpPgD5FgCxvPgo`HHyyN^zq0X-f5H-#mf7G^@E zYD>ug9{N0Y0Ek~ew1Evo!n63Gv4F|nRv}voZgXw4AU{l)=eYP`H+YijtwI(REcGnV zI~_bFJE|LJ=A_D5F1cQxIfLk0)c{v2T{0HKa~|Jh_$hD zCMD-!<4_21dHXb;XMdGQb~x1~A|J(snh7Vt1;T(vcS8f202Z-%1s04_A;GXx5Mozg zb&=|P^B74!k$DSLk58GjpuGUcc?xGh%X#*shqEi)jZ$YjLcqdEX39Wjh$SQ|z?^zst=a>zARmb+ zPzweWQ!an&Vpue@KpnAp&GE!9P7aQ80hy_y(nKQ0T0_WMMpQvc1?H7znY@~0#c(Ja z+Q)ok=F|?M^)`18^3ujDZ$|djrK}Pxzm%!~$ptdcAZXcUwUOk~#GpRj6;naBkZYQ$ zg;*~jp=G;uXeN%-auc<-!~LO=wE_n)oUE>e^hXqmCW!b%<2Ip7A7pc>Ag2brphv=m z1BJ$TKK|*ZrHLu#y1?Nhd;>t6bTB)zCyw#v4P*!gbCJ+t4=K|K()m1^3YjlRgg1j!9qG`|;B~5G&`OMI#;TO8aP1FtjgBn`zjRz81YDI}$Be zlTU_7vjRpc`Jn4+Ik4#1M22Ha`I0ikMV-VcPK`{&YJ|Be>ruK$K~}6f=s1tjC3|nT z&^%sOQkL*Oo>MtVzIG7J!QR_(W$kj{GwP#}Ls+lR$YW$N(tMykF#^_z0&f*Mh#r_R zYa!{zBytrXoj^fpj(by(2eGsv`-~0b2OWTbwizi=Q9sY);iQY!jDw(_1*XFJgv5i@ zppts{iaTWT4E9EOpS)s7bS5QH#tw54H z0zd>NJ`jltR7AKW=m1U!fA0tD^ma%0NC}i2W_U8}aE;#^pBsx|rMb|ntb*PCwJZ*p zFk}oE65aMTso9X^k;%I#o~1{Xw|UmJy}Ynt!|VMDTkROq2#Hr)IN_wC*)jX+O+F;n zVyP?R4Q5G!&YRDVTg0W!Z8u*sr^Z>ypfx+WHOSK*$DN99MrO7Lo`Kwhj(?mT z!cH_ymjyH|21>F$@ohlYt+)Z6N3jI`tS(3%WDz{TFp&)`-)PuX`Hq;>n0KAnjH&cK zxdu++S!SeX`Quk>N7{%dGTPM~Z7}e7=Y(T&82%q~?*d;(RrUSnB+WfDIq5A)le8yk z3oTGekxMBc402OZ@P-!zygc5DkHV|s^E@giV8H-|1}W?U6@ykN&_Y3wf(3&XEKndo zfr5wui&U)|u}W1g@AtR%%sF$?w3O=m{Qvl|NoMxU?7jB7ueH}2Xo}``c`gSvNPMQ( zXR=Sj)Mi_pQ2mfL8njWTS$a8+)gw}RvKKDtW@lcwtSA*OYIbH^P&}%sq}lZP_89C^ zZkS*3L6!YR>{+4DL?Tq~>T=FFYEPCTpBi)yb-IqjL?!Kc6&lU`mumYq@jE^48*;2YOKbmZ&BaMx@n- zVt0Efz==;$SNUs8)10zvDcXSLk_x_Jw-kFVV-5*LVv#1pDEnJNQ{EDjQyBemB20*gZz1EvaVi9+8Gho4IeKnR^P6It-75=x=~U+oP^qpS`U4fT&}8 z)Fl&P1nOFZa>GHh#^xHCEhrzY;uuqQaxs&4oG7-poJXIXjTJt5XO`psxF^hlf`RPI zLe=o#C+&|%Vixb)G4vH&%)L}e(t&z$kq0+Uj!_VGtjZ+${|U?R_qDYL`|Kp}R=)|B zvOVZ6p7Sf9$J@5UYqy0B9(;8>0=^xj{#q}#rynPUx!Rx;RHp_? zt&+wlZA#{0kI6Wc0J)CH^mwce6CZ0wf)3U&qwKs1=7*>$(zAIWWd55dr53r^FqT?& zQ=JffvwxJE?BfK2lAKHB#eb+vY;M&WBdt~~;9@IWS2Br%rux`zECYP;3-iX3jB8G! zRU039L!8_}pb~G2Bt`~W6+|$Tj(?XnC3a(kpMeYOJ?CLageQUvgDK{OVY0|n;_2{A zwGRO5RePbFDvXS7x=246q*y|(a(ekIl{BPj%bmj2ZnPL^f`V~BcRiDWj`Fk1-8GYf zw|~sqHOcnE<}$-X{*A=;(lr^ZaTXTL$c!Mte{@_}jE+l5`{kdu<4o~@#59U4iMiu) z!O=c&gn_cn{wKn2|ESD6w$M1Xc3tdFDk@rEht&l2{6UpbErx610xND=sgBlWz4S~> zWX6$9X;iKuRVlUml8@ta?f!dXqdWGw`s$tRiqe&0n{UwKUPwG*eQg7`zarOAK&P4|DE@OH}4Xcj6RjHrh)zJ+ph;=5#en?K`Q@w7m zoE{^-8ajvhCkJCEoLw&Gs)cn&stk=f&#xKohaV_7 zYsFHh` z9!`VaJZ+csM;O^X&AP9Bgw+sBk-W>1&Q|$C(Z^hU}L?=xPT5?aTw}#PUQSXm24T7?I7{cPpX?pMlj)P46d~4PNAQ zm)&AdtG$a|!-q5ZZSLZkdvTh*u#0ZyZozbF6zI#P%$zMeNHhGI*&?em+Ij zNpTe0HgA4w9p&&H3oKP;{}#x6&72aJmt|y|tI=^~T>2QX+yo=-s=VZLjSa4UW>BBk z>pe^l{P)51CBbz6QkcR`M}X;m@u1<9kqDV>a3=>$Co~RYP6pW%-W+5#t0H85XgQ7= z%1M$29*J4Q(eO>w)q5i}y!MxXY?o4y!3#$Kna2EogNB+<5m>{}5MYUj)%n4a(Y)Sh z4#2E$)ol7A!4^Nak5XD|7Jj;QmA)E*`sm9Hbv-Om4?6@V~{+Ywuk>EwYVahCkdBmye8nb z)`}NujQneFQCjLwQ1@`?(J=YVa7<|&meQF0Q(|%!1s_mN1mCn-!SvdG1!=I3*SL?& z3TAaEkf0O}tDcW=lR!&^Td!L>%VI}sX5po>98Ci9ybdjizV{7YO^kyHMhq4JP*A|f z4QgZx8JZ8abxlJ342gqIlGZ|6n1VUo8M@KPjqSTe$%!UX`f40f$5UMy+@-54TX(fJ z>ea*vo3ezWC<#=w<5!LPnIVHq_=pA6L}W^e&JvFdV@7M4pj^$vh zn9Z&y)!D;bm;lX|2KHbt{W;8a$(4|0{`cDa<+_wU!bg3zRI&uzjc*Cwwv%q=T~h)M z|Daa_F)f(JaPPagbu5(?i7fRp=e~%Rx0M$7)kMh?MMSqs0-Ej(`WubVrNtkziHQN; z;cdiN3B<1Cqg*Xj#A)hL>E3g4V{_-vv}vSjZL61-4-9v`u_Ml%ye}z#K7!HrCSCQr z{4c`rO2M!>EfW7_7@A7^Z-L>f`vtA>sQ?ipbU?_aAodyMdFE{GK|E>I zWH+DzTQ^ zHFe2_^u=_5&cq|6A()fP*eRYPIgBZiHzsPgu6g)~$3%^%MHNQ|HTCZ4*}*BTC6gc! zNp22ljm*NGbU<)Urr*7AK#-eb1=d?sMIg>SBb7CEZ2)8m6a8`X3>eQ zGPpl864>b$Q3N}#WK=BH9_2gTKj#FyADU+2NtUQ(Iausp{*uB79PWQHEFo*FGFa_5 z+kN`L;A?>8`2&OLGYl+bO2|T}9S#t=ZGCpD2Z)<}Q1Gd|!K!Z9t@flQJHboRY*nO8 zUDbTrhvLC)?8)iZWH&P%C7E^N*0w}?D5xyg9xHn*#G#pHZEm9e|MoVJO}lR`DXVaO zXX+!@esFMV$AIKDJ{fai4mU>KwFd{2kU8%DgM;0?z?R1q)KYp}SJn$`QzN_Ey!NUM zRSE@o%D?hmf|533MO)YS*i5ZCQuvY^@AIU5XE>6+`=v@W`Mw7gLJ_5QSyWmU@kbyk zUK#g)Gpb`BNTX=&oT{H2TP?ppsZ5R8=K}@z^}HQQl4|S8A)CFmf*DCz(qmRs0;RhL z-x{3c_apWZ+dkEh7bisJCpNn!{nywjDHY6DQbV+YBWKe%6*fvPHtTY2*=N?gO?XPO zh;gY~%6*iPhYdt5zXD?gQWzxeF3VW&aZ7 zJm<=GA?0Zswl1O_`>nwhEBBG5;}hdqRZ?1p%h zN4FHjBvDLi&jap5hX(uQedd_ggJL3Lfi*`2D$#3N3Ui{xZ+y2Y!4!&LW3se=f#7g#gJgwf3;`4`dxararw^}QH7zmrKm3lz){d<_(ohLX_QR(q6Ax&)w6inaM8fnt}O6QLn zmw#u_oG-rBqO!$5A!SMT&PjX=bwq0d>18QHBWn6&_UD=+W!>M_*(F7wq0_R^4%ewg znqiQ|!=VC$SR3T51P=cKqN>OAbB12rcWb#e*xzNxHGky>(d2v;U)U>>7bm4Ez+vj7-an7-5-w*CNwSV;A#qAdtK|>Ntv*gEJ@Zb3X#}M;=nLn z0v0-`h4RzV*b|n@#FQCo2+B3fwdLpham>cVI1y*BxLO}7LEKNL1e2;`G2tG2d$7y< zRsj92gittK2z96^`w07r%G)8y<>?p*o7g_)9l=}3-|c=!FwvxUEOY%p6eS41AaqOR zC>+77P3&Wdw$T6vnR-Q*d*Ruw1omc=!KB1UN3O}f8g{+kpQksJEsA+7F_&wWO0|{r zD@EK=A)7$%w08zmS)hAo@T~)I$`lxx4%jEW41Gabse$A*cGFt0SJE{-FY(T~E%(7= zg1vdmdB+5Eu{L7kL8h3sOhQp>A!*c~zACYRj9(Qkpdvq8kWbLY5<-%52B3&pJ-ftK zPjhVbpd8z?K6zu%oJ&QkXWiDc!EO(SJtztKCN@xB3oc43GaQP4iixN0ZY|c*cTKY_ zKC4)QTy7((sjnM&alN^pzB@Sh$ih?7e3NemxvAL>6S6W2)Q!{|1sEo30xbiDez8U4 zV5K|a*kHy^COJGY%$;jiz{mkGmNl}2dwR6y7njYZnQGaHF7DVZRZLwd3G**<(~t89 zwAc|`pN+UMst(I5O&~Q7DD@Qsz`ysDAiOX(%xR$v7x^M2%$vm_bytVcb>^TtdlpY?K+nV^Vg_nSaY6k^ zB4*&qcmY`vFHkF2dz!6Kc=O-~f?i;F((%C*55tiaO!uARgF{bH)6rZ@%$}K!arlHi zOOPbyt=Kc|16YHJx|AGc`GaNZFz;;yjAMg?Dnb~> zqKx#(y~0mZQn1aos=k6YnE2Mral+pv(;Z1Mv2&|M%|v8U=W#*)AO#%sMIE%)7Ylho zCZ9Y7iT$+b)L(qoTGo;|A2X4oXdo_y9-}S;OPMD-fg+lBEoU~$=QX;^J`^0;FZm);PSXi#&R`&m+1j18Dy4SwHeQ{#O&R4 zbe=&4mtEwlGJ_2N8ohx(95kLRl2(3(k}+g26pEPW;?rET!X+XPaBp=ny|2GRX{4r5 zk{_-z3D)IW_BBPQ&E>=?skQF44+n1{agEzzQr_o`ssdA3XD^qj>b>9CrheL0Gs;w# zfWhKB_1Voj8mPaOiEgKSIL~P3r`rAbMu}9Zs8o3&i6rrJg_x+{ z{8yKP^N+P)HnM;jz+v*)UZG~?v4*917#`TGEF_i1E?+Z&PB11T1%pxSDkVU^)hJ$AeyE5{rxe9V7~qR0caP=PxsIOyOY#Ln-pMVbrec=SAK>J<7IKo7%4L>f7AW>Yr)j_vX*US z7>kU2LXRc zH>)dX&J9bR+bXlyFr({@F~i;UAHlSEc`cy=T&SxTC(WjEhr&+>?_=Q}%`AlPkti+@=yf^69O0bd17+M)xlJWRs-%?> z3?k-e0Z>bgX@_Cynjb=HwD3dd<@G4e%b_dzS9xLO20xAV?YT`!)s}Id^?&IT6C%+hdH-JHNF;ias)oc?`b9wD zs7~}e3NgK%HF-xAb1|9UY%qK47h_nAGBxs+=uzGpJ;3XC9hf`FXSevs`XxD;M^l7 zJM6zhr_)Wm**F{JuK#?{QA?E~<|p&uk? zM+=eIamfPIEE{8Y+GHmhTFcu}>#aGFjzPeXP6Zhtj>PpKx5s=#ecUJhV;EJ^Xl2Kt z!)guo*`@8#m3LCvL5J$#vf01Zktk>mT!}rgOcoluP@vd)mCTXuPdboUvv_(`&fZ=7 zgL{GNh&smy^P2r-&i$c;*{Jv5KO+gTD~I5^!eLtG37Yo+=GCo|pjQ#k33Q{iM{ z&~a5SGJ}s759DS{r5;)+@=d1hh2xHo0Rz5RT7mcBg#_ z*N%ucUJa8pPLBDyh`5b#x>xZVCOuz2xvzMR%v&S*)Xx>V4p^;V0^_Zm0^)o#O zHkDYTJsd?|D!_~Ao8pj)n95RIJ4W@3d;&(%h>dzFRU^}Ago+)o;HqfO5AQ|gDv7Cx z1>30zYX*=wxwu+tiw?OzUm3O@Y#R|RV^!i_tj?c6XI9HvnAQGfJi#7sXfY(tOz#zb6w^C*00xSGj0l7BLr-B}Dx!hsQIm7A>IpFQKtEz<!ToMvKs_mK~9x7#5feBu21B9a#LlTmZ9kg4@o@+dpEbpx2Nmf;_;90%PI{J zAtVQ$&yD4$$d!7njI#R`E*b6w?cNe$EpKXDatS{OfXRrVNQ=?Qt=S)(^T z$}fxJAu8CVcbJ6GjX{lgj9(VVBR0h&Oakb}7L8cPFMY`fu_NALTwgb~hyzj>!Qx&6 zf+R(x^1h0cux8$b7>K@zg6g50VP}OEU{e%#_f?hVCj*u2D`?kgr9Bg(>T?kMI{- zW;_O=GC}Gol6`_y$*9jJP!s+0pM1F?n?FONmn3gYDniAd?z2oP{ACUCrAF^{8R!X41w*xv__O_bLT|x`}*#yb`}IAd2TJKfRL7Ctvp31dCC36Ii6HK=fb_`_kz1 zVdp`pMpixCOu?sHn1tfeRi9u)GEiYsKTsG!b)nR@#AB3)W7)Y%x*GNFXq6yAkuB+s ztJ+|U*fXQ~f1~H7N=VQfEq16iu52s8v%*`coz>{eSmh58xV#)m6|#usNNJUw9;)o5 zaqf7t#T&cEx8-{toEMzG%}%&bK9Ni=hdSg1sy z%uBDqN#_}zo1D~WSMi0(M4+h9zNi_K#41kYq}<1kz&XuG<>_g7|J_612>v6#MAgZ8 z(_;PP&PT1Zx&69=&r#HT3hyLP#@na(9~MMZ9drc~Rpnf%fD|+5r>}OdVGH%Q_pq6! zj5_WUHS6)2JXBYXvII+Qr1~+^SQ;zCW%D%2tk=CY?twG#MBy1kF_%5qSy`b}iyAdk z_YO8i!|E*&p0EEvF)~h%dW(O=ntE5L(lK3MjVsLX zbECT@AH1Xd^#zx92QAZ~6_V%*cFrrMYdJ@=&hK!oaM6W|a(o;N^PH47zyehi3*??>&5U)c*PGUl|9ORoFd~`|4r2 zo@?M?U%keQw*K4BKP#A+89(&IC6*U=EV=C1?2Q=br)O8WpL{cTPn8*weQx|b&iEnL zRk&@`m*vyp4384k%mWWk^~S!eLKzL&2U*q3jN(M5)Ovp@ww{tTbUZfukPToPY=Aj) z$TOwnA_eT_lThQZulta&EdEK%sOB+?s?KY7gY$yjFd1)aI6HW_JhRF@^R3`>K;g)q zU^YMUJ;AyBs69963;;d$KacSoHUoX3wx&$EmBogci zS@VPW{B_s-;KZ>YxF*t3SUS7mW?m3{L@5sFh7BuZxXHeR>%?_eeK?CPqV%#4m03vN zkkA|=RoROS5pjkX1QgOYoX6^)2ib|H%|ONPVVQi|?&J*9=@c}fk_jH%NaaPRM=Oz6 znC`wJ7SNl{eDDnvG+(YntDn21nNAX^F*CDghe-^1dUTcUXA1ZI;n4U?xOYbQ&|d7V zzJ$}L@FlK9t2CR@@tfF8LB~MlZo4q(&J4KO3xeHfJ@&~3!IGN4J#>}?_a@ia8yrAe zvbXmJlL?ajXD=gfcYp2+j^4#RiG7o*2LZ`O8|mg&)axswKm6K#;o@LCovLqeL30&q zdnH|#W?dSb)kZYkh8I#^#f%US&a^Td&cum z!5J@cCtVhNZ1<;Ylad;Ca+(dUP%3{|PshA?#WB6~mu($w&hY4PuP-bNXC8D!Onne2nKVjy@+OF?m`ctwO< ztD{>|U%s1)YBS0>M>@5}etdnQzvMbzUmk0Z1o^Z8S1iRCQ zc;736NujE!Ge#?_qgU3+R|JnFfPYYcPg(lg0zCV<*LQ+T@@wcg62I;lxH5@8EMo1b zt|~T-l*z)b3@}b4Q61?CO4LXD%#|6+=yh9-#urTwSH`R_zI}D!?az$r8f|Z<-|_B# z)3x&TT;?2rYFln7d~6>apGuMBGn?EOuMEab-8f3A<4Xcf#Yz!E&rr%9fFC{2?TsslW*$=-{4HcDg62hztZtO`{Zcj zw8HnwXwp}@D!-JgJSD0)eYu|2_ucyM27A=|+3CVLuCWg;x!%p`3w{(crB9#t4>)Ue zDN~}Ic=ok&&&Od?(XXBYM}qX2y5yWYjIT1PrL746|{sq#9dol z)Axce%o%~pr752XEvrOz^Dtt0Mpg~rn7jRZ@Rjr2#P7ogf8vh%e(-5J9NzH#;GlhC z$#7*O8(TfSVo^2fsb17w6E|C4SA~!IkETBjRG|oN%G9gY*dBWhPjc9nXO~@7FTVd; z5_C1LVS1Tpwz<-w5qIqpV(7ru=8SZ&oEy!|e%oaBYJK;2_T7Gc*Y~nrSu%Fdcr1P5 zlYX`;R#=_9j7t+$t%WPYodY7Tx9A`-CwD8(s z>aI)=ZK*F8-IYPXg#hCjW+l(=^w+AWOEY+F&@pOJV|16xTo<%fU8a7v54qfR!Q>M* zzOcwt;m#`AA*9$QNVnfPk1PL=|zGI;7GCmtN1m_qg2k!7iD5-BH)GDQCIQUmxt6 zxyvoQJ~$4@zi@ppb$^zIVbeDSI2Dr-4Dzb*;~_8OOOR(8{05w}H@j$A(7H?VO%LQ} zCdEMR#x$|9aOX+c3taayn@is^o6FCa1@Cy*mfkB29I>U@p!JMuXd{(3${b1EfI2{Z zN>Ekba2ENI4LOmwAs-`rC{$UnR^v>NRFi6ZM0#NfQ-! z^YUOi`1s87U;@y2d3i9q=0^`u47w7)?SF&a`}hrZ@9Q@Ndu5(;%WnwwgF-gk5FC>E zwVQGyLiGaoksE{4;GEC>E|})->4g)ZBj~#nY(~?Y_sO%yjKdwwM=h3ihe_Yc~t6=icO-?cTd? zwtJtwnS0-RnCEJ_x5VFj$lv?z&B0+c8?WKHfO~KGp*?rv4}%HS{?o7cPhb2)K3#z0 zN`|%DtQS>lzVqzgc-Fnu5Y?+c5GTlyD&WFR~5M`^sdpL>42-E~^;CSFk3r7gA0r}4J*{NH(7 zGJdg*@3DVl_$;hrd@tjf>^=JNqWi~fC?)-F-<6#3deMD$WiX4@#h0%P-icB3o0Y-- z9&_V=F%NM%aZV&cPFL$tkd2XPgSF+p(pN+9#^d0v2L3h~W zAH0L<|6&VRK*IrdvA=hxzqkI5V9y$_-8UZP{#+M+vf?dSBaOqG?|TR;cuP^6PtkxG zQnjLoUmFE{X%(F55_iR_;BBn&SF3`KswGdFJSo33m|G2PoR^_B_?PYs4o0cH=}x52 zQ*Q9iV47ci!(A-C&;eqPyAl9z;v5>pL&xyKhHMA{@M(7;u4fesFS_`^YO=L{m}g5i?xIsn4QjRE3S0vT)PnPQ8}SI?u4h6b%3c52rh% zKUf6yukUBk>s|ZZrMpAiq9$5GAR^1IU{^GA@?4IFX z;hsmvbvz0d+rKe#twT?=uG z)#|PZ()xlXF z)`yp0@UY9=6O6^ohC>d=jyvuNcCA{gnlgd=`1dB|T=l)+!R_vddoe|Ce3%VN3J}75 z6D;eyxA>kr?=>tt{O7^cs?{E^zWDQCu8`+E*BFHF+I7t)ws5wZ4e|mn?85AWI$2bk zosUJMrhd8X#qK?8a3(zE&R+x4+~a<<2Bf*i{bEg!i8rn@ z-%J@;VcOgkXm87Dotf%}5DLhguYT>SFN?h{M7&TqW`Pr!QjxUekJeU=idDwuEDF^X%7Zh zu?NkMBLok8D44j%>TjpK{E~wTpY)?G#H11vMsJto;LsDu!K4|I``$ygKhqzE?qj}v z%)=-RKX;#h7`y#Bch191@fLT}!@;4W*Nou@IfRb@&DY)Gj|595EU%6(A=SBIoW@E8WE0r4 z%Iu4-{87IBZ8zi5;6!w&^B)aP$-LxVew690bj^u!Gx{J+`#?y;b4e^#=0xJYG;%!s-aZ- z>lr-N(S`@E(3`PM_WHbf|6w(fZ7_Gg@=@-9b?m~;?xXA2v<2?Wb-}pImCmgLBWzC& zZywZklyKhy!$ny+?W zddGn=VQvm6aV0OiC5F zl+wmEyvZtL#~h?x+A+*p{Le~LD3^X5#Cm^)q6lH)D9V&qxSpqjY~~7g+0#M$f!M6R z@O|l9!PwMWjm%fmzuhJ}X>OOA{5|G|o`$h4c6qnflf#MB5Rs@$j{Fz`%^LjBiiU!nDGY0vGFH7x1 zmb?5j!I>zlnO~y9ce{iAkN^H<5X~|MTj%-g)JXl^M`te()9unVj}JA`GteXZ*VB=G z*!}*O!3RrJnHGj^fFC5nviDlF%h3p>Pd{q{uJ>6JaMwSJz*=|KIc6KW-*e9k%Z0=j zpm_T?5pb;tti65}>|OKp4-kk_oag>38Nb3tE>4eMs_{4e3RUTEn-GW^|NFxDwSHuJ z{AzpuAoKbAcVc0D>Y&Z_@zOndF3Mrx!rb1l{(~kNahOnsppXx9KL=Y z{_vvv$@9T(Rkyrs8r7ek5B?hpwfA2BDDN$y_JWyGp!Qfr`|O6`lLbZW(G4(~r`&sA zNHnlBUO)pY0QSlk5{Q`h}{9YDLt0&j-n`HcE8%Y@to{GnB z()dsO2EbmrIW~;0ER63ZhJ%Ke9^cch_y48{yfs?nUONQ5|J)dK0Iu^lA{m}^cW(^F z)I4GyzYreRH)+bHzr_xWVYfxEov}l&J>+*LD+ThF2Oi@wYP9-z3ACLkEF&mB&;6d# zg1zqhn-VSm*-e1Cpa{OUDFIt|v%s)vd+s>kqMl_RT+dW$zDO+4}<^ zEhvJc{%H61{G;7_`yY9(pa`DJJx?dk*mu_0zw!30Yvwx-{SjUk6|pyY;UJ?<&y~QZR`11YqEGE8elz ztOwXAwbm4Bee9Btg>&4yGhzKDKL|&km{t^0FES-SfAiS303Is}@yAU0@}^4svFvx; z*i1MFP50PLIN`%-Eg|(K(*WM!OR<&^e~B2QDV89AN3Jb!1}2 z;qWk8SeRYuW|xPO0nLeMYXab+_QE$dl#Cn^QVe%m$ZJe zVT|^{P6~jUcq9wF^?*@fVz6@?C1y;Dhp!zAZ#`pFDBk+RQDI^&H$T0DytNG+!un~* z)yZpWHq&Nh_Uq)4JGLr3GV>#MX;t_ZOo#ic!jE)byb4Jnb#Li7MbK`es_V?m%5hzR zktrcHcSLpg4zQuOdiaja8*e-EP<6O}zF_wZjpJP_6D#Ny{ncYKY8CGX5aZ@%uff{^M%(pt3FZ$kia(EI@S7omZyHtdHCZh zCugsI2|=s~Kq81gGdg_7sFjVKweG<3a9XLkUSuTu&nTd6%tK=1eX%&o07Bg}V!sR5 zy7!L>_mJg1CN#@?On6%6o33_jcnAP|`&a|um&b;?#Gar{omimqw+`{5G7Osq?&h)K zUWvfoI5wP?Iomaj3;)5g8^(u+m7n(ycii}J{D+E;#>(g}fnq`|_T)3*g&P1yl5WG5 zXuZaiB(>B;n=vqh;b|>*@pk-ekB<*0;n4W&`0&UiyCrH4&lyuNV+qH|aIHfqKi$mO zbKF0g!%t+sI!hH(y2D-7>Nivg zHg~m#U&-{ieI|sv0+5eR2oIfffne4keZ ziKKaf(NAi;MbUbQ8CpcEP!Y;q-3AG~?w)B2C#pT$D{bM-1T3@K?bc`7{jG1chi~?ccVhUxz+S{kyn&6-X?Fjd7|!<8zyGB0%i!X&NrsD0P70;fyfP`2 ztFj|!e;t+!6IbOcLQqjnH;P{wu8sxn>p2i~GdWi{{cg^MiaxxM3lGHuvr8u;>1uaE zCjeRCF6s>5SAJ&3z0etUw%8#-v;K+P0b5)gO1YcwCU%AY1Gp~h3J*JKOD~X061l*N zDzy2yWWN52&bx^FNn1|4Ur06JpoHG*&D5^R#x4K6YdFoxTVMWIulv*FaQtCKqIm=F ze5@^^J^Ggxz4J#+3D0RS>R=;`_$?z~I;Tzzn=|LTo~ek7Yu(LLq2vMgi>aZdne7S# zyGE-?C^E&z-!-ksz~)X1b8ho4p|sKyc4dzqa$nz-{Ud3NSG2sYSKKp#(KzfKn$Mr7 zg&mn6yT<9^-8^n){@J3-Noma7rkOvm8;mS|>^E|)@7QC}9$>@*clsXT^c*o|j?WS>UFI&H5l$bc zh;o^7IgFj*5k@1wz&$bp4zt$%eMb0S@v>XaRmi7*1Yi%h&?l9sSB97KNU_2LNk~$z ze&Y+Z}w~WtTY-uw~H1;<(wohX>*w{rSW5>l~ z7i;VbHg=K5{@TX&mW&;ny;xOlNaohy4K}z}gP*s-Jtc$3#4nz&vA?#lJsSI*##;Ju zl}`rGcEqXR4TJWY?vmGJvMQv*p(DNKxy;dewp;J{l?~n!=Yjcrp|W@|`QeqppnY>o zC1aoU-=s83Rl_gsxgj@dpKvn!JZ+zFxBdS{r-o>N%*$$tojtNYE(eGF+0XydmE*|a zCRZ=}9h|p}6e{!AL+;#t!u>KAx&D2^FYsgLzTtoHW8uEx^crhl5o9lM_w0)*c#&(} zFZ?(^zP4X@Gz!A%{lfR_;{Nu)8T%&>)bPNK``ZH*v)S{D+{v@;fu*zUfmdhS1D`k` z{lLWs*aI&f5WY9_Q#WT$IK5t>B4v!s<$Yh66Yk+JH24cYm=ikQeDHx_(`EtcSp(FN z0QC&O(aC6j{e_1arOB!qo)*#hqKgnG(m09D_(KSp==WuD8kef^L%{Z;3bCmQJscm` zBNg3>0ui_I#cLW#QYPrYIX#u?A|>zj1H*Cjh4Z5E5;lHD+1)fdurzdvdR16cw~9=; zb#+S$fZ01Lv&G?Js61JGpsc)%g0DT))L}84HPG&;8e+WE^;&A-@cQ2Cbs#QjP^;tG zoH@3GBRYc||6M#VqT?d&&*yaQ;(mWn*g{JTpJ8hW*yIKH!`%yYj4D)(c~aS}C^u`i zRb>Jc8n^`KqAam!wy_f#Q(5 zcnGb9s)9NziLSG^sS&mD2zsJd1+^+g><>>C-%*uJL?dbxJ>>Ho_bZO5&*uGlIgMNu zRLiwx$FWjF8j{cGq(YU|9Jdp6#g`;!HL1W+6;#SN_h;IQBS^F(uYYESDi55?@SOv6 z%8U^@rU_C-VLXHiomD}FB3E`IBsGz{%isJ^GM#~-8o8Zo;=MZYV_42Hra`^b*G?E-!IYj4Iw~L$~Y#KfK+)((kUe zOJ{9khJ%*p^+HY;QrbDC7(OFDR?IfE<={>+Y=dihY^}_y;2f2%N5>nyv|QYi4FoEc zS)XcI&f@bHtL6H#a#WP?l<4%g3kaZMc_#_?7#HPVu80;dcblTHwS3buSAAGGF(2nz z=4ugoU^==LOueg;{7$kimp$i|%2CQvw4-#D(Zc2Eqcfo=P1!mi;>;*_zUu4b1hFfwN-pW3A_hDg6R<$<}%1K3vA@|$E z!md%9$7U~gjc*J0-QNBGd0Tjlm4ugGWi>v)YUAagS1B*|Gxo@%f=R^Tep>BeXZG_AJm zy>?`k7)_M7tqm3_aog7>n zt&gwfsk8L6&YvJ{X>#zpm<)KTL%bm020b7=&48!Z>HH#xs4iDPNI+oK4 zC%!4gUT$j}xox>%HDawcP*sfh6EO8sXCE(9?tN9XaM|U}*;&PtC1-=A(UhWRPb-!j zC3nkKF9Bv(y2@2IqdcygL1+25hsW;ZI(JX6(@VmU*C|{cah)vWS|+|-Rkg)gp8l@TkhKkJHYi+kuDVFN{$ zljG|ZadG{2xl8Px~E$0R&}o;XANL#IqLQV z$YABY;P+IZo#y}Ob+5lOJeInl#~wpdk(XM;?dFt~8I!2MXWe~FIQ!p$BmYaZu;Mq+ z!mc|<3zOW+cZGZHycM!yr%znRgLebXfu|0y=wQYw8;gIbRf@A~Rt`3;%#5}xB zF6m?y>deNqI#4R}9BbgvoXIN{ZSq`P4os=rQKQxNGX`xIgRV;<;kv2o{&04sxAUn_ z;Vmx{AHL$csG4GqI%Lxd^?A`H+RRsG7Z+-6OX>A=qxxp`*%k7DT6JnPvl_)ZP5!KD zk6!=AX)0hR#j@<^^!2aB*JF`mg|zIYaT)eujS4C$zhD)25bKa3%4nS27?=cAA$HUH zRA;~!Y_5zesaXX#n3$~$<zUnXmE(0==(%YE;fqvr}$O* z%S-O6>^bb5Otvk@g}D#O&`oNd#kI)=FCh{+j+hePK^rlPrV6`OU646JCx|lL|3jPQ~PH9^8o+Q8Ds`C$-S3yUz@BBGDcS=bJgqp$$Tq0CgjkY zXf>|YnSTioNCn~)HAfjuR+Ji?yyWz2(2WL6hzj#%`^vi`4CZeYcJTPF1<9x2ch6oA z+zv65K(ICQSJA+smT*f?c-(26 zNA@#*xG>`}+oILt+zYh24HH@`Hkwu0HtDU2#p|J1nf)&H!?#{!qGk&FsyzC+q4$N& zW3&~S=wYQP-OE;(j6UT<;e={~Y4^;E%vPtVxT8Bu6u9%9uYS=^{S z1!n9GZDF!DDRU{LGD3;>(7pElu({Q*OcgW?vxwTo+vL9ZsjyXO2h#MpcYh#k&Qpd~ z9%4CB#f!|7VR{Ecih94w2O9pe(muf+WyLDT;~cQc@w%_;+)wr>`6=5%rS+_WJmZrx zgyv?E&0qxKfm2=BM00boP*`xQ@paFkwI=O*QaaUqT`@dR_1O?(hB9WkDq9EF)?;-7 zoi8>baI3(G4mLnU!SdX=eap)Z%bi2LjHQ=+ym6wt@OV_T0jc}Zz?&={pKe!U-R##Z-*F|Ab-Fse);!$(zG`-tuh)LTj^tmVTMR$p&PTjJfGc+sIwy?pFG>XP#H1qVs;ffVjpA*f^&mg>)HC|Yxb?c88n>l2k_~St zJrsYzs@5^qL})TL&o5S+4jp&MeDwQK*vh}uiq|Cr`MFY)pbRzLP3_~>{gfA439r;R zHI0ZyL2I!Xvr@Z^lQ%0pNwtkQT&u8t5ezW)!{0sn&;Z7B84+A_So#`q(Ahv6xh1NH zj}pP3YbSKoDU|tByrXlW-u}+~JHe^QG6BCS&szRTioH#>`4Xc=$8FXL& zXn0`0&j3k{(SkinnlCS>#%OvQdnVE%TYO!jrYYLDU~{|yMu=`k#0XTXUx5w#^QuBZ zy>a@IZT7A!ZF7kc-rwrJ`?bc_YQO*r~O+FP;?UCah}p7)zU{QjG0&vp(85wM%tQeSf|fVcEENq1s&ds~b8g z+;=CTFgxXB_!ppXpFv^v&g|&F4GJfIEIeQ*p|Ib-2MT9CAzaw+{}~kWZ;T6)wIU8# zgVqML+X6Ex$5|I!gY&2~9YCI9Q{AJxsH#4s>YRjS;TFco;_B>M6q02iF`TS)s3?!= zv`lr~u|yT4P(^lG4Ek8N7@m(YV-m_|Yi7emaAOQTu_jU&q_*{jjgXJ&d8-}URIo=LJ^I!mC}FmFM1jppBP&|J|r z_nj!iSLs9x^RPy;9*RX{kH{OprRlxf6IJ*6HW`lbmOKw=mmt&6fX zZC>1i^#teXSN(nKekBYbFTzAN!}9e1;4@M|Jtb<&B3>DBcnvrN?X>|*ln|i`p#&QX z+Ye>ww&uxbNNB|X3hh{5N6lelD^nAdd*={z%I_#3+2=eaPVAcPvjGy?}U}Lq1HKNN893hnq0+a!b9@fSU_A5O@3oXtkP}PBUTDqS7Jqt@za)W z$*XYfCu)fF2;XrD-7TeI4vkhnFViqjs)#O81((LO6;++eP_2lT=>E9)exh8WW2Kc# zI02tV(|>3YUv5)~;}^{V%oaF(50*AdecYGK}# zR`jeCSut7P4i6}40si<%ZX-1|Jb?FEK8@~5>B~@B(W7)a}P*~ zms+@lU_C!3X1$~bBJR@)UR2{LSY1>P5* zMHBD4RsKu3TD`h$#k}i8+a!xFfJ)h{*uHfnCNWBHq)kv&G?gR{Jz&NswB>!hj|5+_oAFipbOeCbNfTW~>pWQbJ--iZ#6xp6Y}6#NF=y8SXb) z#p)%Ndc>}UlI`wPkTugzTcB{y{Ki7d7G*~yVyQ$NfuSajxNqVJ4AoQe8kk2951o1R zG)|IM;{=7&IKz0n86_Ccxcmoc`{4S>9M9{{Q4jsX$sUr{Pe?ytrN)-_(|_(AKinb$UHTBnS984IQtuj`!hr;NKNq zG8f+cO!7Db*SB@Gpm|$u|3UkdZ!VGYLZFdqma+fAvm3$Nybs0`a6X6=dFMmmxmDtP zuqVy=AZ$%JAM8n-545H7v@f5M=RWJjpt#QwY1A$?r?OUDUOB0c2boG@gEqBxf@pq^7f;;}hVL zuYoQ$afBIYOs$*c$aH#&k0U}86!a`v4k84y{+0fzQ-f`}WHcPw{q3}H=J949!m0+v z942}7UiyI*nMS->RwHO?>OTF+P2HDr%D&kLimHhzrN*>}+}Hk3*qV=lXxwcpAOZz5 z)2f!|D&y`kn0PU#FmBlc0bET_Pa7u=>NiOKvBC!F-N@OkA&jdy4{#yD4$r=oCel!} zI&jA$@*0SRBpQ-dJrBb+*I-vr^8I9B(fSA~O`&;{a7i7>)Llgie-A&sT4SO`B0PId z;{moQA`Q**23~J|TxIicHG$z2ghl7 zbBqy34+&EB*`->Jl*RJ>W%LT{Qc&Qw*=rj+s~hH^_B$qq zf?U?)?o(e0--=nM3tQTXqPxd$YFlCV`96TOK)09HP0=0&H|NsExCdLGsnysy1*Ad6 zHmyN=e_GXUG(Tfi^y&kbH6OsMQf^M zsZkpg57%8`Ok$}v!^?`k)a4cKrqfAQ-nljVJhF1bgMVSwD_)pQs47+I!KD8Vt=e7q zRm`k)u|pI|QtA-x_8}_vuhh&+dw%oT^?KphbiOfRp9u3d}$nMClQF}P(k z0G<|$D_4{z-^Dg+3T4a>v$tnT!kJ-VK0iNtN?4W_>XAtjbai*=i}mC7@abAYE$GkE z`m*~-c3B!5#}Uxt;@+q{apsHlEn^`H4(lb z-AlnGs>P9UZuQM%+`veOUCnrJLAAHgHo^poJD#J$Ijdh-(3Eu3KvVn$Qm%YyXlRTzB zuIG=B()3aqC&s|Y19TtdM>=)MOH6d~RPs{$TY9hFybQT9y<*V)_UqxH`Q@=sltoaF z7}xC$$s{y$|3@56EX3<1D8ks}L_0RpjZZA+-{363a(B-+!aV~E6rY}}Qdf?R?me>_C+5w7-8r7TS1K^^20U5(WJLbs&sS7L zkBhK|r5T{6mk`!(R2MZ>RbDS0LP4o58TFjr)LiOHOHrTqOPNndTq-PKQ|sNw=KX6- z-iK*5^CBgw7DYJgUESP#_-M3}Fdwu1$_j~xi!gTY<`VeefjoRo?=nNBE~ zJ17&aSI)S1?jbetaK}>^z-O6LKZv5DhVw0ZF2ofFEz13&J6RYR&fw%G##tb^3-E4h zAr?ByV9sc+GytuNJ%sqDKcE4{S4$C9Xdw0ajJNjWryKTqS}AJ%B~SO+(=IwI>^R0e zpiHMGh8KRjYHuHU-973ROx3wj(`pg)(<}zHNEEl^r?)C*maT6&udib_b4_%a zGe}n4IS%Br?#p*XE8HdrG6c?7ME~rDZw%uuGJ1-&A1b0P;w`B{5_;)E+;gQI=ndrm zFb-r4s5WWd`S0OC+vPRdhHj}yKKk0-Hn%(vSv7h)zBx9v*}wF`L0NfbVsm|<<+D7E zeeIdVcprC_lfmMy8flTD(R1tL-~drw_?Qr|TZhT-2Ly2oJfv%;6Gw*THI6SY9`-%VCX04?{`rf02ooIZ|rbBj-B^!DA z5;L(x>S9!S>l(mUWS znk}-#7Su4j+WxegaTAMQL=ORJyh`>O<;(JY`7iDIfPQ(H0n)b;z}p3#xaLegev{;m z!l1ff*}5Kj?37%fde9=|`pGbHElc5%r&$b5E+Pu-eFrPNJbMP-O3|0*pUsmxLJX)A(>?=~$RSrJlRL#b>DlZeNwN zf|GdOnUVgi92`n2Oj?PSglja=f3`LK**IilkX?z#!|VkayZY>!Z&R3_a^?E09fQ_a z&0&}?uGW(FAOMb}#2rI5K^ti(5vNMgXstMoH&?`qKN z1Q=Xx+AoCYR!0r-hL#*PT$?GE@f16uY(74uibK_$0sUy0$x*}k6ORZU6b^35nCM3) zNv4f(^z{`AutZg-)DTH>h>Nqw{?zM330l+<2bxQ3ig zjv8_XI^Bl^sc~RNr>2Q~m7FwGpf@5n?2&V+72prRD!}Mz7Oc|&*amzt@D}4C(gqTz zbw7~xfn*vv| zu+a_eSf}TMI(}SUQC2*DytV*+xUzxYLmsU(dpatEI+aPrFjfCI&Ow=H=w6hg=&B|b zIw7u%aTb8calzsb43qaL$Kw`BxI4pv8Dmv*%FfYG2A-{-46vNix><3Y2@g8-X@M&X ztNOg2BPby2b92`iV(|U}7abdp9mOuLcJG`YwtX@sRuEnN5?#ok5yPswAIF$XtS~P5 zIpA9l`V@1UX&i6TGw#0mVb@qH1VLb55>ao`KJjy-dwqU5RWKys;JI{8>(1wG(_wt* ze)N=QlafOU2dIY}$IO=%PD;iO9J!D0n7maAt5|A@WwglMdqJ4nb_Q#IoWZ(gcn0f0 zDuXqXq#1Z3Mb^d?47q}Y=ir)Kt+r1uSmcVcd&W}>XAxu-7jjZP?Pjbfi9u4_w0~M5 zG?Y-X7%0x6_(O%GKW^=X;e_1`g}?-hRNfQgnx&znQAxQbF1sK+@Mx2}inSXG`VAJc zq)|J@ky?Kg)3Q_>h< zL5{64pwa#Of{-NU#yE*tXG=?(B6}2=yJ9#BtlTCFpg1KvBbUZjD3Ej=v4g|%*Q!DqcB1tt2++Mol7)ioC(~S(T_JQnkqFhZWmjY zn@L7wZJN=c2F;Aleb#ke5{}usdz=7{7kXT0O=RaMrtvjX8n8{$H%SZBz*pR<3n?k^ z=4Z77lj?c)qEshaOKZ735fHv1LO8L#%%Hlmk8%_(ReeV-z~IdlY~Q3{%N{1EKuU$ruIN{Z6cI z7u~V%?Y0a4*X^S~dnpRsxM9&;g$YwAAnkNG9B)U*!Hn^vy!0Ek<*`iO7CxB3e#PzO z!uRYvG*|yypjrR_4w_4ipi+L2nHYFmLvtGh^JFBA@t z!i2nm?+sEr)-i#sYK}U?28Ds~Ngq4)ALES={eY_)c^!O}{;0|#$KEG@uCosJz0v&U z(P);~xbCLQD0$lJcDW*)rKZ`i-UhMWYHD+6K(9OHig0r0&%}|8C3=o735|i6Yss!K z_kO=S@p4Lp{{MIl)7w(CT$ue$@$7HBBJ9Xx9I+_OPf``?qyC>@a#LSgAY84a4QWAg zQFsVo@^@h(8i`8V!Yb|xV*}VSi8LHT)`nxTDSIedSTy(7#jn&EFU!MOpSRD~M|b_4 z8q0L<(1$IlNN>U_vp2<`G%FnyuwZj2+Sn*2h+MxuH|u z4xg8K$>U3cEtb`iF6l!WMW0Ig)W_nN+M}v-m}Gi*S!{x{kIBi0zL<$8R`$XUOP-I` zCzc*bZ$%?}*HnS2XEN+S)hW?8VpDH@rgI`%F_vEM26yUru=M67R$YB!>5Y%N3)gZJ z3zptUX0?(n#Lc%^z{fM^uBy1uR25F{BLe~ne(wQ%IpupQucRLPO5^I?t;MW`6?SDy zOG&E1Pzy&{r9DY2YKppwSqqjE$_>tU@o!%-8Cd7LeAYtoni3$CAdGjSDjHmHIRzoQ zVlHm5myb4`Ce1GJQm&R}Ei83^zB25bu%yjvujIWC(_SrpzZ)wljo-7_(gRJ^KEBf6 zF3L0!)5~0Rs!b%T!=0M0{exCQ?boK(U2^(2@%AF)*&49J$8qlROX~CS(Yl+uR0o z%5Fgw#Hmp6uG8=g$TLLwUVdGoUu%>)Mz}%0hyM3ljqQ*Oh-{$J(^y&AGs|Y>^ITA*FtWlFruP;UtfRyOZL>No(cPtYe5+ z8wVnm(p8!)9#0P|ni53eA)|+-O{Lr4RI>dK$lBMUmul}VJn^QInMGsJ<0j8XN#GQ3D&a%NK(i_52<>eW9Bx_OP zF>G^sU_%rBXHlU~7_loE}aoNpcW8xH2X>EZ+O_n_nZ4s!=jcS>+vX4|d4<_|W@{DS= zG6ooIX+jnAdDcDBx5c;dt#fJ1*q%msUHY?0YVIZ8-V39>P8XV^D!{5bc5+S|F@q7v#bolrqRqtAoI2 z64$nLQC(l&o}0yEiiP>qiJRRwuMH3Uh@IZkOLzsS98FDv;j`@TS!Dy^VKplCVIgxx zmtqYy`$M#IJ7!W(0@l9%-O`Bcz=?X3wp>Ru8qDouRnd(57%}gQR8Q=$=;HL~f2ATj8yGebLCiDI% zg}A`cx7Qt3(Ldes<$aUT4Tpz_8wCs~}+}ngTZ7ZSy5S zc!1r3X(=a%oGS_nRz+tVW1InBq*dCGBTcZ6Q=(zCmC>4|?jy^T_|U;t8VZqsW*dsb zvbdpTY^biYp~)ue8}&K*#x<-mNXKVi?cB4pB|v80GPsMW-=YN{>+(?I1nm2SQ1Wcf zEYGZVuPzHa>b-d+NcOv_%fspUTMZL?CsIC2dWsS{7IR7JIAs_7;T%*Y3&ZOZvX%uO zkCH#Kr`NT|PwQ)9g)_<&C-DWym|t=j*7DRviax+wPl4!s_J!)$VHDhME;Xzah+ygQxrH_D(ge+WAZ$uDC>!rg;=hH|KXrN(x35BY zKp<*g1?oba_;D-22Kc-L58p@mlm+4;-;1oC zUSv1jK#hn(r=YUjTS@$nQXPN~F7~@B%N?wn{l)gc=D|%4MA$)yWbc{W04cM|;IJqs z+Kxe2R6>g^R^jWnX+=>^A$n#{q`B5}O_h-)BS_K|+T7&6b|dK*h4S@UITi4?(^}Zh zvjN_@v=%l%Rd!l>a5;QXCmGm!5)Xv!Fq7=fiDyu0klI+Wl=MCtu-)htVQcN}ji5d^ zm%DvdgtP0#Iz9dOy05MXcb(v|mLolt!Ah`3EA3HA#Cc=f$`xTpqy9i^YW4p2jc&sV z+S%OU8gJ5ZyfI8k0n0gMngxuakI{#9^IzZN*FAn+e*cO^x*%e{F}`5SfxFDtN0ep3 zPgxM=W<}mxMhpozmJuUolgPy2t);^@e{H+5gwlO^o3TZ^j8gh!k(h}K`{gMaw&axA znn-i-WT{ggIuof?^^t_Ym$HoYj9tuCw3H$xBV|ZwND_?EoYBx)!SHOpNK1^;Rgpt_ zA8yYy+~(acjk>WhWPi;?V#yJ77r8M!ts-@kSlJp-UfpnU*OPXX{2&b+!F4ZBIl}G!~e_P zo50ysm-qj3@64U;F6T_hHZw`MGk_#WBq#!c3nw-Kx2ivFwXV?3DsHGnTdQ{v(1@s` zf+s5KSW#jP5-t9W7IajK5m5)F6f0^_el;R$RH|rc{l7oY^F8ODdoz;-1F!b=2VSq4 z%)Mv*exK#@?0-|0pv~C%f=@P%90=^3ZvSNCF+&(1tfPYF!~eH&QcGFZT2xi(i1k!H z+N6P`m5&lSvhqG5Z z1j*{VhkI;y2}Bxq-qu=aycy&woR;w>Xdwt6|r z36E^S(&ZVq@;NI>KW-LbdwS&d-%AT315FDOTkp_kgR zj=3XF4Z9ymeXcKppU0Lzz#>8PRgr&@45@;EVPP@%R@rOmgl)>ie z=F<54>FJ+l9OSRkSAV)O{``DQTg8Tkb|_ITHjT-pJy4>?jJAZvkPu`iLz3TJ`wTlz z0(!@3{Mb*W2Y+F$RVCKXaxNRhUTZuZoqfm!EKq-{N# z@oJ|UE!93Lu3TM4iy9ZAMGcjrMJ0_owJ5pkZ8h3|cJhz65v0*ZNb=01MsqEigV0Qi z<{a6nMO{i2wWuEJf$MuzU-iHwJ#IT5#NQvHb~9%YqV7|rrjNLqmKdf^p(|6TJoW*o zQ!;?$4j49M(aqaZq;2(>23SXT)TP-*>QX;y+QD{*8=YY&2q9X=}kCRbt;Q21BIyk#)IZ!#=StTdRbP!e=Y;Yg`-8wE1ma;h^wl#Qk&Mhl7 zq$j!Ycj>ZYN+j~Wj#77)%A2{y|CCj|@l1Q}VoAFw>aiqmq&e=@QhXJU>V7R1P?hNz zHW;UUh0pYfM5tLRX2p=X^cJuk*`F&Xjl!Fdg1JGu-6TAk%yrM(vbBU8xN8^^8`<>;=Wm`-AY)Y7xR$Y?|-1=XZxi4CNC z-cyF$ehs9M)$Y#Ekm&dMNP&DFNa4ZJ$|0)8ob%FSj;gPDH0@CfMBtq&3q-JINVHcR ztTizp!q$B@{roi)d%yo`0{ah+R|5bxn7q-O=ViGi>)5H?ve%bOdVYDNZ|0XrM1wN* z$rc^wROp1vz|}&9B9uuy2b_&OShMbhFPQ8}R-ZY^%2?M~RuMpL3uqE#hb`{J+36Y2 z7+;)DUfXyA(-je1Ye`Lji~v>UBF5Kjo&^MvJPHYan~%?An0ptRBNn&@oN@!Q4a~A^ zu8!}*g=zd@%PU_XsF=s0HkWB@E}}AKv9~uqVkDlu48E?=UMXV+_s-yuO!1&uEB8)5 zQ#nwv6(2WJ_MGtD-qZwRN^4tdOmUw4J=4g%zg0v@E&>B>L4%m7A|Ig?YCBBX7}RI z`zk-^?8~Z&J=^EgGrlZezO79!UQN$MWl?RBk!D(CZH_%ApBuJ!70NQ1dn;Qu9bH^D zeZSb2Os~k(wwJ6@yMi^7K!6Dsr}1@-qb)zT88mJup5x%9@n@keM=b95_0Aznr57f8C7AvH=80_H7t$N9Yb+aLft)PUpI_Hl3a_WP zel1$rvP3DZ_egT`K@jSu6nc}b-zQ@+n`6YwYV{CL9o5d8b<9awM*>f4)Dm|BvRnps z*WjkL>a!7zi^DWzYM7pip z@3Cw<52-Cn=dK^^FR0G_Si8d(TsspTwsz@BUu&H7e3O^mI22AY9O`^0=qh0ua@I|F zyJXW{dXwuxqwVbT9LtbS6`cT$H%DMQTDdfzhjYJUGkOogW;nIti@bZ#Al%-8$;qA7 zCZfy*ECtpHIh5lS{8(*EfobLc@%6^!AWm&tq4de)wH0O%rH`6x42%{BmRJlzQVLnN zD}C-9j#BBR6Zu5WaF2wl_Rb@r|Dfq=)%@!(4utG@<9x(NJ35LKIFUISH`P4}fh{u) z4kox85o*DMufPA46Ff3-qQxUUIPez+LKTNIAsJ$isNMFY(98Aqrk6pZXnILOTUa4x zO1YE6Id=-%BOCMvA=u|f=AWmf=SPdv7v9i#*if!N(~sRikJ+~Rv@n8ThRemFpg=>l zf)G+xtst+7G=XUFBDB5+Y+M+$|0ey0M$gmde6Q74P{qoMSr&8E@x9%*Z}nP(*k+-L zd~+4eN%HOpy-3ey>8r2#M&q>qoo|q(55M=DjU%Y*OwYKkI-Z{X=%|v`zC|UF_3v~B zS1pkicg0CPmi@|aS+ZKp4)c^nG_j!LgFx6xe8(1DajC3qh2tz9#c@V*T!-V#F=Eal zXu2i5;marO#HBo_}aqqX0l*7ew#gXR!ggnuI#2xwwk7(ga)$1xC9%frLzIX zbhApoeiJ#97PFKKZ=B%D??Rlj?UCk9c}7p=b?2aDg3$uT<(KRcY&4IdWO=4*TW#8U zLzbqAM0wvU@eWJtw7Hms<+JvQ*lKR&$l@b?oS#a6A%bL$(gQKFLvK!RxrLHrVye0@ z*KH?$o8(7jAYb>mh8hT}Q#RqH@CbS){qbcp4Fvs-;Dyw#Q!Bw7n(7m(nXQ~QYpb#} zKi;ibU!A%1jGG%v#=es6k|`*Sq4@`C2^?DK^v#V2l{TrX_>%O-o58F1OB?Su6gK#N zY4blu+SLE2N}HbfrOikG|0!)sUrFEpoyIaCt%S?+bnZKirJ01en}4T&`hjR=8_b+O zKbWER-$0FlnUx^Jkc`p+V))!VsokIjKkg=h8-6n!@AMlR%TGKYw4&}W0j;DTzbjhO zDu5K}u^ps{S}T$ORTv%ZP-RgV2Hz<>hIy1CQmGv%QnC2rJ%=j4H&U?_=6eQJ$ZxVt zSbp!+mMBljSLbG1FEI`~-GN{npiJW)W0|Q=Oov2G-(=!}_bC zadNmoy(fL`_Zvr==h1Gw&?x|<(%PO+(Wl_$6IFXdp*+1ZP*S%saF#dKmbr)>Z{Td3 z-JJK$0UnFoMu%FvYG7)Ee8>C%X8@2dziK_hje6G%@^xMHAVa>RyC9$MmbPh_Z0keG zPEYzalGHuC@n!K>(x3gnW%A%{ZNT^V+YrZ(yD6I7n^`%l%{c(Gl2*q1WmaN8OTXfMcZ9Mk(_T(;c0G|2*RR|c?* z%jiHww$buEFfJJmoY#oSxV%(}n^3R~ioU1DrIJZSy~6yM=Uv8SM~ki2at$-E;skzM zWx}RhWXpaA_VE3pccV^Ok5yoXSp9QL&bkk&KTRh9oL_C{JYjv5MW5L|S z+N{khp9eEDGL%P}8EJE$Exk1JLig|UkaOw7e$*Hr|9WPS z22YJT#3}u)hM;=Mj|$*%=0|Ptcs>8VUs_qhV9rg;}s|K_!`^eyV8Oc01~`_??aqj`~eHUeS@Fqh8Swxqvw? zbJTNO0a1be(&n#m)C(7qZQXX%-)zHaLh}rdXlvjOu6HyNUCw3t4(0POX2uj#SIa#O zY%oOSL$H{zF@_hP(ldGK<#aZ`{i1TwRo~xi4ICwB-@$!m}DtV9ohi6)dQNMO>Q`7YLRW2kIK=6jRCppxq+c6jf=1k zdeoCty97AiX=rK?-~S?7sbKBq>z(=ThuUZ=yoEYpTD7ho-q92-d)>j$1qz;f&=h#- zm6*DhO;k)xoxPsAsPxUhXpFSv&uJ2o+{KKc+?g-uy}uxqm_4>I6@AM=d%Zy>(JHs1 zu@wX`=mB2)usN=G+uLQ1d+a{DrdznLd}Mh_Cdd?*8gS=+-zj+ssJqU?AXh(Ret&zV z?QaLx$=_}*vBTEk7VZzeo_1P^zEiHxEE}tx@U1a)TTZ6a_x-Z5vh?-zs$Vi*2#zrt^Z_m`o7@RnX4aTGjj*y?S4dpo zLLrm92X202`rKbNRt()7f{{DZTfY=dq_6#1qxM`&6U_5LEY$#e{w@`o@q05#gbAM1 zX=xKAy@e*xD&!9M=L%KwdB;dTJgb}Ktm5}f8vh!K-pS&1Xc-hr7WNXM`Yc(q!Fg+X zNe%1pT+8fQuGKZo$nXwq5_Q5>P}pc=OhJg5G1h)ax@I~57}P(|HD%W}q;ZlKfhTX+ zSa6CZ6ALQsVo%$hqdEe^r%4ykKuBuBW@~Y=qV3N3EiRR(w4UaH;V_|;lTJ%wbCJ#D zsn4cuZj_c`<;;bz_=Z+Gd*scBVqG0$Ei&k3~gdXOexr$U1FFPU%KVA+pSNx4G9W z3ZdIE^~Es~=c*=tP22cbqHSZmzpER2RMR%so3@FlW?Gbdxu**bJI^$NCb1!W($Pp4 zjo&m)0+|?N)x$wHgs(@gP}V|6;%P+Z%%F2Fn`kXJQg8y118X@5N| z!Px$Kh(9G~=rF!hnu;mR);FDXl+@7Y+x45zw~aT)NTZ#2Rk)#x+9x1uO&T;`#U{gq zmRZV1S}zME3o>LNzGm1b=IvN1=C>RrlMK|ki3^cU>=5gizVx?^C4V8u3!sY+@pQp4 zi(n%kmv`Z)gqyJj&B^Da?j!}}2S?yhnwA}SsU0XcCto4T*j>NPqWDJS6l<8?{#&MU zDlsurSPmeBDD1X&Ani;RPdv_~xFo5inn1lXk^$Y5_rO_}ze7!*K2U@kj; zDouwb1CKM(ib%pH8$TMKc%1g&aY6}+b>0*zfZ_+t8LlA{eaZJM$p4M@SP7Lw-Xa;r zc@_=oJEmujaT4FrXFMx|O!;l2vd(S}YOKu;eR6hlNdFebZua!?N-NT>qf5tIG@X#f z^m)*9A49qte6&H|NP$%y4V!3lK+=N05Sg7f%-f!M&YVM}mt<4cR5alOp%>?$Nd)a) zd_s#hwnkXJ$$P#)*7};xBt=1y;=rx>=G_n|>Y6rjjjoiDA-Gvu}+A004S%0@t@5^e|)Kuic3 z3{b0G7%~Y$L+ND~Y{7lHqVi*fYfN|;+?C%@BBBel@-kgXBKo8&NrY2Wp}k+I#$QEq;VqyD%YJ+y9RlU^kvD+dVO#7hb<3Y;3o-{4VlM9`KZFi=n}7RAd^lT%J=Pj*x-e6 zyw!%abh^rN^qjqfvidR9CFA{n;Z^0;m8sR3IxJf1!3S$E3L?&u;D|L28Rj*s&X7j1 z#PX=orx%?qc*G8cT?91lU;)dsVAWw7as2n%DOCdNA+{llj*FNmGr%h;O+#7Ympb z8~>X|&T?S>d0zCB%_!qb%~AS*y{15T3DCluUJGQ`VCDZa5N)`h6TGL+{&7N}7-ZRf z#0lTNZ#cmSE>xS{dz|2RS%?$#s(WyP(e6RxgqZ>-%oRD|>%;eRg2$8Tk+)wuAyCYH z#|gLGH=N+YQmEqI)Cqon$2achgahM*Kr#0nCw%9= z;RF|!_ckZ^T^8a5z3LvEV6+=JA${pcRC(Y5prdqVBwDt}!4N@}HW>Qy$o+(WV1yqC zP@7_0r@3QgUCVaokLWHkZ-GlJoC!rk(f8Bwa1?kEqIpJSlB>as_?kD{HAp8yW zW88^@^XH(W|I7@gDpA&QW(R-oUL^Pp^_sgJ_d)2H`AZGt0ocDA>J7j8LIkc?a{}K` zj}(5-2zw#APWId0y`+F`zEY(04=%r-()WwfA9SBn`p$cm(yR9>rF&(+dpPIu@aWYt zN@vK`!KC!}6e#_=0;Rum#r=f7UxdE)J}2~B?^Qw{zE=r7D2#(!w2^of(vvI8W~}O26Ug`<~LX#FxYIUdxh2wk>;x90<>zA%o@MXU}Y?A66U0)VIv0&RhYtef|BiW(MlY zsM@lk%Uk&Gfy}o*=w8U0@$r)MIq$E%%?M*2$xfn_r_J<{xY`E!Cn)uKSqju`I_-zb$RltAhH@Yz*}DmP)1GUSCg=(kij# zagy!o>s7nFSqaNEmp=K}=%~<<{Q9mG=K;&gp1&uzlKtlG$!*Wmn|$;0avJX1N%W z$f~L>LneBllpuLrR(RcmiaDxzil5#MNu`uc%YM}un%bKnZO`1>61UrRJGC8Vgs;>+ z9p$AlrR1xs27f`;%6rVU#>-+LbrH?eW_2^NUd4DEa;Ic41uLZ2`nV!J zx|6BOrY#LN{sCe&6imJ;++fp)O09O<(U5JMfNb!Uj#}Gu;i{GrYV|yfyy9lFs`f3W z+Cu5d?B_N+t@DthMiK284^2fFQx7!y?n17&n!-HwSTRMYQ@jZY>u1@##)JbCRb#tB zPqf{A8bw2j9U_bPNgaVy2yY(hGT*{wLvX^2j8q_`<8^5Uk5L82!PI+>rXweUA57nr z$a+?yYBvkQdRFzLENOZss{+kfP+8TkpIFR1>sG@FH`FI(vrX$v-7}j;Pp+_OEe)v9g;4`mK9Mh)N;MS338N{BFGAitpLf29y+@%C zu=pNQY+di6d}+PMqwGbrNBN7WmVH{gL4KY+k`)_Up_k=u^Ns4kY%gct`TS@}I&xC9 z^nZhh*rbKt!L#qbh|nn}BL0e#78DUWO1pcniHHlr4fZY~bkxBV5u*5eK}5Wn%kD)) z+))q_AAI<|AtEM%zN1`CyL#|Mgf<`-5wrUxBHs8vQAF5#Yf2Os&r zLqxnmJ9uCsLZ_IBc%F!9Arzsb?o|@somxcxZBO zh=^wG0#oV-T|{UDauM+;{tZfN>t0Ll=mHU&_9-ElRe4{72z!lOLOep0Uwn`K#t`hJ z3y3$J8r9NAJvusWr7Q<^Ad&MrLmfY1i0s4AL^TI z0q!@Acw>>w8^T3ov{m9?(PrkXq;zJZx2NaAk`a-&N^Dy+jARF6k>#e=@McC}{ts+a zYZPm);*M*XtRe-UV!+G;nj*Q`@dxxC;BY#CBoi9}nc&2vfwF2OM;H*)F-7j4xoleUA@wG+^4WBc z&cVDZx@>OxO1g}eBwk7$+&Xm2=&DN396NjS>jVRy4xGJ>_@7D)>hd{}o7}O}bk`u3 zQC3^m>Dif0fBx$F5iP6<8+?WrCox|D%Sbh<>iXTWGtCRM9Xm7x$f{(6t4E*Yt{%#~ zwVAfVAA$~O%CpG@kb!VV^Rgl3&r?}&i`K=~JjhJW8KxfT1Z&Q{CAz9xb3f&2m>QNYIW2nP%6wRi z3c+ z6A-MlUE^k@8SBf;w7)XEwslETI#te`)#dVC>G)%K4#qvos{2r%Rr0d^x;i3d$Txv2 zT9b(C<)K3ar%tc*UASl^yKp_F?N)#5&I;BS`_uQJ%c*moNF&@#PfCS#CHWI%f!d~7s2#5Ct&qr3EDkBwFhZ(_Znq~yZe z(wiO|9pBnEesWJwb7NB0qJ3ig1#5el@677TpJJq|8;eRG9$W?dHb+z-r0FOZ#I>Zg zQ9hF3T^e-nlGKy!7QUtBxo>H1T&~lb#}7|R)@xS&nMVAdCO;wmCJyUHa9GcbyTpW_ zg2beqM__m$qsdV9qF}5`@GWTGmGuHK;u%REh{QDa%aX}KN#_NIPVU9Qji_v0{SC&9 z^>@la6jz6=yz*9#naq0$m>WS(0GpOO=xzlGlOC$SU3=2|SRCy-r{+tB+QQSLT{4M5 zw_px=lqeMM8vo-!-_4eo<7cp5Yt}pxg_z(#NH{bKO0_oV-WLDdG~I@JwKh;KpTaeC6&p}^&J&`ePWTWl zSj=A}=4}ME*k@144BjB$x2%9&%j;X(H8y2U@4n@`iK+HDQS?`#nZ(|7A)g~Ep50kc16kArLx0Y*5j`k^{+5s4& z7adaZDWc|8##zqP!_|8E)r_o7Z+(5Ve2GT2!7gf91C3i>?9Ea7@#AV6)2Sy$m8I_d zY$)of!{je3$3IEm^TcSP^oI26Cq_rD_1Y);i&3PXb)XyNL8~Guog)Eh4a_1MjV*ps zw7N8%KH^EZ;XtZbF7@bGS8g@l0}_>WKhC+0T$ER%)bx)$uDm`8Rl7(?L*f# zm`-j}c)zxxKWu~6H~sm8>dTsn$nKNE79lgb5JdWpCr3w??n?jZ$&I$IqV}9zmoXqZQ*g&sEx@LB6X%z;lJk+fT4@}PH0!l->b=a&)xZPV8Kp0MO7v&G z3H>^CL>Cb>G-Cl9L7fYdL7^2Vy$c7e$Dn{iPjgv;Q8p3gi!Zm+cc;&PYBV`=$9NVC*o9IfbdX;1)M(uU zXZ3L2P}I#!kOYClpPQ^E%4;`R+w+YE()gUeZe(*YgI>62%-1#KeSWI4K6uuVsDt@V zvnEyQV%MMCB+WJdl=na}`(!?8yL~d>-r&1|j48}QS+N$s0(_V?3agoQ8bF7|vc73J z?=uC+La(sxz?mC_iRdGCkp?Pb7fkCnBTKAvVhhXDnkJ@zw7%pEou|)o`o*hEvI^Wn zw*qtVFCvuV?|#>-P7?YoP`0V}w;Cf|*S?N!eW5iX<4*d`t*s3sgfg|_k$k{&?%+mm zR?s7ea9tG8x$uo7y4Bd{Wlv-F^mKah)1u>#GN7Lg-vy#83Xwx}AHZuGGyY!sA5V)O z-kKK9WkKwzSYkbuL&#FU0qJMJv|yDrAPc#q3a?n7(fusUd6Q!xo#WOIPE1~av|yJaCH zh|1V;n#HKZ!r309mVYFfrOy(IQlV1q7;;)stmX9*y^J_|k#u{fPXFta6pxM$y zBe8z4uSm9hbn`N;EQZKH(*J~UWVE_DC%vyocKriirEh*A7E>eMTya-LKO2FSqC-kp zFIZO9YO)n!;ApSsu*$y~F*>o@S><%s!51q|bCYfn|bGqvp(bHSFK)Bv?ZpT}UjZZeU`*s2lp-f=C z?ko&CVzcCJiP^bLhlHJTDM|)QW)H|haM9egfhpAsD};Ew54>04jfICSj7RqnAMd8!-H9{a55h)D$ud(Aq@JQg{^%Gml<&x(?J zxC{@;tQ#$Xq|<>f!M}fw6?n*o+-@-{Y_9=wt_HPis6$3Ibh2BxW+rzp2;+qfi0P=+ zNZh?H%;WdHFauDH>3&zsnN}Xm^s%y95U99Q(j%S|9UGRg1Hl??&4*yylyqzn!S0=s z%t)M%l8h2ty~(>|P88i`$&Y_~-m>Jsetq6DSfhUeQq?iFM4e4-?I0aZ<8#Bq`#_pwAy!fsMcNV zDm4mmjhtFONlUMgu14QlTnAH&5_gO$qpj)|piN034=p|V^k|47a^#_{N}sf*UTHZ$ zcR-onXBT>tFKH!513hbPMYA0!Iq#8)+4m4M`vS`?6ySv3vD`aWdMeuN%c9~y@O9s> z9-982Gom9~x>?%?Su#NwlFQ76q^7fUyOkfzag}FQ_-cK>?j9UaHF_t@$ii+mrea2Q zR}VJ#%9m*7DEo?SiI@D+FsCJ}F{ewLw0-pW^wtVKd`*HyZOH|pJ+lM|cT-6RV;qvt zg`Xx+YX-z(YX8fQf(x zv}S-qN?D(WK7PgDLXPUj+>sH*@uo@&$E)9Lf<Wf*!EmbFdwY=66 zvaYjaN#(>(<7=TjC2{Pq<9)=H#b~rNwi7!P_+W9sCS^xBWL>!?pBuNazVe2VGSv!v zW`Bi~tywQr8Gy}g`Kfg1R<@6C!f;3^3|*GZ-3$!iaT%Sfy>W3`%MJ@yL5z;8eKd!S z6V{*&w}?~CyFEVSqQAp@LQo+&uKEx4(H;K1cvcb9SS8M{eS$Y++m9v zN((AkZ+OyIdti*n>?-WQZDhMPTMhdYY&AQ^N%^d|fb`e_Omw>m`=PWQ(@iW6fCiVB zJ8DB)R+)!z*PiE~T?T!A6Z)OJJxx*)RWaXzNrlA(FBogy)9rAjQ7QkIkqCVEQCKs zncj$3K93f*g`T}G6}t#Z&cEjJ%jqudx#n2-Vq+7N{FHT1u6ZaBfu)`B7`T!V&IXg5 z!2CTa2XcqOlxy9eRcHbLlj*?gBHv}R*V&k*_qIH*U2+!Q1ZIvOaoI$hIWh@cSF}b{5Vifb!Itg?1)*xQD z=<|Xcw$0{mn`M9jYEQ`V_H@fp?I7W z2`+4lf9N>=mL4z#u(+t& zHxNeA-XPB_t_qOnDgtps7dnygkqlxL>##dxKTP-C9h1&%4CQw4fIpf?6tsOx8ss=+ zO@GiDHV;HZfjcPKbnfP5xv@lF^JKdC!6rV~T>L;J0}4o{vJZDJR<1dEC&VaCSZTtc zF{vBY^qaNUlFi5}3}l8iNtt?EJsxT9QnjKa(=Khp8hzPeSfA5vSpTJ7MHtewt{HpM z)gWmsP&!@6>2@|s{+Z!b;M6C@%YjO3E5)0bd6mv>y1Uhu+bRcUkjkrzfQ zj@(u>m94+;Kr(&xyWCWcFqOZdm6%KKcp=VxH;fhZa~@_>nK(cCqUe~?uJkc4ik@`f zCZMGRFpwdSaQUQ^`AayYFgrYexdX70Z2Byac`;YYddAC?lz90}di0AC*#~T++mjt# zw5*l{A~^7e+jg~V^rPpE4R|m<1VMRVoIZsR&F<0kSwD(Kn<~!089(X$t}>_@3lY*cwUgyx15D_;S}8wOm<(vJi%o1 z71)NYE3%q7gKeNpaUcX~;->BwVznE@YU>`6bDD&-aT>#pNd~Z_qKfAt3C`P>^HOyx z`*Um`Yq($LeqbWE?*WeBX|LC~OY4Vx#Xq@RXk6AlfTyJ$1`CE2cYo$k@XAb^v@dfs zSzn{;H)JAy6XQ+HHuAVyW_@hY;jDq-Lz@?4Fbfp}aIMGRu1}~APlFX)$e?_#sg`EpnlM{49 zeYpJ=@qa|@fP_W4g&CV`qTs=$l0&v}=Fqj0JU++PZXvq5tWXZY-BSkKU zziy?@@psu;%%tniMkj#`%#4_u&x|0y#LNiH(~cSzXDONIwnds;1EMm7V(%#3Kw1Oe zy7R{^OrSoWXhMkAn*lyN3Al%>1qSLBw`E8(jVf-AU{Doszb-Kt|0bBJ7*KnB_uI&w zomiSiIX;jir=R#gO{CdqK~NUvgT_RKagJMz1Z2I+&gJJq&waOD#6}My7qy#Hi_F62 zh3y)a@qjtBmuvi&U8BWBDH}Vdi%gIClqC(y~EGwHQ1>&xy!wqg2juZoUeeX|ndATlMj$q2epPQ4i(P+}c}G`<4* zmF0Wl-$XBLnNgDkB{^C~O=kSGO{UDmv4|Ze(7r62%w#eP2TN#P3r7rPMSNy-;1a0D zQ`It3+5%th*kL2(6qpfXxiSdmL|fMD)iCv?2sj7`i!1L9ww2}uK!Dl&Klvy;pr>^} z+#<3!May(Is&%*Rro@PfnT_VTO50?x+>Q?1%zwOW*hE=uo9bGay?xXMuw9L=*KZB(t{X zs!`@l42f3dqHsEsNhhzCrxw`MH27d`&yAz`#E&^NrUf%4E9Im`pql%Rn+@7!G8^%f z{-FKL%)^REMCk_Zpp7sA|8kLRb#56O_~r(S^WMF^m^{0rCl0csMhr^W;JC zeF*Hyh<`TL<@~jP!NZ~T{UitNmTHr`O~0!lnmDaLh$@;JLKs7ohp5Oo@HMZAjw(-6 zAYSxg9N)>4=*xL{(&M|vsm7Cd5sW7}+Pn>XpT*6M7pD=yzRQ9}HL%GP!i*wesnXZz zt&x~k*nd&%<4mRUE{b0e@2Y1KhQpH0b{%&itS)bMwL4=UW*LMti#&L4@HhCUH^-hq z$X(`uy-qZjSj=311V8Msp5l_Y6;k0Vw%~(m@JyU1jiM70fSw)1 zA!w+v(OOb6tQRW5Gar|98&cX`{8*sjkvw~}mv+%-J+ZBOTytKn6?hHU} z4ZdLZdY}brRR4xPP$PS=v2MEa%KS{djr@<46Od?j<#H0ujQr1L5FY=A1)S0(W1ZfA z7fpl)cbB&J5Sn{v`O>&K9DJD!7xnX62qI1truneY@09dzvaNl8d*W@&+5O8VWG0I7 zE=5h7k5Ru_?Cn}?FbF@lbEawiKm=RIFuY(~?bepD$wQfoDIE`H^#}Na09h*g{)Y}Le8-+dJCb1boQKRW$O}jQx@1NF4`sIER^{j!=mAH42z~9 zl^k-hEt)O;-TA##K)t$r_n5tU6kh!ry)v0ev)QW)KbwcSm25Y^J2)!5`&{AOuOFLS z##uUA!-W0*vz%aYH;V^}x^K=-VO4i!)Js?LRj=SI@WbEC=UfloPS#`f}3 ze&)sIy1Xi4EU!nRM`!KkQzXGipzvTJVK5FRcqe<@zL~P!0j@C?NtrckdZ)(I51fng zET6*I&&i?WH$7)I%Ekz8KGgsXs(8ZsSv<*L(4polC(h15!`+LK`$<_j%cWim+wdS* zRTLQPkZT#kZ+6A{9;?g%%^_pnhk<<-aUp`62^qidFDb0VjO!LwRaI$9N#B_hLsZ>Y zd2+(vXHGDp_n~A8g79B|9v2$219+QibEPiT%QrnwsaMu>$ZICfwvzuG3%aha#Xs*) z9(C5_&`CX4HSB4Il1FW<_pUDyRd~Ifv%W?7h#_X3yG!^{MoKNdsycBtW}a?gPJsrb zg3KLCO0Q6=4wWLmiZjp=?jTblKyKj)tVz5dbxAQc_*l8 zLeEK@W6}e0IzDNRKQz(nVirYNZ}__Kd97#sxBu7TsS;eHdYZI$KGt-<(u{-)Sg&UE zM5Sby`78T5q`#ir_M24Ykjc_iii0#w4$XtmW>?srKRS8mo8H|q_3zQp$FEDtbM=&n zm`5ic(gE3+&F+|fHCSb=Eo%ftba@I{Bp%G1VD=kJP>^wM)RMVdp3DtSs|ufG+1$!b zk=Wm@4N$PFy#c%P4Y*Ppz%{kMiupB{wKw3x5j(}A<%m09fAC&={UJuuU>~hKSUg>l z8~4(Gd?x6fJqyw2UXr& za;e9xVS+vuR@h?VP%!OWW2_ssujD(pd?RcRpog@eg1)3EG;bMyGzT)U*yekSi(d7z z3HA~xDn_nQP^lQMB+fvsrz#-8tB{AzLiU})OG`oyrsupdIl7@-xZ3 z*8=R{MtAP>4=#uva`~A2sh=^jIX!|-PS;O(&iMIV_D|kE+U%c;? za*8a^*1Ul7;Pv3+u@q^cIeV_@vB_aG2lbFGU>h_uCHd{Hy)%bi-IS-7b#y(rzp1-z~1UhB66&?)VS9;bT6PUaEN7d@nA#;1k zk>uKg*y?WEr_)>C9xZNlCvg7!(}f@$bfCMdRJy-Vo>NRbZ08;3;h&C-WBHmo@(x3q zrxZsyS(n~59UZc8w&z{x8O!TO95>b72yauKZPJ58>M!g|4ldHyzAailFolRG6)Ao1 z+sJ5CG-D(=p^f6a(c6K0+TQya7U_gM_+>k!FtdVRP7KLR$EIJZO>erNg0a`y zee3_|0Jh#UKtqbE#Zz%yiFvx+ozv;~MObWD%rJ45XK0zK?4)u4bLS#52vuf+7ecp^^Nsc>p{)+ zl%I?Q&qGf}sXBRgtU`^s^gk{l_Y1VfX<$r#g72X8-=aH}l>=b071+7Q5x=n`NX_Zj z6Pv05{RLHm9&Qe>?XFn*OwkNt#kJEMOM`=hB_y|GQp*H#;r_lBuQ%z56+2KL*3;a% z*zaXKArZdE8E@c>LUb@RIj}I4XETP>!uFfmomv*iVwqzP2oZN1P+lO)bc9pO> zh-Jny3O(;2J4n`N#bBcrcW-<_VfbLPyq+QA>6y#x;&!2Nvqqv{`p$_36g~h;o0oTkY7TiaUjE)P`f47K+LM4E;_q_}Yxx)sgurE{ImqQ&WN~`31F;i3MBO>?mkcQ{_)n7Sr#wLpL+wvU%uH_N zZ|qJPZf^O@E)t9qR%>Mjl=F^4@#54!40-JE}E9o^u#Vp zAqN)cMsv?dz~df+wa7$f`AozcpCi|?WO0{G4k4qlTX`T-ziar-D$Vr9cSnb}EM*fc zXDOSAT#IL-P+YD$-^>-me8x#i_9=96*mr}-z$C!zjJTkxdMP={e46Icl7}y>QPOj2 zc$dv;PhwtAwT1L|89-#ME?<;>{5{c8er~d}$_p-4 zW5}8ddiYe1!I$QGFc0F;gLx`d{k{teYjtF3R`ZkGj5hQ#=Dl6m&)i zod2~_7y4^y{NAW?tN@g_sN;ICnCE)g#nO%MjW+Jlq_!Y_po4kE4-{!alJ7gjk8^#f z@kAF}#FIUe7SBzXo#U!mmCP}|xA=A@AiPPfC6v4z7Uk4U__C~i)Zxouu_-NwSU0(+ zaHwf^Y#>P=*+Nr`%IZCv<@xk&G$bR0q>!w8-mF<{ixpR^D}8S_v|h0=To(qiaIRT) zq=bNfOx?QLl--uGuEZU|2is8eWu;CCNP%~D=@r`aJvgZ~Z?jd_H8eyi>KpSoTCbaS zk5Ul~N?632d=dF(B4i+MwW>%_`;DnhsMH~ewLRyp2rt}%k}a6F2kNtn`WxF-^ERt; zJQLn9o(Y8yh#OcG^38%17p+9J^X9PY;KTrehSnBd=%55J*Hm1!@q&|ywPyYq{2Z0~ zD}5j2=YV}#F*_dC)xTNYT3FvTB#Et`QJ$;(xGdA0j_xi^fKx4}2)#Z}PLbo+Hj^r+ zNWSyG)>gTpR1|vic9TW35#Ey~;Tg#L7YmyH<)_q0d#l8YXo@|yDn0fC(VBj&XyP2{ z>9;iwO<&quJEWy3sD!^oKfwjMN5BPQOujX@k?;YkO)af;v<9un2e>of275B>e4Z;X zra&5mV(P!mmOIAPY0cJ9Yu(d)K0PM5^iEt9h2GIGW4u*y29fX^N&dCN3_Jv~5NSK} zurHa?u~K2#qwGxf8FCVTr?|)Pd~b67cFesmWogpE-cy=Sy8}mW@{VW8D6ABX!v6Rz zz3o!Z&ALkVeRrH9C=D?i3xO%w;RB#s+Dkte9XhsPpR}E3_qTSD#@bPJygP53B)i{O zLAHMo{x3IDGXtwTzD+6UqUFX}IB}{Z*(Ey5{A%fEf~P8EIyoI5#sepbWMnJm zxz5sikm1YE_S@r=u}|6y1QD+3-3cf+{RHD(O78+^Jd3F#DGQ9p0!Y^w<9$JdBo=7P zNLMWs@8vafmHJt(TY`|J*^lTQ<%GirfHm)WipatYd7Ck zO%5X?|5!pY>m6?0NOXAm_&ZiDNq0V}w{L9gAqr~MdiqQHueaP+s>tjn zYM!>XM9cB=r?-9$`2H$gc(eI`?iz3RTAOx{(0GS%+%q+a|Un{}e#l;6USyh=HQLZ{V-hL%g{{i}(Ms$gCQ zQZ;$k*Iq{_rtD(^vDLzn=|evn{ds3-3&}T6XbXnvFb-g-j(Tmr&y0gCW|Xl#!n{Av zWM$5YX?HCqRx*BwU85)PVlI`#+Q3p*5rGOUMISahg@A%u(i!&G{_IMhxHUQw+f_`l zCTotCD^cXWk@ent4<1+G2hvmRMhm2;8uD1&!Cbo+3k(`N{8?}Eg*TGiu&xLyP(;*b z7LmtZ`>=KZ3!qUaKL9Mm4^3$tYc0(KS-Ej{snq_pVbznh;YF#)4y9_Ke8U(6@t4Xt zg$Uxmh>emMk?w_j1hi%25sEd-Xd z`<7M&lw7zihAL|uKn?>SwMxX^TdMXE-)fg(BxZol>E|C>J3PJOZ|LV+`)V|tKKIp)3SvsUURbeF zDC;1s*L$^IBSlUgo^QFHn3ati({@wKeYIZ87h^#vE+bDG(6 zGr$pe_H~HM$A0iV1T^PR}U9*NB z;iXSU6E-Nd!?)@B%i73rRgzZGSBY60x`*0R=#E~^dE=;i4(3KD>zzZ#+@mQEi#-(O znwfL8GbaC7i^D>K)SbvvD31sp1b&X;Js743w7-r8>x6S<5A!;_yACHEKD^GRk@Ux( zRwrUD1IGOp!I*9@Z02AfA-;rNo*z8d`}jOm}`J`jgYTd+A&9iaQcy^W%w??^W$Q zg*?r90(iA|)Ax(R_bbwK{z;|F2p@+;g76?ANy$?3zU@S22Z5dMwMfou*d7 z&?}E@;nevwzQFjs)y!sQv*-n}?I>&cD9He40H2(N|CBIVLvt0y_9jbo|istap>at@2C^_SFqiyo)Jfx(qdG#~PNSU=g-?z3` z#!HTUhh@BjdC?RG70l6^e)npd328gF|DeONxE^Gn!^F*AO;TQDo@{*RbxpGMb=aa0 zn!jE$n3*!UO)GWg*%L^{;a5)2_z?+P-}!7br1~Qm$5&a4r)`kdgbu*q*FehdIDav2 z6WJ(|Rv}1EB_=u%*r6?Xu~yG}Gt2l#15=sWFkgi%OLs070h%*?YaWEQ2h(G9Kmj@1 zhbNMw%|raM&9X?v)FLcdQNl1y1O`n7Y&2^?USmDGj7Oe_yn{C(jpzknjzIY;CBjtL zF&KXn*0aJ~^B$YiD6r~`3X$jRYjfY~JZv5$fl0aD%SoKN!M-M8MrQ}m;{I2^H%n~y zrY?E3Rpjoo>M!MTidPRI9BzU{2?~cU@)l5gX-Igk#BaRcGjp|6b^Lkh9DGWZ*mgqQmpa8#g(T>`=cun0eg z;wR_VBTe3d9a=`0jL19-?F<++fWdgEdq;2}$jgnaOjxdazLP8QD3}f$KKuJP~o>e-dh~D$6&0#C! zav51*;kF!Y<(95=t58g)dm&`=n}xhL3!G$i-U1rp{CUGRVfCdAq#>i^Xjm=&=c;LN zi7x$N+JKCwzf#%PV}$KYU}cV?Sq=+2*c?Tk$Ag0<>*7a{#c5Fr#z1TQjyTIb&IF1T zA={-uaT7~xOVV}MM#~mcy_M53=OWSLccjm}mc)U3X8)_5(0g#3SURbfiCp5AqBewPum~ z^yc)|FGa^R%?BIAoTx=q(-w*(K&{)MQW(()u(6|!1xPse!HU+lvCWbI2Kp#DH{=E* zZA0WaeuLTCn}vkL5tqE>Ul5PgJb&@3Lb!l=*YL=$<%C)!Ag9K{$T+sIFPGaM!buvwFD2%h53Q6E#AyIS9zoD{FE_}Tnr$p#n5@843 zP}E>mrSO*SYT{bdM@Wc|d9IKHNf}y2zDv@h9qZ17<}&^WnIxVWQ+)*{Rhxn<@kNBp zv%CZ|@|t~rp+auD01w4C4f~^P8=Iuh%&ZrpR8yTY9=v&6QI(Y~&Ufjy=PHCUsR6XA%q#@+g>CfOu>jG`ljHCp!{9TNd!##YBe5#7;o>;duswgtnhzLbS-S81C}R!fJzWML9!uj=-IVoKM=BQL3@wCwP|!j4BJsC)Buup+ zF4p$^V$8OnAQh$-d93xG351&uR+`sp%6&fZX(Ym-oe}{I*C7#hryu`%bjX@+PHAh} zdq)U(EN#kcE2uI4*>DiyEN5_M!bcMFMV6SeOqzv#UT{Uh<%K`F2-3KjS+2qBm1_*$ z#4-$cjI^yu%BQqVZ>}49!p-KCaEnpGlxaILYfloHqk}=RP9y>G9mmHh)kr!^h-^D;Mlyc7d1MOwXUBkE5?vByfYo zj}Vc0dL4mkVTR&&AyUliBgxhxtZCR{eT%^0>({r=*8^`_#h+Yqde!*3@pW81v;x+u zc#087B)l96MIDXlbd1~!rDRRfsJDb7dn4+1vP#ma6|EQKOYFW2SutJ9N4Y@99n7(d z^+F1WRxfi>m={9dVR-=3>|UEj+p8y$O#X=Pk~|Me)>_Z}x@vnX&GkUBqAhW6VTo;P z!R)%MA+!Oq*R@pSGSzR2mZk6eX4IfGshtOK=$zuMI3ur-4+0Eo4H?`UD_36n^JmqU zr16bWqb0wsd1QrBIm0U8IVSVi>da7;tlJ|8u#g8y1DF!Ztmi0`Qe_&RiNAQB52{o{@*wIU z=ydF+Xk?P5PBtrB=;J6h56++PX&Fl|k$Y32VsKF`4d&? z6XvpVA`U|m_ItG!gV!~fi4hu0PgMs^i~lM8t}nQM>HQK872_+5&#vw@VzO!_Xg}0S zF2w`8w&zo7jwI8RZMVD0Z=$>NDp<_8T??;)$g;C9Yb76E;A|vwPEN_4-v|t(r%3;- z94%@+*F9{{4iZVP$)X?~u!i5uRxuhT^LQ0FSH8GXawFoB(}SKT=TA~3+)WEFV9hh* zq0Ua#T^yJBU8Y+g7J1)~DPd}u^s@ZF{(MEB2@_!tn2axX)r0W&-3oX~C+K$;rE_)e z!OaXDS&bvg29R=3;O{Upo75(V@re{|LW2a_5<8{EO;CS`r<>LsHC<%-Vjq zKSk1pch-8f7bZ(Qwk*kK@Zj0VY0E*PKO6~O{h^>j9_sb8Oer!gID7uvXj)vxk3c z1ULUatk^C|ZFS*TR=2rd$KercTAF**h+14rhwkVrPbcUo0+27hHGMR#@Y6|!OYHXUl zk4&!pN1PFdb(&sswC*L()#~RU7p4___q);Z;p~-m$HC2X&)?~={r9j)2+spcW>-AV z6ijb&ncl3+s3d1%Z*PVeHAd*nXg5j8`*ymScBkJtqz2036mv1~Jd%X?;mUrd9Zc@ueX~G2BkWUo4Rq)BG#(5``A{Df{=ewz(SqY!%sR7GBxsN z4A`>w$6k=!NrSW~g9a~dMHRfjBDi*zy;TeFi4yS(Wx|_Qb&%k)gRot;TYuV{{Pse8 z00W(bh^g7JUGLVG*26Klv<`f`JnDi+Xm8u~jD2pG9<04xg?=7cGx7y!W<34uk@Z8z z_HqB#KJPCMZQs9_jV@*W2jk8R$b#_ML`a4>1bMtj3o}85^fIp<;#%1{;M3e|*OH$N zxz_Ervw|gIT$5~uMHOkoXJBH`hZb0PT%>0g#`eP4ju85uK+L>es;7%MIjrKJZ@WLP#C8)W5y5^Awh#^j~2mWR;yQ(m_6qQno$eQimN zSx%$D=J@uCnveZT}uUwbRc4 zl#ri6fI|>0Lvv_#!8kWGrDr1!fQd#? zMc}R-PYD1R^PTQJW)vg4ts$5Wom~;+x78S#{JwLsAd_|Zmp%Lp6MDg*xSBh}*QPi9 zFnY?<_8+DOi_O*_AKs0!7p@3Hv54+h>OJfP4?~jdz=pK++{O}{yyL0Y&NXa*9+2K) zxP$F4YKpuHUaDz?s3gx+{{@U!ZpPhE?Xp)X#YKJ4~reD&5JpDR_U1N;@K zO(RFo0DUF-_}~lTFAM!lr+;@lDT7_0{D#zS&mK%7SDlBcL#BclXH?Hq8A# z7{6GnJEyWPTi6{omvU{-^(*}8)yU!7CF{8$>b6;Ja+862@l5a%hyWU8a}BvJog>8T zwfIA^on`%SxJuI0;k(mwejFXr`Y}9zOz9F?D0*Q5Vt=XzPA@%`zMc{=Mv+l+X>Hq8 zD>G3SX|L_Mn~HHO{GZJ9=IM}%c=jw$89CAu zG%c1DPdZGe>{2|(!>#SvCPvrM!#Y}ndF)u4=kHdOT5)hSJ2Cq)errDTLMVE3DZE&G zj=czVgM%a|rEk9@8jo)L&AV;Y(t%7_aD z(&D1-6NPsdtz5h7VljbdvLoVq*@}+RmH_V-X73J#C}UM;q__Pf8r>I{uirl|AFS4J zn8>dMdq5>POp}rt_EeZL_u8LE54eYm{csjLUuZ!w_kDFwwGv^^D+k%8-sFZ)ylYu{ z*w3OxYr0qVHoi$7kb`8iT5T26=ltv*6Udr_MIg`XO}6R*k0@^ad;~HdJURekNT2-N zXyv{LVdMT0g17jzG8CSm(+m{h>0T6=Apk5eZ*p(}1v9ztJEm{>c{DmSBhi2#VJ_YF za}u6FB<2jU3KvPJ9wfZQFuZe55JP#QAbooj@ffjC1O8*7Ef=_Qm<3J zX<>Ly31I=M@W;G7E**GnV_Eu=W3d#gUdzlvpw?FySr{=NLW5oju>(TYIjH{C$DxVk zMVdfxPLIVOj@zKxDB^@tPT8I2pEdn;NMmZMbDMUg>wZaN%`G3=yljFA*%L!3dc%uOh*RYipnY9zlbrc@yWSOTh}~D6FX|FT@`Et6)xxBc<>DkLXl}8gY!G zJp7hJ*+N5rd)+Jp_vWhAjRokOV7#Oqxhfda89(GATYxe!kk`^5GDCGTu?!o?82V$AxfaiS3jg#> zNRn|W8dE8Edd6*3jM8kR)>7gGgwQb(463w*aS=%FZide^CJa9>57XUuNfju$jHT}q zGIowzWkm*@Q_XX`sX&K#c8-I-`{O9cUKcW7Zx5&_mD&@0aH3GNmKmT7_wq6BP-oVw39l@5vnwUM2U>D}1^2OM^aasv zwRiVWbpPhN_&OGMQd+m4_095!ERRp#YvG~`AuNne(XD2P_yoO|$i zPRn_*5Y0+UaG^_K`s4p3W-hnW802ToC@uBA?DdT1={7Tl>;$4@rb9vm*R@AG^}b!d zh-H_z1tznecL7((H5neCoVwe4w4^R0}BVaw&-~LC_Mg5 z`q{eyOQwQQMcc%h-Kr?dwaW?`*$`7jZuTiwTvSDveejn_8A8DQwI_>#+|yNXjfXV- zKwd2?ip`=@{I*1FgpJjkgfx9QkQ7>zM}3b1CuK_Nz%P&4;9e&vrEYwS)}^u|Z= z5=x|+Ok3k|ZIvZPKJ-FqB)MNpR27%d| z7rMqho(JlW|Fb?;CJo8$?X@Hq4Q>>%NXpqGt7pabx+iOjS0v-L3H|X+=?g3INl)nB zxgv};N!76oE|I*tQDczo9mx&7JW$3jEAbOdA!r*cF492^X6q_ivh1WDJeIauMl$?u z4-%}pX3v`5r(2xZB;bHmQ))SlSf+7lu4XIxMCI`gdAYQL;tk10rT6ME<|HV)N-F?@(t%U9 zQ0Ik(qVlAs$z#oB_T^4$#du0f7t$~|bHf3+p-4`pKUhsf*xy96;29!5Fg|H#RGZ@_ z)mmpIhgcG!*lf3!MJp$)`sE%tONAo($6fj+NMW&CC`EDQ_HG{bm=e_|da%Cca$W>^ zV_^gpqS#zPAhMKT%{*xGB?ZH#Wf>*7me-S9#xv`eHf;UYcYptzANR^d{!#1@kOth*Y&-zP-Vb8_htA!ibvHyeWXsqCBdZ}Q-T zS`&^H;!a8@_k2K~uR>W&5^b5$AN+p5z61d%8kd>w=p_WQegYj%@WEk{7raAWTv6 z2dYVnmi!^uHhl<}u%H!*{5j0HP@k$3Ohx-vwZ#!_aHq|Fg8nL=agm=<_cMYrI>K&T ziD%sHYVQw`?-&=w4zeJMxNlECSdSmr+PWp5>F{*Tbhwhgjb3}2PfND9zkHg$oNa$; zd|}Co2u>9ch+k&)o5!2e6ku(nTWmYbZbOj9AF(~8ilk;Y>eDO|b= zA()yzF(iQq{75bHx4{~HufGLTSSJ1@jc9hQk6Ym`vTZXf4)Dh%rdI~VrZ!>CUI1-+ z!$|zV=fSe^73!C5fjLKd;i~YIQ{*<~d*B4@{v3Q8sv;gE8Wgn53h^BSaB^9)E(Zq8Li!)i-FN z39I+l9<3RR&GZq6#3PTIqKC55D0uP1#MGPkJ8(Q0z?Zj)k)6SWEIhzNtwyk?Z$2cR zXnja?l-c7;`K_Ir;e=`X_nPw7(h22DmNt*4{O7p4t6~7C{4$+Dd!ae$DsFy}{`P10 zEi=s9kCJ&i0Lotgf<3G`5?@fRZLk%!m9SUxch@2yF2XVEb&NJY{rsWz@#7H;AqHm) zq|+ALiiIUfINV**2r$Ei{0GuLaxkX;v5(Ft?)9Ct-nke0g`c(-IX^m8N*wF z;M1osjgM&U`5qoE2-c&;O$`ZXtK88PAzdhMba!5?pWJ%*J9~*6I|j0TLyo( zvx8SLZ^;#+6zSbdX-?A(oe?BYV^21+i3f)L>q1U5? za)9*5zYHOup&b!mF+WPRmuJJl;a_WM=* z9iwq`8DUrCYA=KjHHUZauRSuoVQKA{LoMYL(f$AN_9oy_7RmqkyfaB=a(7SelgV%h zNB|K95tLUK#1pSo*Y(`>1TQ@BSal+o;ZUO>6%jQkYE;0eXri(j6*VeiP{g1p0Z~Cf zgK`8!_mShI1-Q=fxUCk0dM4> z1K!B(D&tRtPsZRN4|oA#K7=pgf{aMW7X-_45RXqX=0WjYPB`St?&QP5EGh3nw&Y^z zCJAAop^9fN09#IU066xfG9P~iYkSHce55cT6pA2NVUP@~1o`+SIm)YOSW$GJU?BV1 zC!mJBm6=a4N&@zs2Vl#%Spyn>9L|6aSUVARqafoQ71cxtAh<>MP74l-7$O>l1PTZw z04ae4AR>gBzw>1IJ5QD&#dD0)DAv1(P*7E3NAS;KmSXcpVE_a|gfn}`y^$stt0Ry6 zH<*PUqna-P5y0HcTn|r$*Ba>>@|UP`2lHGI0p>B18O&oO19C;>7P#_6k<5^nD3SpQ zG7y*uKMycOJD4Ns>3d>r$pfD{AZ)Nns4rPu%M!SbeGtaNP zYG-u_BQEo6!WVY;-o)M9$_h2F>x9L3Mr-3CU`E2n>`ds8puBb4GV|$7CYueIPX$?L zT;RQ9=i(W2>=h;yvWL)9BXm=;kIZ`VddT*0UQ`3?7}q=v#%^cT%tIm7^EAc+Q87E5 z6~uL6Ujb)sTi7w478_48WplH!+?SzheC{WzdPkxtEK{Am>m@pUG5xhn6=;OzNw5!s z1joPq26-B3Vi`D2vHp=3}*4#)w|AL`tEHt_s z`(Vf~=z!tGvB)i zg0kky%pTGjYn&k?D- z)hWWtr2USEZ>Le&-aW~f@yk^_E{mYDGlQAHLJg0X^()*Bj&@Lrpk28vHWr!wog(lEQFGV8z&~-t>6{_ zM!*Knvd+~#1W6eT%eH21F;*6nt1{Iq!p|ZAmlWYZ_)zSWWu`)@a89xvGM3|JJH=1f z_`<~KF`pioVq-C^aX(IBU^ns`5Z>7Q$~|wEuu_8EfO*AxbX9{RteG_<;=Rp_9!6*b zoaB6d+tU#C!=A?w`$jHMptKcB>^>HhwM>ZF1i@4nlFS@H`Y3bw|E4l@xlUzx(c|qF zs9bGY*Hsl9FoSob|8T2HenaV_!^wwR_sKSm+rL1Gg5XzKuCV?>@#3Z+zvt@4Fx2)c zcf6Yx=M@v@-XGVp(d$>X4G^cArg$C}4B+K!exok%**l*EdmHs*!-V(}1UjP*x2Th9 zQKo|#wZB6w@R|HQ;J%4X?(qF#{$Akzp07r@pGB+Z0zs$1Fvpn=>Jzs!1uN<0jOb{R zQ$>{rs8HcFZLVpdt?zhEYrGyW`F%Gw%=zECu|e;IQ!TbFj9gmFE-GFq_pvt-Op;FQ zu0jV?B76wr0((t|#)fXZEzIg*&cvglF1~ z^NPer>VP*@OxRh?2rACXP}$*CxL*0E5MhZ)l)@ib>3AN$$AUYLM723ZjwhC^y=k~~ z5&G$68?LP3Y*duShD~BH#_i&SR1e8%*T`eRwGJq6k+_b#P}unFy8Xsm3z+7bD=7e% zTPldrBc?sg1rC#FC>b06F_{-4#+0X7D*a(1=7o7_#C~-ZNc=uQ{Mdf+cm2lW6h;)B zY^}jFkFU7VXgZ!&5LM!9_!_SvOUQP?zamW9IN1IU68^q}Ef(1s*dy?klAQ#U`+QCTMfR1WuGYLD>7mqBemH0W%AM+BI6EpI^GiUD7Q5S@sF`)m@ocS^lSbX-Z{hgTmJf-p^mx&sp zItLmP6Ze5;7%k*>Jka1|%+GPrRapf|zh~5ZE+6gx(npZ-87z?tpJNn^t#q*KMQ0{d zAni9$M^ub>q|W~i>Lve&P~YF9fMD1J*J=P^BZ#yQScg>5%*G}3&1WEWeUV3xko}; zWLW;rx+P(GAy13l01F>Zq;EtZiyN0i(F^sk$csQH@s?&zU2$9|P+%M!tWN|x-sfQ4 z_yMiOIEb$SlbldHawQd|0W8?zsKaz&xDwT<#H{W?Cq(YH>_kLpF)nXnZp7ykD`PCx zW~z30onlP-VJ5B+H65%}B&H|E_Ch^q9@JB@FkS_057@jrwyXQ&9Cl$d@f zvUvPVB{;;D-x)59B0EIBlXBwxf|J1pv-c#n6xuvQTtoOz^lwHiF2Uyfzy$5ZPs7G9 z{$A-JDB3{N`ojd}9lyCRd$qqxD=-S7sG%{3gLxW~D+jV^?XQYQu$DHhFXILu_hQa0 zaf{g2&glyWYBB_GK4m3@6jp%?<3Ss&DSU(_s26=zkd_w#{+LRJGBF!7or8LUP{hNt zWg5q<)Y!Xa2-oE5OW+o*W_N{x*>86p4h3LW7&-Nk-WDLfyAcUlf^>SsFe85Ty6`X( z*U2lDF;YZy>N-3fljJeP>oNw$>!vzY#a=R272YzYYE=7;Uy&jpGO9-DOI&Pldw`Ch zs>5V^gDQeJ_7R}=@U`O&Xt@dl(D^-R-ZiX6;j=CTogWY_!7egdC;I^CPg(nfe9_o4 zCYy6lP}k8ou_a=9Z|<>?m#lx4{+LXY8fKnh|mm z4_A=Pfdv}C<}+H0V(hX91Y7VfUk9`;H(TXEuA2Y_56{JaXapn&&6wR1_xPYs#}seDR7Rj$6Lm#G2=v2y3^P>TygS9y;bE zc15flWdQNM#14UJdhHXed*My0-h#o4L$Aw`g{4EoNzgR-)nd4{9 z+a4QG_@UXr<4O~tU_b^-94t7q?m;+xRz`4CF*b1tgp|DqDkOXP4k#jKVNt}#+?a)c z0zrHf(@Kt%EL*0+iydN{z62k!2ZajQXkBzDVw4lpq40D}q#lFn09s9f-5A$eVehAj z$3-)ZJ4~IOV>HKD7vb02in#e^+H#l*;S&!S_a9?y?^yLVwh5wYQnx{>TVKq{Uj7lL zfS8DhX+302vZ0K{1DIzt1EI!w)v1PV9i+mnppivlJyudQPL&2%WFRK+5~Q`8V7Ky) z*_TX)yYVKeEHIY@ka18l{?vSxoJwq1;rK;DC}NXm^(le@;0K|uaAyx~KoO@Vuv(FQ zHXwkv>oCEIf0JjV*2-ksEarqQ?6RC`W$%yE%h`iv4 znjRZ#TfQrzl^N^7{|D@q!_$uE5qHq=U@-7BcZ*Qdxy*o2M=gzO#7sp+*Dv$fhpD9x z2SZB}zX#n%OVcE;4V>M)?`s1sv1fOUmlTgFE}nG})N#bKF8{83ho^pIi264?wHZX@ zcL%|;QG5_(?m@V&3jqsk1oKQ$C6oLR78F|vtQFKjIBVm$X4=HBweu|lENA$^A0dn8k_Y6~ z#r^Qj4Re-ks%LgHNAopAclwd4Qz@>>5!@^pG^YTkW&9uJ$d(w*M?%%oE`+E75Q{&E z4v-7Q=cldH)$=%>5Dg@U;Q{4Pj@*?uV#fSA;5@Fq7y4`(9Up-(;VPcR|ItH)xE(4VbtcyYqqdXb3jI{rI;n-y9@3s6Zjp zK&6Z`ffxEjJWq@C+VrwC*s>wPu^yxx4_58S2=_BLE8A{wj17FNJcA)r+8OD6(%Uwf);k1Ap^INyl;DBqxQ$;_oExjzDcjGMw*nw*xqd219OjIsvF+6Wf>(g*tQg1}USh zM{7tZea1?2d*d{TxVr4Ri|&Q5a)RHNnuhTxPAj(zJ|N(F2;D?pymwaYPs#ZFYu`MpEMezi7h7;APA`ojDX-^IuiBV^va;tQP*-XPzL zy=8yv8SW^eqAI8)xfQedahZOY)b@0!cR@5_fAh5&PCH|>BLRDcr_;rMQkh~39Y-nb zCXCJ?q1@B~r7(`dvVM#vapg-o%T)1X#xVkuN-?X(ATWeFVd*Oir8D!{=zyc(HC;ta zU!nw-z7Pde`U2LX9pJOH_8nT|k*vpc7Vb|?tD(e>qW}KHrO1BCZdhl#TW859W<=fR*j8WBESEGn@l*{ZgNufOrA0q;?Jt^}|U4k+IcPXsm< zd(#Aap@RtG*kGQeVSqCZf}PtkOqE2o2o5*%)o4LzFpD7#1#JI0*oYyGMkOs7a_BY1 zwbne$C4~?lco?wbtl{B7St9n;h;2QO?W{q1LjhQri>(Zp@qu`+%7;mhMm>Cwut9bs z7mPFNhk6%s2Bw$~>+MCEtTI&`0fTMwYw?L;(ZUz(h%<+F_%dFe0*TEH3x`z`^Ji%9 z?Z6n@a}3fDfbc(FC;>hEN#?yyn~7s4t%VRKEVZ3Vj#0%C%q7)UV<7f;j=5Hx2zci& zlFQ}zvu=yz`gm0#%Hn=FVR(8TOtPG46Zji@wkDtX75Kq3j8>s~ih2(h(70L1XH>wQ zG4E4Rup}dp*qa!3A8S)#M3%$g9bOAU{9c|eerNOko-Guq4(wh0^cI~ow-3I2&MY`8 zBpTs&t205ljWfG#H;Q4x@XttAKMNQpFxqho?=xiKas=E2?ZC1vXHR+bi--qcJJ7Es zWSx1ftx|QGsmd)UW2wPoV1y7viqtl9$?qGJ4t)tG>jcaaRH=1PrVWLe&>Dd`#=v{M zTZ<^vHNBLhuyb@%{O>|M4iJDG2RGRj#=%>>{eXPt`!wG9GQvr%xfMsYC>U^a7`yYW z6GJCRwT}a!^NT8;{je8p=O-vC+eZoI~GDGp_xwj)+`08#XE9eCyj^*39v9EBaFDg#DCT3L@EirS76 zRaI#?@hldh!O51g*kNos9bhMiyxlp<4~*D>4nC+AoClcL`~gtHvt`hx_2(Wy18papbF*tMS{Ub9oNBc!)3}G*W4h@-59Hf^>SRUU5RPgatDx>#TyCJW5`P2BfsO*Bf=%Px^mBiVknI^cE5gmySZ4n0fxhjqJdxN?G=9#}yMKgcM!WcgiaF9LYV`#|m&M6%B@=5cxYcjikXkAx4@Qmg zf!l-u5&r`J&E=f4;p&%xZsm5=yf8|*=~nwJV9mK5;{5?SzljR?3m0>qMq_()27gT3 z;k+$mgjwWv+?!4G`bdzqePVA#*uxQ!gHky|$XcW#CNDcdVJi1gcks|b;{t{-vrU|3 z9R32#K)O=k$gMINR6G-c2$bGmB zu$Wz|i8y1y_Tz_G)mqVu&SkSVe8+JR6q`11QJbtVZneRbB-nh?vp<{oR-@2vaBIRr zK>QKkGH`1WNeOAuADas?prah5rLeSQ6+TK$IGFJh8EnAFI(;!@HLOSqWT7yg4)xE! zJ*GPM9D^2V=_x9F7O%^DYyrSLs;D0eVFCzl1EvgYUnXRExh{j(4@@6*Sud1gaVSV5Hw~)4+szRqM%!Z$GQdy}{m%|%x z6~@jyQ49a(I>G09fk$EviY3<+L_IZ%e!dpELnyaAKPd+{oas(eT?VshIV;>Htahmr zxCD@vz$N_7t;6Wf%@iDTRMkv`)D(K-G`Qii@h-E%^0ouU)!{|}D8MT!tTOYAtQ{~M zD4=U1ww=IJZpB9k=b%AFWhXcx0-@ZE13w!}Wp5jjo@v> z8VNx-g(!JwaTPSqK=M?qdlFP1T&aatoUTfXo0yN_;V?~Y!a^iN(P6v@TS@&HW~odm zxJ}t|BCTNOsP{#K(aM}gYE-MmguF$bQ!4i~mz2o`PE!dx2=R4k*^OudT2QAkz6Ja3 z785f?RzTf&6c!#ASNW0fO-FfD1^9PP2afWb%3jETrr@fGh0f@3WTS+~*^KMh;E`Jb z>{*G0iz5yjRmEt+kj@XM&zF~Y@a3pb<&)tc80HKx*=!H#$eeS5_#ibQZl#)r%&a+G^Ka>xm=)^DGw zPO@I4?6VLOp*~|&xd#No!z9h10&vR^xeO+9hR`no(L(||bSV%My>}Wj$nz2Yv_HZv z7E3Bi@!N~(3OC|Y(A>%y1lC0ex3dGF+q%&~u?Z~AMbHIiSy(3Y)X~d9{}p{)f?t`G z9m2a~dB!ki5x54z3xK_!%jkds0v;*GfLzX^sPc~GW~dav0X-on%zADOf0cXIp(Tt6 zcfZixIc~6KqVjmHe2nR)qL_xJ)Q5s;m3&lzctEMoH>|O@sQ7IeG$$+rgfn#CUbL@a zTw9SFZ6df=bu8lm+qqFMxAnXmSwL|q%lYWU%>u|-syT70SzJwvlCv2_%xbc-B^+>G z7)_QddwIbZfXHx^eo2n5PxK^*@DlPnryXb%>q3x^Q9!$}M`Ys`UabsPMRfNrr;E>4 zoeLI=>7=F8~w{f8|l4s)IoVoXp_rA=Y<0?#W@h0NS~|nBDJi1 z1=o5EOcKrgm(h3D8VmuJdYt!fr$tu`s33wn)N5^wuL!5Nn^`!dB{T!ZTZt7FXDQ&H zTs=XhDEIyIq}fK0G`QsB4C*^qS~Yu=FQ#LYujUWDouHkABDs*&+t^O@*}1BFw^~7} z*cd@)%^SV*xRNs4qPCER_D?G~1;ef5t$_`_)fw(Q&|zV3fCVAEuqzhgV35Rr3quy0 ztvYAOP==v2iyUUi3+bNoR9<1D>{5364D|xZhB8lm1gzsAK#k|Ai@R2WX2fU%;yt)n z9b5#+q^1?nap$XJB2lfoQ7>ddCy-{Z`w*NuGirS-QN9COJbw^45L(7R)~5i)j9QfO z5xa6BF9UX^+qWo`0erA`TVw2s3f`P`T!tZSo^S^WMX(yi##EH!PES>Z{2Y5j5zvF9 zr7&(VQR1C|odT@*G=|U4GYbIl3?2}w8S7|3fU7X@K`GTLp*{;??6X_RMyoOpE z9S7tK^%Y%kAxLhk^cC;XJiFWK%&FY77MD-VIXxz4(~3gp=tmETYr(gO_F=|fjDKtR zRxzOu{KF8#hW{3U3ZRZ2QhYpp!q3#nRJsiE3}-qV3V<;Lbv{T)5!2t-{l1}w$qg|Z z%`TAw8Sr1AkCs}Hf%!0Z!P*Dhd(nmGs~kG%Zz>phJG$h2B6mg+*vxRC1Q@b2jvzB1 z=Pd_A0jB04GCqS#G|-6{05W&;+k0x6LW(sZultSUW~{-O0Zg*D!(A5J6oxrQMds?} zK$w2_H@(8b#OVVQioTD4-a13^i~;#mY4eoK(jijDh+S~8YvV(!vvJpBK@;wKJPtWo zF&0c4GHw;#$1`oy#eatfAVi=fj*H{45x_OUY4s(rnZ@OpT)LOv{$XQDfB;&7__*1I zhuy+JWk=&3NDtr8vh(DI6w%f*3&qK)u&RR;yyi39#Q}J=ZrK%Tf zYrnWubtyH*c39MZMsIKv?FW{Qg8?4rQORYhpsbEN2K7D}_0j)O30{EP-{o+C<@cD^ z(V3U2W31)$$Ytt)bh&BeH$J9V>V5j;GF4`MLfbA=BM(_F0|73hkt=Y52JEe9Api|4 zUwNx&HxqDAA>q_a+c#gXjz`;XU#@zViZ$tvY|}2rtq!*4Q_m}qyPnR!LWQl#H0}y@$T=&p5TO7zQ&N}%FJS06oRh*38Cap^ zi{3P_w35I=f~d26ydY38ae0V2>N5dMD$isZLIei*{w`6?q^v8|AghH=x>6lyt)vI8 zRNay``tap*dizRsg!L9BTm|>!nbiF%b&&N1oqZL?wSsQDN_7FqAH`3!_>!yC(I}v< zmI4P~Ed|cPPqe_etJTqkt7I5JRWq)DUG3uqPE@OC_0=l>0P`I)2-vgnC0RY~Shxo4 z8S!Bgm0qJxvF@d7u2Egi{)CA)7HfS1FBW930CT#_p`do58KWL(3nR^3#Kkb!*1~A8 z+~P-alGT^E6$OD!R$n1jxBRh$ez-=R3B(vaM)k$o+&D%JNm;G9uS#!NQB8Jk4mE#P zkVq|KR8EqyL#DRR3UVlLt;({dQSr5A&5vm*=$e)owPj$ezwTN!0xd7PRuyKUXSUgs zo|$n@R!eBZwdyo{A99`aeARVoh_#yLU8e>{yE)~T!W`Oroph6RJ)r$7)pae*rW3Bm zZfK>eu1AfPH1B$Zz*1LLsA+FH9VG+5XK=F0xa=UjLKcu^akWdBAh@o zlWMe?OzH5bjzdKJY!SfU*3#Iqst1NrGgcKR%bsS2o!%a+!rougX~S66)q0yUZ&cx4 z95MxqC&Xc)mwV21HD^pHQgAtQ`w0mVvTs)<35m$0+iz68`&Xxx1tFLOL)lM;Q}}$g z`@!bG)JY2|XybfriIfmdv2Q;voP|qg)NH*`9b#2e?oANrK@~-D${VlQ8>NNph-RSX zxLiBKo8aT&r8&iN`TPVk2sVuAc_zLI5eBl6#@I_130>)>-tN76#^G54j>H>+ccp@PQJT^~*cr~@`f5x{SE z?={ekd`?SmRtEr^x8Dqc^9A+1MRil_GR)H%avrH|nRLM|Di?Tj<1Ok=gEu42e*Ib8 z2b9I{19+Tu(3r&U1H5)Cmq~NVfWctA0!A4{&2R9`%XsF+d!uvyp=LzU^SoPCao$_o z&`x)!9R{Fjh3>sowim39!waS1lV9&+Y@!~Yy!=As0d`5Rfu+t{c!x5>pBBJiH=FmkI>TLYH zUZJ*FpVM=9sJ~dRQ|dSn*~!#rocfdX9bG>TE8RkGjKjgbo_-vsx+bmrekLv!sBpYG z2U$0cS7jq{h9|Jw9H8zQ6~=W4pXG@P1}RuY^#Okco%P!5++Sh*68j1s(`&B>Zon8w zJxAY-SARX|1Ao{!m}1Y$bJt$JoJ_<4G8y14_ncsh2E^eTeBc|do1jh^(XkrebD+lS zUVciP-#X_Vaj5qkbhirKIfDaoV7(ydpo~2autz?nO;lYXjT>j?@hv>WvLw6jD7)F6 zUhElVx4JU;Viv)faE$^pO}iob1soq=t#mIh#~L@T(ZQvE6K;}?L#(f5#m!%21xcFQYt-kk=A{rLMIcdALi0&k_d5%|y5ygOc? z#!8iK?YH2~lfiR+MzbcX!+-|MCxgklkE|)+UOuK1r>OI-B{YAE>WQBg^W&QeOkF|) z%+Khl>H+HoN}L7)@FkT`Q_rB4FYZ#sv~8N|lKq0P@_uFwgb-lezM}HG)O=HFC>Ox0 zbttf!`rNHvLV<7ZR)?bPl6%nhR2p`VIxwSh6QK2Tig#!zv5%$bpw1=P=cI*!3DA|> zEGVe0%svA)iM;JB)|D^gXK6yh6Lj|$t5-P*q4We`5h{+5fw3blh+@e8=x#i@#|q*m z&nUGmg`Rs0OOa3o*5DI5qe>0OupX*Xg&<@NRp4)4riVtC4DUK_G8QHrCuFR9g&s0a z#}e$#UsKt1P{3EH(-kFonbG4A)0k$36X=u$C3*Dbz3Q?O_yU{#!V3%vK2fAlhF%gV z?J@VMt4y!m>8txxY1f(;(RK6@FVJQ)Sy$KIj$P*jVl;B_6(u3MVY<3B3rwHm1a7?H zpc$qgr>k3?`3;`S^NU6RKqoxEz8&ZH5*I0Q(`h_@1{j+8GKIzQPpS8-$}pbLh`O9( zA>qv69Be7936&(hct5c6Wm<8+I->tOJj2gib_7nt{Q9Z$a^XM_6z(m?XV8e5s-Lx& z{xMTMnE`q&<#25WXi{GSpL7@9OzIMJfg7MBybrxZp=y;~BFu}ZhH*o{WDIx{j=}4c zYJ_V?V(*8NA*)gacyyhiz_fB-0vgde~rZ?>B{WT+<`5yxB z_&Htv5Qy9Dp+ws9N^TLo{g5iPmeRV1)Rj2lPJLJ%YfYrt534ab->p7!_@ytieSJBjrRI^N1Q_?Wq6y5y+(_lW@<0Y+%Nn7S!cxBA*Q${FYfA z%9x{u42}hIMHC+da`iI(hazwk2XX}=uShJDwm~!ab|&)(em4e<;&sjEt(o&na_H&h z#hFw)M{(Som9075XFjTqDZ78;ggnqT8yiRN=*vB`t+21C%(CzOenOtTX9HdKC!7L_nE7R({yRKJ?rD z+3?U`?kQAD=}TB)X5MP(DP+q4>U>h4{b&sU?mewPw`UI`30lWb`94Y};ge}g{a%~@JKU)d>4MEU4bpHR;QAcvpOumxaA zHqmVhRPoUuVKI+U=3D|SRl0JVt0&{TXfMP;ooKgVyTPvjWEdB#Rs!ihN!ir4K#jyk z8TzdHQ~w=PfNib^T@&q^C>t2vvEXh~ypf#3|X($V{-_poBH7?`b6l@Bh zDRWi;!Rk7dXKkb%b?Q=p=ltiuMXjTkpTl9drhfBtN?Y0MWWXtWuq2}?_RU;dAAAAw z5`RKV9X@?sf7n7GAQ~C*qB;>x&U_I!^K0t!UqS_IZGEqL;Jfv8{qYTIGnYE-6?LU< zf@zo>mUeqGvoAkT{VQr@&N9cw#DdTPRW1kLYpSg;dKHL+E5Z{OscP#7`gReH>1Cw< z37yd*dgz}}&b&ACHAu&6>Ic7uPu6>M>Fb!z+WH4yR|j(P-A3?!U)Ohe!+ah2rg{Jj zYW??bf>}Vjd*4#uprvmXt1~dZ!EdYKbW)S@*H3<1oy#-$?j3*%^+z^=tFhMA-~67s z-pbhpwG;y*5m7lY1wN3V$?$%FZxiXp57d!a*w3*il*~apk>2}29h-d*fQoQp_$aZHcMoc36L-n+^n>Kw2 z5%x~fOL31nkuF%O!YQVMY2L7x9$tdOcp^QvRNai$OFlx`iFDLQYAm+m%O3$ClW5&X z(#}22fT6V0n|3y1%TA=SWvV4>33N1$INJY zWi6_#xXFj1tzv&x{I)=MlL2`0e+lY#IyZhhRzl-7k%p}V(>;ksbLvj2 zS*hMCt%Q;R#wSTIV@m=($gt-oNi(fUVGn5A9iOZ0L&0$249w!I4RnJyjx$8R1uF|X zqV7Zk_BzR2j<=1IO#oPrx}QGy90YJ8`M!Xz2e%C9W-BfGTxHP_Ux2lqLN|Q@I83Gb zFVs6wY;4S^0&AG8`vHIeF8n=NkWJw}#jx5!5JwyH<7 z##^xS;Ilmbp}xKqJS`Tg+gjkwZaQnN+MQa5ZNOF(#p+i&|7+DHu#hv7k(&B7&e?_Z z>erA|?xgi!gZ50M;5v|`O1fqpK>ag4y$(FYWXk^r3;$mIiQlL(UK~xYf2Zuk$*iTJ z#P!(V_fcQ|nNELQuMW(v#-YR=R(Syphr-gV^;pve`glD!#>rH)L7iv4LAPvB-Ls;= zOk`lfxi>|9SO4M$5Ru+4vf#60q+l*{44Vv*h>`&x(+2-Wb*7s(Afzp}DQLr(jo6kk z+OUc1FWy88H$r>&5q+~!_4Y6G^0K^2g`3oU$eX`OT~db2Bp)mV6T%62n7~)=c{qW6 zwS4wGsN!G&3N=Lk%{Ydd>7~setIMd{4^R`lLC5_7W`7yI`vbVU8YYkw2=u zZXdH&2o1EN0SiXwA15O*8=KE(&qbEcuJz%GL7Mg>6cfwn*X^pfZxlAnBBwBVMdJhF zcA^aE?&H_08LfQw1pIM7ZTk`8=JNz2yaV?O*hrh|_OAdtfoHb_#1PcC17JVdvM*pU z1qSSppWJ|5!u0{zil0<}{eChe1sP#G&Jz!`2XFtR`o;jg`zJNcdY>l$tX4oSyKy;sInl0#Hz1viI*?a&7o{r3za?uX~(LUC0 zQxvKL6gzpFI!Z51j*3YZj!_=WW81J;&(pixfcnd5>o)ZmR;hZsx(IwTL;heF0Y0=-0R9O()RErSr>JUrX+GWuNB@)Qx#^`BlWGU5eNT2f=+<{s(T?+XJw4J6 z?eCNHYCE>p^VHU^3Wl!3aaZo~ltF9c{3UT3u(*OByFt~w>U%#zDVQCGQ)f40^LmuV zmo0ScPPF+vUAz<5G2hetoggF2>B0v}bLfbfrAc(eE{tP3UA;^FX(0E&z6Q|B!@nKZ z>xWwarsz6(!i+^)b|&aZaqXt{DM>I zQ=0G#(Eod?{{=H%LEC>(mos24+^s@qeU>b@a##n`r^jyHi?cR16wB7f8Y|?Wa>@eR zqh;5}%N`ILw#RPUOSE#gDhm9R^kmpOEWMciSzX#AvumtKg6+Wtk$|H21Epy`E1J6U zSFo1yrOfAxX3pXw3DMMRoJx$Qel-=gJWyKZ^G7p#&O(tuG$t;&^0W+w~}Esp-`D?Rp4Y~W^Er?#%uuuh$JNd?m?w{Ra%a#Y%X7UCfHC)(~)~s zZr_TE*y7?El1H*KIocW@8IbUYrw8mN6l1(Giy9EidlaQPXhQ>}(NzgT8>pV3roE~J zOT1@hX^;Ceb-H!m{Uv&-_5J;u^cB_<^cPDPJ74J$ODFPzKViXW*J(aKohRfPG+S5; zjFvrIT5OgeCm`YCQlc;kJyM!>ysHZC4q?f>AGC0x5H17}8+boufI3v?)!9e8X2zFY zQPgtv1H#o|s6w}~38Ophd-D7A+c?M$dN-%SdX-K}(8EikTc!=qq|9cLTq87$DZE#0 zq~{ZKXYUgM8kwN;l1fvt$6lqgnsQFSZm|<}j(4M<7B%G@=3S9YJDTt?9nyo|%Q*vr zRC$sa#MQIuz$H0hUaKWJCDC=jGg&&5*_UM933Te@Wc?@q zMmcYqsN$L2e0m{S_rr&8ll8!?={RO!D8;8-8nhr-)X(Xl6rGLl!&CG{Sg2=G^xb+> zUTP-RMC3IeVj^v#<5KnCa36Zls)BB*csK)}K#YFKtN$uhUv7EZR?xBO`dQXSB=@n> zV1s~n4_UE~8>n(m2DA}6Lx<8t8^OY|Pkm2AGW2UWLPjbbrqWD3Ir{henRwe#Uhhx$rx~EVdgL}@;3IVZ~DWyms)&I@A zp=gL|ROR%>ZHQ58iC~M<9LxbYBDcP=qDWWe|9CeV)v9mw+=kx=w1xHu)V)}j{b?H_ z1mVeMsDxn5Xg~hxM5I_uu7l8GXU1!ZW&yhW8S`f4se__zu@3RaHFQ_8?p{>G{U9Dl z>>G0+xT*<29n)N_2dnMg=qJ|Xe1W^C61~m4-lE-Cc~7gqq!g1)SdT}9sH{xq(%fsj zN7T1<(&d0^eQBAlu~fU?Vbig!9(&VQv^1>qj@)6fCd`|f1!Q2+3;)557T7|*ee*n= zcY}o(!xnDGun=DLI(6x+E39AYAMdO$_m)R>Tqp9eDHi}q%ILNNVK2J%vpyfXu5wQb zu<6`q67Z5UFDXdSt+)iF#?jsb>L+&7L%hYiqN8P*ElCVez-q8H!>@k&s=Myv-Q%as z9{MfsUOz4Gq3iKme^*caSBwAn{y_Z?`Qy@ZeXwO!(1X48up~S=W~nOrwzuvHsZICM zL#^3#Y9D=(w`m+T^wFgy;{s|tzlH#7q6GdcQEtABifJeP+z0U7O=W#G3x9v@tINHW zt7!^;t$V1UuO0|Nc}rg%##LSRL3#yJ8xPV4apR@^bZN*mj@9FqacRXrKG~fJ^v`OM zbNcDtXn$%yeF9fn*-sZaBjU?3l!kGlBcCO(IJcAZ!MZzY3_4g}gEQcngY_s#Ou7B_ zAc#??^vAd=DbgR~nnVr#b?<~qG(pcjSCB=&_SfB@lI?N`;53D54$;S1Q|mV#q8BId zb>?$}beMlX9i%VBWCk3rFY)g3)0hEe*-2 z#j}U$6Y%roFw6lFGx5U^Nf@pNdE39Dp~Ll|P-fjY97sQ&o*u44Q2u`~Tpy45<{bkH zIE`*SMqdef`u#Dw3yNfpK#|?lcZ5DrO$YD=f7OgY>KMyMndfEYP{jzHI{d2us0m0b z0u})4uu_~zz&m!V{K6Fp)_@sa-Xn*4(~u)u)T^Q^iw{5`S~f!82e4gqtbW-144^^B z>7#fCw;!iZw(h5oj?;fGy^rxLZ4|6JjCC+N0mpzvX4+IuM;)&rLeRCx128jb{_*-R zn2-MiU1~i*-A<5iXPlrz`GQP5B!gCF41v@E^xPbkQ?UP0qAV>sK_6f}Nb63(4!M`| zPt=!MR9|tTe%(rWC{P9sFLa>~)3GCUuY^YeYBt?GQXd6GS~ODMZq1^@Pu7nD=wF=- z!u=2(e~Lakw`tscc^EsJ(8*S{1TOUIDOiHL>RV6I36^zn{WquTpyl1#N;^-}rI|IY z(2Aoo*c4xHx3p5X(=oql8g{zwVf{i^pN{o@JwXtUUO!!*0#084S)XJ*N*DYYtNlIA z`?J2ydbGah8Q=nu9C#+E(WCV*pQ!_!eDf?l5y=sM(NIQwMfGRvL%i)4HJ=p9P7!T5 zcD9;BX@Aw-^#j=VEMUoI#~z(oKkTpiGb^QX_q}9pbOmv?zm77 zD~tBpAZBq#bGMB+U3o@rPPbgCc>#ES9%}Euxhl0trK4o5PNj-pWqvdM=C1SwDq$#P zf5%X&Oac4v?gEv6mjX+)Do zD$8Rkjkwrd>7I*SmD;7!5UvCbkW^|mm9}-LH24x%rD`AdgoiN#c8yc1)l_=)67J6UkO>|)O2%I` z3gd4w1?oB!*kK9`8I1xhrog$Q-7U-=EiHV{1=gDaDOb1)oN|SGT!{?$gJoRMrZNQD zO(n3cM+R!6jRbCB_API8Ua@RLZ}`UFrO5 zT$S3TQaM+ufKHk#wU|n8cc_#$##O1>&pkmC1RQWGtv8jf9fO|Q@wY7J%cul=-8%;3 zuc!cJn?Rkebr-ngS}8E^S`?@<1(tLukaeA_+ji*|8Y7I=kZrrhRJ!>(v|5M1rQ1e# zrRo5WwY!YfsnlR9b-UhO>B{R}JvB%rD0zUdPNgPO>5~qX3U83{UvLA)-(m_>+<;cs zM~CvRDUd!E1=>u3^0Dp$*N>GJUO@rZxkF!#1q(2N3T{N%iaVTnU2vmoh|S47ug*Ng z7QSS}!d0RmK>Q9@s>ThG03-b-SEV+o6y{2DGl@#orqXpcxhuVbO4h8IH)GVb=6mR7 z_xDS0mQl~=0u83XhaC!J-y#Lhz6AxEOo3Z&K?}QVIH# zfW5v0D?o=t^Z|uW2J9opz`6X2!>zV7=;TBPYjkqE?|>X$9RZIyi#9~Scg?1Z3TVTo z(}5Mhp`YlI3Wz7OX>tX=RnvkB-7WWVhe>EEWDr921`~Ve*uFO<)9-+2F`I6`Lzmz+ z)!BD|J$sy9yv^fTkYt#6^V;~*2jJYzgYa3>u!ULOLH<$vcj|5ZOc&F}#wl?3XFD{PqG_9I}1cf1%M!e25@Fw$Bm5?OwrDrR3kIV+F zZkZpS*`feiitj2R?7U8$CbL8}j*gy;rsvW4$@+ZL*SC{(rL~K$o`P}DrG-=UO}9EuA<`UxE=VgqKao&7zC_dqa^u510*?J9gK2nzl~^lpm+?UHS(6RNtkCB(QKp zEAIkpxQmMJ)+cv0(hl3uLiF(SUgPn>0}4ztx<#~&qHr#)`6f4;=H9I@pYD^Dn0%jE3Ey;mvFYfF>H1ip!%Nfk$-!#WW*1}Wk1xvar_H28G;oHQ@1ryH znUJk^%>Y^x4Y?m1@^!lVelvyV@7K4QCi~3<7H^{~XX@QTP~?3l)b zyd5&-71grJ)%>%I7FWxRlOHfM9{&J@rpGCH7E}vcsNXDo9=7tmvjC$S+BFMO;;Zzx z2cd8IA&-h?10*ld#j|yWD5lw`(@dpRK!z24S{77dN{HKcvF}2;QtToI&S4 zBtqV;4*{nZ(rXXtPTrMUsA^o-Tn>DqcF-=)TK5bUJ**1@xZZ7j1}IAR?kwm;tta~u z=z@p!g*odu({Xi&*$~U+keXLKtV6la2ht!)d--mhooGQreX2E?%3jIIqt1^2WY5!? zkAOlvPY*q!`}BA^z$Jll_+hzU`eWEzo-{;3HCmT=3u!fc_m<}z%57Vp<`mG@M?s|4P}yVpS}Yp0Pg(TjW6=0arq;(G zA-_hw=IRTqm*}p!x~F~_YB*ySfN3V!pKqb2xu8d{)0)S15p{kXD&$7ePw3o4R;LT>b z?MZ!LQUkbg^%5<3QV%R#9B`~1L}_Ckj%9({-;&#iX8}_CcsyvK!V6b)jM-~%qywLVa^i(MSf9%i2_W+mI_oK2k^>kz@uL`q zpnqN-;gqNJ=19fQ_sd0<+=TGU*Y30$Wmmb=`J?RWSURm6j~CX6A^gU zsdJ~%OoNlA?N95>NTd5JYB#ylsNL*NqjpOyoz}%Qh}KxP8N_;b8qKu1(`csMNk@Fb zhsGvTv2`DqxYG<1cbZ`mOQ(S|jWyF4%QnMla;JG#(KLend2sgHEzt~58V(g~X{7D$ z;@$}_qgcC@+d2-f%AH2L)$TN!t%;@6I=Pyujb(Q?$FfcLZSFLhX?Lg5OvQFHZC))zqK|{a$&X@il{?Mg zaHkm@v2>bTB|8|rK9=na$DQWkMAOmLs@M@-2E?0o4JVdu+NpAb1tw=JqlQj##$ozs;Ry zaJbWGrlLK*I&#bDv{UKi$5zLkW^lOE431cu??PjXTpPMzp85+^_$< z5NoF?mTlT;cBdIS?leQEo!)v@AK$NOXLPZhdhRsWbEmo9P8wRLa|So<+NYj7&Gp=A zt`|$EDQcXjhg+HCpRb4KBfJi7ntzEA1hEIG+I>GA`ke0Gr{O18H)yubon~_EPS^ZI zcRiDHg*FJ}(Y?rvjgi*5)2Q9xPNQ~XEX}-8Y!FScY|~7$JB?;q+-Y7MCr#J9p!-B>+)Iwq z)$TL~Rpm}s#u6Nkz--u97ScRucbX^VPS?f~?6b`?7$E2F`dGf1LYq5{M%&$KOrc^I z{duA8*0(W+XHMttG{27f|;H?58V31d0Is(fs1T+1ZpgHAl!H?%QV{V zZUl|Cxzp=o3AS_-h}6WgO#Nzinjz{+!^mwHMP3BKTkqbo+>1NSAaJL7lrfMP1{hf_ zZcIm&W_KD*Ho4P{v4q^P9n8eHI+kZfUgb`sfl7C};wKvZlCHAeq|aW0<;0t`<0T05 zZ&G2s{0ynrw~~FH`ePz4R5)K zcGv5_oW41F?GBe8V>Q`Bb;!f*xxa_+WJs7k`{muBDa_J3zEBW%;7L9YW`M@m1!?;g z(3z(ibf=(ju!{n)PQ>VZ@MGOfpEu|WykCAugJ0Gq!}i_-Uf3IJ3^t#}EODRwbg^xG z;mJyVswmd8*a=~z^zh5jQ<<*lwU+^+m*}gPb(lqgn0pwE?Jy|)iq5ijP}f&~q=zhL0Kb$!I>YXF$#8F_*P}wD@nC8kXmTkD^9i3 zkgDYNB3^JUQgobYv@q2U;^U8|(E`)xa~q`V{8T#$-=3WcX5I^{BN_73CY76NziYbA zF{SR?By|u0mIt|WqolIf=EG-iFyDfyc7z-J-Xt|k2R{4l%~DjEqVJd#0&MWNdw!H} z8RlD+Nu`@_&;KCb(#*FPOe)oM|DNeS#gzQqq>@oGf<}LQK`I8!$JHw*@$CF2HR}sW zC7DL2Zj@A_Nwu}!$t4p^YSRmnf|EGUu;z|R{stQt9`<9V6$>%Uky<&0;Rosx@*YYz z{Zs1$zSweqo@&cW&5%80uW^#_N`Zw4L|O!`_1NETp}K$Sy!;)w+@9dIe38x`J2X1% z@8Yo&U^4Rrt^TJjJ5EGKzGVjnCD|Ojwk(k99SVH`-^x0&qvLIa%^Q~;<9Tcw!jjl= zuj%laTQL?`^!1H_Yyl2c!s7!m1>~ z1j=rOEDOCE00h4V%XUQML5+IQN!ac&XD!6pRBy-A`>nPp{vC=W7%4z0%(2U0_r~S!mNZ86eWT1@CY~ho)Z|VdAlt2ECz+I+2el8wSe26c0Saivq_DQVd7hPwDzM z^ay+Bo0Hg5>Jrw#cnWQcM{iGl3z-?Q%$D)+6F|QD8h-c)Z{;~AHMJXKna7^G*65A; zSROcT=D(>2b^CD*)?V3L@T0cmrREvz^up2#`6>1tYbBpO2YJd;;I@@y`$xey^Et$cva=BP z$3_SbFH26+h(vf5Np$}wX^s0O=1$^#bm4^R)mAmA?{>n7&RaO z3cJ1|)fYb%<6za&3)wImWhZzzt}A$Tc_Z$jbuu4<@KWh8l`!%V{pfnT?-;JAQ88ic zwXQjZqyVfu29E)Rk~(}VL+||=R$chNtWh>Q>31Kco`i#RA1g8tjrs9p2vY<lZDy11n*Ad5 z;B0&8m1$xlC#WVmtO<7rYiUdqE(2CFWn&h@Y(2K+3;CXe&a}1(I<9J3(}a7Fd#U$( zFd#Ki>|kv9_o4#2?>&8fz~n6Q(sQdy^GUz2?WCzzH~>qMv)4^{Yp;_3NW8pnzxYt!4-?lhOZ9gAT=tP3i=WjW!6t7b6*SAwY3ApyW_>!Ggw{4gZ@H0DmfGa|Os&F|By_+!vhCp zfTW?I&+yLhDQ>~Y_9W4mk8(M; zPvZ7rsU6bY)@bHJwjWVjY2@cnv$xaDpX&oM_ke(vdn$ax;0_2sV5<94=g~W#>l}2m z>T_J1?4-;u^kAe;_yR-NNmqY?dx%PU?h9P7?4(s+;4-h0N><5j(1=yAlSGkKdPHHR zpZ6LfNQsFOG|WDGE1wzIJUdvNr7rC9wyki!GDYKk8W6;=j3HXHMB&;v8TMLrNR zpWVc{Y8RdKrOt%`^u=ES4!_WhFQJx~;g+xyybwJLJ8B~B`4aaF<_n$qmF`vaPgbLQ z)h0lzhp#%ZECr47b%>?zq=&!Ky`?*QHv`K4iaV!OUtt|~Q~GLL3~!~Lt0DNb(>bek zKDOQst96JMm{#jfxn>1s``~!S6A)`8wT70g)}_w3dC_lmGL0QGTXlBnud+ss&Zi!} z245LhH#6Nz{aSGy+)kIb;)V!4-P?+-Ur8%kVbS;tC9TnR9smg82YwAww~bEzS|4jqaR1F;)jC z=`=%ceuJyO-SopZ`uHLP?#e_|#~m1RlN=Drpxf!+;)bu19{v`V#TAtN9q!Ix^#7gi zSv(%zs`k%(g0de^kt0^y@8eaZJR1KU7WF)8`VOYtKLp6SEvuUkuP?(p69A~7G3&AE zH`6og^&mfb_x7>6(3-K}7*jXs+?)z{l6%J32ODdDc#H#=aq+tWRD3g?w?V*Evq7H$ zyWSleq@jbp*PWBifx)hz=OW3MkC~fwZ>swq_&tHvelHX0wNanXD{}XupDgz8W=e#U=29!jeroT|;?o zI7M*awqgA@(BwAY-gtVs4Hs(_)Y68XxtTJzfXD-Uy|=)&cRW3~1y{e7l<<=nmtXyp zK9(o@%1?Skz^q+W5`DQ5b|<}lhJo&8y8UOEicF!|KkH+h0(D8WIT=KFbwV(e(zogY z+#(*ZRgXifPj3Y!-ay}N#Q?@r(l&wmuxVrLkwuzlZ(ajxD0!}Cfyyj`E31+asJ1F#Hp zYcw3!{!Wj!gOgcHxjVrRd`$y)Vub7HhMoE!yjVZ&1kw44@^|U3y&2>Z>cWEO28bo_ zxB-EGFkE;fdq$O|*qf%1_)$WO@dl0FWe(3NyL8Wzy{HSXbuOOdNRJjv&E~{hwo4B^ zE>78DILil(y;KelUS^9C3Z;$q2+J!6(PkOLZf+FT9l$ssO}hm`@Yi237g*l^0@zm& z{h}}HiT!I7K*Q1{1ej&_Rxwh+%B}=R3UGnMG16nVKHaCXtft{F#1>n<3-8-RTt`t6wV&u%D&=P zUCN-I_N(p`*}x|GDA0s}F`!CbyIE$s23}8y`RinM_cr4(7JOVKVA9TFwBj>nf@y$X z?Km=yEQHww_;iGkqtKWiWG)xPM}oOf&po0*M=$oxy@1GOQqb+-& z&HF`WSAlRaQAkjDFRVYfNFhb`>Mq5IUTYv8Ey1u#U|4xmsV0&YqSbr#8&V_mZwiAW zj214STr2pl)X1f+RxqR{MdL8B{J;)cUA)0RB~MBz1fvR%T^i#J9zA$Z6ytzN2{;tJ z@FIsA1n*W^2!tviDfgr?L+9fcO<~UDWd(@^He&;|d4r{046!Nx7qljXvQRzt^94`E zi9FF4?2HrnDPOP)pU5Bif>&ezk4Xq-(Wwc+QuFu84i&yWMk$#8>693J7(cHi27h+; z@FI)me3#paT9SexVDiqSUp@IXz@)c!J&y$e}Mvv);tB8x2J1yGJTm6%pFz1nQTyE zZ`&n*L3Q`N`3q{h73MFf?3&GAAa(DXzo4p1DD&FCm=siXoN6;EsOmU1$+T_3`_`Vf zgw9P3mSj6xv)CJRIH;%4-KoL6$c|{h29_R?i(`f*%6Lhha~cFSSC(Bd6~$P7;o~HA zIMgrRq3G+uJbRq;B_@(eHV4CGVXtGZJz?t1zIoVpeOV`OrRHD++?)Itjgghw#VB{G zD)4@ar5fq;+e=zhwkRz^?F_j9^!9 zRUUnu5$uWFUo(P3I5{{o*vuJ@pqc?Cvk@+ur39u_7*cnIZ}L|!HQo8EubNJmWd%p0 zpXFIWcB9J5#`G)b`0U_#9Je232d_bDXim_^&$t{JQGE`8FopK!21{q=2LFr?XXMKF z>RkE$VQ%ms^ZmO0eJdL*w9})e;JOjxDfjeOcgc9lJ%_~pI+Xu{eCEORN~+^kdZYpf zj>!*RnS>PtFln&2%VFrkfmDxz;4}LLPBxuU7~BGT(-B3%v;7UwLSqGJR}pskT*@!T zY^KsrMZvCE15O2};_$%ff{u-+tC~uu7YBR5=I-|5;B`gNUHI$@iQJoLHyY0*)BsLZ zQ(8&z7VBZUvm|(udIZFq0pFkh8wiB>rX+ZXwXMFWH0ZY?h^>{(>OVDF)PImgaJ2h| zdhrj{5>5oe1scgh9;gJO#D?(_6nt}G&KQP=*J##FNfH{sP$D9bK|RGvX9yWweY9Eu zRU7Mk@W3a=%R%+I0>WIwl|IQ<=km3cAN0G!CsuZ$b@m=h!P8R+86bnvWJ( zI0+9o3B$%a3lM{dE(8GEQ76D21d<=W7KKIP@k1d64`)`?qh@Rb3CAPKxevpS*2J@^ zScMjZH|J%*kv=+ri)`H>JFiU<1vFLL<*qAQNIjG+-hykJDc#>yXVRbYkfcId?GR+#&2pt{e- z@Hepnr#70|<_|^KcO5P!h=b3c+E8clyKvEV1@i?SRN#&>9hPyvP-uX6HunrHs^$u4 zb1S10!p?9bZQOAi2Jcaeqf8bLo0qno>v4Jhg`ya}`|Jpw&fr1uI%5PL4qAh3%pd=+ z_m5Ifr(hFxHC_L4NC6WZi2n|xB7YoIzlGE$hLkI0Y!p(?aQ_ITPVNXQ#^8Smsy_m$ zF7(Tx!9qMS$xHRe;nn$%!t0NLDi)mOEDU>JaB;dV z;-J{?V^-(?6tlvA1X7@0|H&r&EoQyIkctK_bwSD*?(bn%o06p!zMOFzo*gQe~;M`_N#4AR*|bi&BHg zRXCKI3FZHYB#Ut-+U{5y&UPv@D-|(QN1y+WQsXkzO{@8=#SU7;1O)N88~7J|&_e#@ zBW!@LHhNm&*Ey|fg<2!jD5RJ8X%#_ROxF2Kh!JBP7IILrTJ}GjKKQWM8MU2P-4)$1 zN=cll3(WC+xqw>^sm11y5_*L5)|{~WP8^3)EmICmcrG7K07MO^0SGztQUsLIGs?^w z#~#k8s0mMq*#mH%%rVi3Ucq4UV;uDiH^P28vR5#l{?RK~9C?P%H2BrY2?tKqVTgUA z3o)6fUlm|AIUYg`BpO}-oD!V#`87Sf3-BFSQ}GQ&iIc-fboZVfPEzv#*m6&$+*A1M zRJ>v2kV2UNgNe`la?dzeT3nB(sel@Y6$Gsd2lV2(Jen*9#7^Ug;tkH%qvrF7;neq= z5wpVPkBk`O1xF{tJSzhJ6~jFT3W2GM5Un>sb!Vf4A{$mA%o5vMn~sm`;x$`P+~lA*ELqq_JIXG3!E!JP#BdV0AvlRqF-{`pK6s@4 zFi#A^IP)Zc6+Ch_7<_K+x*FhRqbq7NAYKRkUOK~$nI~qJpyzQoCHyYt^${Y3QE>;2 zXvU40#rTeL`yH{v?)`@&js(D|2~FT3xABnsbReL=8S#HSVCH(XBGYcFRwr#Q%$&NfqQ>0uOR#!?e`_E%ggFIGxi3P1~^0@AZgoZzhxWqBiJ zj=v{Ou`p?*vJPjo!AP0oe=JRzDMsAV)btN9_Wkg@|BtsX0gR$b{+{khCdo{WPQS?k z2}#c+K*EjO;cOJ}TG3Vi-nZ)!bXUAjCPeZ`PuU@?>2j4?iPk{Dss3%y{fs^*X znj&M`A*~MR6zCyPDnx=XyvE$$!gC*Pu%9DJSuui$*PRZ!2Tv(ze_`JFEmHjkr4SkK z&?uEjlnU`u|G&&s(LD7oQL6vGlycB@6u(1Rq66>WV5;As6k-HMC>1&=V@AfIG1VdP z6vSynQM`R4NY#0HfceBO`FNzyxjOi826 z0ddgq)8w{MSclXxI!9sOxnuAi)Ywh=ukd21LkS~WAleKyFKX<5O}yA+UtT1_9~LhH z?Py+{9BYX@7$XQfAyLp}P|V(qtA>yagGQS@i}$k51sAPIfb;4sT2R18m90?F^;jsi z${C~0+~KoWH*0l+pLzWi1Zz@=Lv%S*kK@$}X{S5rDK(ystS)_W^Jp_KM4LSAh&-zs z4R94wQyq~6Rg_atdZDEeenyF_BF@5II)0ZS7WP9)?g^G>5YAtM7rWKTZDC*K@{T~m{c?Lc4o5z|MAOZn>lHHGdQ=!PrI=X72Og&S!v zq?t56*ucZPO4xD1<|^;IBw9Z59dg?{v2j644J9hg#2Fg>S~#xIn!LO54T9&v-k)JS zDc>4vcAvb$O`?f{3Rr?hDdY_8H-TI`Jwaq}2bdxq*%83}MS80zMelAUV9k$?m^Msz zP2B975wt8zrmxQz-Kas3n`#+f>~8jqaMQxcZk1<_GfR@&u*hUj0|1gW2Yzdu z83@q=1HlSBK?7NQAT8tZf8?n}Q>YrC7Qo@b1Us33=HOQ%|H?%%9qfXLvy;gg%}OZk z=W^Muqw!%5RC2UBj#?e)K{!B+vGoA`mCLk9HJp zu9E!nN*c&esI4?Y0=ACb%%e0CvHo$INd0113>i@tE%%@}a#HiWl!+g9`= z7$@SA(|s0YQx0erO=~ss4--s_&0OU1nZP{eCe4}`*I2nl0IH7mVYH`#C;DW{-of*#N^s86!A zNt97upMW%(a3XL`pMs0)1kN%~S;Wz6=uor(U*IpL#B>u~Aej>6ake=&pC%*_mXd!Q z6V!5$&!SAby2M2ckNLJ$uC&d5A%}(_9RtH=WPqJ;!3Q*t(g6}cAqJ;brK6o%Ae}eV zh#FIEykmk6*wy&4AR5MIupN~se{L=*Z%fF zOdarhXW>Z0)-jC&9qJ%8jDQ4p6c($$wTH#dONy(oTz8m+)D9YxduV};NFeQW1#9l6 zt1@UrjHU7DlWr)kjE1{Cv8GZqg-Ft+rIZG_LlTQg;R#v$v>sb-JimDDyWT|0!>QuB zXgOP59P-mL{WlGx=+8hpaKP+R%L8!urLopg0AZ;FD=ElH%hN6_qcH?@=!bb+Bf4P= z*k_rW3KOo&>W(SqVML@gyTxHN`s9Ek!FEtw;Qqk<$E{GipQs+J)QNiza`$sz zo#Q=XoNoIv4l1T|Vh-n^&V%Kglq8vS6ecA$Qb5%HZ_!^)=#SVJ{iIMd;~xt8&`Wrq zh$dSstSD6q@pvm;6^}=ZN*zW!XZu#~;PMb$lc9L9DYMIwPc!c!ZU{sMJwBz4>(@zN z>|#HJs17N87=1@d8Th_k@qMSA>x0o3Rg_|Ii%g%4sRA4fKN=*yqlt%PA#JOGu_7e= zEt^$XvUIY`p+mxB;6QW?9h_ax{`9XPWEVCo9MbO?1E}+$#{eTWTn{lJ!F{B7^D2-C z3j+W4KCQ#DMh%JUR7soF9lwxPm z^6CJ~xHw*6*+`>>YMU_WQj9FtP>8JSh`FH#G(ru4)JGkKWOyadU??H@0zwhvr-4|i z0U*3|t`KXtX~x^};r4*IYL)0F70El9{94G9MQK>1B;gbfZ~Qa?LQM5rqN)csu|`XC z!e&M#7eW=NgtGZ?{AT!Pc*#G*IfzQCWqUNha68zW18v)?0P-XQ6Fsyfluo7wK>dws z0Gx6;2{AhqDWqeT(V&0KI+`||0IiY!l7|ug_>DCn*FbGWLJ;~eX>TQ ziwnaO3>zPOHV7=T8R*bjfR;F#(T0S0T0r(RL|kz z6lkU7C)J;9A=-3a0?>$RKuv_}8sY@x@mq%hVao@OHIvgJgjfxN!xZIbg68+)v1VUi z$67K@?mE`&4Pk}2UjUERh1DlGvvmjn5cQx108_ycq`<1n@sDLI^w99`N8=Ts4)c(5 z0b-QVuvJ823_BWQmIu86p%B@?#2CYBkXWTGiJbM~d=1B{lkQb1jw2R3@VmlhAw-{u z3&iOmc>x^&syWgZC)18Me}^3m%J8nEhoZ;$2AcGA-UY9tpU9W0V~jP@Fm1$S_goWB zOOM2WPCk-w*xUt(tsNkm6bPgr#KMz6Gy}vV%}9~l>llJDkzgPb;XY_k@+Ak276qe) zU~m?*oq#cS{q0%yFalxw}$DiCpW@~%h(>ZT5hKbchj3aNPOb5SG|ff8<8EF@u)yED!s(dpdbASdlcJydy*zF z9teE>nL0pA$RV3oitQq-=m=BtglN309K0)tStEGQA@UzG-WBdxyk8}}!ya5I;{Xy| z*MYh-1bIKIXt1)hj5{#ueZc4Z;e5HEKnye&Ir|&G*w$^Vbtm9 z^Z}?oE*kYZ2ld(*)K?xB>b0FvpSJ;25AXD(i~;K8t{FicO9<$Fp^lTDF{pniuF-FF7#kqoYx8aZqoLLH+&1LcO^Y z>T~`A)cb@{9|hD0MWa55P#+qL`lth=UJ{LZn}d354C-GT7V52?P=A_G4?EPRj0Eb~ zHRxa%JfJ;+7$GzAz^IRkM!hV~k?S$2|8Q8S!>k;`Fdq==-NRHL0n{hJp*k{BClKmK z#iBmqz^IRmM!m{G9cJoi(M~$qp^J7h;6_bJ|0dMKi%cwi5}9%Y zaK|Q06u)0axL?7zUlHc_BMyu^GH`$bIIrAU5pE^?l_K0sKhej8&30-C4prL=E1?Jv zAr171=;pUO&2OV`BD3= znqPs#;GLVl7-njx`DOGIx-}gqsv_%kRXhSyZZ0E36q-MXK-6>sF^KF1wH+|36^v?v zv4UVA>#_a%PB=9VAQ%HX={61VO5WGeXAvqsMIRnA73-+ZD53cwQ88>iPU#Pjv{vpA znl};^Z;c%Q{SQpV0nr@LIgZs;eg?nQs+`lK>Ul03Urt|~yIB^z*d>JpR%Qbs( zKhFNA^!a`VM!$bF`ppjdn`6-5bXe$b?u7nJO+Y{Fd6Ci=*mE4L2m@Cz_LabXRbR?o zsIc#QVC?%vW8ccyXNF>s|KzZcZ|Q{m$Ao;?^qSHK$dh+r2j5gN@{0-id;3I??{i?} z`$i+*?vTDM2K#Rh3;VWC*q8qm*hi+@VqlLP+ELh35TYU-k(XjfUwmNf`$S`3;Zb9@ z3?@N*=B&N<9v1dxp4c(FmaxwYPg>C7?V1?uYZ&_#ov<%DF!sgK*jGE)SH)nz0C$Hj zajH6DzlE@mOk2GHvNi_$TE>1AVIS&X``!n}9(jcT2OGMbCHER?ftt8#=qG4J_|#ET z>aZ!dZV4oJ50bjQ2}ENI2=ZHoz0)b6u`!56{gFw(xGP_NI2*ntqc-K)irB%Uz3xwK)wA%sWi3l2WyvnYC zS+O%U$VEFmkI_#EOme^>k8QgO8!Ag8XcQ2L2{m27;5|Ww23Z493`Sk&{KN{t_>f>^ zQ&WPS=dlE59;449W8x{Q{LtqysxxXH`-QkF?7xkjbo6cvSJg3Bt?9&77SLGmH+%^( z9B}Ppl_DcL8UFX7;}&Xxq5*F`ut^)XcWMgEx%RF$a8)0o0X907xw)e&9IoOm5Cx-| zz=3fh&QRFnu$wmlMn1tnxO?8-+y{;F#0VOWKNWVxurq|mb{TB`?F7Oi5PbHe3Z^cg z5zAFbf*5W}1^oo$@M!^pQ4JW4vu2X01Pne60)y*5Xsqb~QKLZA5C|A*(F*`EZv{AF z41r*m_7H6rc8~*wXpD8hpdAxD#GVPp7{S2ahyBDOfWg`kg64x%U4Yo$0fII`Pzw7V z7-IyYg{bik&WQ9k-VVjnffwaB(ix3C9WYuIj8=ltPB7X5W8((E$fKr^JGWhdj9;BW zEQWJZc#P3cbVU+CRFTh8RZ^+FDMZR*F2Ud+2Q^*5Sk(ceTEVC$7_|hW7BJp_14d&W zW8?vBH*^7FLkEaD1)`2XG$!FRklhFvGv5LlIfMqe#@d^^fU&g$Mw5ckL@=81o06T` z|Na79lyns9dINe4jo%WZ~INbw&x#Iue1%w5>GERB8_kPsCw?P%Pp@fkikX7&|4W0*zPb zOPnR@LU^*XOj{R3*h4K)G-#!tU>w4yotlEQbM~_vu$F9b5fRwqw!#<164HBCyf;+F z1O|@aA;Po>@lI%)v`F8m1z6HI(c${TAWfMsmNa4Oxgrhif51)m3WTHyTQ?fHx(!5| z4s9hFqzNb434e)b0@!TsT&j%-nA{}+Rs1`j>h>yXadTHK-s-ftS+#gG$}n~y#um7+ z5m6AL>97>fst7ScYS(9vGgqoMk>5_|HsP7Qb0^}}AR>B4DeSt&w46$XDcc3>Ajuo# zTy(cnbPAd#*5Hl9J2mtZgO_luqYvtQrS_X&gvTKJur@}+a6oMA0MVd8G!Td;0?`Bz zPZ0>#2+8-zZe|cTK?fd-?EP==fU#M@*i0~5=z|u(_==_p-jL(H$+j-g*wX=`Re@+F z5bXq_9U!*NMGSh(R`HZ+0UOyBNTt7*)ikRmr9HC-WiHiU19@oBsF&a6u2I)&%_{`6zgjeMxlHc2> z7(A9^*q%xqfbEr6s?)M&YT2Pn_5-30XQK$}Va&+^sgW?xj1fXPm|}Qm7D2BTLSaF7 zCHQEl+M68&o03&rl*7QXDF#b~?g0+bg3$%?-cLA%{qo>m3JaNr75Y4{k}9sjD02W> zlJ@}ZF%#ccrBIlnbL5rn-fl;R&#Addb}4^0{EF;%yD63(luOOnlZfUb(p@$lSO8$V zfF;vJTIZPOAWY$ma}X8jGx^v#CWR^d{2cRCI*5tDf5`zCIVr=050?a&?*~nf4`~z~ zX0uBLGJW`fAN7p7H89M4hDl$ z@_yWSY=7zS5JKm$W26`N*raqDk0gZ;43LK|KBOq)_ykeL3FJurvIzV>6c2{R9z>=u z;PiDoE^y&E8W((V6fW?}n1G9SxK6vY6u^aY1&k>*ycE2JoK-fv)!cnd5P{<|4C%f@HQ`WS)-OV=m^Gl^2^A;_lmv%`0#@`wxf=w_4t> zE+4B){w4hI{7cL%569{B$X%D1`EuDMX0ISU@DXv-ITmzV{;U&I23p3-pl*kzr1Yn# zm)kEv47$}a>r(R~TyDA4JQA0eFD0N$FJsU)E<;x%*3>|)S)=fo$6&;9R=jg5Q2@nA z1b-PbCw@_C}h_kgM-h!U|pl2xn!NyD`BS zKg;}SW=>WcyPW}JsPsSs?eDlsoa9D-gkNwVZi{ z8R)NSuk;2zDc1t_PJm(8akBhthI+d*iQdweoA716dRRf9T!T+4p*co>BGa!ld!$8u zc@2Fz_DZhvf==(Qrg!tyyQlH4AnLp6c*g|jz<;_Ac_-yAl#fRIkDU=mR0Qr?M*L4y zh5g6h_#~#Sf22~D@1pzZ(r9u}b7K1GQu^{~d>Q@8CDEVk!;_cL zC)(9!!C2Mb#jG4<$x)YC=g*p7T~Bu^65w%gaUur zZ-LWPfs7ks>pF$Lnu3R5hG{p%_Vmg0PO5h=c6#@FdiRBT=esfXyOZeMh#N7zR?0K! zvRdADBf_+$^psQuin{gg(k39Ki^Gw`5c{AqC zU*xu%&4B*-6Ef}=M9*9;$J}D}?t$G@z}x5v=;XBxpjgV|s1r;M9^+S8b_*hkV&dh= zF~8hQm78ucQ)8bb+}ioc_*>0P!2iEn&3s(0zm;d^`diJ5ahH3WIo!liOw>@55IgGjt;S6@yC!k5Re}TnVc)PK4j)~R}$!ypvJz)tV+Pulyen-i}xs=T4rVq zh^pG|RMiBkYCCP}LYskfbYn=JTxRw<`4^uPAa)Y`ARPr^e5a4c(Z`4{N*|M_demfb zRWwd0$2e^)Lww%V@{2OFV3AdDfsP ziXPGFGq?%FXi#} zJocGL-N*_P_ZNx4hdURL?d1&3Gu!MJ84oASHWLBuwAp4pJv(uMStM&_n`W*eUNZu& zQxt8dtNW`~$hT&jN8u}RhdBwCGwv`)6DgM7!B0O_PXlw*({tyTMHtC*=73RulJ#@U zk@%!-4!1b7!VKVUWCb(vWff3Uf064ec=YY909)K8yWfeG5)iyoA$a4RW{D%p7kh#@ zr%bqcsAGH5;g5uf;>#_mDmUX(tFbZW@G;TFxXZMtsYl#pmH_aT&Mg$>B3XZznHC|< zr+1lt6!_sTMzzOWvl}&Xr_IH<|3#M1MdaB%@`brT@+bMjTyET;O0%b_=(j9B zn8cX~X)SPRr6T;2N*3MESDKdqzBNxV@A!G&SjH_hvx%Ok zFEoeHb>2dz>ni8*Yq+IZ6RRSmkBO3Goc|c*_p0)FcPlU>?~at;9#bBBE-YoHqX3Yo za>_q`x0!?6*X~wBj(eZDMdmKzLDFZD;*#)?6Y?g!n5Hl)M-5S<0(&7b^x>*k* zDG;&0n}#>euQ0OTEJZ*5B2%PUI9e6l64T%APT1izN({E(QigMdG>e0eCg5gID&&K4 z7a6r`)9B~DWOJ+FpWn-R2~Bdjd{>$!S+~-+&HRFkZ|9#YWUp#-eCWO;oSOIf3>KEi z=Qrn>yO5nm#W086iEFW2QcLZ43S*!WP0>jlh*nJKYg7PfZfukooDQxZEi zys-VI*xiyEOhN)F7*mo)=*hS!WGP}ZnRICH(N7kg%CtZtL?Rh^VAQFTo2oGko8-^c z=7p2Uq65PWEIMSEfsu%gZCbd)d7WGUT!WSou-sB(zL&y!dpK@4G7P*oRnH0OY?zV{ z-D6JX&&9sX%0(K66^5It&{1)?G06FMM=X7HFwY&auNeM3vbV+ZFbf zcTghYV(yk_-Ea2rVHi1f*eI0!VcBAiUoH6r* zpNa7umMdeH16`07bH+o6x&yXDZa+UKuJmF+VQ_dkl060(5|G8&U%5&-yQYrd>eNF# zm?%jqH!`VM2UF|i;fCUL;~_OP#4QC@jAf!t?fmL!G#Qk}u*dciHN6&%q(CkhUqQF=G=3JlYX=7lXps=^NeB%ROd+S^xD}D^asZ(tdU}q{SjAc|$C9#A~B~etC7EIG|aMeIANQc4X-WoGc{#;}BAwkp_)ly~( zIz#ES7@ZoCDd>v*b}%;i5{ocSVJRkB7U{J?5`d~76~UBf`rk0_@2N+>B6p7PsEX=Q z%#nx7xLT6D?;*1|^#8Fb)RM>6gEvb$t`<{Vqt%MNGS^f~2TmKBq}ML9SYfLO&Aixk&rAR0e9z<1(Z-ViF-`73P+>WUriAYfjW( z`$GPu7VEKU`Bg1+cp*KHnYa3|^A_e82|b~^T=|$e1e^7aE=1CUiH;Wk2YL~hE=Dcd z{S8LQ$c_=B)FL0P+KA?I(R$My4B2)lx{(jIkagy;q4&@sULWa5z78D;GgzX%4kweL z7h-KApya9nfGn+g+yws8&tJkW1CpUQOo@8HFXudA78cy=)9v2AUVpWZf(lHjsGC6pa8A!d~OFFp0$U3NGG8}_Y zfp(W{V#Z@TRYc&S3SeCtUtzmRQD!piZGjDN~JH=7#;>A^9q zptSF_C|W>Dyu9Tp^8_mlv4|k{qmhtk@3v@4ho<5}B%U3B5$qrg)}<)8+k#USL=F|i z{$;AMLS9}6(-{_n5969BfNT(~AwR1#&kFI^KEeQtVtn0TMNf(yb1O}XRW#G8wS*cc zjy88_>xY^Ncwngz$I$U2CkM#Zj>GtR5gbHNWovPxF5V+MQl&HxT{V6ma@q$ya90gehPf9^_54r(C24inqpO#vb5cw}LAp!r}=f605 z5|<#)dd>{epJC&Wm<=EiVS^%DkRJL;074vGjQQI=y~Oox06#NITp!alzr^(sUGqv@ zTj`oE-+B&)0KeS%oH;xxBJ*YHQ|3T<=329t5@js?VOL4Cw?Kb!ChRkA#3Z+QF4C0c}=v>tU>ERy0UJKJ-p7kQ^JLz)8i)K$GCkss}ag~EU)Li^62Sr!P zPhK=Lw0q@uFCux_eR9M)^So{yQUTsIiR?Z=vjXI?@v51bWgy}lD)k3_h7!%$EJ}&G zr>cRIO{BuNB$#y;%u`-6Cu9QyiUVlrf0&a+f5yKv7hs|7sO%JO zr=u#iYyAN_@}cN%(6^%D2fV(Cu0Y{LmoHFYK|x-PmAre_MC%T#pxi1fA3^c=679|P zb2pfqb#1LY`*m|(#^;qhRTkr&PF6-3aE(vpudic6Vy*1ggw4*i@|-4ft!Hf#LLukM znHx<@j{2*4L{ENz6BQkwxly7U<%mn>W{(dUI|Eswi0GIv^$q?)1 z8-K%Y!dm&$->{2QFNeJW+vZw%!5ijHxP17Ac|0yf8zHgR%E=qCN3&Mmy3srhmn|F3 zXK|@`6PrwHfdL{%B^OO_ZhDpg0HIv08ei<@7G?HQ$I4F*4~ub$L5`S z<7`>^F=*5vUv9xJ+d`T8iP@`ACH|{*L#0eIa412yDQi5rpdu_0q71u@GEJWL3HAk^ zmt~)r=W8#>ZJ$7Gd|tX+K=&p&uEi|${A~@)2=B|wTCj1T>gkz)W2wO;3p$p8D)%uD|=;-rY}10cFi&i)jeT#w7A zJ_UehWb>!yRPF8hqR+4ap*<%XTd`+*;XkpV|B`(DpJv{WwUL0yWM4$^83YT{EZ1tV zJdD@tfnAZl3VuN}l>$COvewJ&&&>g(xE}pE(%HT&FaI2V5^u}O&&?@Xqulbjc}c-5 zaCtx%{28N5x0^JGQp;t$sN;st9Po`i=L_V_MDMf$&sXJ*t@xVpyI*??`O?kF+9vtP z4s$@(%5chOELnAm%}n?iugWiXAi3#xGVrB&2C>nX*h2oU{;MwmEu#$y70BuQcQged z04sT6yeVJ)LCXT{2|K}0x7MG((_F4=>z00NeyOdi|LR-w2MyQHcA0;tYyH2?4>W4y z-QSs`Fo|#c&TORH`QMwpwGH)azeh*(yi-GHT6j;7u@fnSrQ6duR z^I7R_H!tbXpqJe)_wF$V6>fcBO;PA~#3^u> z+FPO@Y{i3(@5_^aF$?q!@5>v1f$QjQ`Oja>wR)LP-v2AQ=5D#=S8Q=g`Qxu*Ojm zX6xinPiSq0wuMF_?i>tJj$Q}9TccI}eJ|R3pZsO7nLTPhFtEJs1cN>ODTD(e*TL80 z?@LY)mubtE)C=phue-(rU%=F{e?0kp44%N8!#+?Q#{F`DhAJMAQWJTTmiruk<1X5I zfI4DnA%D_DU(mjnF2?23nW=%=Dg-{qItq;XWIQCJ8}qftSR>1HaXF5F5I)d#aAX0* zOu!DEineJq;Y4o=zF8oZtb{FN3JKiGEqziExRR&K0W`2eBuMOYHV3z z2T!+`F`IZ1+Zre?jHD{jtN0ZO7}apVl|_9>D<54*)^&ys0A(GL^Y z7@sK8XBNpzd}1i*e4kJB8Ui|F-E2G(p)>i!{|!FFtiu5eQm9`=b{3uF76dztc)NY# zHgv<)$&qf@gV>=R-Ox_muqs&$(jJu`C5vge9Frn0>-Q`Sy&Osr=QY4Vx?RP8{iVhS zOtHm!(1S`+fe$`R5yxvQWuH{hE49`apl+j>8T9JhRFMmqx1@?;`Kv);V2HN>^CqlC zNDQXHK>I@3O{wB4l%0?!GBUSvEpYaxS~|c@OB3DoMLxOC5X0O9jpyadX<}m5-{&G& zYt+Vv&S~6UkiSb8Ssp}x5!ih6L3f$Kk37v&ziAIO2dm-zm! zmlKW4g836Yr%%+Z{5vLYbj_OR`u%iSYl=acingttT3!E*DGW_}OzssJQ;*BJevyyM z6MoTK|24CIi(h!O-cL|!JPnbl=t5eK$G{^5Ohxu#x;T%dmMJo{C+f2@MGArJnI&$( z<^C*j0xmnV#6nzVWb?)7CJJ#ktedF9<+W~N5-!Qz#kIK1>@LP>kJUGJ7vE`}N$_f} zn5wOkqKCMBiJIRrrmc`ujs*yV>bsdcfkQ39nZX?>6DwJp}^663=<;j$v^$-QB ze|pLOdBVD|jyfq4y$z$s$~RUy`5#E^Z;b9lj0m@}N*QgjNTNz`FyD9q1cIf6?@90A zr`WOsqqxB+PvxmT)>I$=l(%mmPt6y{Vy3<~Urg5Ck=ydcI!LbvEO8-NN(+cU=4zg| zFAHkO8{kaa7h{}_42ab_gxMDXaV-h6bAqC%a9MLi^15wAv9uRQKcIk^mCf?8peWGG zVayMT8=>q>DG)=tmp$0=A&IL(Y$MQ#xAq#jxIhGH#%?GO#fF-S<6w7z(?cp|(Om`N zOzb8fUnmBqw8C+RVvZw*EUc&%g%Cco<#UDTL4Oc z#3Hr0MpV;4Qks$KLrdo?xlCoUmj;r0Rfb=5$VhL#@Ttq6Z`1} z_e4c~p)zsC4;srf?lrCn9W$W68qKIq4(u(4_1KH^tf&=Eo3sR`z128lD0&%F@N8Yq z>@DVN|ByzJ7z^2SLXpTuvinPm@KE8Vzq3G2C>EJLqj3qJpE_{u)x~027W5B=BvQu^ z>(NnE9i4n!A23I?T-rzc0aM$aK4Mr}&7;aYij}TvNVvX0<|lbZUvWB$Jku9AJt^&e zq6puW_Jiu6XrJ|u4WM2)tO?{`7nGe5yLW=v0x5IE*!Yyl0K~)f0rkgfMx6D^Ce27Ry@Q# z5Yg;u^NI&%WYl!=J~D1fd5 zaXDy==mFxLFh(2$pkS5ymBn&zIWuRvEqc&A16JDRXwf;lbrEJf&+q@$T@Q2K*<+;w7h{g5Qz!J?^Ian zIa{F`$Cj4AABSPOQr<95^fnxwiqh>cDEZ_#F&HDdWt`}i4!~HCL{%ic<3&#PX4bDr z=k5e}?0E5eZL6#rFEXJ(uN)6Wdb8X#US#3!i}B*v?3I89l^70+v0v67GXb0&hsk+@ zeBz=WJ>>d1p48L|OxxVe_9hqfv290+@$!U;BH_Y~^T5}UIBH-j42naUs--glShPB4%um|R%ep|Q(aC!lAB*~r0CJ9 zMJXLE8lW09ap)J+mKfdeAh82YhfWp&x#B1>#lJ6V@jiLtc#$FflSCf0(jk*X!Tz!7 zAI4_Md|(rm`w+{FgQJzg=CX-hAp+_kaCQ`pK1jfZ5=c2v+o=a#lprmNs`BH>tnJuS zL}50x9hBNk%R8+8V5NXEvXRymw@rb3{4uM(W{Oy^``<$lgi<=M9>74e;s$Ci;|h88 zv6!AOlXb_6voJlsQj>RvZ2QiiDD&geJ@T64L=MPu)gOE0$j6Tp-7>$x%tF#>Hx6+k zG61lH^6|L*0Oj)9AFHN}c*ug(j|9{nunTk{7&L&7@r*Q&7sYzT<8tirA}i}fT0()l z-E4T`Vkq>UoOZmpr2BiYogk?!Cc~7z6sx|B7T0LKu}gk+yto`X-l@M66SGO)6E2M~ zP4A=$leJt}>Ynsu*QC zMRs}Y@!075f##w1a5;SXrR;Me=7IO*`6mLgm*m_N#i;Yk9?_}FaA~pOw>-QbM$94QpV@G+_5zt51%k&$1 z1OY$6fiF+Ip@$duiS(*Auh8v_yr|9d>F7IV!s3%5EkBSaoh^$^yYJ-S+=Lw(Uu;1T(clf{XHUZ(!Vcd!RyCPHR2 z7lnG!jR;(D>%L&GK1E!qt&=UMK+{mfs2_AH29&Qg%a@Lx1=ls!-}rx`RMR)ildqg9 z{K?zr-IrC0v6tqv7t6TQ#c}$IdGe&w#Ws>ZXNcGI4fEyCXNV1Y`F#FBUol_4eBJ}Q_xA;4F zF45igx^j8*gWm4)vvVPTs^#SK(DJY4jPt}0{qsus?0KRf`)fF|s3}~BAeiW(&;ZcP zX31~P6InSsNO&R?OLvGfim(6@$#lB;`ZH;rkM(t}Ty?(4DQTko>AEo}(yf$v32E1{ zI~)`PO@A^j$KTSmMQeo&VYU#@;XA}cNvKjZf=SBUQQ z4_prYU2CoX{WS47UGZ=OOzrZ|e-hWjmOkn#bj4lu)2|X2XqjInk@*PI0UxH1SJA#d zmaWsFbABw7t`?_yeaxSr!4vY=7VoK=SDv!Xd$e|vynmawo4zYaHf;0Ws{boV4*%5K z-$0xObxK0TBJj#p8VW72P5Q496X8E}+BIT+wwElC zp!8^MkXls{6ayIka*fCpUnDx?9^*HRT)%5Yw?RByX>Ew9ioTD7yv5nJTWM@V6-172 zns4b!!Wwl}T?>XmgI~T@6!xbEJ0Fd3C$ecg?ESSbJlXd;(JL<+Pq03WJ|!;0K?x(zaRo985^;Tc~_t!yE$ZqeaPt;Q6+3%-+Cnw$j#kNe&yg}p)DuYu8POuE6 z11vP7u-@f7%y1skp$fI3Y0QxuZ-A+Ij{Nrx;yRd0FTW9zvRp2_5gPMc`P7Y~r@G6O zU)?B-g1gu`gbb$Oey^c!X}EG41f=i!)g$w65?MN~BW?m&aJ}RvNRwG|Azi+b>u$mt zdWU@XCXr7szrP8SDZk8+BW}hV@SU7|vp5nNP42o`3=Pa-pGZ_d^(7nXmTeh)l+)NI zKe-tu-tVM-ix>dtgKrT6Gv=gQu$HfoRky&PRUun$0Z+Uv3vPvstB|kW3JZAq(%WEG zyi4}EO%w%xr8B^=aK^FTOd(Tnh~ZZ<4#M;W3%s#IS|Jc(hrIVT0GunoyiN2;*umOU zt~8z#K{<8?2GMSL-3&l&m&<2hxO^uYXFxl=OMWs#^vw*{k<4{;0e6%Ph0y$$<@}JC zgQ_yiU>DdXN0y16M5pu0Ae-jOo6E#8Rt4Kw$FQCc{hT)b;rI*XZ6p@+J}lfnD+8&^ zWa>;YDpipTe5=`mjoI?lnTqb$&m<$HM=qZ!dZuz)In+5f_MMp+jI(6U?PznEv~L$j zz_xVl?Xa2SgQd4afX|XY-VVF|>;(DBo4I~jGE0ogr4~AyKA@$+?WEmz+W`F*%!28& zOg=RWM$C5kmsv1Z?U4VPC5H4O7D}Z3FJe}08V1z2Y{Dj$6dxBek0nZb1}3YE%Ar^8 zkfCzWb(?&kT=cYdz>$BT<_}WeVcZcdm_4$o<_y&QS-BXJg$7~i1u%>B3LO?( z&^){=%JvlIO&$zO?|`9tha7!}$h}k{v0DSCO3`!J%&CD$&yB%3zBmX#L>KlvMI#sK&_TulZ9s$y~RTAXBn6StI$q4 zZjP9O!EnbMaM?~-tM1xxm)Fr0J<_2Xt)#7Byiq0xR)|w@wzRxL^hqmA2!I@%Lj*yW zcF311#6az5`E`YungK$AH;dgzI}?Cr4+8t0bf*|={VU6tta|q=mw}u6G$Oc$usitU zHeQxb-YMF_Ei3PW0lNZcTfu7RtUt^JuCwKwxuV}G)D2L0-59rQ4$^Q0gt)lBYkNXU zJ{}_OD0Ldtf2@PBuulol7B~%()&%M;$5;@+Gj1Au3S>eh_H1U$F_j{R>N}$nqHu?t zSt7iXM$ z;vwRk0>;;}_dE!&pXL9}6XVh5rSn*TZJP(_SSC~EgH6Ac{pR!C>GMT@txeuBUyMS@ z#`#!!R7h=sm=I8c0UJebq5(5-90fbo9Uk5#FI<3;_qANG0CMVU`TPP%tqQq|E@iT~ zN{q(cAF3d{%H$nYVsL*25a(#!DNwZ#e$O3-;~TlX3S#FQ znYIv@Z)M3saU8(iu@K^;Tt2@LI%t{vc%hhb+UyAV(`c}2;GH?xheKE3Y{2p4PY2SH zV*y?$0?+0qg0q0Ih33qB^9K_zOcQZxS-VIsvFZtfx=*@pg-y&SL z$q9=k+V1A4TI@o=*Te&378Dkc+%sYFrnA9)QWaw7DWE{1{#vC00$DmjBFZS?u zJnrYnHy4XuJrwCzc>oq?esK`T1JZ-sOf6|F5jKeX`z50H(M|*6(EvbYoD|Aygc@L{3@7{xXs6yu5i*b0DoO~~H|JC+chja~;AF&%&ZjN+O&bu#X1wQ<=1uiBs{}^~6axQPLgLofW>=- zy!!z$(do=B^eM6-D}!PUOZRLr%B}~X&{fER%Xz@uvK#_>wya*RChF&xi{jo2$B}HW zBq`|#wh4eo2S8Y^jll>=s=>EqvR93mJT=mhuuVfhpq@+*k24R&LUTl566wY|PVu2Y zqQi}eB(IS9j7@UbP#^*IVhyw0FEy}*l*v;c6lV}8JctSXXZe>0xf@a+VhM8WLtu#6 z^2Uc)k6ZVUn1K&QKMYIJY%E=bkdHqMBOd=QjC$lvffnNhwm>8wa|IVISOJR8mg82S zANI;~R*17PieFfPW^R-3uMj;=kOq?@T-4E?LMDw&eMI!kQ*$Ynfq3JhAR7id*&s3; zkx!oV2v}@~yzvn+J`SGH9=R|ivgFYZ0K0!aBAT^}`wEdPYrvP;+>8C`6}_dqELb#1l|I^{7ZM3V87bhauE_B_FC4 zBho7pl5sX)ryzTHJ`@b`Wv%E%It!+=E!ZQ&_-DhjQbl_|Ci2cwRc$ARqPi%?B=Mtq zyfgfmyt`QcV#L^wgG!T;NyFAfSSN+PL(`MebH0`}kBLB!9XLfm`{S(ZK(evKVvbD* zf*(J|Vl?k@k(t{unTlAE;=y=3@o`adY)ja+f`uQBP>^iJ4o_sM!qrz3bYr(K;yV;2 zqDU0;_}%ij7@YNQADzn~AZ|RR#SC;uRF?aM$jih_Y+B%fTI49G-^ml75dO>OusFcJ zakwhB>^nuMVvxm|*to%wa#-+$zXA*>tKqxvcoST;CmNMEfl3-s!Sog>Dt~%H6zA}? z$?2ns1`5dr>JUhsajT$|ZIgGbf=t{dpH`Qxt6)^DkQu8nk9{wXUCrx&KdcrfmH*8$8?ZCERXFAY;vsWz*y1Ho2jm49WMOnVBF`)4`&DHd}#Jq4$e3i;Yo zV!-fUaaxEYFoDz026wn8=){Rim(6m7(RPk%5oU6lS^Lz9ylmB&ScGW^$n)z&rqfiY z(r%urS(QdpEAb_U{S$R!{19iNP(>imd6w0qCP{ZJKPQ02dOR&krbO{G?MPA=w>V>0 z4O)a1z}kf;)-aR1;o8HRgj#;M(Kh+O)9A}G`TWzE^mfRvpN7=jBXgd?)bz3(|BSc? zRQli<(IY3SJ{Rh9QH9HxWwy!mXQ5>5kb|Fv=DkN=@GO>WFUvX4iUxc?{i{;T=$%K6(Li~){30q18}Q=P2DDwJpWG7*#%m=28T4z45MAXv=)o6 zqPd74x+6{wJ{JP<{JGh2aj(MsC*O9b`{k{3vvW?}_HVZMCHQRY9@rmIWLynX9ozq2 zc;ybqiipTrcI8W9E24qBU8*Fkh;(=F9R3bgL@N0gxf9jO=hkJO)GcD!bD~tFa87mI zen95Gl$kZ8_FKoe7k-_fCbpmf!=f|SIaBWk6pX>?y|$?LJ1P*NfNS+}T`@`G2`gZ4g%^ zd}3-+;H0SB31v@ zxmobMxY+&zrz@~g0%569ek}VF+!woeD?lR#wBL_@faScsDf+?dU(^qMK_qKKVIZc{ z4#9ZZM9^qZ+k57zM`XXoqutIelKr-WjqRQD8JhyRw{Uz%#Cu;ZC z*S-Y71heT!>tTplzVv0WMO!A{eHjMs`{j|1;(gE{=M`)~zFhyvE8Y|TT-)ZkpSiBxW-OK(EwBD^XuOYj)J2V|px(Dc|fc*w%Z z7<64U=tKevy<5R1c>FU8UJ){o!5-o1?^+d4R%~E}t)$BIdp-;z|&2lIMf_PhON=Sy6nHM(PT0-$C3f+6uKJg zT4FCm+QE?GrXbcChAvst(cST-#1L5hIw!6?ykv&cQO6U@Q>>Rk7PZ7%J(NsVj0@!; znss6FQQ?|&j06mv49-o0*g+gSM@RY+j2uMp0Z)(#eq)jq;IJtn+CT*|0op*V!{G$y zp2Hb6B=HbLeiOLm(HBj7;4h4j;wzJQ0Y($vMGdAT8cZ0KK^5qy1zhl( zeOM4eE7X?9W|ORQSpT`KOzIvNZ&mP`nF5aldJy+O{pe0toB=RNwgd#~Ajt$iM~M2k z04Bx&OmSgZ1V1aRNsXx-oC_Osg&DLx_6B#x#8c{wfBjf3z|RpL?R$sJH*XgPpm^GWuj|KdAr zcrIE86$YIGo!r0*nmM5nxRR{cfNO{|F=2@UG+-?>?q<9Zn)5gC?h0)Pbq{&R;%_be z#bM+GX**L}uVSH@|DY{1efVH9H(p1NFn73m4EKT)U zlIpWG)n`dqrKvu{!qF3A2SQA~Mfz59Ny2qmLI6Efq*o^pr^bqzAXo+?n^>nSf#dCn z(*y_0gOE4gklnmJI!OafdPxWz5uH^4&+JdBRH`GE!0ujhcG;)Yyh^3%hQ$mCEu7(S zMs#}@BO2}Bt%h`J?2u0F7}BZx3~6UXr-nx~C_>CIRN^{(m9H{ zG;EYOKUC^MczEsmC@YG;vpiRUBB9cSbfm2EkR_UgIfqi9dzceQm~);i8iypm5i815 zjNT6xa;&KEelkxC2PKIy6lk2l`op59ED;Z=)7((H|axqrEU<-_pgCPVnG!NBw{4HGhThiqKL)0Hnf;x_6CV~=1 z0G9z>uw_+ZG;EqB50Z+EDC|mMX;V!!z#c108*~&&n-x?C<$H8V~FfJbg!?UA>(mE)^84Y#P1Y+a{(&drd2z8+f)Ftjm z6=+1bjQ_IS2+>N+Q3F|U%p$>&y7PABN>d3()|c z1JQB=SrhTFMb(5@rxb*Qc!)!_!BM>wf%_DL*ezrlA}gM;f)@JyUG4F3AHuFQ8`>ZMTH^x+T z9G$-|2`hb4sUYZ*g4smxL@OKrl28bPI*xA=)r|`yI2#8%g3w)|VW;3bM8vXE_VCcM zQdSdg6Sup{Rt8gfT%!%4MDz*AAFQ+#9vZ7a>cGUwDIx4q0D;m#Vwk~TRkE_I96}O(UYP_Gn7ei- zGIfH-KW$0l1cXb@WfVhDB_&>@SKKQ##VjXtDgzCl*OzzkLvR=Rvg z?Ma~U-PFxkD)SnO=<0!Mv9SV3!Z4K>z|x=l!&@3mL4O33Cs|38$P@+M6qrol96JRU*9mN%%2Az> zVi~9cJcz$j*mgwGQj-9k*~m&EieX$OS|*hUVKPFi07jdf6M*>)UenXyT*$qi7VdQ_ ziig-d!l*-m;d(N0A4Nh$G|gjzMvf)IEP*0%Fa|1CO#wVrFMmrt&0*BZE)S#!mig{^edOb2ga2t%9ZB|FjhB2B`JgKLq77Rh%$g>z)0-2JKO7s^pAY(aYU zhde`WRi@~6hoUWX#mKcr=&eNDHYP5{ZnCk2DZBe{Xh8CW+Slem^vU*c16#O(?P%cq zjs}te=O9=1++|o_CfK>tNT6g}XdHK15w|Wc(z-G*8S&2^D1h9&A_N+znXD3Wl?}w< z=2ayeMi-ObOUc8HTkqrK$=h%1(*(ar>B z7=wwbc2-j(+f&$C)m+d@6g4TBL%Dov2T0;W*euZ_ltwTpwFhu5?}fuc6n+Y8DQpNM zZ&`oW?%tTs5hnq8nfu_1v5RaQeK4CArJmkyCeyYcau%vw`xwuS8bpe}!l#JimAw=@+bh@zj3<(U8xAE<|g?2a-nv7wH#U8HKT~w7IS=f5=$TzAUuhou7ug_qmMc@z52yi zThzgI(L@kt@Hg;Oohg6!jVKNMNEDz01&MZ(i{n3s0^OK{f5OHp&bIxC$(n=V#M>)$ zsHYhB$mR;OogeBS$kU?3V*%H#dH@vU0A{Il-J%Eb>AG1D^dbgs)B`*a5lB0k?i)xa zh=&@zMu+_m59+{*&?$7q@3ndW?)o^4tOqiIVHe{(jbfjZZX6l?bbKXRpq)+t&faI> z8y0-Z;{sWifaWpUJ{E5xV{{Mh zaNpb)Lv%O2*)z7(uGj+qq@ozcuN;T?`5Y#doZ4|E4&P1pVEDG)U{Xq= zg9>``;O$+iWAyI%m*^Hsag&Y$2>^lbK}g7Q^wdDDtT&|$!Q-ZJ_y9)=dIdA1q}85Y zARI|6^x?*FwCcc@k0c`%0Z5VvHJR=orF))0dD>9&6431yz+@SWn^BPa)CTNBQavr> zf+?74Ad@n}v05pZE=yse5bX{zJ$8?k+j{!_p>9|b;xS82c+aR#;E476CH-Mg{_g6T zS$3~3D32ro=vT}XMd$)2-3XE2>N75y{0?Ve@4Dx{U7>oSIJDBCUoXnn?E2p~>h*s^ zm~@1zK}+EEOMRjHWc$BGeow_ZzlD3h-EaU4%Bj0?kR?aX*exy|T7Y$2I{uI30$2#d zapotqsv|gZM}PJT_ovMKPV`D*C%HZ=5j|$ocVes$LIT>g1^H7e=Y1z~0tNJ7PjMif znX(u);C@vxYQc3yaUe&|{Ym7zgUOow_9rnwcKaUw-a$FxdpJnu$VeJ@I;Z5=z>^(O9VFg5lCQ|5Qwi2QIP#+Gsh?mp^Z zDP`cMS~k-dam6R?BpMIrw6ZuC{*Q05OuR4~g^0el^ z?BuO1v`{ikl?`JfRB7q?A5vvuAAdi2tJ^CanXJrU^=_(z5%kgrIdBk0ldLHWmfF z@|9u!{&HC#e?V^4{9`A(OI(}#0|9VDQxUWf$RXQP;%cKS-tDC8I;iUk9A=aO^qaKc zM2FIi{r7=-aw~Hz)ci2T*Y%H3d?)zzaQQF}0~5Zjef-Bqk$mH?qE|u?YF3GB3&NDb zu{lR}+bhNo`t!J$k=l$If(cJoB0OCYkiLnon25H>ioIgs;Kp(213cnBfG_uN&LpyR zFMO|iElKugUznmYbu{#*i3_t|Z-jGemEkQ(6FScqf$HwC%7q0y5Nfr-f#>AQ_c*W>gZ2}|mX4s>`Be53mlyCDw&#$*90 z1leSh2Jh`D=!85m$0kGY(el%H0`p-MdSLegnH=vQVb#Es*)Al-mCk&)Ekx48 zJYctiQjD;?DBfS_A>hU;IWOMdw;Ps?@H5Z@*>tUB^%DD0&`Rao@&4?gu`A@*l`wYc zw15ThYhYM`szJvqg97W~9v**I2-7);i$yWaue;J9mD&8di&u779YXPf|4>?>h^~v% z0$8~9cQtE)01Th_)uIIp(H*u69bwOan#q}?w`$G`GOV2D(CXbaxkE|KCC!uo$Y(wN z%uqSA*9HrQEmqlpD9~JI1%pv4TcZ`FvRSOMC6UU;=USA?mP9HWnXb`e5L{Cz;iJb_ zV?h@VsYZ1U1VCzxPwq|d=l6#m(n4Rdc8!gRei0o?%^t7-P+~|}su9Uej!yLFjU}ZP z3J46m_=lufom3A(i!@~dO9lio02|+s(-=8M%Xx|Z><|`7o!ag!zfnKy_OVp>Pl>Q> z36%S&Rqf;sDk$xKC*chLBqAcoHg1C=O<23%4>@|sKPUM!oCFk3KmJeKGUpStrE`bE zXPA2QUavnVaFA$!>-8tOdm0bQpS}KpnkoDG{Dp|}aiY&ZJ}C{|iyf)}GE4gWc?O%E zoOLZ$?MSBU^N;oQq=C>FAo(X#ll?h^S*cnTpg_uS+X*PWajQk^Pb^<83^80cTi8;- z=vo&D_~e<%{xR7YV0K%elonYo{Dj*0mmqpW$S0HiJ^GPByTrADo`9#e2hd-k5y+*6 zdlPyBUD4oXx+cmWlKn^KV{2>;703S0D!Rh(T}xL~`g$4zm;n0+fMjx)#H5$@2)$3Y!BoH#7u0E1|oCY#c@7w+CQ|qBMYV-eA8a zKl1szXSIjF#f-uvZ9{S(ko1aFe@?L?Y0x2Q&>?BSA!)!NX&@kEU8+CJjl*#=G}u2t zo;b*#EsZpPd|#q&O+hquzbgPS1SS=_<}rD{r=LDV8}?@eK;^}_B`Vj_V^F!AuAp)S zT@&T?Y5pUVK;`Xpn#RXxqUNP8jE3(#xzYUYMrjW^+obJyV#bn)Dpvc->pvYQTpvYQYpvYQQpg47B z0gjS@dA=+F$DW_=zaG9ASa%dg)ASMa9MQB6CL9p*`*goWgxr8zB4jf?1|iqb6@+Y{ zYoZ)sDnc%%uhZl#(_fI|FLB*Vx2W)uo+YmPoZl;Yf^(5mC?_d9%(=4A@b~ZY|CoCh zIJ>Iy?*E)KXYMnZon-HsNhX(bP7;^^A>m>Y!Yw;;5ibZ8TQ4Z6cuROIAoW$N?jQ&e zV;wZQQ%e61ZCXYQ?Wpljw0U*Z_!@oFI%uk?&D#z(eT|sbvBeq$<^BGiwf8yu%$!^Z z=>PwTa^{@9)?RBp>silzJ`1VpB@RxTmN+==TI}GoW3hwN=wb(_`<9fE(v~rOu3B{ke*8Ma zy;HJq`ne<%;hsKyhkN>Srv3Q~{pk+;66=rTzeZl)m`Br?-Acj)ZKWov2I=fgXC5d z4)Hecui^*#`~`)L7sub|^B3ml#y{%w2e00Zc#O7f_Ph@x+)73sU%Fmq#Be8{vgaQ< zWqrjrr~WKwal=#RR1^g8!S&u9hH0CYR%YNNi(lLC4-6v5Q4nrWRz>`dgJrHD>zBs| z6)$A#pB${T!pZON_vc5m!i6s}QHDPt)E`+Yne^7u{9DTNCF~HW@BOs|pCXY>wST2H zNy=S_RCDobCYI5sb4pUxth?L-^h|D3C6?nA6`Rbt2oUDsR5K?(L=_C}t293s{m_!L{%qtp6=Ec3Q!fr7(54 zFd+)GA6iZ-z z86WZ^yWbGwrglHuAN1199JJ-EkBbnzDKWv0pH-Vr2ZxM8l`j9?04RtITMQ5Ao@#Tt zque!)A6($ipE29}tkJ>y1oYPU?-%&jQUdPmh5qx-QQ(CWPWloVMFeTj(vr>@N@X}G zZ3*{B^YYw1r=71qNr&#=xVR@YiyM^~y+5~??>;T%SUZ*)1?g`6M8Wr|V0v-MUo^*2 z;=M<0G*t4J&7WPG<9*KFw(qD7e_Ha-AV)*IpzO~*OFB^At-h@^M@8S;BpMMXFHx(^ zH|VjVDDc8k7OT`7zqJfQ?~XrQ_7_J#Y)X9SAb%T48OmIaq1fB{@+?lbmdpVIVzQEJ z%Z{+|>6T3j31r^VCnQ@aY3At`K0!gn`Zu$K7gbKTSbtYJkLUvkdwQmcieZNf&&i<{ z^hY#jD>%%g0Ktg z4D9KqwOB1|Ieei!Xze}Fye11Tb-Pi3DGcB1&yWB6O203D(E`8yRctFQ2{0`v>Zx?P z+wR*-Ea^^F4`cgKq=X37&1XAn^oT2nweu=(g+m4|?!}+n>j&|7%l%7os6CSNc5vdBtlYl;6ibN7r?Md&QeL$Z)>=2Xf~w4dU? zJRii@o#L;g(y+5c7Em09Px0S0_r4Cz=EZKl`>|2Bhi790DNqWqbgKWlE@$*SxJ(2o z#t?jZyz^B5Y=HHXQ~g&1{>umb`#Rvl*p=(V6ftJzn%zC+Zcg7R=ILv@9PqY5SDwmU;h5w;Ggr$b`k%6=ectHX z{tJy-;_X6^mbO*IKQ$KLbejLt_=~6db8vO;yU-aXAYE#)EH~MM6htt9*}f*#X@MD# zTsVPdhrx)qD<(kFYep>W)+6yLmfrPcjlejSg%OJcviRRg=V~TdRMW3+xA$=}E1kxv_KXa8La}a1} z9zssGZ0n^(YjS_BCiBs)^~nj}rccQHus-qgA`EWVpKdQ6(x1upN0+V1{gu9(hXPBc z`VsZGJC^|H}90vdA@#mNO|Cm2DzGnpnnhMy88xui!pft}JZ-1)q zu;YK{vs8}%c7=aQ*Ajlh$SqTCYdp5xABrD3%kL+n>;F8M~>>U4M za;x8euD`me4N2(l&heKO|3*u=dX>MdiJ;zi`MLgp`YQ!U(w~cE^e6Hxe%-lge}(cK zI5*RKIX~RC%Af7YB0%_^x65|>jzwD1Ib=GoTM$Bd^Yburl+R#((t{CTy#HZ6b}Y6Y z#H=*vfjWCSJ$SNhDVvd4XeT)6wFm@LUtN%pu-LG5c&W$>c3Yxl`He^(?@L`sc}W2U z$7zjN;Ci6t#vURjjoHRmt@K~mf)I3P>5O=ICA=W%l#CLtS zZ9)9Qb^e0<)`#=Y_dD`~L-#Zl&T~V#o zLh~6kzlRuC2|Rdm=TvkdB)=^8RuYgxsAlQfiJbHc-M-&Ezj@otF-YNs*>MOYL8!inLjhyKgWqy<9(G`Wn)77`YJQiG!~^!n$IX9 z0_P~Znlbt08RU?s8!t16WH(*fGNtn!p{i;FGWVUIjU1G4s|*O?kok#UW%2=ZEAbkW zg+no9=CQ}76}I8A=TlnAA$?*^Jg84(nh8B0=|xI5Ix@k1MHYK-PT~^Y9$$L7U%qr~ zE?^B$1DanWh{}Os_uPt^vS0%0jvztDvANLwTIg+Wg`4ntH5J5n$Dh93AE3tZH!kaMiA zhL&;VjWonW+FhPOhoPjy$X9uNchX@?w_z?9ne<}UeyCf^3%7Q!zmEA7y>AGIhqJvq zliu69E5bmim+Os-3tlqKjJpjlRf+oq!+h#&*sO-T;Y~AN=+BNul2;p>S(ygZZ11k+ z#7t*4(p@%k!=iR}o31fteUmxv$<8rd;oiCx-XJe40l=9#&SO&GfK8GRpt?!!iNE|p zzwdX)voEgBa9`b<9qh)Lp@!%<0Wid0c_9kme!ZIrR*<>q?J<-+&~0TY!OC<~6dE(Z zrf56&%QmkKB5dYyee!HlpGVv*`RgEmRj2t>13(_>79kRqXa<7bH{-9r$R9lGkY`5_ zGAH>{^rL{*_H<{h0;nipIEi6{e8Bu1-}6M@oY_TBnMsXQf_t5yLreU^7yI+`E%DWl zu)i$?=~LBJM~~nRtQ`D+aEo%D`GEt`p&y#VUvM->gfXnsHigm%5IEE&wk9RPkI#VX zawmkT7)p7!+hty_Vz(c3M@g5F>~2qd{Y(9YFaK5-^Fl-0i0LcWb%Wgrc1##5&Z^8t z)FcS#>y-7^stMMuzeDREAd%tSFYy;09eT&=J*TNaBJp3o#9tV{>HU2(FcAu5@%dLm z`wa*jo96-upqH7Jkp2YYA>gJ9^rQsLQS{48p)-^~2a;WYF=M-GL7YZ+MZDHUuG;iE z<6m9r_r37Bn`8WmEB$hmPGpl!UvG<4B6}oRMQ~c66dBKV`Q^jWA5#?a^+Xh1ZLlJkDK5F}go860@blxg&=6pLyv_W*2u+1OF(6>sB83v)` z>=McPUl71$xgFfA0WiedcNp(vq~VTAQRKgIc2)LK zsRMpc9TanbnK*EhmT;^2iWtwWV^==xp?C{vPF!Lp+Al)9DBcCydA2n~8-(?eX|XVx8b}65^J+>ZJS-{qZzZ)D zH1*XnwRMi$FAR`f=hn_uO?$X+v5e`KebfUdAsKJ6A1i*?!d)XWl(B?xY(Q__ zY9J|ke`g$U&wx@X6xQX+E0yjTi8)vw9*Gc)%%Q8jpG?2(37kxNjJZffvEAIqw6vRJ z1OV1)D& z&4mXc8v_QN{tEf<5@BR7uY#>leHXsf+PhFyRY(O5r#Cv*LXcQacp)dbj_6-4@Y zg*ZrNU>lB0U`|Ee?Y)g4TZ2YaDBArLqP2P}q_G93h;;7n)CHt)yb5j^MaK=*_ch$A zDX5~CjQ1-XG~CZSs5e9ZuQ_3{zJJsh*XSr^ir4J#=f=t-Cyb<}4u~4eU*4>457*^! zkUL7TTg#nOe}?*%DYSZoAbu!+9R!Q$3^@Ebq+i0jqws^I2jSj10SitfK0Md5Uorp3 z`!Rr+^@J~z{i=Rd?5tsDqR%$q*PiON4=RBvAp6pXkev`tHfO;)hCi zELzZ^&;)lKPY{0#goKY9D|3z2uvp7`0Ll1H7x}l2za}@V5w4Va$i^7%nX^8%{qQFy zQ1T3r4y`noORo{)PU9(R8oENbU1Ic~PC8V36F zNDjnFb*X2#x6@xNAajZxeky=SYn2-*QLBe@-rlRO(uIKt}qf~=-O&iVHH+zKelh18S9&}rLK#ZbIvhN00^ z6a{sspy)Hg(4H)Y=8un|(c=jLM!Ks3!eTi9Lnj0f?F-qWNAY=yNA#&@Mt>Uch9jas zQs?(X*eN^YHhspYf68kmfx2Uxaj$MfR%;5+LX`F(%`Uj+4wKao$qe(NV-aVEdQ%2s z7}-mkYQmOT9;%E&LK41br1{+CH**~X$$u8?cwfz$wuxC4%)8+IePm2=b8Svfc^Q6j z1SpALB$Iu{b4?}L25QcBL??WeUivu zHrNRf!D*9JV7oCqdCA19Qfh+2ZwwC&{n*qbN*JEuurWNULm-cWu{Bm_vNCM2x);;e z@l2MHl?)<9PI975F=G<^jcTQE2D9>@8*F>FTby85c2qa$B!d#HD7+SlV9SStBv;1c z3@XDkzdgIPPUnwX)58R!^pv8N4`8!ODeHZlB?0?69~FP;K;o6wPB*1AvJkCRZ78Ur z*_5SjVj3C_Kwq~h3Uq1rzL(NxYx-rZHLBsGduLPecF~Qf$qAn1zV0@45MMJG{_$cU_gLHOJM9hS4!Bzv|B6`H zbOP6>vD@v=RwpBd4@Z(Ad3^=5_6})dyIR-B*FEOXLu@UY@h{xbs*L?I#8|u)e~h=L zM!>OFLc23=P93w7On@y(Q2SzM0xa?SeiHuGnE=t7WdUr@S^!a~l_+&EjLb79nP-~S zEw_y*47HO9kd)2*U9|v?v^mq#2(YTR01n$P4J?3LjS(v`tbhx3=C5KH<*FvY(FCmW zvUi0uZVrpi7)ie2>V9CYxjP?jCY^e9=XR#um`A4N!Cd&keE6Yk%Q0(to%VQ>l1_z{ zaM3kYg=uqZ*6(JmhAcIeA=Rc{5&6#e?ukae(6E{EQJ@rhN@7knaai$OmTL!wIPphv<|K|A;~O zLe?(tA57jmJgqnCObf#`EsHOFi(h_&OCF}^s+zN7lG5$T^vV3i#O-nT;k@^QpLpAZNf}h)wga}wKd?r(wge4S%hkkC8`oDmLtinspK89|z&YHUg|@AQ zLNJCJfawr{4OM7QHVb=REe}=E`)Zuu;18U#NuyTXl^Zpl6*|=%n%8^(Xt=s1Ubn$t zL}&rpkitu9SpO=)Za>k6z`iQ68Ep3#G}tRO7^p%0%Lcu93JY%3d)#`zBJJI(-WNIJc zqu!&|`8I?mPv58Vx>Vy{}4pf3qRrcnpD0y1zD601TZrS^t-&{cmTNrehd zZ?x`LrrihGDl8 z<1|x%ddPaeDDCYv>djNmSiTWL?6>YORHR?g`=@I3*9+7`)#RpO(71Krxg&)CJbkX# zcTc0f(H`r3xzYzWhnrE&6QmZa% zRaSCUYYyL5auE%4ss+nR)!=%|PDR@ga+a;nfD_EQn8T==BV7{xp5J0{-YSxnONuBFRFD&zVw6H2dN z8K_oXGrAF>V6ZLARS4P+X(>z-r99=80|zI9h%W z2nuT&k#5g|j1klBi8djn?~mJ;p-WrUKt>OpPWpq%XCpLUJAgG*dC%=0k3asIciqAPw}n zZVt4&WNJIIV%kt8nh_6N9qa`f4A(=O)$C@`-v6s0peg=^y$G~=Wr1k4a+lj))q=TYYQ!navZ+P zpEv6;U=PT)|Dk3>AXXp0?Oj}=dk@gs*vuPhI7t7taLBsxegcWrFva8{lV1pDvUy?Q z2S5mmnvzD)e0Ih6zZ)igAdm230WiWbc8OxWU<}c#`XnY~lh&hHD}8QGUeac@y$d9K zyvJTPR4~^HLs2cTYU;X$lp7%muTGVi9N%of$*sZ5^P1)~;HfRf=yvcr$Y-g^M?l!IG$ZUu~I zeQv*hFJe+Ap(s;N&n<6X+u%SE?`Q{#jj0}}W1gqJ2qM6)7uTb}b0Y@gu|+kE4|BN$ zJ%k$twh|H(@u?;hGZu+CSb!89;5U)1;T|M0{SD32?}x0-(xyYjRZSc5&snu!UPsYd zY8iWv0_ju_ z*S^p1qar5vN@zz#M5B4wr1Y}*?)Uj~2QcE0i3B$bHXNZ-uLK3EGOWpMiNE|l5)aV3 z)76I^3M?pUkS%PI9m8HfrRKJiC z{kev3IvX`KNp?0rpagSpGv%Cc{1&OnXa}BIW6&u=U3{BA5NSExTms-2ki#JV63p=Y zO%YvfCuzGlPwP~+yo0Kj{GFV?!mtt!fx!8~+NQPOHC;DUiiV3Lc6PM0s)8#bg27FG z!GwA);MMZM!gVPh!g&@Y+m%WnTE3%XR0(nQkWqlablu}&hKWQ{S*72qG6{ddxdl|_ z(h@%U6ZG1&7@;O*luTLU-KzF8w37@J)TDXH_p48+x+EhrntOaA+bG`#GU2Ay%4*9# z;f!jWz$!nOFWe9?=%9+^?8->9_YX4@;~)ONYSRJDKy6k(pa%J1uB%)VeS}u#^?ASNi-Bc*knahZ6t^!t_*oN`cwSc_6owTGu3}CLrD3 zIxf+#(k$V)wKO4P54ZfQLdLkgtWPgfP4r|u?;n$~_S<8sESgS3;5Isq+ETEE6@3C} zrz%=Zv>*+NCyK?HZv`AW&{FOwMQ!F>5d;r{*az)+;#aZe=1mdK%a20#BszQlYAq(GQDMBY9N*;sGi!SZ%wYQl~ydak}Nu} zMH|h#w3`=%6K>1GVa0YE?c=>EQ6QbXk-!``=MxO0lr4Kvzt2nq-$X_#v_|qxbccso zqd6x6w%K`13N3tj24(BsU=4Wr*lz?g~ z-fuE55Jhh7ura$oW6YB0{m8IoZ~X9Gus^w{GDytg@JeMCM?$2waH}=4!!V=(rBveH zY9q{06ZxTPYAt2xGVf3W0#M}Hf-}`Awvn-w=T@=(wiMe;F%s643dqwG+Xw>m|D|F( z7p{5?#kTJfs@|Z?3jQJ3P(zySb$DQ{@NK4B2~HbvGvphMfAv9#s!4o~>?84ESWko} zE;}X?pR+$4DLTnxvRE!knRfrQgNaC7Ok|iA!fs3`hSJy>>U1(H`lX#MlTn@M+=SFz zTvPPAn_yBc5{r4=M#6D$d6@Jj}-AuHb8LeA9>gCgmx$ZgTJ8|B<7*b|M(CW$!1H@ zAje!DaKMN~IY}#wDE01A%Yeed98?+!>~oNooxt8-6WB>WCY>S`*vna93{(a7rc#-C zl?HB(U%A;|csV)BL>;+&X1kQi%?RMBh+C7(na>=zWz*#H_{*F9U>=+{k;0vgC4C#T zur;2f1;yY1ghT@-fb=66^>d-6LcmtLB;mCTjAbUr2R?E&)CJCTe)Fo6k_7IQ1+?y! zCU(hXBf-AH|C*q@Bn^-rVQ!0{T&Q361n4Y$EwE&VPigJ$~H#5&yLL=r$ZVvt{AF z&zI-o)`sVCk#fHGNc@_Qkj@(M4Rr&-UPZRkg!YASH(zuON)KyYuyG}_W+z%Xdn>_I z3{@Jvh}mc>mCH)z{M_i#x$6@1>S0BeHF=L&lW)ggk1(V=uhC^oi;=}O$%l^yZQiYn zd#V#`qVaxG)5K?u1&bmwjZeGPsHivHYOHjoCPKm;Ci|0^zeD|KW9ADu*pGETV<2Rj z77|Tnhtxo5>45l7@JKa#1EEM&17YL}KiR88g%=|(J>*Svt;^{O8vJ#DIw^RoEbLd= z02KC%RZhPg?w2M>BZ7aEMuh5;=*pc+3%)t1`)4RZ6S{n6xiBzg3547#l0X*Goh%p~ z-Q>C8s?r>a4lBx7xBlgyly&cj;%O1JCX(+5^r_%Yn})r#+gbZ~7lE5L6}{k)#^$YC z5$EdWp&-}1n?=MlF!0fn@YZAz!vV8^^aIRfv7Ib5g^0DIEQ?epdn+Rh2P$WpS>s3& zXk?4zT0fhftdlahs+ECyqA;x)@%bP1XK>;Dg&+0jE+o#;7KL|$d}=+Ma_+pN8~l(7 zVv34aY{CDHd;%o7x~JvWx6D~f2I^C|d%g=35-90njsPHWg$B^a)&2>;8&t|Qgp^Wkd?#J2G4JWco z3N0syP*VY;czKYPm7at3IkK%OlOsf7wrYTQ0i7Uf+TG>gu>a9*raqr&r`}+i6BWT0Vcv1Z51!CFmokRNKyg73UzXo zy`YJEqo4c)DQ&(SKe5F>?JevlC}m1bx8+R$Jw*>51n866a3aw0^k>41*nYiDmw3sF zprVy7D+23=@Jz{z&~4Noi6j2d$NjmzhMY|baSfxX2#Sf_A%kUB=t869@|+HmG)jd? zxSM%I6nBuw)Xvc+yFNM@=t{n>K-k&jc80V`Hf_5OwPEQ9-^Uph;lH%hUt^Gou85+*;{y zz~_oWZ6i8tBW!1y!-rDr=VFmzf48Qf^b~H#yDF%Iwh=waM(7;4ZHPSFmps-ioOr_* z0^%gk=roZ?#dcnJ;kKRQz7e2Gb+ui$y_#}vXd_af`^X6`70OrhpJ;!Saf%c4QZCEu z)5sAA%Bin43egb##R1(JYIwl?ZtvL#6Xl zG-_I^@^DN1`mO#sk&!ctV{!MnTeU3F@1l(ToB@3tvR@3@4tXMr30>gb3@P?vq$EAh zZssW{96g0-*xhI4CZg~vKGAhzlHy=Sr;OK(^y+=7lbxMaoQKPs+n0pdfXFhaZG?w} zM{r$d{Gkv!cS=(;<*I_6U4yiCFHvteP#n^Hc<7O>eNot+*xKMJa5e82iAck@@X(L9 z)`D^Dd3)XSyFPn2Vo%avb}gN!jTjmACrLp;s8(~V5+MZ2V_Y-8sxw#eK8i%fAHTML zZWE@Fc<9>x1y>{pXzn_YEA^GyGLWkc86EGbM!;|wQ*XaWxWS$Tbys~}DXfJ7f_Ogs zw0LTp3<4>{o3!CaWnn2JfKNITiqJ+(p~k=tdkHr%nFETcd!}NIc83RKxPWm#<#;($ zOM-@l4ya@)c4xUT>=s=%%da~NlzT6C7%Pll{m=d>S3X~MPz1UuNg8t7xI`)Jw{UU$ zyB03qBqtF9@-cw80Fk-)ucdRa511_^+-cGuPsHc1iGjwm%f9=w>|@+yx-J8?RT;Z2CMRCgLE3ytv&73S1i_ z(5jlNp~rGHjJjM6+k^7in&MbbWhRt|XmSB%SC2;~E~l3}{O9s|nd&6vS@K6@{~?$y zc5;=7C$8cp5J-qI7{;BRUl~1|2wbj{1Coh({r*6#kUElqtI@D8VCW>OXFRC%rExlE zTviE6uzu+y{3ghu`*xN3lDi7Xmb`6yzUtu*^U>Q&r`}RN73X6AE#-bo?7Ql+l7CAX zYhMef>Onf0gn6mt@-a1mW%qbLw$rq2R;3AAZ|FA+14A@`!qE)+?LEe2^DdKijfjw4(L)>6D-3`JvOk|=YZHq|!H$ek8 zVwd?;DuL-=k(__zmPw@7%G|61Hq7qvLn)|fV8gbB+p=3obVhZbY+)cr!bd91!GbkY zPBJIqB-%(g$Bj9i;#gf|fEZxS5Br_J$B=Y@)V3jsKke(zUXMqiLNP z&ep=`QG-wGm%=QPxYP5rk_Wy+99||w$gL#9$qXHco?$?EnKPxvcU{{*Smj<(Y1KuN znq>h}{HAkuH(>$z_bO8XwZdVW>pf986qQ6mE95IlbFxI+G;3O6Qs^a`!mSRXxY#$&JntIZW@%BlFK$?{bVv zLa#1^{pW~3)(KRz=ez5ooK?bt2ZI1(l+0k8`mYX)5L z*nW5{=x%thySGfGIAok&mt$;4FI<4{qb7Vt|mcM=TZ0j3bPgEVDx+_vnpT7abrK}x!}Uk#=o(l?Zl`vwwvCQM+1_Var< zJ}_O%->nB#dbUU10X}9egik#JOyOVO&QbE~X!CwJZnX~f4)uZxj;aG2XQ>2`V#y>> z<`y(dC-N8E`7kWNc-s^fN&{%zr*9~oUYff> zo{#m)beBp>a7RZ{aW6QH7=Yb22SrH!2Gl#AN|P9rYHEL%xLJ zfB3Y%`7;MfB-~y|2?>q2zlWj;zaGJVxKt+Xg0p#1(p~DqX_$ZJu78pJUXZQG9@`@> z?+R4TX(B5XE^MaF9|8gS4Xrhu?1sYGy6AgK^`fs#6e0kUX1h7ycp+h_bBQ=Zw8zb_ zTe@?@Zkf(VYoedmIb&H<7Y1laUxf{B@>~JzOk_*Q(bLpt$3CM+FtHi&XHM@6T21yu zA71bv7sSYzBHzq+d$}gp?!ql7dsAM(|L2vkJBPVMR1Ssy*pY~8pys>8P6)+!brQFl zz)4&wnShZxo#d-Cv^5Xa5!}oDm`%=(4YuTrLEh zkiR68j)CV^xu9XTAOa06c>>~xk|&gwxwJsy%1RcLW)EV*P%%^9y2~?33sr7sg)Dcb zmRkyt<+XkmD{p(c-c>l*1QNgj8nglkWmR$}1jVkZHKCwx{aV0K9=V*)E&0m4scT;@ z(N9!iWIz=is${{41Z07e$%fU$oM%QBIKrX|B1%RVz9be~no1WE7W+Jew!I<@Ku=^v zgh9kcG8mLagu!J#07F$x)D}NC{`7VNkdIH+7SP?1wM8}Rl`Achzh$Z+!$!5yY~)4Z z$DYL3JvepVHF_2qL1W%^1Z~0b2paZ49468*h@(X6QxZG&qgC9PsM~%bHrr5*=22g| zn%&Cwi#I{z1w86VG4)!;0)wkeS&x!B|3n3NBAzpNv%fS0 zYiEm+c9%NcLFP{mqw@c?@Mg!nP&j`XfAO!l3m=HwHrasdRY8PNM8KMi*kb?95jYOH z3QT_)Bz)40sY)(~&gE_+6UZ(PtMjjhs}(~HM3rmK8DL2!Cj+b{lS})UFO{~DYkVMS zEa2~F7Ke3of#{*R)kOohxwsF#oy{o|u__->Qh7&GLnA7yGcGvLKOUEio?pj~A7%Rr2GKSRSPB)ey2Irbp>em=erE_e{ z&T6!A*uv^$#K@9~RY57iq6$H!6Ju}B(!giAZP9LF&cKHZ`PbI)$gCJj#HL46@C4FT z8xGNnd&-P}@+^rBjF?aTJ27@pG9+`%d=f3yGRHXS#U+JEQadDRCY-dhpsw$N((_KV zF9RwapN2(yX)m2yIunE9pPNEm%TINbfwg*4Q0b04-mY2#5FpQ!qjX}ax(n|rMQFhr zVYP1j@GPPYT1sb5&oFhAchPSXwBCT`xjIdo^UR0QH(^YGgU-`jBwTD34Mp^puOi%p z5DR-SMyekVKn=p^K(BtycOsXjxF;f{Aj3}Y-mVJuKT@gbm@9OAUxyVen2|7ji9_G* zm98@H)>}%;k($BKosQz%*~*76?7^6+@>({t3HaVB~Dri2>T4474(4n4qsf z4bV>cCdC6y4w02R^CvR11f9+@CFUVSse=%=nNA9?(|N_oQt~cK&*yFV%N0n`O8C~K zc&e31=@W)It4Z1pX`9~`YJi0T<$(Sgh(Go@|E%cn4COvx0pUSJ$D+4U)ON^BgO-Mb zi4?q_b(KH>5=!*QH6KdF8KJejY!{3-lxwy>5$~KC5bUs`&gSpkDf{8kT)jv{ z5CQ4sz^t_Dfv^-V#LCHj6)qb{n3j1B*)eu*piI&?LNAzpGy^keCVsvarGXTHrO%XP zi=VH}TL^FhlXfEiDupFAd9J9Nxh9!|a_yTe*i6D2B@wofQ0&dh#6#_5#u29rj_p(y zcN}%ETyeINJ%lFc2y!B|7m941x|V%13BYbGd;+V*l(j@BxES-fz~K)mM+pEvJB6xT z@(!xfvukr{L$Gf+7ixTD<`Vz#VPd*exUykmjL(_Ix$-K&BBzkJ>L#*vz^mscGm$Ib z&17t@2_;HYTW!ab(}oA#lpz=*WZ=WZ`MsjiS%+}F*Bu=&<8{y3=9>wG@&)l{cKZD- zdKn4$zrB;v;Yz^2s3%SF?&6Q6@scW5T5hq_jd>f|7k}mp{$Q)}x#P96FQ53LU&KI% z7RQtPjQD{s_^*s4s1d#+!JPc+Xmb0tqZogT$Oy?RAdWmQB|iUN4^nL0B~n&6o7nf;vQ0l zdAryX<_AcyS%MWwktqxSU7m>l*B2@9t{a4+PmftTS=a8&a#fQp3hL1^OkK=yh?aqmn?eX z6(1IF8K=1;DVz99f!aCmKa;tPcZt7Tbdk!6vj*1+sm(pleuiPxm>7T``S|;IZhh>qPJOn5Aml#mRL@9 z%KLfJ?@+teaDn=bW698djJ1=_qUb#-abQo{jZUoPD*fjgK=5Bl_dVAAe0ASUQ-N*B zR()SPiQgRyMK94my&HA8y%wByTRxMvoq!9>GBi?1TIAh9>*e`9I?PRBHxZZZP6y}I zTe}wT7qu3Jqy@r)(FIzt)+CV$?Z(+Zv4wk)r+X_dGYhi>j7_AT=Eb%#AK>9%!l zZyzkIZLrk({1|d*oQxX)DwoLvGW4IUFmqojyFe+@)AD0*k~6tL5+jOviqhx+C=ydM zUyF+lkdIFbmP-=@FqJW&c(-&L?Re%e zX8m;_e(mHg3_)i$mJb`k7#Szy)vUsJqTdYc9Yn7>lYwyX* zOn}qEB!$WrhS@e3)d%jMLrUP9Iy4NMH!qA;ks? zq3xVuPvm?SiimiC9iYfevn`#rJa1uZa4^(%^Y2@fWGk{y6-TJX*3sP&KRV{mMuRz? zoD=tc1)J@7pW#~~);fBoD?pfH1lExbw{R0_4XgrRb^RkXuo}4S7NuMq28bE5ZYZEt z8|h?J%XQj?j4H7nh(<1kPV56&)O;B%P;#mXV~80Re9JQnzE#WvJ40)o$QCa9-IGkv zB-IuSKA2nxdtgK-T+FZ-D3zcwZlLF1VSfJ!eItl6X7)D(8V> z-U)1Mx)J1&8l66lZ~m$ubdchgqp`84G%FtZD&>GOLKvkR4NDOVr!N+&Ljnm%M5}M^5--Tm_;hze{rl z-wN6lSq`)vLP1vc91s5#N)FRH{_s!YCbar{R4TRldx+oA0&>qHC?ZYD*e34`pNM;H zx88Do{{LW=G_RnpM;W<*hE53v(ebw!ZMZ#_L$ua7&)qI==Hw3Yku{#sB;yHPc4#>@Sk>86oxw6D|+7RoXfI zC^foZwZ>S*M4N0x;USKZh@Dxvs~rxvWfMNCgJ|9%^zO$w7V;=e>UbPc=kPnZ@F$|y z+wzy;>*wdmHev}^*~m{#dr`R2L~)BDc8_F1N`H83$>bx zAeIB@*+lmkZLSk{{`GOBo%xM1WEz4*7*s4_k3KoPGPpQ<-O)tK4r$zV;X5s+iC{hK z%TlEzism>nbL8?2pmVX`D9Ji;HILbJgP&;y-+cso-)Rk3I6WBGOb~mDx+5m`Dbp|7 z4Qva(x_)K+$!q()OkfirU?yUF(**DI9AOwfX(;6>VQZEp@b#-{mRbc-0(1_OcuQ=E{faDU*kRFcciukB5Njxw51!LFxsg&oFjv(pP zT%a}eRbuY3~+mcppK+$p%jhbS&>U5oCGgP2y978 zNYucud8U^$uv^N($m@2H)DTmh)387tP`T(7bSzVQwWTx~8_@kH-O!OlgY-Z_Ba;u2 z6uqZFhiXcSW4=*+c>{g2iH?uiMP^DHl$=Qf<)-r|Air+@jTWGZZ)bH*G_{CtT5v6bkw+XK;K!%KP!}We{CI_U7bW@10b+HO=sW_g5f9anMDRx@!@{oLW>ZB)TrD zR+K591l2|p{nWn9MAZUZoy8MlM#twx#v!$hwuEnNW(f0ByNdbi9K(cz22Erh6h_2nNS0cq#G_g z8xC46E3caa7C>=S;_7-~^3GIH)Z0_`%5gp|J34Q-`XP{cqm#6=oDi&*X7N4$;9qch zl?)!=p_$E!TtPW$s976*I$r+gsXptfW-VQVRf+;WCJH!^oD74D;Z>xYw@!d-L4^7y+TA$J z9NdkIfA){uT1zEycWV~26vPsfP`5m0p-JM$YLSvp05OeMfZ7HmmYtA9_>tt?!d;G)D%ZvE;;9H?c#FKrr*KX<^td_1ece_dYG(oN)UC{AfH@wNZ#pY}3y zKak(9i3m`(@}Q(rJ|`?J#^Mf0eZzHXmp`5)V?ip)u@8NcT9`F%DG*Jw$F>{9xB0=s zNFoVF0W{9a+zsXV?p&0;Cbl00m|dDL@wJOT@aFZVK9a)aQYY+MXDeBFT+}8ayJhrq z;4@3Lx2?%t<8n?3)E0hGS~m-1#QE%>SGD4T!(SDcg5-jz37f*CqX?Vv=!AbtFV}TV zre{0w0*HWt=l${F3ID9XPzI2Ryx3cWhVh@izuXsJ@u=T(1s?zP;d^fO7OUIx{0ORq ziI?2nm<8@~LJ8jFUnh;w2k^!6ug84>noxKg&?0v6{g3+Pb-G~1&h%I2U{X=^C;#s; ze*_Vc8zla6$8$#5Wq;1&uQvNjS$f)VZ;_cTF8Mi(ODc8;#*=iH>- z9Y9u(B_-Lc5*J`PgvXXJ$STk)b7@Pw^q^mfl$J@+tyff928c`Dm?tEIeNY6y8Dk6~ z*%Ryl9GWz~#1yZ{ToU2?`zB$d}vg+mE8 z_26Cc<}t8@Ca3f=7MML)xWv0c2)-Zn&h|EY8tr@%XJ_{*B`ir?N&Kb~+q{oEL5{;s zUzcXBiL;susm6(~C7PMTbJyU^vwgkF3IavR8qnc(PAlYz-T4<8Bd%UlcnZ5eKDUSR z!}sK~c4uG03)YS&hyJ)GzCfo6f7U64EJ$+_;k6h){8@GP58)4Bhhz7WbU5@$EdO*68-(lZ znj*e+o5-0YZO_WWg~-~9xyz9q&rT9_jH2I|sH99tlXc?TIbz)2Z@LT zRT&l_itCb_8jczln6l*gySl{pomk0+k_w&eVi?GFYFRks#h;n*7sT&6(+X)|szdqG?>JuJY;(GVl6Qa4U3|r!JS$3JrB`-F1-eoRh^6t)A}UW`T7dFX-!wEJ9qRqlbD`8Pj zEvZK$zVHh`k!3I3Y6D&ezaw{~;usP{v+y#;onYhZgb(RkF82X6A<2?tf|_O_{7E5Q zOPw>(FB{In_@c+eWE#PJf03x;9t%LZ`7zLsuFoj?-#S*xUAC}E*fD{dpOK$mTB z&}rBv1rQQ~bw<4lZgy?-+UEY~-;q$JhTBY?-0Es*wTNi@vfN^&$1&E!EEuU@IicTg zM}8b<9ho-~(Y`|{J}!Q5D@84-Vrd!m-gh%Bbfv@ItI;Z2sNZl|Br*M%7^H=s4BO3M z(E9M3{{s(pWvZIZbvD9;jRhVRDa>lADJpyQGx6&Yd?9G1IVD)Dl=}AYw!-p3C=(Idmv(JDK--%(!q&)?i(EjaFKnFzx^{L&tp-B=VW~! zeER*h`m$pj_LJS$_^JQ!2YL-e_@mNuOmHiuYGncoj9iKvw1>O#^>B`L zH#c#)iFBHTEycm;dm)^7kRrG8U^&4L{Rl^=Y2C>9#w|IMrXf0>VC=(6%JbfbjY6P_ znwK1+gN;$)FH>Gx)Jr#HgzJ2Gu5fUlb4>+@ZEGZ+6Ac%&*qj?fh3GI z^X0Z#kSWWTBJ!3G&p{`_T9w?QvZn}pcHbw-Tz#@s0UPU4y(j>%jx2ob{aD`!k*$iJ zb?9?)*i$j(hE*oga-CQVe?T3M|M=(rA3h7_3orOBGT%GJd>h{onr@=}?K5BFeWeX5 zyCc>sUYKFLVpS1{YzKBKZ(CXAZDuTL%y^|BHXn`gg3)5U?)ur3VXLDOvE%4O{Qe{U ziiMJS6V8cDmEoLlLO5sq$PrS^{>CZiPks&MTyQkhM`wNRIilDfR0#8kh-FO}_#IRE zQ*fap5=`J|iLM}tmUIr3#X484FLygd8X;#&R@iA2)QxP-ZJQkko&@GGXym=3o|WOr-r?922|Fmh}B=G5N7F*#5_yR#A+g|j;Peb zPx`0ix5oeUq`$a8U9M$MvE;BK+C!;=lx=LQtI2(an7~M_Cik!!rom++R+Ia5HKA65 z?SfO{aciJreU}XFT?~xEve8`#5n}FsK-}so{W2Kod zaIS^>>gHixht)Own9XA=kE=7;R;?BtzyDWcp;y_NJXP{oVmetBp5aI&leO2WvQ9GD z5v?kn>}azhCk7?-Pw|AX)#2ZBtt^S9pwI2d=yE{dXoSmy40-oHQ6tTVAMP~_(~J;+vL_i9EV#U%irF5998*>=S)====v|H zkJ_sx^bwMjWvLgwC|_TL;Jx_U`QYqj9WO5g%{5cdpK~=_dbBfJIMWXl z2HTtS0@>GWsByCzHlROY0<$PU=q}&! zI<1%64c>$Hu0t+pgWWpV?>P2IL_#&Ysh2ipV8{FkVPs(B2eZmn*u zy4}-ov*c_rzS&^2EUBeKi)bod2#lJwA|CJTcG%FabuMFdVT<$PI4pqr+g4R!4aqem zQ1BW(4WJ4ebyy0C;0R^{5(w@3| zW_EkWwW>WM+Zcj6ldfX}&N*ZkdBXpb#Uwnx$?9ZAd;dF-gv zLT{SvWY!-jO^N8eGy#X5Z7kKO_(k&tTGXPwr@&GL!GE9BB7)J5Sw!bM;?1fFWtk4t=&0y7y*VPby*5QS zj%95hjJ%@?7zIzlFEa!vnO|!8F$yFq{^}{z#%KoHX^OAom_{qye%5vo7tM4Q;C`#( zZhTvNuppxPDNS{op1g?OUEQUe(|BC!2rhb~8^&o4+?mxed9KHDL!B2bVp&dt zJ!*nnHu9O9r&*x0Luv9B&-VWNG1N{~$0Ok3_VE~DT4}_Q6L)MErpB^4*Q#=?uxF+L z*}|X)3TnW#eH6>HwD1#5lxySG+17rgvb<@8o@hnQwl>{tME=KVS>I8#;%oc+tDfs> z%r7f`cE*Xg=QaI(=T(HWlZMomBqAJ^_i|H$*OP|VWy9-C8)-)7VN*0o`0Jea!K6J^ zd^rcD&iFNt`3p|xVa?b3tDIw+j`(20kX|91<_?bHu!ux#q zD^6YFd3JKyiK}Nzyop9DS*n$sT3yJ5Ere>P+<|G-{Ax8nv)X*WHK&v)^e5ANg?D!X zm~m?`PYw2D8Z56i*c1OrPp~xF#zWAJJRY^o?Ay8f0O}w-NNuOcDB{aVQJXSY*j6;z zVbs0T>F8V?a~k=MX&nh^Eh>3yxHIWci<}F4xm^4zA?PmR32Zz&BpQ$F+(l(BvRk|E z8iy;9klr$8R|s85ZyCE=Qo7-r=5nF1N_@`~eFGFx`GOZL>}W|Z{IqVcjS;)*|4%!7PV%VVIZ#^5hbcU@vxLbIP6Aqo+<8vo+UUi)B#mZWM_;&vj9k z7h{KmiwHa+=O0RTZ>#N|W%eD)G&rx?V8|Mf=#CiZOoP?c2Aiw_1wyg#)du6%fYKjedZxjwYJ)x2!0ITBB@H62w5Qr) z%vx9}qn(*od^1;VvC~?}%NmYmTC`Uij9LQ=qaDdKXsb3Du?FsLcoSk%3h&lxi(zYl zR~xLzyrQMrV5>DyRiSVw)1bN9V8|L+UWZMY2E}TFP1c~b^Ub_sW2Qx++G3-%umH0o z*ay>D=Bo{k6oeJ#cACsIsDWzI8kk&rDE^WkoYgzq+nkbXdnWTkIH70ftV*9u*pmW= zESR%M5qAx1;j?L`D5D1oSdOp$lQuuTAPBmnuR9e;X=Ze!Eruk~u85+kY0_zAjN)Oc z1_;)d&@jcjrnFx%%e>1-ei}qVf8-Nq#^WiUD zurH)5DK~-?X(`yCv>l?fCWB5GDQ>D7vW;R)H2hoF6Grc|zCd2tIJ}Uh~MH@$v4D zo%Qr!{Eq&B++XW*zeAN|9;=H0cCC1vUUQA$< zdhj`+i9j=z{^0wTZkdcq1O z+>|fH-|G*SMUw?VcS|1d<_Pr=ch@;p!Z9Ug{Zz+uu+n2JG^*~+C%>7gQ1l#ajk%b$ zrBbc5rL5c#qm@j9`n^bEUuKZQ7g1aJ783D3*ip`R8JpP+G*r|2Oz#bG{;%@o_;YiD z#nGV@YI#q#(1&E`6y{uUV0AjOKnAK3N zG0YY=h8aOGDa@iI%=g(4gC7fK^;rZAW}0{vW{lYgW;!<0fyTV6Kua)A#Gwx3pz#@m zn*y_iCknG@I--$~yAh&gfc6}~?9Pt@GZf(3HEse{ML;$4;{mN9%q)+1f@miWGp-0$ z5lu?aNg^5<@=p$C63E$XHunT*_%;EpzOs(NOcOsTn9aBPEYBEd8JI~EJV}^wHK3G> zYP^|m#^_9Zz0(=Xf)Y7`srS*16E1q&#q7!9h@{~1L)3ZG+QN3Kd z>0``zRx;y^>?D)HNl_TxbUW5zK_RvjGDl}+GDb`R+KIwU*PD(5GwuH|MC%iw%J&Lp z^|oB2Fq-&rVaEPXXaBl0a4eu57iQRJPVfwA{f%WOF3vRqUZX?AJ5KftMd40?uP;3$ z)Je}ABEE(H_Aei=6%_5BDOtL@Hhj4!Ng{AG+UlZ2G31*upXYda;kUKA-gI>q16(DN zd<&!c<<01lx#%R(U8!@9gYH7CI?Xrwc-^J86A(iFRV!*Bgvg+GTy&p??GbsVl;r1G zp}S0lCkeC2KM9~cW0*a#1(?;B*D(?ibaMWlInaO^(|&d^^E-1uZCRAtBNMpG?=B6C z1F#qRif6Am+?!EE<8Jg=vu+UIx<6^TPoK}q*m6R)i!o{R4V2QO8C#C4l*%Cq#vLpm z|CUhU%{qfDcOYH)K2t$uqse==@+{S<t;C*%iF&CXp$F_k&X*+3c-S5g7HqZ&E8 zR0>64n~Eq&x`}6@m?EM4N{6*7gXtPkO-AEec>9ZlnN{km2E{6UQiFT-yeh)9D(o6- zTdDKCW^Jn!2P$0_UfA~DN_Sjd6wD@3XjiHGcOQDsx;+28z0!SEsrvyJ)F2ghNane& ziwc|Nh31CgmOPMBk}P;yxS4`9f7b47yll@_wVkmp_ez>IVH#xQ{H99RyZF5L&UdX_ z``!|n3w~$dfem5vJ0vfw$XIzrp6k}QzLhVi)28NKqsW;~?5r3u@?6pcku6uM1nwE{(JI2Y~%a8e(KLDZsZA0@^_9A5(VE_Fdd z+{tIyu_iZ0*DA1g;(`U(X8YX<;QzS0OKt}B+p2y&s|#DyuYFA}{*xua%=SNPSJAV4 zS7*Fq37#xLjK@h=c@Gi|UVefqO#`7DnLQdRRwk4u$|z(=rlM(9540F3Dn@2!n_~8m z2O^mT_X29O;B6;73xta*Wd(K(XThpuZ%nOyQ)VG2MluVzE2^+~CbJMar#!LYEW|jN z#?faXV|_9U3%g8>($8cT+W(`>g1ErM^m)u#2)&Y7;6MzF?b>`-4lC zQ8==fTh>iugd63q?9R;%x7+vd`kTYGlpLBHj=JwR-5g$T-*@mm9NzNqFHZ@s&G+oi zzIxY|_>F_XnbFiAlkv#Nh&5nwuJfrvp%~@ghpS|Q%UmtEN?JmdD+*h6!BR;bP^ecH zd_Le^9v}JTx#sh8-TG@;j>1rDa)dpaYq>&0B@dB&xT>jN2IB}@eQN=CyK$e+2gE4rO5tkI%EAlyz3NGMG5uS@#dtt2;dAe+ zAzsS2?u|I=3XyJEp1&5V3#PP`81P&^7-|L0co&cPV+GwW;PZm@b!KMfDIB?mHsIw| z@#~fbexw22B$PYllH|NyG02hQnsZFu!Cjz@V&}pQT09|yVu=}E5m&r;7>5_f@A0%x9nD6RKC zZ>M2JHW)~687wTu=>*1~5zk*147O9zmG0h`it%mz3;g)Oee-$%F~T0S`oTI{U4(KV zpBd%ATBwxcp3IU&l&bThv&Gu^ zcrh;!3lYgHlIm-H_;YP=#za*VowZ=YVu=w;bO4j*xp$pm-RIGLkRJwy1wXSv>;!*^c>%gGTDw6nSe84* zdn6UwFQptRKsi6(r8})fE2%W~W!h$5_6mC$T9X)_VMM7p6H1Vm29*=Obn#JKlM_A> zVhkn%FvT$#+VDUd%!4zmgBu&MvB=$)=?i&_8~Z&rHrbe0!dpm`LQ5OF*$5q+0clNZ z(0dq7)U``2tU8}I=5w`X;$$$J7nrgpcfFg;4cXQLq9+$wvH+XzwL0TP7;2)(6~T&! z-*tBIs(kk*xewaJ;CsAH@x(d7efbZ@?>#s8Z~439pPU=~ui5v)4q#`tNsG1l-Us7v zJ}>xC{_clgzA|`aKL7LhuU7?629qQ%5oS^5NU0eq?@{pnHvaPJpfCS-@oW9&h57e9 z{EOAWCky#qaeRL8zw^5u{(}pGf64Rt%8P<;>vQSa;OqIqcOQ=@!yuUPi$pckQttX} zJbPVG$v+m~^N+KF{Dbj}*9GV2KO1ja7i`M^B%b;FV4yJef%wel2MhC`j<0-v@RIyJ z@qNz^&Rn?t_Ky-5ke@l($aX3om`Sv2O;e;+TrB$#{l z)Zg#=?A>Nwo!i;@@w~~K+8gf&ZF9+L#_{t$7jL*E_(<`~?VT@-Z(e`O?EF|f>(XFl z{*L&HOIgFi@r{=T*J-+cducG3-yZ+hrNNa<_tMLP#mRKviTDkkR3HxCc4lm+F3)ry zzU#7JSw6ov{`}>^tMhvwp7{a+bnnBTd13H^LVvmiFLm_*I!SU4R5 z2B+~ca%FH1FFkN&@OD0~dTH=p_c0Lv_@%+`<^Ss8@T%aE0u4X^yJRCe`(=9m@aW6v z#BT?$3AXU{T`y-FU6cQZuh;xuaCRR2yXzIf&4oRO<6piac;i`{+hs!$-O_||9^X0c zb>Gn+GvDK(S5nA#bNroG25a&|ardi&4f$`z?|N16#{9S9e|}Z)yZLWDyz9*C*#vz6D*whCGPRsiM87!P2uO` zSG^{7|2V$+y5Q>hkHPble`VZa&mbL! z-QjNK&A2=M=j(!U{!iol^}*u&uj0`qbN%?mw|cYU7hewu4#l@#ADlAtQL?1od?`tb zY`8Y>(fHHX2lLs9Z(bj~bC#wQszkNr=-=B91$f!(f_L!vTdxaNb$k@ev5Vh7i925( zoS8oyuX#OCefr^>ULSaQzTWnR;QZMW)g-7;IS{(r`$7EeHv}(*&Mdqkn3LZWpLav> zdL7#vg4$YG)B8#Mv#HYU9V&zi zZw_0P{fx3_fJrU3t3@}P`R=!{vLD9}z9l%b@KZ1T!CQiX%iKV>9Dk$=sRO7_s5ea? zB|iVH!9~3K-ER#(Nao47c|G`YFg|TP5PdBEz4gIs3qSMXht~%SHSqrA^NIDr>Sh07 zOM;pdE<5Th+-f?#zldMEfn)jO_>VWZC46%OXY5dXctg-1)?gFv;R1YujNbz-1)e>v zTEN~`+mCb!EtkVV*;SgidEbwJ@P|P;{^X58a{vQs^Y(XW^BG&Gh_CU+8-ueo{~b35 z>lpPHHydEq`9 zhhui43!&ZEtpsOF*&Q2=PB68 zu-M7maIogqaI$rMID9aE-U3soSCJ+A6%?ut^fVtHTwMK z?+2^<5@_<`aF}v7$&^Rq{7yswm zf~7D2X+l?qu`J}eh#t3N;PCS@()1*6Oc0$6%ho{nx=V1`)j>@@pq>zy9;if{_rv(+ zn}Q(!@%XNrg7dn$qmZZNM29W$Z*K|)YD*l~J6Ph8n}X9>;^Lc)so!#QaPl`rZk(-0w!Y{hxAKx65{ZY|RWb^$>ggd?fv5YYn+ydL&62JHsR`<9U-*`*#p8OZ$ zw|y$OZABqTEmeJC(6j&(d@DD+x{TfR`^0be%)ULmlp0NN%=d1 z-WmU14bLv(9FBh-1-P3L~XT zv-iFDZGRZ_p8oyLfP~(q}EzWwB9=GFV|J@@o;?mZdxk1?4Z^53e$ z3s|i1KL+6u)fWaX29`ii4g{i}pqT?9N57&^21?B^Wssg8;g{yE2k9x$ZSEPQ{{{SO zJ?>m5k-i@U0XdP<2IJL<)Mqe2GLe=JhNd}}z8nm#OHQ*j8C^GghRFyorKSx!l=}1DK-s;=-Kr)8O)ooAjHB3gtYdsERSKw2BI_I&IzzY%%MEl zsLo3zL-d058IsxVP4Fpq6w9^v>F@Jc7o@7|t+XB1bW zi-w_Ov6-hL5Z3ii#jdzNG8JP=^pW}1OaoXL4^av?BvJrbP{9a2jt6-A2)$;69{|N8pskIj zX(OO-mD8FLdPjit>Il7d@&&)hSsusJiWr$+#vCT|Wj%#?_X98M$vLt=s3Pi+HI1SE zv*?MEs?s#x{E-FK>ontKXk#bn{g)v?j?lT6bx-OMNQZpeWsQ9Ycnl?i;sWndUa6kq z8iBTI*-PUB0`bvOJt5sM!C2yz13rv}9BN~piyloW)x9Fn@$s_bFjy>=aeJxWJsIK( zeu+fva?PG}A0iBF1h}-R)UXV6`7zyJ28leLW|ryofdf0rAeMHpSPJpZVp&_bUo1IU zEJc{x;B1yxv9<$j+zGMNaU|>A^wLO(Ik4avBlTBmW72~Qv)R9}4_^G?T*=eUNDpHn zQjRAKZ1kZ~dMb>umqx)Tdygtc=^4sq`eKyc8AeLRXnh{~wso|AbIxz0(KkmZ2bTeX zDjfVA)gPl9IaQfNNL~^Mj|U+KMRQ-#b!SkxmW|OzwnDd~0{lh7TZ6d8qAl!I>+iP< z^lag!otKYBn8(>_!<4M1o@1f3E~9~C^{$V<9q$WzTB?j6P8TV@A<-d3{xz5&<%3{y z_`e@vA|Q+pV(jo=ze9D$>D|>|<7v=15cV>9bDX{l02@4B@6`3easVt)>Qd4z_V&b5 zhfvxsTY+byssa^@%c?vxWd+4d&_$Qf_ZISdkyYQrx;E`+R3gm zJ%`>U-BV))_dc*J$PXDT9;`Cz?JH>cB&o8romSb~Ba`$AErJ{ky#+;v^frjt&=2H{ zNpw4am2_}jYny55WPJ~q&d@3PQ0~$6nR;_-GgWsKea}5gtArvbub@M89b#PHNqDTyLsbS6W|nhD1K@0xG`$~6it zg!{O@Hnyl_Rj=Q4xR(yo?&*?!fkpTz`xV{uz(^KXA$)@CEnuUr5Y!AeSMxI=Xde#a zyb_e*{{nTvG^G9-1;+R8E0#WruAL0`MPVdRr!|N%=%fBlYBTi7HG*&rb{^1`s0$fl z!PyPe{dk7n7cP@#Gog&haivhvOkm(znmSW&)3}`b98z5@gg5{0hdFvpf1~q48XY)C&*E>+oufaf9G_Z#g9=%@9HxI6 z^(=>!{s%o)u4lNufP(@X{6<48mM03G=9R;#G?hLmhlKrr&X(&v#A-J)MmW+VO(D1P zwX~@_=IXW73mO&8)w7!gI=?T0JI^FpjlzUIl)%pOZIP_LTaA1*7Y;#?srjaEs%PJ# z@vrKMH<~cH^uw$A6ODgncT0I9yHBNC<#K}d4lH>{$WNS*!LYWVs~`q@QZX-wBOrm? zOw069hh3uGugR7}jUD^E8;l+7g$TP+8apngfIH{uh1uhO4kSQ8BgWFqX_e=e63M2@ zck}e-5cO%V!$ldQiPxBM2{;wVBloLzhY~~CPN35lTfAGJVFH!N?%8eQ5QzD%0{D{ka}N?I;7dR*}XcJ-bM6W9%9YM^i{?z~31LcIi_13V+jR(Net)&)$ip`no!o z0>G^bc_52q!(a&(twe%Wb}rLvxzGME6<#&`(T0$^9P_`9YA@GYxlZmydJ$`GAJfyz z^{1g-9A1uzE}<*)i*%wv; z9_Z*{S{E9#LT};zh?O0#4x|mG-FOANQzX0vme+-vyrpNmN3uWJfBTWQ^en^|O5cJf zZX~__mQKnRYQ0iAf*)I{=R{2Y5uGogi8zMAw`Qf@CFbQgo_f^6iS#akh8P5Zq&hnO8k&r4^5^g2jN)#QfEIKg?g!+j`d?RRdLa zbKUIUe0Z&#%!lYQmKpCi80WI`S{~ zxR^=gs{NZP(!XfM`QgUxmm%~bBrDs@Ba5fW^fNpFj~5cVD$lXgg#U%FA_Lphg@s43 z^gtO{fa&#sfe;S|17~6ep7%TC6!;BrnFHFe$5d^<)XB1M}E8;eJnDr>8{VGD3yqS*JHHkib9& z6dDf&MqdU7ES)N2#cmW<=vX!{1MvT#$?M=rKR}|#zy+#I6}ZXR_L0TZ^&P!S zd`OR(*3d6{{T;oy=pF1JW8<|H2s^j5G4sYG%WKlNf-<@#5oTVY_BF%E{{T{M_M=$4 zjU6qOuOZjOx+=$jXtit%3)-!#7)i*pcK=nHw_exsttXHU8}dZB{|R8f3~5`fMQHo! z@OoW0tXJfagP(HZX7mOqFJ+Xo0oLn8dSU~}aT?JEJRhv({H|V*y zlqnvYRS>(C>EfBQn+1J<4T^@XY7tJ&ReLo>wwaY2hEx{Jp31n68~s_IvCH^AKR=B+ zybCL|jQYK+H%gKc8J3mkjCpi)=1gdQ=yh7p54}z&-i42%j1CVL+050G0fblr}X$vfvAE{`I-V5E@ zwMEACe%PWg|6!kYuedlGRQ1*LzW)gahgstO>FXFmZ?#Eq+d>&*mNsMl0{$_r6~9HVM+IH0lRCdgzCDaOfadROf1GLvE(2 zuy6E6az_95ybo@BfSP=udsUoHZzXE_LVyojyvl2UiJa8AAL!$NFbzJ`i3*|8Rv*Z znkR@fOq)oPw?pIDN^7>mdH4w(-j0aTLGtX-n=|l1?%K0Ef^H6!rk$|J?h zukCI#3}^Fy;`dltdGowwI4y0F?4c28ahH5U+F(8 z-&MH&0eeQ z@el<7!ROBcQIKTF1^`Q3Z6w;{lVX*#{Ua!@Zh9`H_x?lrgO1(68SRQehrZ27Nm#*2 z89?jS0a6BeM1Pl&lomohtR3asJf`&4OENXT%gATQkPYk{^Qk!lPJ!Hsov`CDn!rhlimZBV)n z&xG#cWIe-<*a%n@QF1prWc*^3oYtXzPGi2)Yes=&FgN9ZSu_e4eycagXZi$j4c&nD zf_pdsgm@9kO^DCZZcf-h8Ao(cOVUq7l-v;y)+U}r|ANMo{K69J3mcE?SyX&PcYvTu zkLY=!-;d?*H%?m2AO0W^HwW1UMjLe$3g23)dlU-cxefHxQKVO_qv1#O8VRs4Y#s$t zgYAn`E{r&e4*jSn+8e;*RrCnIQy>w(XIHoUy zyf}PJ&U4any?xGG{C2?K!K91sU_ZA?H?ee$?Vg{K;N|(Z`VD0ii|m6?4@Q?(9haZ^ z;kf>~a)!pAfcm_b7N3xgC49{SNxhYfHf?_gByM+<+}f{{iHPT9Etx;)ZIgcDm#Ukh zX+P*`31@?xfYXXu-9ASj{-Do{kQ3vuCZ;CZD*h2Ox%yq&jG4kre(|Hk=3j8tIPh-u zaO{3kPxak6+q|NU3q1O9`znn*2|H*l$+o^-kG2AF{mNM<*D3kEI;Zfxt^7;?@*&R* z!84&aYL__Lb}~O+?{}aU=4QRz!pCsSn3f}#7a1Hsk>|9ob4m)k7H&E%=c(Um{c~jz zbv~oF&il#k=k-!*S%?iTjoiqQabAd9nV=3e5=ih0EkC1+PHWx)le%WO<00?)c{ATU zcKxM><`-+Mr|wY{&Y0H%OULAHoE#X9I*UNS3Tk;4kv(Z^8Fco}q%azOR&Sqr-i00L zp&P*GfUM1?>Og$vG8l@You}{4LissQ&U1Qtl6(#`0t1#4(p700@k6c8!3sK0cb|je zcb;B6hi}cGh3E9PI6Hn$AC3uo@+ZB+^mQkgpTmV%G9M_ ze$vNgl*J)<&Pf#k7d!GT*&P5FA#}IkwSVdgLcjCQ>-FO|1-7AMptIVzg}y(pw~7j` zG+5vsM)e;H_g3`0fLs&x>=!iYXMJJv4#gA6dB#oCKyt(k)Agp1a!?G=WL%%r{0_EdPI%u}{LF?O3`V;Hl`Wp238)iQ^ZZObT?(Fm-RVoQ zq8^mmI+|Dv2z{xEn^nB+jT3sC%6Prqyu!T*1Q`8c*+WvaF-RiJ$vkr=t^}0{>K`X2 zHoXG0W{pIf8}PDAnw4utWk3c#b|skuC_Q0_ zy@+eJOA^DBZS+Bs7_AJY+mppuOv$&&;^w=4W*$)0C9W+ACLP+%G2?JQkeef?M`gR$ z!djU$s+vdGKgL%pr>HnZ^n^)$=UUE|$~Nslrs3?535eqNQ5wrZcG`A70P&G%y09c4?w1&R$3pH^V~Pk|t`q z*Y64hPaHHhUgS820fTAo@l;flhpYuC&Oo! zZ?-O_`z0)pz`p}gU2_ZWnGIQaw`YJw!6vAeAq;T0&KaVXBE9_H{AreFK|1=`Kq!DK zKNVF~VCb^(fFtS6V_6JIyfH)6SN73a#0Q`xMr4ZnEK8xTc{cd5r!&E(kJG|T(F8Mk zFjKT>xbX+fSkp?yL@EuEUc{#$Rkz9t>@C@d#<&dPD_UY2-yK7r2!s)wd$m zj~iQ=?Uk+U@`^WVopr%U0^;LUJyv2mb781wmDV~^O<0?J%_mwkx^@Cz!)dViW@x9m znaWOwGiMA3XN=8~pEI+>>-Em?=Yq-=v`;Gyij7T6`{es9kmT7Lt2Ol)#!&jLJKdJ95$cFLNGQ|5J{juC+vi;K`)WlLNIx>m2FF_$9&} zf_n+3q<@DgbJWrq_uAA3Pg_M#))w{M#p3}}pEfMXU3Jlzx+8&lwug_i`c-2)AUYtH zz+5@3WUU%&?nbNt+!m|VwJEfv%oJW)sV05fUO#j8{IJkcDybVQ!W1v6Wg2bQ{O zA$^r6a?~XYNy!(TayOyOV_@~8xdtb^{#C9LERiyr*B`VhM9k@_d~sX+FeRY=Vh*w9 z28hzLu1Kbn`NH)(Hw9(X6`AqlfM}>Lf%hQ~f^R+!S-qyN_&aRQyn5giBk0z8Vsq+M z7uK6FX2!I%E$3g@#bG8mkR~7SQjhwA!>t?Zi}sF}A#)Yz5Rna_WUQq&4WPX&r&SFw z^dIO{12DW5^iV^w3eKPbN=A!6yUlgq0CzNV(k}e?w2~xuLO?hF%w{g#`g`0Y-z)$s=$U4sFV;7H(+rFlEsGW+Up+FG zI<^qm%oY2z5NY@XVmMjUCRvH3RV{=U9PEo0q6X&ZN()gFKRGSsPj~C**_NV(M>5DT zw$EjBL=rOmyjJ9p!xlSRispR+5uBNb;A~?lja{l(Re)9%2&@00fNoDjcp=kt7t- z)eAR?VvO>+R^mSmXC%|5R$`K}n#Q#j_d3pRL%G9`iO#eJu*;}M8_}@Q`)=^{q3rSh zU=FsEK(=In8FIQYa76pcU3o<@bG1`6str`z1N4tJn2N9IN*heYLCS6mUh)my(N;8z zI~05c4j#zKaWu27NG;kLyo>@{W-aZM3O1{;XBOVW$K}8|q@$Qy%gPLdkAhv{ye=j; zu+nY8HezvM8xj>@G@2)&GnjVm6SP!MIRIjgj+K4i?#34oxpkTu{Y`3GAQEbvLKsTg zI#_g4O{=_*9(qPk6^JarXi|Y_kg%0okuLo4Knx^CAkgq-f#~|+FoeKSKL{;Gv-NNu zDvcF`4RoaxW;t##7!85!<^CRKYi<%4iG_UK{Uh7t{TW|Rdxs=_18Xf~TyF29fs9y)s5Wf3)9Yk)<(a0b?6u^T4ava&7 z-$&vTa>Xw9n9g+oU{8^EiailO4LXW82*5nu5%Om%P3Z_(d5Yfch-ogPeH}$t5dvhz zYK41E%{z&9dazH?4pw9=kkGhJ;;vd}{JB*DbSkboh-ld#*-}hru=!Jz*I7K2v)BqC zbBrwDUFU*A7L?>GdHDA7&S2MDX-8*K2QU8GS-gWq+^cUE1$F%Ib9RvoNTEx3p%P8x zJ_A;ZP>&@k9(C=N#Bbw2>U?ILPd zg&Vw1{;&GFiZ+!jO3tN(-00FOC?cJ#Jj3&N@u}kfKI;H`odM3mA(Z9Sv8MN!HKE7m z+{0GFf8`r21Ew_!xbfse@9{$+Y|v6ir&W2T_!Pz8BI?#d-WEhv6z?$twTm;n%(KiC zUErdXz8#2Y+^``Y^VBWi;rnSL-UU8EzUpM}CZ1B}(cim4n4Y5<-N4;v(uQs#x8-`b zHWM3G)v_lc)`K4n5jEHbc2@kt{I_zgTOQn!a;-k!dWAB(i&UsjH+2Upy-fFZ7ipQL zvZ=?d=O32AIWz_TKYOpSkau!-al7HqTatMuvihL(ORcA?-O;TYhHzR^fprP*{>F6`QswkdO+eK3_-<|53Hw)JwQ#vD7~j>+~}kOlZQPauxSZT znA|2Sw$fJ9wAYi|*5)F3pC=5;TuD!u{nO}!o={KNN%e@xB+s29mQs2_y?vej)(hs_ zY?_3#togkEtPLCwTMA;H)&9H(gbA|i)1P-6!B`hi*sXZ}9M!#5WVD-MaLNfV8hcKm z&j_6WB>;L{2giuyU$CP{ZMwlMHF5#7n#cw3YNHIM{>Y8m=!&toiaQm!3y$0d-m{R5 z+o5{CP2Fx6rZyYCf9c-?A)9Wv%4XdzTBy_Br+v3WXB|ad?ht*|+@4f%2W;W8sdqw2 zdz}W~3Cm_SEx8ken@^YS1WGKR^t)uAJKrVh8|&SxxpU6d+%@Ma?wE6dR#GwcE^(g% z75~KDvLk8t$d254k7%DFI}%pV&W4OW5SYq)FQH3G=+e@A;JusqK7DtO7>q9c`ChRA zNaMOsq=l6!>OkuEsh)0u`>y*WxL>~yGH5pKzfatT{^i{-``6=s*}nnzi^eV1yVr65 zT z&zy=iy+wqA4sLuv^rU(ZiW=GNY#}=_LcfOfRp! zM}Z^DX~jo58EALcM>(A#tam>uGSS|-M`1B9r0#zec{tnraZWS!GdKPAan9ZKSfK6# z5tw_yDEW66>^g_GocD^|hePZbEB^vJKn6Rh_>&xNr?8n4C0AHK!IRbIt zn8$^{Rw{kB=d@JT(8G`8+n-YLy=SVmBhbMmEUC&64Ximl@t6G~KTc2!r5~k>BYP~1t zQGuo5umqQfd!vxz7kKv^^?M3JP*p zMYH;gdhuTd79rcYng+P!5<1AYXH9(;dSDsp&kAqy;yB0+nV@$F&%jLMYGaU2t9psR& z!0({KPh+$RYYGdTEpvpW5!&NC5U97oi=4FLSKk{KS~d=v>L!~C(IbM-OFhC4=s4=a z9}e~!+|&v=gV-R!=W1r}h0nl64i1QxIp5tE34D0rd68}~S@y*>7RL-$q!>xCzWKb! zut#c3Y3K7ID`T~kwBvb8JRI?WMq>-6$%-{3iX9;Iu9bs;46X{jFlN87I0I&0u=5N7 zRofFXe8rsh5j{CTq-4$FDF?fT&w}^n0$c}pUFabvsl#@Vy<9v%q}8khDKr2HQt*%v znDS0vY%-l5AcRv=y9=4}Q7?#;C`EeLGwO}IsB+4m_v7uRbGOf@tsFjz<@o`OUC)*)tq znx(J9WU=?;{R}ZA2ai?G01`F1D{=;oU6tSGAm> z60YGIoc8dl2!qdGj~Lt`jv!d(_C;tAcFjLKsv$cZoE}U5q4N|sSWU>6ZXRsLjV7Hz zI3D7@V#New@&WjQu$i~z>Nxb0(6cH##%gs4AK+I2I2Cm|lLiW3P5_xCwV&iR(IU5b z9BpEoV#Nf4(L)2lxA)Ks1B0M~n}8n;b~zA+3ZVpkdEZ48`h`LdoP$Z@rvpURZP5B} zq$cGCpfPfUv;pO-vtrf=VL{ehj0XGT&Rc8==q7zKp~J?Tz)t4y9N6b=&w39MdB$;U z(IQ`BWHskO*q~pCVb2*Px?+vq4}+i+o}-R~Aw`~`w3N8C7`4Fq$2b60qSp{8_g~R~ zA+SyM(zYSeaeR7+R31`?;@+#&eyDg!d5ty>g+%z64iAO;G;ZoJacAt@IG?qw7atGT zrqd(C#BHwAh|$WVhv~Fwm}pFYA1r(x4uHLbbgpfO|(cl%BQlh=BWrnrhHb37+B+{ z2-K_PH3m=-?7Xu;1jk0~+Lvxfrij4qI`n>tsFj6hV<5x99<;quk|LdQ*A7IBoaSfz z<1+VB+;EWq0Ot%BPsNtUp#}C}I_oJmt(;yPF7D0(X zFo)WX5TXc9LjP?Rl~>rS5(djNc#VAy(JF7`=NEeZI{^k;*}R`S2RhI(T85!Q+>_Ru z?5P1|`&;YlZw{Li0&bCPiU+UwDYo)8bix`_Wv4Oq=6v>lIx9QPTMh9XRDFv5p9L?A zq@p=-flu(urTjA6+Wyb}?#qF3f!w9<0>*gNmKF}P+zrT_0UMCpA)p^s9%E{6&h&_9 zzzF>57F7XjIYvSu!EdqHi!Bv~drlnQ1sN;mC~ANU%_~RAUz2)?e<;lJf$Z0Jg3r0R z;!@ExdESy&jC`97F5H$jU`4xz&9IrIYAzW@pO%VT?19|C9ka3+kUU;~^hr{>#w1B?Mi$MPFbe+`w#ZqAyB&mn%} zVixDbN+|qb%Rh8H%zR}bnIlDw^bMuM_!)nf*op8AfEC-vqohKgx@Ce$Rljr6+>xST z6$r&!5Mm=A5IQ+h)X4~R0uX{7&|88BL}~iL(`1y$3V`RqQKHd*10F{ZJUd2-rVp&C z1P?y+d*HeEM(})vZbe)V9&pYo;CXm7?4u3z@@SEs@n#q{>jGcQRlD(@JACY80qEnA zVh8YS87&%D0S_R~2z0Xrp4c&>c6tbSoF3<&C3p(@1fX-*m`da^Ct}mesbfU$e*>bJ zAc%e&BigCk-={WXg}84!gX&u+MxkQK6G;4eOagM`+inX56`tG!I7L^5lg%sPWEB|4 zcC`y>=~$7|>UuOnDlKnwdCk$s69z;Ig0F*wVE}wC3w*KTM5`*`yMg|6dSRSM08jT* zj^&_!qdGENHcm7S;Ms|BBLBYuB6fKhU`^d^<3+=wLw-E7d_8Pdz>sd&wiNU$Z++{Q za7C=__W=7)C}1rTh4F$w>2)L;f`bfONxt=|^GFdJ&!Rn87LKL&2FjiQ46P*6$0vvu zRls-y7A7}vw6onFK)w86r$DkCV!;0z8G+k=x(%Bsng!qq%xwMNz%xAv&vg@#1a$s= zikSqYJkN0b&H~==BjxSEZn9#ca>kgMRuyp7Bjv5tAZ2k8INY^swmzQle}|U%RI%L4 z+ASTbrl!47aeflQjzzz@-R~=kowG?jeXN?zm=AkD9}2iYJb>5w9E|5!`(>5?#n!`# zEParD4}B9!_?q>D+9n)L;(S?v9!e2zV=E!H4Q=Km){)e|oA$oX0j(SmOL&Vnnsl+N zNoMe`uK4E^u|-u}^!7BdMv14Vri-DCk6~+3beT6iW-HaKg|MA}&#FVb*B@pYyW^y> z(FaD&n=YS=q?}iflDLH)eFZ4xqS3F2-1I8#gk9GT?S4h{qiJ=V_0-V^>7zQ%d#PY1 z9OT9H;7m~?uOzUaYP8K>pt3FvZBIcu|5ccJ`L;a7CU6Z7b_JPT%V&zj)ZiPe7O8Me z$fYg{b`Lp0-^_%$ahQIc2{+wFsyR!{!G@wA&Vq{l0~xcSMjxgYvqb?ezBpUt*@v8( zI_arjYtd)3MbDyfr>1h6QJmWxW*up6bGUUB>o#Auj<8a=%sPs8o1?6wD7QJrI&!{hBYeatYs}U+@hW|0^&-FP$ z_{?O&G)zU%2!ckyS%q*~sI0szP?x@$3+2CrdacFROQ`=^7&Vt^=2|4wmePr}NHZ#> zzpWGZQ`j<*%pcEOCQ@KKwOj_b<6*jc8SID?G>FfBp;!3l3RNt#I(A^0XsC0?xJw0+ z6Ukf|?Khe*4~a^p^we^E=Mt@74rg}h)D`gmV>~ylK&s^s7Y)ZRp4quVbfePwBHlZ` z6x$2fwUbV(#W2|-G$ddhR3`*lNL!uYqp#$)MB;15rRASFA#Opw;`X$ zi{0e;$1cHGBtePALP#&3zd$r;1ysiBEi%GlNH4pDd zs+!rdcVsLSSt%p2w~+;V-g}?l;3+WUm?Y*3y|c;-oaDKaN7Gzx_z^l|SmLB0)%1kC zU2wx~&S+D#gS331cnpaTNsAD5I6>_e!GUs#9$SQnL@BLXBwFF(FNRF!u+k z*&E_m`Jm^iTHA3F8k;y)B ziF~5<68OPS(9|We<<(1M%NcKqJDsHfTs>tTmLHst^U$#nYvVd(p-vU zpG)+@QrXq*OU14K0m7k^|5zp({fGF5w_2_cU8v0&u6m7t9hnz>T+POxqdkT_v2 zI5t^yE?>GUdb7iOa4YDtXmuNK`LmoWwP z;CPQ)1?4jDZ4n2swRu}SfX8OO4Mti_C*MYBr<6KX0KS)LLInaiptB0ZP)aFrjdchrSNa~Z~k0)^WqIRuU6h1xkxr*+RS1cnVg|-wUO$+32b?ig5nFPfuzv#cLcfi zjX}CCq`USV-MU_^PTqD7CKboL1NM3(NA6T}Hf3%QM#{o7mJJuYEvv|>=9iB9;sryOID$FN$Jy?svhs65`1X+?^#d1aTSLvgTfO?}!cF7;BrxakPPPlkBDX=Y| z9JG>2*W7^SVU^o1Q(^hsr)b>)XAi&Bx|`hJ(`{Znj?BT5QJ4dShdZUpH*(bu+!hbv zX08Z@p0Uz}F5jdi;RhR5J?>1YxOpQIzL3K>V3T;JHkU$xjMY}enbQ`MnGU*bi+CQf`;WJXhU$mfjM_WO8 z?~?aD7$7VC+9vdX2wtj*QY!XCu=Kdb74%GObjfwCRGD9jjo_d+h5`6V_yx?^y#}oj zzE+u?Ai<9u7ECUez`{{26>bN1kzhX2PGCM66R_i2u1_t3vW7@GdN)5Bs$dS>jWeZH|du8y7(W}DB;bdxh4sea#U@>0oNo!;TA^QDkVvZj(twXlLh+K{UoH0XWNtjH{lwUE)mT9ov$Gv0zvq%lp6#(qeQdp9Xu#tiYnWMK=9rHz{+|`D}O09 z#`DIqB3c8KQsdfHXqD^r;7eSuCmXiYDioHJ+c8BzA=AdXyxasB0_A=nTE^^8kYvYy zqO{KR^amo#@fQXvPG0!{aqj)}$p<32$e((XV-9&^sGx&po7J+ zoGVnQxweJ*{2m)R9+jCvN1`%oGae)Q$ux&~$io>>kr5vgkc~6oxW_*M9Qi=LET9jz zA}at%zB0kC+ATZ-KlFpl0uKXBvPPeSBOFM3YzBr?1!Q?`&{Fn|AOnyqu`CU*@^BzJ zf)E2I4L3I*p@NS^%5~Lb!p)sWsKKAS2~_m4aCEsIbbGMKDY*Gx0Cag(Ko<(N>VVc3 z9Rkn*9|oESX8;;{$v|WD0MLLM5^K5V*jX?DG>aEg#EH!?*UQjdWyK`#GiY1>xL&cT;|5Wd+xM#0;kWX;? zzKXankjB${e#n}zM03#(SSI#zl6$&hX)b|z=i*PuU*@ggSR7*LAb!bic=8d0mc0gt z372eH`b?%q(XJkz#3Bhv0JPmT{1?m22*a|V2}mabima4dz+4dt;zpWXE~`(t#6Wc~ zRDp&nm!m9z?-`gG{CE@}1|&4Z9XjAASdgNh#We}=I7zUK{~=2j}lxCqr@1K zsGw0?%UITZ+Y=VX2*va^5PgI!AG;;k55+%$H$wSdd~a65J=;WftQXWOal zE0L8n?zlB$ehwIG{`Mm-TreuOeT9{H(0T4YfYjh)H1&YEzu8-H&~8zOm=igaK8Whr zIH8`E7$_GIG&XG&^pbp=-1!w;mX#N$_7>%S4byZsJ^HnH0~u}FLGiPawBRTivNoA2 za}_;MoWZ^39HopyB1tVjO3e?6dTG7lg$C2sJz-5!T#W(LqC8U)Le6(8XOb#qb|vdQF6B|iW`ui)6ipxY;U0%$B^#3lHA8dMu(NC zA)c#xyqq@%oR)srG*ptdt|xi9LOl3e6qdkSmlD0P{D$V$aR(NVQ=3`w#Bs4m$)Dx& zBIJe)G@;=z6^Dr)gAdbisAJz@9H!$i*^{KrqV_+CyVN7&XvPoXNo8Y&>qq3(=3I&L zHy{uAb$P%q%L9J(3f+E6tiu-hF{g!SJQv_V2jw&>9L&d}eoSVB2`4lvj)F$5)3ZFY z=J>H#&^eLrIgRD5i|OcT(MircY$%YkSqBeqrJK%(QScUgcm`JfRmwRl3c{8_Lnrf} zB87&X6>(`-;pVgTGk4nLj=tDarWmi|EW*(YC>{F!M(a52Kt5 z$dUhqNt+r{!y&V3ViB=^&Bf&|Cab=fumbBNFPuaE;#(Dq&tZ+OYxMUp(c%#WevK-A zk~w+l{}j`ZKJfNG;k8>t*ZwJ5)jAvI_3v%Kn|Y(e3p`Y)y~D5Kqm5yX2DeKCqtqu_dvqO%vVLaYlt@T;g*dol==)6elE?e2V= zSL16QSaHZcb8uk@o!lj*ig&;+K?pi811pt9+b^w?dp?qy$=_j+Cy?IOQT zU{ens2x@RiB+#3eVWD=RLzhK*V>m1(`;x0JRPh?46XYI#IFlFP zvm`T$uLnWd%)Xq@Q1J$(*)27Va{-{)9>8^TFUq)wSOdrzlS|xC7$;yNZ0FY<*s8w> z((*mkudJb96l_T(QGj}?%1Y#5@EXy;;#Q!q0ycTQwyRX5?E8i+}^;bn!D|DK%2jX@w4+{F*F77pKI_^DdaLDI!co4LC4?vJQ z9G9tqmsA@|snCROegX&UB=%MhhyN$$@674m@)6tjp`H24})hbPkPYockL znJy1Dg$2d>qWLGx7sK3FdnL;2hOlv&XQ8%70t>aPZkli{J#k8ck)Uo2vyNLCk18{! zylOO1R!!M!WUIrX$lKg#Om!6_6U7;OD8|=aCvwP&l?JxfSXqbC@P$Kl7lpB2RPHYt z4G=-YX@zAaL0pKTYHoa+l|^q*c-R0`UqMHucfl0 z_&76uXl2MSVq}{Mf(hW154V}AaYAVT`Fv0h;Rs^%E0ta)-ukwwTEefL=LFEeZUPY8 z@J!9*j7CUhJL_Eo@zo;CJr|zFRC6h65c<jLMNyR(3mw2m;6YX;^EAoBR<3#Q)D zgy3^vpmt={42m#@U^|Cv5k~8zYwmzK3AzPNN&K~IS81&?GtF+-Hqzf6#za4&>ewR1 z=z>@O>NIL%h4@$}kYysRavHsY8cRx~5v>CEp}r(gt4M=a3q2T#$G@Txkw$a;Y>YIz zIL?G=--ltrQLM{&LV1M-xr}aT_hXll68{gbKLo*K+BzfSrceX%)Sr@`NG`YWB^Ew> z>o)Qb-inPfUXK1p0+f>I!a}IE=;>=wMorf&pg%N{aA*;GqYQFEHL%XF(6iCT-yy_K zL>sroLfrs9TMxRtPP#S9$e_n#jKt6vM#LC%tQQ*4-Lb|ZxEU4AFpD-Q@XFYk9B;6i zDov#j11i*N0!jOArhsc17%t5Tk7Z7Tn4r}eP2dblF1@Z9EsLbdbe2sfCA2RAsqjZE zcWI>rXwqUhmcbUFUCW58)^N8k7(bnc#2LvqSm&X>BFj|-)9F96&iyV8wx<3o>zp+Q z%TJP8x%oznd5KM?ks*d~g88%m5_|N`M046aXyIi^^kck{nOf<(2eMckcjY$L*|fjX zt?vpiv|{sYX>dydtP#M8EXj?f%+OZh6_@K|m5bQ;snORzMJE%CRHF-XOMn_}@y7#AP)4GW zq5Mp(6OANwjzUjdbS3u<=mBu+KuXGP<0v@ub|(iEZ~=VVX1;zBLOxib1yPO>_8A*Gc%Ru=|*iZu1|F%Gjf&-8ayRZj3|k|NrM16K z;EWLDj>=sh;L%5dtz~nCnJ9b(OB-Ye%+W5>c*Dp@v7YCNw7dkWd4LshyS9+F8AdLe zJ!cq(u$oYL2Ehse6>ee*Wh5D`9k?f1=bw{|`@wzRO#D;-1k+t#A2P$|)|@xH*b*f_YgQLy)3c8)-$prjVE3X0kjj zjAXUYo8)P_z?)|7h_GO1aF~VI0@YUNhCylVqaWjGw!oL6eL|iz zqlU7BI;R=8$6f^N{CJC!GT+mpG~*^LK02CaJX93p5$xvh+hcj1+%P~LOmyC4rnV?= zeFR-(0lFoMw*e|q=h_QY`43W|1_w*9w+u|b7*9=4lz*9~FHyT94d;Cdr(Wl(dljFX zVTWqKeY|lT!STv+c5&WB&6DWOX60?P-3%x~d^vq>PF(Pyt-A6pgh0kC>F7_C4W#Z_-7l)was*PiK4qQ7c z`-`*{SRhQUKG5zyKoG{4Q~?jj;vLWd;Z2e{pfnaDs~8;6=|P!{2|+%ij%K`wF!wQu zmqhX$;A}MBJ24W9pztTs@`OMU#FBX#dth=u%O7XZ^PZX~al%x8oG*e29O3hea6+)} z<_HvWD1`9uTy(0wX0*GnVHC-cU<~}EEY=g_Yud~%N7S?#w*xy1qk$gwFGlk_gTV)_ z!Qf>c${`xUJ39rpCc;p10%O7UvoIPY0DLgMrLKrg+NJFiPKRGi%CTcSbnL~XjMkoJ ztSQO}%VY*3gyVPy9{|8$WB^DqgDC_ub4bR=NpB!Gm_9r2OEL<8aB(sp43|-m&78Rv zAqxd*Gr;T1i zuNjYa*~fr^9AKpv$lB23^r5IuEl+BpueJw;Ug}^;!6mQZtuNg%FiPPngzXf-XP!oQ z&CyUf=;TxQ)Nshw+iB|N183e!&-;w*galk&f?Xn9^8R}0im%dApHU|PJAH9M9IGFw zwOokLo%9*4ohhlFOz^60)F8`ft+?r_ETca1;=;6#vA@JF%BjaRz(q60o2Jd%rk;wKN) z2RU~Feq^R9Pn<{A`NhJB<=Fd?v>HYodN|7nufYv_;$YgcR6!l*RZJv^XnxM;u}#%Y z{OhJu*@jX3y<^0g_APCD`C^2*9-Di>&RN69dr>#dWxj92m)uy3RX4}D1-rPtm}9hw zsMT1GOJ)iOk!k?N__$5Y1>*~e7+7qYUnyq4ihWG{@x(3~*k zVBN-gob)~J#3!a=&q2^r>N6e#mfka-R7$CBWJIccc$SbcO%r-2rMo-^PTig?|GCrS zk>?3CrnZq(1Z5)3V}Pr85`i&=a7bI20ZY3-93vPM<2@X5Xf~{nw8i1BTJlM}Ac54t zjP2*Ck3W_Iu?zBteGbXsmRLqv$M6*;^Z0BmQNnoN0cHVeAFJQ+Ne46yKg!~3e)xr3 z(1S+SU*;TQpqFES{7eGS59Mzds{<_MA;uZ)(S4Uc@Z-VK;%%Tb4_&MSHB*o`&&Y%( z;>2*7WBJu2Rd(AF@BdaX{%bM+2LxjgOUBc$!GRL8)AjX2p2b^GtU(U4_UMVl*rdd> zB%G9;OeHMkuuC6gE;`4cj5xH zDK7+D4mNk*!D>c8FkGyb6<}?%!;{WLci!O>VERZsj|=dEOw34TUBie3U+036o@8oU z*J#i-*^8~RFx3ZQy_!^vqFCExWP}sk?a5_w4fiyZvtOOiTUOV|$duMr!xOK)8X0aMId~Oh(L@!8rN5 z;LnWjvJ0qLkMJW>q-^{|p;u%4$GaZCmhUWLVnL1__#xoqHtqdc7x6V+U^dqfmH=jy zI(=?*qjGj-2}Nw>1+bPlrLiztlAOO4HfAc{9{;;<^ZTQnP&=lvE{I8(>gWXfH?wZAb9wmnABKthWhUUlMF>X*)=zNg=0KX00k6>N~mEFkUT(}$^o0r zAM%f~*eQ#JR*o{3F%7$R1ymaJ_Oe6UjmM)@Ls3$-ml{lHhhphSeWPaL zb$9};s@yfTE5JAW`$jlD6WxFlg2eP9LWHRVCu4-gKi=`aoQe;K#FPvS&wHT zsar!M0c(06ZfLaDSOb)_v&6xQNVugYaZ~wd3cXq{wTMlQer8c;p9@Vx5@uC4pXbT1 z+PuSy_b`vzJnne34QDx4hoJPXVI)YPhZ`AsM3nYLXt%fCYRFz$mD@&O&Kmv$nLI4NJbb z-C5{{iWD7Gjw&JH6qf9>CMuxwK-?~EYzQ|iJHQUSDHR>FiDg?>Vq_=GMk-IL%=Uzf ztI&&*LKdC}k_XDEV-uqeqQm`~7*o`eN#ty5U5BXXc@s61*;j;V9pqpA4E#LxqwuR9cev~&_7^TPnZPe0e z56|-RE#Vg#PRm*vUD4CuTEY`uL5*%QnmRdh4gbqb?8Im^(&6mkR1-`TBGSHS%K=Sm zZ!+Ft$FOzd3Jq>$tc4Gvd26F*s1G8N&8WL;XEuWmVrOgPPAp8%Zez6V@@W*%3wyZ_ zG-LXiVZ3=&q={IcIS-bX-`I+jW|%xFmyu5Ei4X^eY8+N9H-4FfH4tUU?5IgkzN45iT|bT6x+^dMng=a4e}Q@ zna0h^!ivatXgf7X$dPi(mPivjsrU(*?rd)q*MMRy!(XWAD#Kr)0#hP$G{XMx(53dq z!=T>Y9gM$4!mzOfF!kzabfEZ-n4oRcxuY>mg(kbbqmk@*O%7%CW8RlE0U&@aP2Nl^ zZ!tO~dWmSo<;}of!4i@n%yjaDktGfKYl3AGS|1ZY_-8#UJSn`f=4ii;YYuN zgN{4}R%8+P!4}@9<-Nc}7Sg3&Xn!IV+zNx{MZUasorK`6gCN+_sxm`}nirb9=Xu0+s#(j=sk|O$AWdmNj!)UG^ ziK2p%q|_)@h%szV_&G{pcN%rwQfRYX0yDeyokmY2CycohJbWX4dZ%%pTJEBlnwiP* zm64ESbDIo%&|P-{T_)1lyNn$7_HZ@A@(v%OckePDEgFhgBX3?G#vsbKL9B9c65%VD zLfS?I);OI}-sGGgxW3_~M&_{{lf&*o%p4X51du0?nd}b`_eM*+>@*OBfHaWN;G6N| z&qXL|rgp>ob_|x2!eyA+{P`HLCy;CaeuF;rMjv>R+QB7=)e)?5=HY2*bGbN!x{{5v z=G<#LFvS<{4-Gf>MTUlkao36+_->t5rCmmRASJ6)n zvLkza>IB450>N#-l@m*DOsSBm!7T#263Cc}9H_zrwUQ+mEZBX_%9W70!PTWo^o+D3 z%jn4+=mW^B7ls5X1!NgQ05_9X2R}Vw4mtaojVOFbVbh5>wCDUA#5nA5V_v)0$SRtB z0%0^HkFF1UkAJ0E{wi7zM^F(}p=h4=C~+Rm~cf2mp`=5P*Y^JpcZz zVH~9unpbuYt5d~b1Yq(k4DbWS4-+eWgdF!9cW|}2`2}ba{ln*v zS&6=Kv@+2*jDcVU?m|L|nz_IagJ7kcSaZ|$mvYTrpA9px!1Sy85i*)ab$c5Rg4)LP zHatD9y8UYz*rH>C8U~4=1QG_5&2jUQ-(H&J9{aqSXi;VH`^abKyeXs;qy)kUuq52sw8{?Z^M&8tB>U%l(by^X;O?;f%%sxKb zgRa4S&Ivh~?KO?@yS%EnD3a+%^Y~6M{b(W1aK0%AG+&P7dcKFZMx6!OW36Y~3b+&h*JGzopM$xdZ+pZV(!>A1hcxUrjHr8!|CiIrL(&r@uf-(wgHLALkVnUJeKU3DP2MUcT&iOoL}5TS*0oI>?G1o!(Rxt`v}?;lFGX3>VGgUrlTgTTxgOtNQYto2b=SyR-w8k z0O<~*W!t|Rp|knF#muj;cmT`duVm%=SU1V zOKWibT(hXO2;tw+K~}d^)*7Y!Tn_pUm}62l!#OIa^AAIe88bpPA?m^Zg#5qSf?@CY z9Sy;x-Ghuo$+U|8aL22eYB=!|*BzodWqqq9OG5X|5_H2TGA1i?Xzo)+^9Iv7PaA1@ za@Sgi8Q#*iOF_ z8_P}zB)TBhKyMZqMiv~og+3kpH>vO_4!X@B%!_859#Dn=!1T6!_FYjGsfL* zE4$2?#H;*xvC!;g5Brr7%%`aSM%rJn>W%FZ0Un*%FRX~e z3j8FlnaXpV41nx3|23>S!u#8icmPpkX7-oa0SLJO%19JO1=EE)Z5+Vv?2llDJcS^j~sMs=VDyX-?M*hFo+qN;8)z@;XVGh^P zndLH=`kc|V8z`S)2t)V=ZXX~lUP~WYpPOUnnq z?w?L!FBr7|hq^Bqx6_grj2!HA#R1%CWHrRTVEAbF3r15{4ALMF@_Yub#l2|c^JW$W zFB*keSO>=aKd$;B3qd?Q5=rYwjfo1-`qKm93Z71JFBzG3_}^($MZ8X)fkuQ9Lp@(I zQqrTX)Bp)bypQS5Rkta=s8p;@2zlHaBMD1FA%>rk8IXlmXx9^!>WQW;FBz>fG>^`U z?y&2kRH#7*m_sCY#4`9|DSe<}V7yHRA|UfIJvGqiz>Em423k81Bg0BKXh`WrSO@t3 zK>_?=H8zK-TaD!TFHM=#!wiuY$#K?5j*s<0;)jhF*Mgk^y;*n^3`T$>mYyAK)MObl zd$5!t?+rE@IZrVm+LR|Xr;70t0&Tb3^ph5=HRWsV9vE#Q4}m9OJQk&f9@^SJ=ke>A~fKB~}Kp4GBpA11%Y&sJEd7Y44U8boGnu{EVVgN&4%KcwB#l3_*x26t+h7-oNODnhgQ&XF%@%L)^E(*D8` z=>n`M`u%La z;Y?sL_?4s!RV532Zl~E3+(UHNpc2L*JLn0X!+T2iF<1{LufYjg~prk06~24>60*!+MRwfZ= zL!Tk@)S7ZP7gl?biOWek@NK&K5^H=390?awH;tvbnb#8a`Xz{WfKXhJ7K+^{oCZ6Q zzPTD3JW=Dwa2Tf+g+m7NO2R8xMoA}T;2m*P4H8Bh@UEqZD+7$Bd{XF&-SIfrz2XU& zxeUBRUy#|bFj*l3uazU}#CmHxb;TK0Ox=hRn+8K>7Itovtox%9{rDKPzz%O1JO`p} zv3l}SSn6+8pI-_C8;J3ux;&4cvVste~oa($QtliabFSimYDnGX+u-^>pnvH;Un8$U=1-Am& z19SDRrBfZ-XpL2Q4cNIj>hK0@sCj3B&{alPba?|@HX76m4G3LHsDdl4-ModQ*Id^_ z;3;>%VFfG%bq=TKY_~HnNCaP>b7gGi)KQO1k(f`pht3Pdby)qo-qL) zP7@#^W1K$QU!ev^{SuJ|#oct#gmM6W5||F_Nahn%<&{>2`dnE9DBxE%THBW;*a&BR zkFX!?^f@pABLy<2wQt2CGWVfuc@LhGl_by%@NNlJE<>x8f8+&eJjMjF97?^aKDiOg;PtBdCTmC@ zT}=^XvQ+JN6WHku>hhbcNvxBPI(5)8`E}m73BEzA)P|c7>-nrY@Mf!W$m_Gi+i3%P zOM#b{gt`ZY&q8lRDL>b>Ro#;aZ9-HpU0lR8ZdMNzP7Zl4NgpC;yhImlcE{EBPg&(d zvD_h&XdPoCpQn?lOaxepvKk&I4I8!U-2PU-?Q#*x=LUTKFmXI*$w;ki)LdOYRZE2+ zmRd4Bs2|{tMGuD_Psg7`n5w~`*)nHkk6vDoWJYxzRV(^5nJfVmJZ#a?v_ zOlS=JsxBI2oLN4a9MGp7>?tHP)o_2|o|deva*@=`gv%tVAFI8q-41ojZC1Zs1d|_O zpijm(0mP9tgP7SOmxr9e`{fG+X5(fpykcIL>ZfN_PocU=i5kiblR`Q|S99IQ0@}r- zCvYc&pCp1$aTyGi6ui_UB3lC6JQ$!9F=f?j#HsmGyqY2c@Pe%dcDv>jb>7^5Wo2}AejF>YuNpuR_P%09 z2?+%uf%(Z&#LoaGxHJ1+!5>M^Q0O)dl9=aGFR^04V!ct5phKA7(mn~cgq0(K^vg)% zLFA#%+Q%_5%Mc&{Z7eQuRF^OTx)yux{Uh(JalD1(H#H()d9bCt7PZ`5k{k&`WX(uz zjN)OqgOK5!?Ih~PVw7wyrAyr+XCxv8x_Msc;uVZyk zh)KIdAAFeaxDC>bAM^_J{~pyL>qC=?oh`ag-cSrh5dBT?i$QwOGgnuzP{mBPlPOKiF6~SQW#t$oo=KA zxpFd(HJ9K^z=AwB=31F`oAPT)#ShM>H+9M#R#8eL1{wfruGk}_3B+td5E=T%Mrsgx zvAzh@pi`UZ?#-1XhGtEiff-2QBdU+6L}_3HPDx9~*5r;-L@7_bSpb?962VglamDUf zmPOQ-);FMW&J=f~`#vHXs=#ob5ZD(OSuA4+5ChP#J(CPiASN7{Siz7w9nPUiP?!@l z^di<8@v%HLSf`@n(T14ZC4RUpwzRU{oC}o#3ge(i1k3`9w5`y$SOQ)(4#Scd0v}v# z!1+w{jkswxDCBd}f{qspJJb^(FR414VX|*FYM}UuN+-hTwE^@}MG3USHiddU5e*>L zc}P*W^7D0glm%ph0w)yx_znYXy7;|r!~=z0q*lf6F640040at-oDv7M;RO+bIfZ&r z+%<^jo(JawE^b@V5w)t>N}kpot*#-QOn*qw+}+%%dW1RzGwXYO^80>q^f4GD(82H^ z8Eo3d9~pQwyCTC7#S*c~_k$1O?)!FRV2U!_3b6TP1>|4^8jY4jCBl1`k^57n>%CsW zE0#Ghy%vxWe#Ir}LJuOL;mD1`I778&uty*~2+*ft6wTub*muoOVL5sM@}8{vK3U`h zsJ9^}K)nk%UwpdxK7E%3fa|AvqZyt{5KM{~b;}^@>LKh|9+q-kqSZrr8C;DleANhK z6+KNi`a7&ToL=V3F(UcN5?SVR5QsBWehol(%dq_8UtqOI*2z)^AEOscF@{UDW_T*S zD2-(X(NHXAX{!LbGV3bIr5PK|~rTI_9&j^)Vd*zS%)I$|uGj zgj%JG>XbiQg{dAK3aK{-h@uC4=mVyi1PJuQ0(_fZ2)+)~?*(b{n-&%g>avHdJb3@s zAplZ?2%chyX|9Kk3}~&-ont<;jewu#I-+RWNDg%(+_N2! z1~!@yJ|V1*xD(XvvxRy2djC}$UXmMFVOvBA?8#kAu*2K>b!N|Pwjc+%I2=#GF6#2Z zxsg|j3Fg?PboY=?!DA!y^`3#Hw@ruk#wKH1Nf^3ArCd1aK>lV2d}scI z-9f(;!~*QXk*~bO%ViV7eFJ*L(BKLt=wNs^*_QMLZ?|b*X0biJ)$!N1@3hka2!A-tVmXffb&BpL~9_r0w@m3Ljd8!;ig&eu(lUv8CdBB+{x?trXE zzAw6okI#qtIgq>y=u7J+7_c)B)IoWx4p*{gs78^PP=9tef3>$)L4B z^RdfqIwjNUMLC*6u^dE$fvR~^@9uV%ka@#}S z8~BOflizJA)X;;3;2GIV6Z1n)_w;P9{WjBmK&pZoImBmHGiO!O%UK2Pi4mZM7%}&r z46^zMLHx{Zv+8jLSGz5@VyTXNc;aCF(?TI9cqhB!zR1b%8-Ld1^Nc%L5HT*EA0Ssp ziFWyVAvaSRN++0wz0%$?G962JpoBzO`z6xK%1i;w=u8uy)>XyYtYqr{j9xZRE(k16 zcg=?jNUl`Rw^>D{MNHo)y(f#sX{(O#&Q>)9N!1(^__>O^oY`&*~0)9tjDf!=H zuq+Ia2!NWnjL0&;vy^CdEj^DjZ z{_xW-P(z=zrUX+3iDmzcC#{La+HSv3I*00IO-O*X_DQQ`vYehoFTieO!jx%4#uZ8y zaL;Iv(*>;c1!Aoir8}(kYTQ%ScH;^%Ca6Pt8QaDFR7lUl8Zl=c0Il=8YVK23bxO=P zbQ8++wDZtvL#qpuADtSX;AWd|>8qeEP0dj1(~AS52h4R+9eHKb)oC)Qh3qI!w1YFM zZZK<9g7Q2{%e3|`Kr!{qv(_%^$fqqc*UwCQ*3;JR%n*bv#1i$(XW?h|#nW)9Z&%57 z>&SGex$Wsvow`)PGg4~YGuCm54%(slF_)+1+=NGQL65fo^ss`f_IcK-$SbHqoCy#6 zIptaF5crBb`mD8I_}z$F{;V}q9sC@kMQ&9mK4%@0`;yjGs{3BB4pWsYEoWrbnLeiW z)GDtj^UlhJ$1kD=ksXOfdUfO9tpnAcR$ApIY~h|Be||29Nj=tS9XJqr782oFFi|o4 z*XJVVa~!(!GD#XZ^?B<|VBo&zt@AxV77~ynDLoZr4F-y}C9oD(tbpZ11VGgoo3}7DOx5BFs-|~bSwaU7x zSg@)EHf)zomF%=mQjffB<*LaqS@}`OMy5J;wRM_)_V7#A0S^6Qu{C9+F!$Nq1unwX zU8DxT4A_NdiXU7to^uJRiBY&FQ> zW!3)`s{v_&?s>(!xIkyQqvA_Vd)1n6tX9vzYMpQVRUPn}^}F!}Rn?l$G(&}|v)jU@ z2?Pw58PvdHu2TcMgwl86wViPGdxj01t>$-Hg)yz}R5x~7^YV}mS3Bqqe;sFUtYWEi z2BL>r+T8@RBr=_5)Q^yK^N6_9u6@pms(atC3PE!ZzhU+7(@VHGdcEv<1IIRN2bU!{ z)B_8adh8u*u6pcEt3McXo=0NK)O~BMdUg65t7I3NncPbH^G!kb5m9hOeojD zR7gnZz2vKmBfw|Vd)5VNx3{e6B#F6i;gpY;)$MOt_vUJ))f)LoKk+~6DkQIQFNPK1HD3n=v6d2^xe@DKjPJf8|?wTvmhsKuA{ zi1i@x;eIwP=~)~H5sp|jqh3a!BOo`YpuT+?MsjO zJ92p;<=Y?sf#h;usMY_lrqyZ-Afm$xkckuMT^pv?th){z7!syFUciQji<$b+k+Njq5gug2aWmzdomcW@oRUcPXsj5D(Dsdd!4j))2frPLBz?y}8YVUl2 zBMX|vu&EDd>8U)mCJ!ld@|dH6Yc}Yd zB558x={o*xRqRxYpGlyKt|CZi*NxN2z-6OeCjC73F)oD6PhG$ZTyxD$ILv96%Bob} zXmO9ig+8u0*9l2jh~4BfOi-&=HraDWWEmJSc5=SFFFFr zkS7~$6@aF-B8vo(4-#4mzDRp@x8N>`i2BY-wAaj6Efvfy_0fCR@N5z-IVK7?(x9!t zaa|AuX{FsJu%x^rg$0Cmy!Lj#CajGCFnaZqpKH%^bJuK6~%A=T75yE-5aZbKEhf|MJX3&KN%Al))#D z9&`2yBWjNwIxKeZK?mj>ebj{AkKcaZnemCI?qBqavR!6OIw1ciN92}QR9d#vud4rm zfrFCO(`!!q>Bv!eaEXoVH*@wLvs{rWqj1g7b2}=9sZfpznIenljWt$RGJX*PDGI>Z zw4L;gsnWqf8`5SB2~~N;yCuAKdyRmgE08G`a}DPRpq+JS2lB0q7Dl8La|ZLV67QL& zYIvlxQl0ySHF3~>x6bmEf@wD>9GGU zfPh9=F%Fx8)b79$vtGX7Kj3SmW9hWJ1E@GmbC--gE8%_SYk%Youu8Rx;KJ*(i78I< z!-^9#9}%*YzDs3vofC>vB-nBESK=11BV&*@k}<+#T6$ich+q{W4GBJURXE+zF`m;U z3m=ITmE`$I|A9VWuBtho!{feLUHQ4yuQt;W_JdBzF~;DddvJTgCIpvwO#%n3b$o8^ zeNZdVkV7xfT+PJ)h=K?gmGE%Efv569E#N>@UN{gHI%{k=GV_B+M5L#A$pLm{ibUhK zpmB6R!*GOnfaWD0vw-4G(+I&pNw*yi^DG{9M>Cr?iZ1ht67JSY9*eQUo5F1*9zE|l zfk6P~qy8`)pm`CPRufC3E+_wH8Jf=(HOldA9QovN5AsIxG@|&BjaCiM+w-zc1sq_* z$k=!JMu>LH)jb=nBkeAJ5t=hhd>-Nk20@2b@W1LWtjb)ll$u;~uBskk53S_1fTl4J z%#&u6sf&F^N$Eob96778#zy9YO6m?y6OMy}CgnqNdZfrF=)yMOHYrLa?)%HZbt^Jz z8Vm@y4f9R`<6FaRay%F1N~jSjCI==SxefLh5$0ox2=!IrsM~YBd4qIgKF7euQaA># z!Z0+?`DYf-VGKUT2`mDcA!jAd6>_QInW9l?k_3QR7o1%IjuW9;LAZuq4pBR`<+i+a z1Bz)(ixx54!(TFJTI}{!H06ul3iK!o;sUQ1L`dG~j1oahmB1h*F~CnuJ|dndYBf7uE?l*G#p? z>_FTbt{fucv2TGDnDE%PEc<<6O$k>0RsUD6#%;%9|P7@MfE zy+BJDa0=jxCIN4lL+-tX*;axwkZ{{@+F>X@Ps+9TF4vMN*DmGeO1X~S7 z6g*!FcJ?m#*Gz+*QjWrCp4*ivmk}P^`wjErgod(93Tv4Op3Z)6(kpMsP5mWPx?4&^ zgn~9XP`J)h$Kq(lOxYeOD+~XIK-u&ZbRWo+-5_QEz;^54=<1A!cr7YQ>+oH>+NL_)!Tb?Q0B89=#v31&P!)=+ety1<^ zEZer(vX5oTwnNOrR9FJ!Tw@YD8L$_nI4L^}7+aYDoWZBNmmR*r4+bLzw zD1=-s4)R$e6h4)O#ralqoB&Nc z=NBFxX^^sWSe9O~eSuh+DcdMzPhnZQlJ+h8Jjzb>I7O2bKADA~N%sqjFz>z~3L*0` zkM%W6(UVxTMg1{mTgCpH7J2hC^shXqyy0fkkl`Lhu9a z^5%GTK(1YJ(#zSkI^^9?S*x@62VTi8+9^ekV^JK^mfhK~`MB559w~LKdOp|gXT55e zPk7Jk@;SK2uy(zAEYGeeeJ%5{US1xpl6ao#G|UxVr3QI^6e~4~(?(!fc|B9CQHmYO zVsz8=#!L^x8<}EFQtT%z2LGq*VsEC4?G$O2a!0Tn{id>-TazvqX^~<-X0cZG$SQx* z1G`n;9L_gw($|tg(|^|{?|!6q&A0ni#?z16nFEcJHg zU)>6)1~UA)P5|*(CKtr5Ix%c(PxoVd|#G9 zFleAmC|#yen%Rd%T5xVdrkOJH8Lyd^IN{lwMOw3pl&6cdN|C)-q%EsRg_-i3X_GR0 zvP^qcnaXrC?NVg6dj>_u|kh)Egc8vzivS`wiQLzwfE! zv5E5Yu1e}_VXSAyphy_Unc|t!I~Tfn zQaTT%jc+PF9tahjg!f47D6Uw%2Vzpdj5}_RGw~F}2r-(LsHh2p6bO&__Qdw6dp{Mm z1)idI8MT4P;ZNO-Xnd)r108E|69&AHJHk`+tVkDyN|*aSt=jQ+LMfE>_o1R@lSXAv z&)|4O%6I}jo>NV=mXrr&QI;|$pCqW2F=$`!b7M#~iW?5ybD}-M7BV+69*?kz1*ozM z?5NCWxVkHqXt;%|WHatq{A#F}W3Chk7qS6pUjr?;P4YPbwy{EQuVquxR6+hQ zix72}Yt;9JXkB>_A{Hh@-AJdizB~v~5ANIzY{nf!DXpj62WeBE7TmfFL>tNj5Y-7p zGYL`q79m=h)gB8IqIN*E+Jh*iN3w$;bqYwM@v9>Z(V9LjxLxu&y{!*I)KyL#&4A*2 zbf~9TEAqe2+=p4@hEm_o-X|1B6-}7B7^>D@dIYBp_?fAwq4{>IwL#nj>+pZ(%k86E zQ!P7!YoSw^pMstd7=!19ewJmSMvAm$P}3SjO`D)*cTqQKuHGmmGx`Z%jkGYUQPkjw zrNWIEvPF8xV$gy%ak!cVp5ge_lpe0deX6@H8JL=bFtrFw(*rPVDASak-i&$Lo=vH; zAYr45kkJO5$u}sxb`PkI zvYK2Bx2IM>6%h8yG90{==dRZM5Cu34bR>z+tRAp5%dbWc)|TOSE;t;o(Im0E20z_y zHsTJ$uQg?u0)>zK_Z1xS?uR&(0lbZg+wD+-IZn|lS1SBA{i7*K!N5sWdZP%n!7(LAO7CEiUshv#d``Zkv2J6UU{s zJA#(oh?WjPOE|#Vk^NgnYjtT5LA!A)3k|WxP^YdH9NaafLGonVEZ>Rz#A{16e!6gV zyIIp6fI-H_xYXCIuIp3T?U7IDAuZ*Qrs7)Z(^JYZo$8S%7uV(tG_(bCbQfmg#U@Oka?W(#pQ6>Q2i(tAzGbw`Q^-_ERd@Rq_kV z%7Il+8*pk_9g+pFWQ>~?Nf;M&JyyjzU+5RNpOm?C}v!V@SCn)Reap9 zEeTFjR7uG^?Sj|#I&fDpf#B-)u)h1gr-U!@C+hm%)geR4mwIuS4JDkU@EgWz)C&@S zk)5xBPk9_z$~MSXak?ZrQjhnV(6l{jK}m3}k>ZU~d=N`FAV3mQlxMqI*taYgauYj< zvW@8MB6bqYdI?V+C%10lwN=B4UyJaIT-u-LS^eh6*-$GQf(Ase21Pf7Kmn**W34TX zwUvEpyB+c+U=95j1CmU&4nf-_qOD7ksr4Q6wPtY4Srkbwc;q2?Yp*Ik@axRrn6nBi zb>sBq^(Mz3MA}1(!UooN|DmDpG5s;$)Tg>z=R+R^zD|JOg}^tXr92+Kz_#G|rwc%8=2EN4uKBEcuH&aW@;J~96H$IdxU~%U@7Q?oMjxWoCoZn-mR&;zN`pYd< zJ3SwWfE9cICsartZNx=~-~xly>hHp`@XD`Z z`e@WNYymY3{3#cVHsLt*i;l87AZx{d!w=eZy{)V z<5gk|=Fh6D_k_oD5xV=}^-{AuxizNI@zG;!{643iDl3g&IhT=POOdDlPqws#v z0{+6R0c8cY!pPZ+G}(zuXxcVjkXub_;#lo zU9B7S$9G*(aC~*=pt#)n@h!!7O^ zQM*AHHHX->DSm~nG=BaG;i^4wK@7#Ita2L?nydPwy&w^szAOTnAR(jO=oK}5!bk*% zS{H3h$WS*)QSb$7WIO?PQ6f0Xx_DDUMzL8J?@Jg7IY+}g(n{dPShY~bCt=_!guyGG zx8VxHNXQtr>n5p@T7sWJ%IE{OOmH@eg>1Fg=vrPuZC%o6yN0vd>lSw9y3I6U^lcLW z^&~v0V7=GqJdfJygxb0jq#UOISqoyY-&CjAXjj;NnZ?`@aZ-&~oxIT>6QumKn zl^3ephS@uA^-9s8iHwqU)=JSJbhZ59kVv|@yn+F&z5!PVfN>$7y7V}&11tgBnU@GM z01Zc7V%KlM0lpOj*oh2aKRpav!QATw?7qysKEdg}BZr|I@hB9ca(p4GFgOfSgoKNt zL`QcaWGGgn4CNuJj@No`h#*6uK^ds#0@zE&8uPQpl?_6K(&LIK7%rQAX8D4+dZ)i# zo@#*EWFcDT@F1iHnm|@?yR;nLh!`5}i@gXLurjNxz}zfNi}=_?Umst5Y!>^}4(vh` z0L|+wN-s(y0Lt5FT#7amNp6y$H{mtf(>tfd@DR#Sp%mphh1OZL8OU0~FLABnm1`BR z?SgX(HcK+dBCb{6dPni?PzE5f% zwgSCo7Is;Q)iz;?n9tuBn|fZUkbrXtwmb81>o&+xIrudXz(h3iu2U&YocThM5z*gw~tn_L%d%jKn*0lo}cQT~L53n~Y(VI2sEql|+ zYg)as8h36N8*{7hYjuI&nYDcya=TOVQJH;Ra5hcPzHTv9k7aL=5LxDa-`*_BYLJBq zSrZ^zoCQ%cAwv8%AnJ!-&45T-zLpfEf_;-#k1Zjf&h>#1-L+DM~)LmnslXm+J#3veyTYK}z-R}0`;mIY5M z;b{|i2IE&N;L%855o}<0{jv5mK;gC^JnaI{D8hpjc$>x30)&f>Cc1untEw|M7sIjM1?olDQ6sr0vzh0j2qSlxDlm7@!`UwPZ1vNcCnH>4iIT0 z-CCW)&;wiA?Ur`++(WADG!b-*R}xzL`rtbQpk;V`YECdx7Au%$#l6B5`!e~K&$gDy z_u}z>{E=t1tc2J27R{EmL}Lq`NZsl*Mwa9RCzupz6+ctKNDG6$d|9AX+)IbCl_qH= znQmoKRx47ZInzp$wBnmhnx&bcY^Fh)VKQz#JV?$PV6sxAG1CkZ%w&LRlvakY73LH( zTyiPh%Dk*rq)0uA;D8nBQ(dr`dTC}bn_(hs!_7}O@_abf+Sm1_OwZ?l8+_wkrO5zAhXbZWhHRDJ)p&kt&oQJ<13Nu_CRKGBD<> z)C1PKiQ`YoAnn$I@FeWSuyv`@j9 z2eqqVn|65f7P$ecZ5Mmj9k-xo{AN8=qsDqR4K@nK`xE1B8sk9{T#sX~(#E0Z>!A7GbPIH|MjPm?`z_W&>MrUxT=U8yq^pt)q0HL3coq z+NhD*w8gJ23VbaZukw)EMDYM0NfD{d{922Aty6!kS$_>YZAqW%F0%=FYe-8FudM=B zKYk%SxmQw1=^*Mr$=w>Sx} zP%nUN9ZuTFOj?5I_Q)56sA%F$bD$oBMCBlPkW2%FCVO76=NWqtw6D!KA(Gtpc0fD- zB1m$>w`RNUNphqC?%K5^w{;F^=pl#^NMLAI7%sXvDuwE$u?9{}2_Q?#EkHTomuC|Z z|cD{iOrXllqjS{LVMu!>=K@uSH5^^v*3?gG0ikQ?Pv67l2wQZvA@3I!bU8=sXTGr;+ol(9o(Ip|{^_4E~rznTHrq12hJ}Jdx<;l^#I9~K^ zc%)4jn$6r|aC+Z57WN%oXipA>#Jv-6!SiEr>6Qk#a<0ZNZUfwiC+nRSWQ1&sjhwu5 z_-y=&Vl#-c&^extsysi@aCgS99vo<=HvzxzUE8hG!;>w4)yeo+3`rhz2Ge&ChZuMY z9T-Xk(z>e+_a>cX1<_lQvZcnJUZ*M!um`2!w~xG`T*1WUKg%~Cxu<0MiC;cQULwhv zmM16RmyhS1!jgX_z1#<~u#6JG025}9`69kADP?jQ-~cc)uK3{avWn`IGVsKAMt~In zx`GUJ3^LHOoI@sklBk5|RQy0YnM&u7$<@gn5O08KnH_JC)h7^mx=+Zym5w#QbM`4K z#vt7{;WT6gI8n^+8>0+5V$!+Q>b?W*vKTOc?x<%Ew5J;7YQ#Zy633GBk}^qlOdt|w zMq-_If`gVx2_UFORt^Y&%qW=zq0s4ty$|y|vaP8j``>EhKcF#8nwAtR5bJQ)s-8c{ z?w>01A&C(Z9cPh-M2h>6NRE^YBp4RRMu22%>iEJ8kyb zglwl`_F_O6RH7*y5*2e7hqF|oNSuLq1-?jUZd-&q++%A86^)!RkGn;v_Q;1=hXSW1 zpflJA<d9t0Vx~JHJ-}8q zJr1a>$3e~xo(!A;3tK%5Ko^HW4*|@ZE9ITH5g2eLkya2_U*e+&65ND}xMj9$n zITCZ5uvuvH5l7(}Y)KR@-xonCAuo6*;0(xE5*TSCq`(LiErOAD-iyKoA|NqB**+$R zOZjsEnwP*vT^baH3y2(Swu3UEPvxD0x>~xEX1ic9b*r*d_*U~PT1A9Fq525R0lp1t~BRU)yUi-8y-VW*Sdr{ z=2&|`{$1dA<|TIFc(vhtvwtqLHS=Bbv38TurS?3|-X+;p27y2y4;}`q!-N8VqmYn7 zmOq@|tZqdq<14l1IJ>HUufoX?ii$5{3@*uKKgEuCxtjJ5r1 zLJvFM9*VTLe>mR$ol&ng9&h)rz`g-oVt8a4xnmfN3$)auMw|dC_yx813HHQEn?Fu{ zHjC!FkGy@Qg(GgR=2`S|Dg>S)9X~j5)NE0YoM6`&uc)pQ?4ebn_{WES!w-R(<9HvT zIOHT$)hF7ONPj)?MEekY^|BM~!}_Fp|JRB3c3!??=z7D-JrO(TU4LemS1n?4ePMM} zsY3vP;9y##Qyt?EY%TNp@A@ z%2o_qy2z#K;uf)bdo)2AaI8y#c(gjau8$eaXP(W7^h^b`{vpe z#knry6*-oiR1YZQWV<%}*OF`R8Bn37oorW*Dh*)(sHQG;7kqS`WzM$?&*$fmFF$!| zO%y^PtQ?%0FcqkIC(Gd8cd}iNG{BQiu}6iQ3)C@t;rO0EoMM;cV`oVH?lW;!cc4|S z{&Wg}eO*0!iv70n#nQXx*b!rg9uYjh1hpZYe5YAeRG4rz0}x*Le%M7;3RyhyCu(j| zZ=Y)KZM3Tqr`i37W4(=zHP9Q(Qfyf;kgy|R1|*HbhB1*`Bn>ux573*!>X#@UgSIgb z>*!be|35l$$A##`pl#I& zn9u)zbmGgGp{Yhb;%ze$u-^aw=tL(w0d@Pf=|nXG`=uK-;W?v2A?^;jrp88x2AkL7 z5K5#7sYd)Xlqc9$V%l#N3qXroIc-uAYyh))0P2DRV9WB(RoJo=`+JcA;Ik3)PVG~a zz@6K}_p#u6cu4qL8rjjX)I}>nxS3LM*w6zVPDziqvRlBzR(5NvdzEssx`jLUvFE}1 ziu`UlaahWJI5In+5Bk$A_(j4|){6yySFQ!D|8Skyav$DhAGR6Sf}=rzbpFpNC)Ij2 z2;TB3qb!SZaMvs2d|4c7r8K-yX?fH8I}^K~6(2haZs)#k9Z%2U7YVYj`$^y!=fE7s zT`!P}slwVKourC3D{pL*HF~+XGJM?#UN zJ%}jeM8?84nKBlnc#*B#yI;Bcm2+W%ez2t4h{HZ3C1nGRlH5T1^d^F$r(<0G&B7v_ zDYi&u5Q+o}Eh~22f(5a+tO!O$V5nbQM<6g{*gK1;2uW2$#V+1MU5Ut3hA^IRnn7l?jJ6!$$zW_cd~}r{?qJ zW~7*Y)jNjb+f2Csal)mSyWFe^hdX!ff*=p!`)GPm<@?v4#d68jXMgEC&ujp8#>p-R z>O1#8Jn{}FrlFc_8=<`$$894?o@xXhH_;`Ej43sdnTZN)Uc{Zpx%M2pqWlU-6jTY) z!Nl`ban$g0VC20@opp{q#Oi{s3@eZrYh+Mhr$L$ubuVh4@+D5w(L;hpDTn~=x$s-5 z$v6LFc&{XRLJYbL^ST5+ktcR@!uB>4A0lW##Xlb*(I8@shOjX_LDV3k-cYO#>a)3O z^snuoSZDxliJ>`EwrkZTc0^{Qy7||3?cohpjcL|ffHi@0tAGUz((p*0*<{IV;kHt{ zp|MB7{s4QM&#;?gqJO#S%7KHN-LtYmVC(PW3VnJKG7D`V3Noj2l-;l46|xoSdICuU74e? zvgdCRN7>{x70KA4r0Y%6npNnoBl&BkgTW;zJs8vL@SlWC6QO!m)LOzLrQZ8pE-EBI((aq@Re`XpKsZn z=KbfRKchA;m3^1~sPoQ+Wtsn)}Exqz}Rnj-|fu-C?>b8OR#5t zo2H<;-*Qvsd(sNA4V!~ac3UH6Li9< zP~k&yqkww6mR{CeXxNKzCZVWWqsWcO0;h|Ih334!f7z8h~RkvY%#6$NQGBFr&>l6g;S402CWVIL67`p_B+85E z62m!YvNqC=984HED%7%L8!=GItP|Ro(JnKVenti z9`f&xH?Ci#y8!l58T3J+n~gr5O2NJOa}eebZ)wnS5b~A<6^-A6zF?UYMq0J+wrkVp z&7|3!RLfFPkGLG5y|J$@f9$0DVT*}|Y`928(+2mx3$5%P4U*x3CI_+^tc zx1tgB1(oy6wE1K+)zq=)+XIhD8&ft@Vl&WbQIg^tos9CyCbkF;^*C7bQ65E zU_jRk0|o}{4aYd)f^&t<8w+ZXJ$MeRGXqzZZ>RozfxVaUKeh1!dj}em9z{cHj|=S` zW2C&FI@=#g0#Ci?X={i!3X3*+U%nP9T;kcW8Odpa0k-lDPH(F0Tw}% ztG6gj^V~W}MX)WLhRfPewwWZwMdfgI%HIMWiTW{j3f7TBa9smhHe5xf>Wj9*KVMr3 z4zLk_z@{e6s&c~_7VB}?>|^@KQr!ixhjPNv4=P`Co})kjUm0h>Gc9^*QPc>B4X6Tg zXJXMQo^__!^PUOqA^tni6=P-OO8d0fzHPlxl#l^N@@c&E5Axio>Z!LURlreDoB8y? z&ywyps&=~=&V2~Y05V5qRU*=bl?$V;4KQlierhS@w19f@gv?S(kvpaIrmbreLZ-STpx`lobGnM;bdwe(n$^ zIgb?+R-=(3Ik*k?JOjJc9JqIEe~CQ<^n1=F_Q=XUb&+=;2beCs#NG+U!F89|LwAss z8vcB|=NA4!-P4-^3yL7K6`8s~ggfao=;b`tUk?&d6S^`f$OEOoRSw7acrXy~@48 zo;>iP>+7uQ2xmzQPd~g=?qFTb!QI9S)Uh|fs`z#o`KIL%b@=1e*KcdshLxeMH0J7q3Bv{c={t1{#N;?F?^K)IJ?E6Y(9BjU#aZc?Ok2 zIph+v5v-VD6Z@cn9L&j`!!v|TbKI3^EaKjCCA2>2(>p0+r;W&2EP{HK2UxZ=C`ROr zHICHBnjzL_LO6+!0KWeC5&%ezl5h6MH*4{vb`Erb2u}_44pR@yI%A`zpa8aR6LAMO zrk!ws#$g<~JkQ5X4ko#Iw;X+z1OG-^uOo$T4i3jtF&l#*IqdYM&LqDFIxzU>zH3-mEHbLjc1~>X_T?4x9=+ZjpUnX%h}x zgbI~ubS>~XyjfjyJF4HZ^zGa20weFL`=OzWz%`@i4!a`n zjq|aa&cR*cPP;a*=~D>Me7EbJ_P}u6f~7~@Y0owA<#(FxAqO>d!v)50qi5F`r(#CB zjdIVQ<~^A3!z0P+5?*R4X-9en^q^pIJH zmb9Or+vq)Nngv-l6_`uRT0(5gy3hs1|#_MB92m1B4>kh3_fd>#ExlbQv5A%7TZ!bQn6l=>-DmpYOL@ z;MsA^U+njdo7Emm?8*JT;#|S{mcMBU*lwNratXYMZ&ts0z@Chc-S>cfwDF}f|7t%L z{;E>F{8tD}bt<~l-iNmbEwu;Z_SZ}8ABDfHR2@t0gRQSBA>t${cE;yo1u2~CfitPq zK9O~9X|>By=dZ2y)bPK{)Mu^s?Eb9_8U+RP1a_D320K{9tbiaFs3RV-oXP=iw&1@y3;EWTy3oKJOO=t1n!+^x{YCpKf`>|ft<H8%Lc=3W&dNhFI6b}*t{jxrc|gf*b7^FKj%q+kVl zQ)PYPZ>%sTQ^Dv}L3Z&W_er{wz?6v)M1*Dt3Hc{{7k(AiK31kD+i(c6R`T=_tSTX# z=;$`<;ij`19u@P^;>@E!>8~Pu*OGqcK8@T-`VKYg>QXC0`&SjFS1T?4>c4nzo`W~J zXSiX=c1Ab?`3q!3PfKykGThzHaO;dSCxr|?cVp+kKNNQ%;>2+4dg#Vg*AjuG>^_A! zX8L4lbY*I$uoCO4ep_LA5I?6^<8^gCXsz@dID^V3&~sMoQ>#l}_ihxW5$S-g)ByY< z00Ia@bOjw5!T8`A#}!I8dbH7HtnfeKaG@4zFba8Cg~R4wmcI44eVQ@7>(gs2^K!dz zI#*#-How$zJmp9Ct5<@@wBre2M82|Mk&EtEm#(n4%YPU>#mqE6QA=@Kj$7DafFi(I zI|miR=ELga6<8hKRzG{v9+|utZ}@g}XbKLc0gH}6MF7`5UC+E&-S;E}>wDE_Pr?!J zezo0G@K{`?e)AMq>z5_!!4U&Tsee6X+s5*xg-;_uFy}vI<|k$9@pgOHa8H>sp0S4) zti#g5_RN>nPS4mwq8Eb@)!%-d>{lfV%vIRu)P|4c>KNtPEDx_%S3Lus{-XNy8GFVc zq-4x-2UF0C<1j@Oh?=bl4m_HTn(wMZp0#TRv|^RPCIo$%RRyjLQ{fybK*ij@)YZ@0 zqs>?Hr^gONAK4C`E>j)P+M~w&3mX{T6;6*_cu*qb8enUHXdaY&hnVY1MWBE@$;A=J zJgO1T*?$=LY@#2&ABs#PRbzzW2yP=lm^*Y{eeyYb>_ED`7J}i{#KcC^GidGs*iXWn zCKI?Rv#^Y+#i}qS$ zg(~T=?I9Z@UMMUUTB08Nz$k)sYC`7qKrk0a{8IaM*n^CX>K7fhRTzBU&|z;k>5)XH z3GN2d)j|Jgg^VQAZY137&i6hsa1|DdfCnmZzxmN%k~w<{Y&qB*)BH18_QJdYWrNYzvCLqX4(~PIob8n&ZPph)GA@e<`Cclk#+tm?oLz8s9n)^2VXIH5B z-w^-%g&OxadpI5*@i)BtLe158_x#O1xpFP}nHHdOkW0WD%*TKy`Fs+%HjQy=xy*3w$FL1_5BE3}{?J z<_!^jCQ_744`FEhc)n^|Ygdg%^%xI9kBPQf3S>b?+Fz1}Zo+#4-SO`U8t1=f?*~}l zd=DRAt~R^}G+v@=*4oET)-Xg62hH!-#LPDk5JM+z=rS;sY7p6DuFCT|;!QY0TZXCd z$Xa_~)g@G1WrOwY;6`>LcU>CpseiXemSF&}q<%*nER6Ugv;u9R*FAp+3t6sa{X=?v z`aclfy$L9FtWnOSgaK`_leR??Vx}QhoToUE3enAdjxj+0roDTN*}n zTsI{5c6Cm-Ju*iSpdMXo5BNs>Zn?pd52V4NAJ`+w(=hom7*)r8fN6HUs{6pMtndgN zWQui6XJEqI`4j4)#A_dbk3OM3|G*v+{|NdBQa_f&Csgf+_RyGcz$erpAKJ4_@Ci-t zh2HiH^3q;_^17HwbZRk;L$h@t#og=dfu(nQ z1+JS@6q`?cA$w69^U|#eWm&r z-Y~PlM*Aq-UAEDlXHo@S25dqPI@NKTuz=51_ijSa%-iaK|JgexUgsWNXWN1p*YH1k8d3Dp|LpO` z#VYSh`#@Zd_!5HJo9d!3?W3#SOo&7RN~27;b9f)nU6{%%^UZ6&g7V{awdYrMRWZdY zT7+=mLXs=vSzjT{0@quf`CMeT@r%@wi-gwPyXP17f%w*1} z(2?$!q1hOFv~oxivUv#YTyujnVuT1mnpQ3~`k|snVXrC%iwq(2E zbIc3WD8u@90#4or-n1wct}--oxPv{I4f#J>Hz?3 zp`6bP*cO1rs|8VK$BFO5utR7ULlw6KAz)bSBIc#g#I;fw4ZB{KmY^ndit-l+DVuB6 zS5c?7$a}jC&!f=1jL&gqLRs~@9Oq!;jioQ=amG7p&s=8*_U;e4PCs7F zJf~WgyZ0mNmK(~-)XTX}a=`ksB#eGdVQ#T-s$?`AiRM5G>aV#0>YNO`{Wi}z$oNpL z%yVj^mm1LMY{+wxMeif<(CwB*Bo_oWG~byB3Opv?nFajck?)MLJ#di$4JA9}?;4nb z)~a>+&Pbz4*#*v6-QeB@Xt0G1t`|aW*S@rss?SyUt~~2gZmev z!EW`tLT9W)2%+y>A4QoBC__niJyOy=rk*HtcE{|^je{EIs)ORr{>H27*0?h^ys=ch z9Ct?h5c4SU9Cw}kW!1IHPB=Rox2XLS&cS(q0}&68Y{*fA3LHy4mT(;Frt(bsR(uWP zlM6ZGe)Va>nF9J7Q{>#i`55I^f@vo@)H_Aa2n<%d*qLEms17W4cEdmXv!hGoTtL2pn`6-XQ}f`$biNNHPa_PprF%EyCL&i z*dG=6Jq)|g@qHqw^QJ6hKBU~+2UW(93N?)1_uVpRkorZLbKIy5dbn@)(Ss!j8!N~I zfTBChL?Yb!D`RNUK;@J>yJ%o0JD6!;q~e$EON(3j+Pmf8ByXr070%u;88uWmzd$2j zRXFaD&)Gsh4`Q%03Y9olCfKQu-c!j}x2qWN5H^@1M4|{RjrcnbdsEZvm8x{=%g~{; z!rtqU+Q92lWG-X|^VrN@_N!s^pdw)CGpuHNY8PiwP{e8_7;+8p>hp zqHOeI=7-cdh~bq|&aMNUG5g-jJVR)Rs~`Sg22w~haZ(HxJ}CI<+k)u~FB*?hE1B|! z#uvdlxfcc?X09l>2TF_9fzGK|fd>zACKwN>BL_Jt%*i!_oLS)q%GLHA&Y1kDyW(y{ z&R$Za&a(%Wt6wIa5@WtPFX`-xDB9&o=T0E(q-tlz3@_mTEngHGbKDD7HA-+?W_0r4 zMyv-XpE$^)?8s*|IUJfC^P#1mR6DmAC7Az6fHD!h&zB!>+0Ic%&Hqf+Qa5r2t(G{e zI2=JMINa9jX^~Fu1N4buHs1v2M?2-!1_&w#)sX%NB%P^{fQ$gN@xkrtn!%2XzU(*D znYR6P*J1F-dQ%Xc$p9`1XYx2E0(g{wM?q7whdA5!{|ZTa=!O)fs{q&FTyrNJrVP37 zg0Q+~h;wrI;?LAqL!43JOFvVihax1%`(8W0xBa*I-twW&_J~aWbg1Km(Rh5AZv32K z&Zq)3PMHJhO4T;lDUS>@z0YjqXM#tsrpIGT-P)`#LvqIFRPwm0it=2pf_dh@Vl{F762H}y5ze*1^2;Nf*)!jL2i^Dc z^kd}VhRUTN!%?E{{C80#?PJd(adWYb5oZPWs(00~BOU9fUtcGYRj08PZRP}5pj>*x z=!qbR&vjQ|4|~!~_*@F9cs59ywgS@Lzh> zjCO{FKQC3Mk9H=8!4*m#udGnZM?1OU2ZTjbs<%fw1rRzu80`!KXU`u4ajRQRAL9(Q zc&ZJvRfxxmja-SpsvuofA+#i$_@xUo1-^}A^=bJYp#;R5R5|| zJ=U2z0qarcLJXttgC`J4Ev~?_)F44I22t9|EHx$DJL3mvwt(yp;_?d4tocF?5RR#P z{Pxa5Fa6 zhR@W#6R;J!PhCC1dC<5=?K{yK2z}FO6P;g$A2igg%bc0Dx7~J;XT9< zT6^Q}+RxN$Q=ILF!3G1jr-f031jpT#*x!`#z7gyQ-m%>1pl4-pV5;8L*$KLj!*+ER zTET{r`IAClMRbGtQ=M5Q59K1jhtdG%N@=nJT>99l5RmK7SLaW4=7phI&D+hnbl4S9 zuNbv9aoL;3CF77`LIH;dOdYQ)TWZyAPJb$1c5|ws^faeA_vJU=(P&kdxQp*l&rWlW z%3u6BT;59Dmd}@tbFmZJVF~WNfgJ>4$z2w_heOGV;JpoGey-+E$5#I;^~rRQiBi_? zPL1*4(n-5J`9?eoqWxz$xAb{p<-t{{3>xBYRW;KYnfuV?LicLlnNB~9e)Yaie>HEW z^UF-dFgo|uOlMEy3bp4f=dH2pf)D}1wZVJzU`_CT_2=r)JwV5Gh3c<+I5%Zl6_m|( z#-Jlt%yzEs^P<1nYfnI=)QNjKH)p>!_j0C`EhzFA9?%6k)DQCKF?#_O*QoRNa&|Nx zUi#O)K+oAVAKMQb*H89#F6sMNqoovRS+tMylNt{#;3SbMBu6X|F(B@?Astj)PGVU` zR9%{B$HS7NR_%+~@w)o&zJTQtHE=)YRtO3B@MtJDKiSV2RB{Q#5KT5hN5C%LR;fw* zV>h^c)z|M>VoM}f~_9>=rGR;BrP_3aVy)Ng|7cA;N^3`0cx)d5$_=!c`ws{xw z4%@*v?%uy_9`9goW9E6%*sf`CY$=xpqCre~ajT39oFmT?*5|3o&OiyC?0%_w@-U3Rqu2h(nF{;aZa)IS&r?VI$T`jUuX_4N zSmu_iZd}G+fE6&pJjP<*2eNR`^+g6_SoH4;4B9Zj77K~WwLnN2^6cz-xO2|-=cm6) z?zIGoANlPn6A)Kxh3 zkFgcMOr3ayvvc@@m(-0%ID<+bFGs^|u!-iNk$iBLW$J|^zy=>x|2)FE0hfz@;+%lX zx}P}v*$?%gfWI(j2_61F^H~=wv_tSCixaYO(Q4 zy?UfGGwQasDOB`a#L>54!|tXH)tOI+6ms|<2wKYf3O0}7o~Oi zg~Pr8WyJhU{q`v5eJsq!9qmla{^adPJ4202maaV7F%3+pw~ldUOl|rCZYH;Hm}gOl zK+@S>vIMuFQTID0WZ~3xWj;DxxGHXy`=B2}lWuh)N_X3P@B? zR0M>eQK}jcH7aUAL=<@WerImU1`vJU?+?$D-JLt_&YU@O`eAy3w;w^3{4P)p)*eXBJ*+=7tsVnfgVW={n zb`R6jK`Z<|Oz#i{Hi>CGDEI+v9dA>U8+;quZljsE=}j(xA3s4co}Ri5tGtc;x2umU zZrAHgT`nmku@$f)Jd3^r>n_ctU_jCfo&SMJfGq zm)<;Y^Vt9*CLi~trMLCNyN{3JG!$()8Bm8a6;mFW_3q(6`!< zx(Y5YUh_hm%t%4YIQQa3si4!N^bGggUUKb-OQM$d>xr=!dNZoHU%x#59h+ukXCPL; zuAxQ`0M|{So(}+k4$uP+=+|DT{ol-~^}nqP(ljLonP{S{6!qYc=)s5d+gOe8KJ9-9 z2joWz9}OwlUNS~w3*V<}M?){Nh?b20uV$1&YaZ6?{x{R2XCC=48;p7s$8sM{eiXv; z<5d1AZq`D|8Kd|9x5LWE^#5w*NE@r`HsS$$2E@~9tlrTa&SQeG2Oyk3_JSB9cLHvd zrjP3_{_XrCpU^w~+q%_HD0HT<&?V#aSfC%Z*+@dd=hZxG7e!tgl7XB@P^M7(Jk=R@ zticn%c8&Y4$%FR^aLy`w$TT$J6fox zT=$SmFj_k@w$p27W1p`abFGCOz~dG>V`Mp47Yd z`-`b(C%6<#?f!vQV6NSm%!_CHu3v2Np6zU|TlxREZZv!nFq&Gf6k0e5Y~{sY^V>-^ zuNn1}9>)?Y79M0ir9Ty-WPcR*q@H+zC08*2BbwBvro2zFwEt;ccO9bNpVobm=5@lvIFYYF_g!F? z0}9m6E3$HC0WjCQw6Z|q{0|C1*si9All2ica$(u?WGbGlXJnaO2TYmymh*VO?? z@bhYouSP9fWLoP?1xmr9XXsC50gyw?-77}ue}~^R@}bR96JDMK4q@F4eIkx|re)GLbZ$PdN~hb)XAQ2)_Ac{ouBf;i(y{?gC0{G?ACU92CxCP{npv~Y>0_Y%!aK) zDf#E%nmU({xpt23<0IIDL8m+sQk)5M^z1N{g!@`RDER6ey(@Y;J_oY?oz!G5$fdXG zrnyQ|GiffQP*t>ME@=1l<2W{=OI?RxM1ZW%;gj`^laTsA%k)9$d zAijZqfr;$XI^d8$6GuSE@fmF@(i_B7;Qof`5g-F1f~|D2NcYF=sa>{ezMe{5=7ZI% zpds`1j7HY1;jMsn49~dSj7la14sgq08PFjbXMn`qF<(zkuHq_(FH|M9b&4<5c+@~L zRH<3r_XfjS(|t9!J#t>#o(uG*(aL-##wdc769fa37U)^>1i$bR2#WaLRWHXIU6iQ( z6-^&309@>)qYEJJ`;5Y#g(1v(n)9sQxzkowk=v#fKoxb+6f%c^vsK5~&2?<^3=0L6 zLdNhG#V#~OS-jJY7wR*b{~TdSg3%SOI7m8hoXj0pB}%UjCW+BRurHlO`9~J&&CfqZ z1jyE@=OVDpduhlb%)5#nS%gzEg%&T;2cz)xB3;zJ0{x5iYcjxp#@I49ybB}pRHj}{ zbe#$c7Bb%yTD=$&oKpIFF}i%4LYL_M>%JP3!OQQUe(-NjJ4~4y~cj@AY6eM*{@(aeNv2*c8LBc z2Eo6cIxp2z63YM^DhRStiUQzV+DCNPQmE^nqqt>|7L-u#GQGK6{6-P$iWSsjT|-zU z&w7Ml?LB&YnV#jt94y@tZbZ1LV-6u~{ucr#07MzyNSl`7&fY}(mg#lFmcpqPs?Krb zdQQ*8y_5BvUU%>ZUQzI zoCwn1r2fx?BLfJUFA{{=jDB9vO?8$W!?ElEHDL3Nr)AIUqf!C?=7ya6wA%{3GECLw zo*KWPw}RN`))(~LcDMo%zJOygz_e3ycQOAnWLkt)C>v!!VTXd}q3UT3PiQaOrqI?G z^c!LMl(iC9$9fvRQtx@g$E=`75MPJR0Gywrpg-T_M}?LMP^O0}Wx6VL?rsWyQ7^~^ zItfswUr2Qx%0@LD4J2^{=)H|E!pNnB{_`RX5mr#=mmozhr6*p}GvlYl0R9jqV-#!8 zO>djmUV@0Nhz`F5>%RhO`7&fMhiK@_xSY1oM=$Gr;jN#v3g$I4=~X=?fmW_U^(xx8 zO83W~h-QsALfhcK2djj|i7C-j(hf#vD1o%1E|l>h0*xzEKkXnHB@k`zrN>M3ES$b& zC9pj`5lvf5aM|vqKTGsF$%k<}Hwjwjf>%43wq#dyUx*6o?$XZ0 zP{nG!2X-K>6pK|tSC+!S@*vGGgT}@kF2TibzenfXs(+GFIHU5WV9R0@Fl}_>)i(p1 zg&P~yU-;T!9mcKErCg3>D(B~Hi0d|<S?s;Ec`Z)yCOW!SuM?rxne9KflD0NB-8X5%zZusaw65<3$rHU}2}7yN^7FFv~rc|Za% zS3?TokZ7RiCP5qBX70x_cLtsqTdbxfBI2r1mq&-*%ni0<3P=#=Mv|Q{UyPZ3 zSY;hXvOhFN8aJ3_Z;R(qDy>lfGj4wzB7_C?D0$qU&|lp&n2xqQ5e#5xB)Udlu{3nO zo{-1Q52{I?357Ek%|cKCzWDGU-!npc-W(gh;4fQj^|8={WaTPvxLRg>XKy6+PKX}2B9Y$; z8N}7Jco~+%Y-tq_RjR7-0>nf61w@64YXT~kqN2(cZh0SJFEBsV2x-T)73Qv>K_0&` zwgqNGu!O`rW0W!vyeSa74AxraA;R0H;XBNz)Cu$U0QO~@C;WlMRD=Zz^_ZtYb002) zF!Pe**S9yesh=asL2be&WQd%GQ5#<#O`SFXbseM|HUPODqWlfG45rYM4Zw^gw0VP` znWyqHs)e+B%7;Ewb)~|1VWL{seEhTgPSNkKWR*CV$nWfu!>!}lxT1b=yocnx#kH(sz+8Cq@T zRvd4b0dXkr8QTB?5HKqf1aLc_vjhzPTU>svjc1SRoP|}PVy#JYATo36u-rqFN;IM3 z#N1$g%n#jVl_JlD;Bj$6A=dn@i@&a+hN8ub*!CTLU7M>+He#` z8^Q4T%0X05?H~f)@I;C^^R>pI3;3n^o#6yQwDcW4C53~%xs9dF_=Rf)Okbfvm5X-1 zqo=#iMAI+t=-C7RP=SA8i^z1Bhhz0Q=a{jac~CtXu31Yk!~PK6Wp1|SuDNEP zeOH%x+c6tPVOw}szzO)fMg6ToVFi4K`Ej|2HTp=>?HOUHYXLLOhe@S6xD{qC%Onsz zp{mmt!bF6#IbQNbmQ%EsG z%xjBjFZ>TRNzm1s^v6J$!!00-`n{*ualid3-S?iJRIkSN)nmNRc8UmpVROywM}jav z$~7QSvn}rN0vsCJL-(KZrPB}Z>FmL4cbw7+GyH1_DF2NCk{h!d$GDQcx>eUTrj;MO zuQz9lYqM*{D(2k^i)&y9mJTFr)(aa$u!w6on7KiCqIfP~bX?zfs~DpA9}`i)I>0~hLi zzJ(pkuu$)=PFFq@Y;=0bhkAZ39|{Iurr9@r2-2ie+OwndZ0$UlI&I#ZFOSkE>u!+WY{nBPHz5*bq zZvg?C1hc8C5~eCPHJC3VF01iOd3C!^Xnn;edWUAae@5$^3%6!T57Sk(TfgxM*nzY3 z{U`eJOz2_&0Kv!x6$F%lhzTrT#h#lCGEfQN;FTR9CpXZp9eRTMl?3WA+O5;s9eVSu z)r@A~G?5B)f>%>9&K&or7DnN8<;;2QPCXN<0ALMJoXqKZao19Hxt^6ZE5@?9#;E~W zw-2Pe89fX4uZ?(0=(C+__dMmGCJU))x!%lsu1`f+(HwX6P@AL!uz3@y@%`R(Z(tZ~x^c6H_m)Wr0+M&C;j6`QHs8-(D z1xmV*e%^KNB-?$e$HVcX&!>83x&mguF^PAszLuXFxfF2(&6pz9K{V}C-5&?n3&+z3 z%chmkyPxXKGJcB#g0r%#6{R597*kw#Lm~h&W;lLTQQT*`ul4abURUfs8|X9Dtie1r zh=ZjCW5?hFP>*f3EbQ+I^iWCmS3-{DHl}d$M1|TEuMYe5A=qT@5HtMpGrdJiTXLbX zb z*{!#!XRa;IvYCC*bhuXj&s7K3LPfC1|PiI(jFH&{i7_UNOc*@7NRiX%D%>Eq(wO4sSj&ok?E_=PJs zsFCyl&EcE}V9X0U?y0L~*WMbhv&H*$<+vA0UsYt@R3BYH$Yc38{;dNa&E9vBsnK(o znjMCnIdM$w9QV5m`4nDgYRVzbnhoF=D1q%tS{{Jl3$DZm;B5BmTGyBQiyrsMc+$?M z*F#7NUDx6Mq%t+(+!{wJU@HKCV!7{Davy}ecp{BDsOO}99FHEcUuN`31hC}`F7nG9 zDm@5vvzICl>U!Lg7(XZK19*7>f#JXw5r_0H*qz>o^m=vPt>&0=Afn%fKb7URHGcVLoldYAwB-=wG!>+Mqh}{6iYNjnXo^eQ zsys(>{D~$&4gE&H64gq-fqHit^sru+nfC0%x<8_3 zwsiGjNV+R%!C}1-KmG{SAe#IBu-<}yrhSW_CDid-_`p`t_-}Q;1p4qc30mbgV&FE0 zfKQAFj)3BffF$jmZ*`HfTVsraVEJAHzQp*en2CAx>$ecI?V*O>!PsXnJ@}noKlCTQ z+ZWQ3?|@6+r?0+)q0)ImGx9rrFYGHnyCrC>};9it_}tAL2fTEORaG~*5f}pV4#r4VT1a_?9u3o3Bx(Ekm`lc%fXFR;B->r+U#(r1o1Z*KP$a`GKGGoV+=F zhu}(E&Tq*}&#yAa3%rg3wccTnqu_O>c~!_JsMtY=sNKO7FVr>JeN&+$+- zeYLNgU;~#Y~@RM0sTXW2r$vS#QWEvD44c zVwrHTT&*;`IRapS@d%HJ*oP4QE&Cb5zkK@qXZ@<={n+dr08|G0HF!7{alr%j7rlWa zjy2)OYphZ|!>b-5(ec0Nxp92h84eHd{S4GqNt=Gr6En>vIiTiViNEpM7kKGhS5>G# z3{oeIcbHE8qW4j|rvOi_0w%!g6|NP4&Pr^3~$av!b%-UaAwp`aVY%a1jV>KAKz=uZi?P#7qHm`&b%sS+-gn@tw79qaq z?$LP>W-kh8r>U31;&u(<|Jom_XP}ud^Z;n8X@(PNVWpa_-jA*<0OrT`NeJka!KNCy zoUD$4iNcyjV_W!Wu*^zLohd;+eahD{Or4P?LB|nqNxfP%9j(Thd)jmjv|tyb@ifA< zQneO@u3qw+!s?$JsJU`=fJ%sVI-XuXic4Y(?LMkE?4)EEc* zM=4BdV6_2bW^}H0Jlwg4Fe7yhF@|9XK=3)|8iE^Vt!pTp_Y~|qdTJTj81u6s6sv@^ z5Wy_^yuh-+Y#34Kz%*?>uHVtc^hV|&)M_WrU0IO6_T~RltVJ9dhifi!^beKfNp;g+yvP0GU-j=I$B1gzbkp~4e-5ARk$TdpgO zicdga^4AHd1t-#wKR{H>q!<6t+YEr6vAL^p>|BGJ+QjKtbVM6LCD z$O!f@`yb3fX?@`=I8huq-=rk*BY}<5%oW% zC-EFrWQOC>%p>kW5NrlzL{`JHm`i#f$6QjlpIG}B)+72Fb4j_Dx|pM{;b9^Fh~V>W z9DsD>HLuwYL2}9(T&^k{Xu!uG3082J!i-OM4_0*!qmJqXX15KP zB0~GB2B67|(w8XiGziEK=#tYQCoAZh)B5Gf=dIg7V;x?u^t7IG{+OQEFE1&y`PRU$k<6V~<(6A6MRpRl3;b^fU6$5EY)e(EP77 zR^r(^cy|8$`x@nE)cpJKDBIO2e+vP9jFV>N-dwr-;+1`1aveJ07-X01Kx5vnb*Q7bd{o!`o zEl$~1r)(CiG`QX#%8anvR$67B(~Jna?MgPjRQvY5Q&#Pib&0g=t%DVH- zampIR*==Vji6~=;2~OD_r)!E}lHIo2DSO2!TbFIu^CsK% z@^kF6At)QDFee*Hs`&wp!di#}^SD|4Xwp=bwJ0W#PsE9NVXey71P$F-7=s?>>(X>fg#(0G|>#w=$Ldd4H~9r z)5YbS1u9+Ki)H9vM_kf=MoI^l53Hf`h~OB+!P?A}wg~Kqu-5E0WERazY3K1FB(ieb zFpCNikxD!W+PiUG(G%Rr=(-qi7M0W$*}NqKLU6>r5Cc%jTloSF%Mfib^ui3m5%&&eh-{eHhWo`h1Y9lf zi>3(7yxuRGbvLI4{+ffD;Er=^$26WykdZ&RC?ywqMNErTw)W%iy#`z7;t32{!2Ck> z1k1ptnc}MGFO_|r3aba5&CE<;N#Wy--&R0!m<%cWrc7}qwnM8Yn)TWdiFo+#2RpdB z*n7f(dU0M{+Rn&nZQWh$eQ}D*BNw#ubg}owDc(g>>xoP7wr|zLW*1U}Eb$b0jM6MI z-Ss+ku8((LLvgc3B0W}Lv~zzRO|NE&1lm$x#JRUe&nrn!q+jccVQIXAtV=^M;?~&x zF6Mubzfi5wz1gCvUujcNL$kyZ%Rktln~>) zpD?B!t^$l^9+5MD_S&yr><$K03+x( zRLuTRaC_mbrngyS2x*%HrDzAJUQ^K>C}w0+k%hoQg-rn^yJ$Urx!(;o0pz!)VpmxjpzqzQBaGC`~IDO2YVp9=09RaJFi=IAR*y*W_qWXfnE5~6Fbw9!Izf)+lng-COK zw{~F*vE1eQos3qZmHU%Wid~mbpBA?g)c}c^t#R(2qjy>Z5tq=u)&N~tCbverk4d%> zzT91IoNZX8@Ud#C&5SjC%8t~forq`>v=b&|tRLsC;Y0R&G>vE@dc*Jj%{HPzW30S~ zm6fbqRcHq16a1a_v1ulw@Der_?X|ZlvaQJV&H*fHXH&>%D?Vy~<`}dKtN=rlS_q~) zDm-Y8yQi|4hh_?GV_{I)ter@O8l_h|T$~4JL^}Y)0h-lLG!Na&h&hedoiQCnD*3Jt4WqZ)i#u>|>UF^Ru{xOl zBTkK-cvOi~F4#daZnxDPu}|x1e@D^HJ-ZT-jzn_T8XFGi@pvvO5*5cdP}*rt}GMy0%WTs z1AW+8%th4YL6?fw*zDPt;zlf?1DA?6?xV4k)J5DEwI0Vl*R>A$kfPnRw1;Sw`Ype% zZ9=bvY1V>$*+tap_@`zmN3jq%AvUB#NX)RI#H$?p1=C_K?40*d%iQG>Ec11SnCU(i zOI3zw*Z5d$ZH24V3lt3@#vGtV?pe8&#J~ukZ&z_CUSfJztaJ&j?<(qY;H!OIMMfJ- ze#@1M(ToLuoPeEe!SIgDJ$OfkPkxT`3xF&3ApF^6YJQpM3YZ#wnW%@41(#tNzoreB z;beSGdoC08G0xe`a5tAww{D{QussUUSgbY+mcG0LM=+XK`zFQ*lM0@6elQEQOotS( zLQefTR(K-=h(k+(@0mSjxOh2JE8ijQiUCe?$-Nm&XxOoH5y2$=4R8P*Z=ti{05_pkzgri~~QGJ=;OBEbr zJjgM>>@ISW$|8OGxEnz}E3juq+l@z}hj_$3X~mT|9yaLx{YkTqYQvu5jyQE3l%N)G z;z2}kTG~^zcg>*#J#ibCQtTDD%#lp=3XoBVv||1$z5=N5DcX63Xo!!$xNrqE?1ek# zFx}q^sN*1&_5x-(NMG~;-alEj8*bvdmD|Y()c$TDZyF+*{O1wW*N;1kawv z5;~=y024f?x2W&_O{22j=w~l|-CHErQSiXn6q^kRi38op-vW^^CG-*3_m~o69|;^X z1+l=Mv25vY83WkGjafqxP>d}N1|H<@U2p(+t&hlRYtODUloft!Lg91)ngc?YK-feu z@0yX=DGXDI?JInV${syBh_{yoH4JQ%>9W2+B^5NSFOJ`@8kL|TUT;TVA^O#T`%1`b z&UrgNh^xFfh*+c?_ew9r0z`Ze)Q^xTcLnP^ix1Tsh?;rVW5Q}E5a}&0&p=u0YN+)Cje=D+E3gCy?V#~ zVh4zl$N@ksM7;-yhf(<60I?0>X=hv|hD7GW$P1Tur1onpB3_9+l#jU@G&IUV1hrL< zs%X#EBHi^0ow!=uh}(9+K#`h!l8sF*VulScP&*3LQQkmN&&!wUr=Dnv8z|D~!-1H1 z5q&oh6IxCEuMv~3Ft5a@4CWM+=Zn|65fhM6`Aujh}Y zO}~R+Y;~xC~3(cA!RsuJv*jVc&U@-A*iI;l8%`FfnpD%y3uNJ-lq z;q&wbIs%HcyHp+GBHByAH;9(*vvJh^2GJi{;i)&^DB|bqH;5}-NqMgKtL6(tV%&Gu}%sw8H(dw>#cZc*iBI_RLxs$ya}|xR622!`8IV1fnz{$`$52H zQ)$K^v-HhDq8-#j)q{Y`R#Wa^k&zzk&gJ~^Bi+j%dx>raw%kka-Hcmu1ATw9cnn~2>n#{}0~OwaSvc)Z-4fWY?-14Q z(IIG8MvI21cBw%o^@?exc3n(3d z_7%`q(84=J4iEI<9iYIf=*%4;z6(gd6KnAqb-oivt(@+=Q{*J?QVy$YLg0M`CP*1| zM+5nl+=&t2q2KNVh4KQm9*$*RO^*y0P2I2;d2zUCY)1y~9?VD`CNj+D!!vdl{XATu z#<;t{EKQ~Xr!fEicZqo4Z`Ls_2sIgDo-^<|t8$T-Ln}axEU_gC=m$1qJ#m7ST^NuG z@DJTG0`OHGOHYpwYk&nijugGKfdv&Oe!V}owR?~+g5lT=s|(!6SOk*@VhgWVMvAoH zcijjXzGI|V=l(T@3how->Qu-0BQ4u+W{|*`F?*2?HYAF1^sc)B6{Qq$517z>wCo