aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorRue <78876133+IOKG04@users.noreply.github.com>2025-07-28 14:54:52 +0200
committerGitHub <noreply@github.com>2025-07-28 14:54:52 +0200
commit5381e7891dcdd7b6a9e74250cdcce221fe464cdc (patch)
tree4c74744ed84120dccae6dc9811ce945911108a17 /src/codegen
parent84ae54fbe64a15301317716e7f901d81585332d5 (diff)
parentdea3ed7f59347e87a1b8fa237202873988084ae8 (diff)
downloadzig-5381e7891dcdd7b6a9e74250cdcce221fe464cdc.tar.gz
zig-5381e7891dcdd7b6a9e74250cdcce221fe464cdc.zip
Merge branch 'ziglang:master' into some-documentation-updates-0
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/aarch64.zig205
-rw-r--r--src/codegen/aarch64/Assemble.zig1682
-rw-r--r--src/codegen/aarch64/Disassemble.zig905
-rw-r--r--src/codegen/aarch64/Mir.zig348
-rw-r--r--src/codegen/aarch64/Select.zig12141
-rw-r--r--src/codegen/aarch64/abi.zig20
-rw-r--r--src/codegen/aarch64/encoding.zig12194
-rw-r--r--src/codegen/aarch64/instructions.zon1543
-rw-r--r--src/codegen/c.zig33
-rw-r--r--src/codegen/llvm.zig17
-rw-r--r--src/codegen/spirv.zig6
11 files changed, 29050 insertions, 44 deletions
diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig
new file mode 100644
index 0000000000..2904d36b7f
--- /dev/null
+++ b/src/codegen/aarch64.zig
@@ -0,0 +1,205 @@
+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 ip = &zcu.intern_pool;
+ const func = zcu.funcInfo(func_index);
+ const func_zir = func.zir_body_inst.resolveFull(ip).?;
+ const file = zcu.fileByIndex(func_zir.file);
+ const named_params_len = file.zir.?.getParamBody(func_zir.inst).len;
+ const func_type = ip.indexToKey(func.ty).func_type;
+ assert(liveness.* == null);
+
+ const mod = zcu.navFileScope(func.owner_nav).mod.?;
+ 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,
+ .lazy_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 is_sysv = !isel.target.os.tag.isDarwin() and isel.target.os.tag != .windows;
+ const is_sysv_var_args = is_sysv and func_type.is_var_args;
+
+ const air_main_body = air.getMainBody();
+ var param_it: Select.CallAbiIterator = .init;
+ const air_args = for (air_main_body, 0..) |air_inst_index, body_index| {
+ if (air.instructions.items(.tag)[@intFromEnum(air_inst_index)] != .arg) break air_main_body[0..body_index];
+ const arg = air.instructions.items(.data)[@intFromEnum(air_inst_index)].arg;
+ const param_ty = arg.ty.toType();
+ const param_vi = param_vi: {
+ if (arg.zir_param_index >= named_params_len) {
+ assert(func_type.is_var_args);
+ if (!is_sysv) break :param_vi try param_it.nonSysvVarArg(&isel, param_ty);
+ }
+ break :param_vi try param_it.param(&isel, param_ty);
+ };
+ tracking_log.debug("${d} <- %{d}", .{ @intFromEnum(param_vi.?), @intFromEnum(air_inst_index) });
+ try isel.live_values.putNoClobber(gpa, air_inst_index, param_vi.?);
+ } else unreachable;
+
+ const saved_gra_start = if (mod.strip) param_it.ngrn else Select.CallAbiIterator.ngrn_start;
+ const saved_gra_end = if (is_sysv_var_args) Select.CallAbiIterator.ngrn_end else param_it.ngrn;
+ const saved_gra_len = @intFromEnum(saved_gra_end) - @intFromEnum(saved_gra_start);
+
+ const saved_vra_start = if (mod.strip) param_it.nsrn else Select.CallAbiIterator.nsrn_start;
+ const saved_vra_end = if (is_sysv_var_args) Select.CallAbiIterator.nsrn_end else param_it.nsrn;
+ const saved_vra_len = @intFromEnum(saved_vra_end) - @intFromEnum(saved_vra_start);
+
+ const frame_record = 2;
+ const named_stack_args: Select.Value.Indirect = .{
+ .base = .fp,
+ .offset = 8 * std.mem.alignForward(u7, frame_record + saved_gra_len, 2),
+ };
+ const stack_var_args = named_stack_args.withOffset(param_it.nsaa);
+ const gr_top = named_stack_args;
+ const vr_top: Select.Value.Indirect = .{ .base = .fp, .offset = 0 };
+ isel.va_list = if (is_sysv) .{ .sysv = .{
+ .__stack = stack_var_args,
+ .__gr_top = gr_top,
+ .__vr_top = vr_top,
+ .__gr_offs = @as(i32, @intFromEnum(Select.CallAbiIterator.ngrn_end) - @intFromEnum(param_it.ngrn)) * -8,
+ .__vr_offs = @as(i32, @intFromEnum(Select.CallAbiIterator.nsrn_end) - @intFromEnum(param_it.nsrn)) * -16,
+ } } else .{ .other = stack_var_args };
+
+ // translate arg locations from caller-based to callee-based
+ for (air_args) |air_inst_index| {
+ 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 = passed_vi.parts(&isel);
+ const first_passed_part_vi = part_it.next().?;
+ const hint_ra = first_passed_part_vi.hint(&isel).?;
+ passed_vi.setParent(&isel, .{ .stack_slot = if (hint_ra.isVector())
+ vr_top.withOffset(@as(i8, -16) * (@intFromEnum(saved_vra_end) - @intFromEnum(hint_ra)))
+ else
+ gr_top.withOffset(@as(i8, -8) * (@intFromEnum(saved_gra_end) - @intFromEnum(hint_ra))) });
+ },
+ .stack_slot => |stack_slot| {
+ assert(stack_slot.base == .sp);
+ passed_vi.changeStackSlot(&isel, 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, is_sysv_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 = &.{},
+ .lazy_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.lazy_relocs = try isel.lazy_relocs.toOwnedSlice(gpa);
+ mir.global_relocs = try isel.global_relocs.toOwnedSlice(gpa);
+ mir.literal_relocs = try isel.literal_relocs.toOwnedSlice(gpa);
+ return mir;
+}
+
+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..494e012d80
--- /dev/null
+++ b/src/codegen/aarch64/Assemble.zig
@@ -0,0 +1,1682 @@
+source: [*:0]const u8,
+operands: std.StringHashMapUnmanaged(Operand),
+
+pub const Operand = union(enum) {
+ register: aarch64.encoding.Register,
+};
+
+pub fn nextInstruction(as: *Assemble) !?Instruction {
+ @setEvalBranchQuota(42_000);
+ comptime var ct_token_buf: [token_buf_len]u8 = undefined;
+ var token_buf: [token_buf_len]u8 = undefined;
+ const original_source = while (true) {
+ const original_source = as.source;
+ const source_token = try as.nextToken(&token_buf, .{});
+ switch (source_token.len) {
+ 0 => return null,
+ else => switch (source_token[0]) {
+ else => break original_source,
+ '\n', ';' => {},
+ },
+ }
+ };
+ 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) {
+ switch (source_token.len) {
+ 0 => {},
+ else => switch (source_token[0]) {
+ else => break :next_pattern,
+ '\n', ';' => {},
+ },
+ }
+ const encode = @field(Instruction, @tagName(instruction.encode[0]));
+ const Encode = @TypeOf(encode);
+ var args: std.meta.ArgsTuple(Encode) = undefined;
+ 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 (!toUpperEqlAssertUpper(source_token, pattern_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 })),
+ }
+}
+
+fn toUpperEqlAssertUpper(lhs: []const u8, rhs: []const u8) bool {
+ if (lhs.len != rhs.len) return false;
+ for (lhs, rhs) |l, r| {
+ assert(!std.ascii.isLower(r));
+ if (std.ascii.toUpper(l) != r) return false;
+ }
+ return true;
+}
+
+const token_buf_len = "v31.b[15]".len;
+fn nextToken(as: *Assemble, buf: *[token_buf_len]u8, comptime opts: struct {
+ operands: bool = false,
+ 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 },
+ systemreg,
+ 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,
+ .systemreg => aarch64.encoding.Register.System,
+ .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| {
+ const reg = Result.parse(token) 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;
+ },
+ .systemreg => {
+ const systemreg = Result.parse(token) orelse {
+ log.debug("invalid system register: \"{f}\"", .{std.zig.fmtString(token)});
+ return null;
+ };
+ assert(systemreg.op0 >= 2);
+ return systemreg;
+ },
+ .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..b6598b7ea7
--- /dev/null
+++ b/src/codegen/aarch64/Mir.zig
@@ -0,0 +1,348 @@
+prologue: []const Instruction,
+body: []const Instruction,
+epilogue: []const Instruction,
+literals: []const u32,
+nav_relocs: []const Reloc.Nav,
+uav_relocs: []const Reloc.Uav,
+lazy_relocs: []const Reloc.Lazy,
+global_relocs: []const Reloc.Global,
+literal_relocs: []const Reloc.Literal,
+
+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 Lazy = struct {
+ symbol: link.File.LazySymbol,
+ reloc: Reloc,
+ };
+
+ pub const Global = struct {
+ name: [*: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.lazy_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.lazy_relocs) |lazy_reloc| try emitReloc(
+ lf,
+ zcu,
+ func.owner_nav,
+ if (lf.cast(.elf)) |ef|
+ ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ else if (lf.cast(.macho)) |mf|
+ mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ else if (lf.cast(.coff)) |cf|
+ if (cf.getOrCreateAtomForLazySymbol(pt, lazy_reloc.symbol)) |atom|
+ cf.getAtom(atom).getSymbolIndex().?
+ else |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ else
+ return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
+ mir.body[lazy_reloc.reloc.label],
+ body_end - Instruction.size * (1 + lazy_reloc.reloc.label),
+ lazy_reloc.reloc.addend,
+ );
+ for (mir.global_relocs) |global_reloc| try emitReloc(
+ lf,
+ zcu,
+ func.owner_nav,
+ if (lf.cast(.elf)) |ef|
+ try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null)
+ else if (lf.cast(.macho)) |mf|
+ try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null)
+ else if (lf.cast(.coff)) |cf|
+ try cf.getGlobalSymbol(std.mem.span(global_reloc.name), "compiler_rt")
+ else
+ return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
+ mir.body[global_reloc.reloc.label],
+ 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,
+ .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,
+ },
+ }
+ },
+ .branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
+ const zo = ef.zigObjectPtr().?;
+ const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
+ const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
+ .b => .JUMP26,
+ .bl => .CALL26,
+ };
+ try atom.addReloc(gpa, .{
+ .r_offset = offset,
+ .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_addend = @bitCast(addend),
+ }, zo);
+ } else if (lf.cast(.macho)) |mf| {
+ const zo = mf.getZigObject().?;
+ const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
+ try atom.addReloc(mf, .{
+ .tag = .@"extern",
+ .offset = offset,
+ .target = sym_index,
+ .addend = @bitCast(addend),
+ .type = .branch,
+ .meta = .{
+ .pcrel = true,
+ .has_subtractor = false,
+ .length = 2,
+ .symbolnum = @intCast(sym_index),
+ },
+ });
+ },
+ .load_store => |decoded| if (lf.cast(.elf)) |ef| {
+ const zo = ef.zigObjectPtr().?;
+ const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
+ const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) {
+ .integer => |integer| switch (integer.decode()) {
+ .unallocated, .prfm => unreachable,
+ .strb, .ldrb, .ldrsb => .LDST8_ABS_LO12_NC,
+ .strh, .ldrh, .ldrsh => .LDST16_ABS_LO12_NC,
+ .ldrsw => .LDST32_ABS_LO12_NC,
+ inline .str, .ldr => |encoded| switch (encoded.sf) {
+ .word => .LDST32_ABS_LO12_NC,
+ .doubleword => .LDST64_ABS_LO12_NC,
+ },
+ },
+ .vector => |vector| switch (vector.group.opc1.decode(vector.group.size)) {
+ .byte => .LDST8_ABS_LO12_NC,
+ .half => .LDST16_ABS_LO12_NC,
+ .single => .LDST32_ABS_LO12_NC,
+ .double => .LDST64_ABS_LO12_NC,
+ .quad => .LDST128_ABS_LO12_NC,
+ .scalable, .predicate => unreachable,
+ },
+ };
+ try atom.addReloc(gpa, .{
+ .r_offset = offset,
+ .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_addend = @bitCast(addend),
+ }, zo);
+ } else if (lf.cast(.macho)) |mf| {
+ const zo = mf.getZigObject().?;
+ const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
+ try atom.addReloc(mf, .{
+ .tag = .@"extern",
+ .offset = offset,
+ .target = sym_index,
+ .addend = @bitCast(addend),
+ .type = .pageoff,
+ .meta = .{
+ .pcrel = false,
+ .has_subtractor = false,
+ .length = 2,
+ .symbolnum = @intCast(sym_index),
+ },
+ });
+ },
+ }
+}
+
+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..d030eab471
--- /dev/null
+++ b/src/codegen/aarch64/Select.zig
@@ -0,0 +1,12141 @@
+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),
+lazy_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Lazy),
+global_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Global),
+literal_relocs: std.ArrayListUnmanaged(codegen.aarch64.Mir.Reloc.Literal),
+
+// Stack Frame
+returns: bool,
+va_list: union(enum) {
+ other: Value.Indirect,
+ sysv: struct {
+ __stack: Value.Indirect,
+ __gr_top: Value.Indirect,
+ __vr_top: Value.Indirect,
+ __gr_offs: i32,
+ __vr_offs: i32,
+ },
+},
+stack_size: u24,
+stack_align: InternPool.Alignment,
+
+// 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(target_block: *const Block, isel: *Select) !void {
+ if (isel.instructions.items.len > target_block.target_label) {
+ try isel.emit(.b(@intCast((isel.instructions.items.len + 1 - target_block.target_label) << 2)));
+ }
+ try isel.merge(&target_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(target_loop: *Loop, isel: *Select) !void {
+ try isel.instructions.ensureUnusedCapacity(isel.pt.zcu.gpa, 1);
+ const repeat_list_tail = target_loop.repeat_list;
+ target_loop.repeat_list = @intCast(isel.instructions.items.len);
+ isel.instructions.appendAssumeCapacity(@bitCast(repeat_list_tail));
+ try isel.merge(&target_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.lazy_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, .c_va_end => {
+ 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);
+ const callee_ty = isel.air.typeOf(pl_op.operand, ip);
+ const func_info = switch (ip.indexToKey(callee_ty.toIntern())) {
+ else => unreachable,
+ .func_type => |func_type| func_type,
+ .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type,
+ };
+
+ try isel.analyzeUse(pl_op.operand);
+ var param_it: CallAbiIterator = .init;
+ for (args, 0..) |arg, arg_index| {
+ const restore_values_len = isel.values.items.len;
+ defer isel.values.shrinkRetainingCapacity(restore_values_len);
+ const param_vi = param_vi: {
+ const param_ty = isel.air.typeOf(arg, ip);
+ if (arg_index >= func_info.param_types.len) {
+ assert(func_info.is_var_args);
+ switch (isel.va_list) {
+ .other => break :param_vi try param_it.nonSysvVarArg(isel, param_ty),
+ .sysv => {},
+ }
+ }
+ break :param_vi try param_it.param(isel, param_ty);
+ } orelse continue;
+ defer param_vi.deref(isel);
+ const passed_vi = switch (param_vi.parent(isel)) {
+ .unallocated, .stack_slot => param_vi,
+ .value, .constant => unreachable,
+ .address => |address_vi| address_vi,
+ };
+ switch (passed_vi.parent(isel)) {
+ .unallocated => {},
+ .stack_slot => |stack_slot| {
+ assert(stack_slot.base == .sp);
+ isel.stack_size = @max(
+ isel.stack_size,
+ stack_slot.offset + @as(u24, @intCast(passed_vi.size(isel))),
+ );
+ },
+ .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 => {
+ 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)];
+ },
+ .try_ptr, .try_ptr_cold => {
+ const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
+ const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
+
+ try isel.analyzeUse(extra.data.ptr);
+ try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
+ try isel.def_order.putNoClobber(gpa, air_inst_index, {});
+
+ air_body_index += 1;
+ air_inst_index = air_body[air_body_index];
+ continue :air_tag air_tags[@intFromEnum(air_inst_index)];
+ },
+ .ret, .ret_safe, .ret_load => {
+ const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
+ isel.returns = true;
+
+ 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 => {
+ 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) error{ OutOfMemory, CodegenFail }!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_safe, .add_optimized, .add_wrap, .sub, .sub_safe, .sub_optimized, .sub_wrap => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
+ defer res_vi.value.deref(isel);
+
+ 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_safe, .add_wrap => .add,
+ .sub, .sub_safe, .sub_wrap => .sub,
+ }, try isel.use(bin_op.rhs), .{
+ .overflow = switch (air_tag) {
+ else => unreachable,
+ .add, .sub => .@"unreachable",
+ .add_safe, .sub_safe => .{ .panic = .integer_overflow },
+ .add_wrap, .sub_wrap => .wrap,
+ },
+ }) else switch (ty.floatBits(isel.target)) {
+ else => unreachable,
+ 16, 32, 64 => |bits| {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ 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, .{
+ .name = 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, .{
+ .name = 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_safe => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
+ defer res_vi.value.deref(isel);
+
+ const bin_op = air.data(air.inst_index).bin_op;
+ const ty = isel.air.typeOf(bin_op.lhs, ip);
+ if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
+ const int_info = ty.intInfo(zcu);
+ switch (int_info.signedness) {
+ .signed => switch (int_info.bits) {
+ 0 => unreachable,
+ 1 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(.orr(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.@"b."(
+ .invert(.ne),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.ands(.wzr, lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
+ },
+ .unsigned => switch (int_info.bits) {
+ 0 => unreachable,
+ 1 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(.@"and"(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 2...16 => |bits| {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.@"b."(
+ .eq,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.ands(.wzr, res_ra.w(), .{ .immediate = .{
+ .N = .word,
+ .immr = @intCast(32 - bits),
+ .imms = @intCast(32 - bits - 1),
+ } }));
+ try isel.emit(.madd(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 17...32 => |bits| {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.@"b."(
+ .eq,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.ands(.xzr, res_ra.x(), .{ .immediate = .{
+ .N = .doubleword,
+ .immr = @intCast(64 - bits),
+ .imms = @intCast(64 - bits - 1),
+ } }));
+ try isel.emit(.umaddl(res_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 33...63 => |bits| {
+ const lo64_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ const hi64_ra = hi64_ra: {
+ const lo64_lock = isel.tryLockReg(lo64_ra);
+ defer lo64_lock.unlock(isel);
+ break :hi64_ra try isel.allocIntReg();
+ };
+ defer isel.freeReg(hi64_ra);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.cbz(
+ hi64_ra.x(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.orr(hi64_ra.x(), hi64_ra.x(), .{ .shifted_register = .{
+ .register = lo64_ra.x(),
+ .shift = .{ .lsr = @intCast(bits) },
+ } }));
+ try isel.emit(.madd(lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
+ try isel.emit(.umulh(hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 64 => {
+ const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
+ const lhs_vi = try isel.use(bin_op.lhs);
+ const rhs_vi = try isel.use(bin_op.rhs);
+ const lhs_mat = try lhs_vi.matReg(isel);
+ const rhs_mat = try rhs_vi.matReg(isel);
+ try isel.emit(.madd(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
+ const hi64_ra = try isel.allocIntReg();
+ defer isel.freeReg(hi64_ra);
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(.integer_overflow);
+ try isel.emit(.cbz(
+ hi64_ra.x(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ try isel.emit(.umulh(hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
+ try rhs_mat.finish(isel);
+ try lhs_mat.finish(isel);
+ },
+ 65...128 => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
+ else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .mul_sat => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
+ defer res_vi.value.deref(isel);
+
+ 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .overflow = if (try overflow_vi.?.defReg(isel)) |overflow_ra| .{ .ra = overflow_ra } else .wrap,
+ });
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .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;
+ },
+ .inferred_alloc, .inferred_alloc_comptime => unreachable,
+ .assembly => {
+ const ty_pl = air.data(air.inst_index).ty_pl;
+ const extra = isel.air.extraData(Air.Asm, ty_pl.payload);
+ 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, .{
+ .name = "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, .{
+ .name = "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);
+ try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast(
+ isel.air.extra.items[extra.end..][0..extra.data.body_len],
+ ));
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .loop => {
+ 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..) |*dom_block, dom_index| {
+ if (@as(u1, @truncate(isel.dom.items[
+ loop.dom + dom_index / @bitSizeOf(DomInt)
+ ] >> @truncate(dom_index))) == 0) continue;
+ var live_reg_it = dom_block.live_registers.iterator();
+ while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) {
+ _ => |live_vi| try live_vi.mat(isel),
+ .allocating => unreachable,
+ .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;
+ try isel.blocks.getPtr(br.block_inst).?.branch(isel);
+ if (isel.live_values.get(br.block_inst)) |dst_vi| try dst_vi.move(isel, br.operand);
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .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;
+ },
+ .ret_addr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |addr_vi| unused: {
+ defer addr_vi.value.deref(isel);
+ const addr_ra = try addr_vi.value.defReg(isel) orelse break :unused;
+ try isel.emit(.ldr(addr_ra.x(), .{ .unsigned_offset = .{ .base = .fp, .offset = 8 } }));
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .frame_addr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |addr_vi| unused: {
+ defer addr_vi.value.deref(isel);
+ const addr_ra = try addr_vi.value.defReg(isel) orelse break :unused;
+ try isel.emit(.orr(addr_ra.x(), .xzr, .{ .register = .fp }));
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .call => {
+ const pl_op = air.data(air.inst_index).pl_op;
+ const extra = isel.air.extraData(Air.Call, pl_op.payload);
+ const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]);
+ const callee_ty = isel.air.typeOf(pl_op.operand, ip);
+ const func_info = switch (ip.indexToKey(callee_ty.toIntern())) {
+ else => unreachable,
+ .func_type => |func_type| func_type,
+ .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type,
+ };
+
+ try call.prepareReturn(isel);
+ const maybe_def_ret_vi = isel.live_values.fetchRemove(air.inst_index);
+ 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, 0..) |arg, arg_index| {
+ const param_ty = isel.air.typeOf(arg, ip);
+ const param_vi = param_vi: {
+ if (arg_index >= func_info.param_types.len) {
+ assert(func_info.is_var_args);
+ switch (isel.va_list) {
+ .other => break :param_vi try param_it.nonSysvVarArg(isel, param_ty),
+ .sysv => {},
+ }
+ }
+ break :param_vi try param_it.param(isel, param_ty);
+ } orelse continue;
+ defer param_vi.deref(isel);
+ const arg_vi = try isel.use(arg);
+ switch (param_vi.parent(isel)) {
+ .unallocated => if (param_vi.hint(isel)) |param_ra| {
+ try call.paramLiveOut(isel, arg_vi, param_ra);
+ } else {
+ var param_part_it = param_vi.parts(isel);
+ var arg_part_it = arg_vi.parts(isel);
+ if (arg_part_it.only()) |_| {
+ try isel.values.ensureUnusedCapacity(gpa, param_part_it.remaining);
+ arg_vi.setParts(isel, param_part_it.remaining);
+ while (param_part_it.next()) |param_part_vi| _ = arg_vi.addPart(
+ isel,
+ param_part_vi.get(isel).offset_from_parent,
+ param_part_vi.size(isel),
+ );
+ param_part_it = param_vi.parts(isel);
+ arg_part_it = arg_vi.parts(isel);
+ }
+ while (param_part_it.next()) |param_part_vi| {
+ const arg_part_vi = arg_part_it.next().?;
+ assert(arg_part_vi.get(isel).offset_from_parent ==
+ param_part_vi.get(isel).offset_from_parent);
+ assert(arg_part_vi.size(isel) == param_part_vi.size(isel));
+ try call.paramLiveOut(isel, arg_part_vi, param_part_vi.hint(isel).?);
+ }
+ },
+ .stack_slot => |stack_slot| try arg_vi.store(isel, param_ty, stack_slot.base, .{
+ .offset = @intCast(stack_slot.offset),
+ }),
+ .value, .constant => unreachable,
+ .address => |address_vi| try call.paramAddress(isel, arg_vi, address_vi.hint(isel).?),
+ }
+ }
+ try call.finishParams(isel);
+
+ 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, .{
+ .name = 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, .{
+ .name = 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, .{
+ .name = 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 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);
+ cond_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => cond_mat.?.ra.w(),
+ 33...64 => cond_mat.?.ra.x(),
+ };
+ }
+ if (case.ranges.len == 0 and case.items.len == 1 and Constant.fromInterned(
+ 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 adjusted_ra = switch (low_int) {
+ 0 => cond_mat.?.ra,
+ else => try isel.allocIntReg(),
+ };
+ defer if (adjusted_ra != cond_mat.?.ra) isel.freeReg(adjusted_ra);
+ const adjusted_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => adjusted_ra.w(),
+ 33...64 => adjusted_ra.x(),
+ };
+ const delta_int = high_int -% low_int;
+ if (case_range_index | case.items.len > 0) {
+ if (std.math.cast(u5, delta_int)) |pos_imm| try isel.emit(.ccmp(
+ adjusted_reg,
+ .{ .immediate = pos_imm },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ if (case_range_index > 0) .hi else .ne,
+ )) else if (std.math.cast(u5, -delta_int)) |neg_imm| try isel.emit(.ccmn(
+ adjusted_reg,
+ .{ .immediate = neg_imm },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ if (case_range_index > 0) .hi else .ne,
+ )) else {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.ccmp(
+ cond_reg,
+ .{ .register = imm_reg },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ if (case_range_index > 0) .hi else .ne,
+ ));
+ try isel.movImmediate(imm_reg, @bitCast(delta_int));
+ }
+ } else {
+ 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 {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.subs(zero_reg, adjusted_reg, .{ .register = imm_reg }));
+ try isel.movImmediate(imm_reg, @bitCast(delta_int));
+ }
+ }
+
+ switch (low_int) {
+ 0 => {},
+ else => {
+ if (std.math.cast(u12, low_int)) |pos_imm| try isel.emit(.sub(
+ adjusted_reg,
+ cond_reg,
+ .{ .immediate = pos_imm },
+ )) else if (std.math.cast(u12, -low_int)) |neg_imm| try isel.emit(.add(
+ adjusted_reg,
+ cond_reg,
+ .{ .immediate = neg_imm },
+ )) else if (if (@as(i12, @truncate(low_int)) == 0)
+ std.math.cast(u12, low_int >> 12)
+ else
+ null) |pos_imm_lsr_12| try isel.emit(.sub(
+ adjusted_reg,
+ cond_reg,
+ .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
+ )) else if (if (@as(i12, @truncate(-low_int)) == 0)
+ std.math.cast(u12, -low_int >> 12)
+ else
+ null) |neg_imm_lsr_12| try isel.emit(.add(
+ adjusted_reg,
+ cond_reg,
+ .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
+ )) else {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.sub(adjusted_reg, cond_reg, .{ .register = imm_reg }));
+ try isel.movImmediate(imm_reg, @bitCast(low_int));
+ }
+ },
+ }
+ }
+ var case_item_index = case.items.len;
+ 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 {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.ccmp(
+ cond_reg,
+ .{ .register = imm_reg },
+ .{ .n = false, .z = true, .c = false, .v = false },
+ .ne,
+ ));
+ try isel.movImmediate(imm_reg, @bitCast(item_int));
+ }
+ } 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 {
+ const imm_ra = try isel.allocIntReg();
+ defer isel.freeReg(imm_ra);
+ const imm_reg = switch (cond_int_info.bits) {
+ else => unreachable,
+ 1...32 => imm_ra.w(),
+ 33...64 => imm_ra.x(),
+ };
+ try isel.emit(.subs(zero_reg, cond_reg, .{ .register = imm_reg }));
+ try isel.movImmediate(imm_reg, @bitCast(item_int));
+ }
+ }
+ }
+ }
+ if (cond_mat) |mat| try mat.finish(isel);
+ 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(
+ error_set_part_mat.ra.w(),
+ @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
+ ));
+ try error_set_part_mat.finish(isel);
+
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .try_ptr, .try_ptr_cold => {
+ const ty_pl = air.data(air.inst_index).ty_pl;
+ const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
+ const error_union_ty = isel.air.typeOf(extra.data.ptr, ip).childType(zcu);
+ const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
+ const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
+
+ const error_union_ptr_vi = try isel.use(extra.data.ptr);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
+ defer payload_ptr_vi.value.deref(isel);
+ switch (codegen.errUnionPayloadOffset(ty_pl.ty.toType().childType(zcu), zcu)) {
+ 0 => try payload_ptr_vi.value.move(isel, extra.data.ptr),
+ else => |payload_offset| {
+ const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
+ const lo12: u12 = @truncate(payload_offset >> 0);
+ const hi12: u12 = @intCast(payload_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ payload_ptr_ra.x(),
+ if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ },
+ }
+ }
+
+ const cont_label = isel.instructions.items.len;
+ const cont_live_registers = isel.live_registers;
+ try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
+ try isel.merge(&cont_live_registers, .{});
+
+ const error_set_ra = try isel.allocIntReg();
+ defer isel.freeReg(error_set_ra);
+ try isel.loadReg(
+ error_set_ra,
+ ZigType.fromInterned(error_union_info.error_set_type).abiSize(zcu),
+ .unsigned,
+ error_union_ptr_mat.ra,
+ codegen.errUnionErrorOffset(payload_ty, zcu),
+ );
+ try error_union_ptr_mat.finish(isel);
+ try isel.emit(.cbz(
+ error_set_ra.w(),
+ @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
+ ));
+
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .dbg_stmt => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
+ .dbg_empty_stmt => {
+ try isel.emit(.nop());
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .dbg_inline_block => {
+ const ty_pl = air.data(air.inst_index).ty_pl;
+ const extra = isel.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+ try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast(
+ isel.air.extra.items[extra.end..][0..extra.data.body_len],
+ ));
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => {
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .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, .{
+ .name = "memcpy",
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+ try call.finishCallee(isel);
+
+ try call.prepareParams(isel);
+ const ptr_vi = try isel.use(ty_op.operand);
+ try isel.movImmediate(.x2, size);
+ try call.paramLiveOut(isel, ptr_vi, .r1);
+ try call.paramAddress(isel, dst_vi.value, .r0);
+ try call.finishParams(isel);
+ },
+ }
+ }
+
+ 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))
+ break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+
+ const src_vi = try isel.use(bin_op.rhs);
+ const size = src_vi.size(isel);
+ 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);
+
+ break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ else => {},
+ };
+ try call.prepareReturn(isel);
+ try call.finishReturn(isel);
+
+ try call.prepareCallee(isel);
+ try isel.global_relocs.append(gpa, .{
+ .name = "memcpy",
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+ try call.finishCallee(isel);
+
+ try call.prepareParams(isel);
+ const 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, .{
+ .name = 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;
+ },
+ .intcast_safe => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
+ defer dst_vi.value.deref(isel);
+
+ const ty_op = air.data(air.inst_index).ty_op;
+ const dst_ty = ty_op.ty.toType();
+ const dst_int_info = dst_ty.intInfo(zcu);
+ const src_ty = isel.air.typeOf(ty_op.operand, ip);
+ const src_int_info = src_ty.intInfo(zcu);
+ const can_be_negative = dst_int_info.signedness == .signed and
+ src_int_info.signedness == .signed;
+ const panic_id: Zcu.SimplePanicId = panic_id: switch (dst_ty.zigTypeTag(zcu)) {
+ else => unreachable,
+ .int => .integer_out_of_bounds,
+ .@"enum" => {
+ if (!dst_ty.isNonexhaustiveEnum(zcu)) {
+ return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
+ }
+ break :panic_id .invalid_enum_value;
+ },
+ };
+ if (dst_ty.toIntern() == src_ty.toIntern()) {
+ try dst_vi.value.move(isel, ty_op.operand);
+ } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 64) {
+ const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
+ const src_vi = try isel.use(ty_op.operand);
+ const dst_active_bits = dst_int_info.bits - @intFromBool(dst_int_info.signedness == .signed);
+ const src_active_bits = src_int_info.bits - @intFromBool(src_int_info.signedness == .signed);
+ if ((dst_int_info.signedness != .unsigned or src_int_info.signedness != .signed) and dst_active_bits >= src_active_bits) {
+ const src_mat = try src_vi.matReg(isel);
+ try isel.emit(if (can_be_negative and dst_active_bits > 32 and src_active_bits <= 32)
+ .sbfm(dst_ra.x(), src_mat.ra.x(), .{
+ .N = .doubleword,
+ .immr = 0,
+ .imms = @intCast(src_int_info.bits - 1),
+ })
+ else switch (src_int_info.bits) {
+ else => unreachable,
+ 1...32 => .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }),
+ 33...64 => .orr(dst_ra.x(), .xzr, .{ .register = src_mat.ra.x() }),
+ });
+ try src_mat.finish(isel);
+ } else {
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(panic_id);
+ try isel.emit(.@"b."(
+ .eq,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ if (can_be_negative) {
+ const src_mat = src_mat: {
+ const dst_lock = isel.lockReg(dst_ra);
+ defer dst_lock.unlock(isel);
+ break :src_mat try src_vi.matReg(isel);
+ };
+ try isel.emit(switch (src_int_info.bits) {
+ else => unreachable,
+ 1...32 => .subs(.wzr, dst_ra.w(), .{ .register = src_mat.ra.w() }),
+ 33...64 => .subs(.xzr, dst_ra.x(), .{ .register = src_mat.ra.x() }),
+ });
+ try isel.emit(switch (@max(dst_int_info.bits, src_int_info.bits)) {
+ else => unreachable,
+ 1...32 => .sbfm(dst_ra.w(), src_mat.ra.w(), .{
+ .N = .word,
+ .immr = 0,
+ .imms = @intCast(dst_int_info.bits - 1),
+ }),
+ 33...64 => .sbfm(dst_ra.x(), src_mat.ra.x(), .{
+ .N = .doubleword,
+ .immr = 0,
+ .imms = @intCast(dst_int_info.bits - 1),
+ }),
+ });
+ try src_mat.finish(isel);
+ } else {
+ const src_mat = try src_vi.matReg(isel);
+ try isel.emit(switch (@min(dst_int_info.bits, src_int_info.bits)) {
+ else => unreachable,
+ 1...32 => .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }),
+ 33...64 => .orr(dst_ra.x(), .xzr, .{ .register = src_mat.ra.x() }),
+ });
+ const active_bits = @min(dst_active_bits, src_active_bits);
+ try isel.emit(switch (src_int_info.bits) {
+ else => unreachable,
+ 1...32 => .ands(.wzr, src_mat.ra.w(), .{ .immediate = .{
+ .N = .word,
+ .immr = @intCast(32 - active_bits),
+ .imms = @intCast(32 - active_bits - 1),
+ } }),
+ 33...64 => .ands(.xzr, src_mat.ra.x(), .{ .immediate = .{
+ .N = .doubleword,
+ .immr = @intCast(64 - active_bits),
+ .imms = @intCast(64 - active_bits - 1),
+ } }),
+ });
+ try src_mat.finish(isel);
+ }
+ }
+ } else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .trunc => |air_tag| {
+ if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
+ defer dst_vi.value.deref(isel);
+
+ 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 => {
+ 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;
+ },
+ .optional_payload_ptr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ try payload_ptr_vi.value.move(isel, ty_op.operand);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .optional_payload_ptr_set => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const opt_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
+ if (!opt_ty.optionalReprIsPayload(zcu)) {
+ const opt_ptr_vi = try isel.use(ty_op.operand);
+ const opt_ptr_mat = try opt_ptr_vi.matReg(isel);
+ const has_value_ra = try isel.allocIntReg();
+ defer isel.freeReg(has_value_ra);
+ try isel.storeReg(
+ has_value_ra,
+ 1,
+ opt_ptr_mat.ra,
+ opt_ty.optionalChild(zcu).abiSize(zcu),
+ );
+ try opt_ptr_mat.finish(isel);
+ try isel.emit(.movz(has_value_ra.w(), 1, .{ .lsl = .@"0" }));
+ }
+ try payload_ptr_vi.value.move(isel, ty_op.operand);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .wrap_optional => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |opt_vi| unused: {
+ defer opt_vi.value.deref(isel);
+
+ 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;
+ },
+ .unwrap_errunion_payload_ptr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ switch (codegen.errUnionPayloadOffset(ty_op.ty.toType().childType(zcu), zcu)) {
+ 0 => try payload_ptr_vi.value.move(isel, ty_op.operand),
+ else => |payload_offset| {
+ const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
+ const error_union_ptr_vi = try isel.use(ty_op.operand);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ const lo12: u12 = @truncate(payload_offset >> 0);
+ const hi12: u12 = @intCast(payload_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ payload_ptr_ra.x(),
+ if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ try error_union_ptr_mat.finish(isel);
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .unwrap_errunion_err_ptr => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |error_ptr_vi| unused: {
+ defer error_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ switch (codegen.errUnionErrorOffset(
+ isel.air.typeOf(ty_op.operand, ip).childType(zcu).errorUnionPayload(zcu),
+ zcu,
+ )) {
+ 0 => try error_ptr_vi.value.move(isel, ty_op.operand),
+ else => |error_offset| {
+ const error_ptr_ra = try error_ptr_vi.value.defReg(isel) orelse break :unused;
+ const error_union_ptr_vi = try isel.use(ty_op.operand);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ const lo12: u12 = @truncate(error_offset >> 0);
+ const hi12: u12 = @intCast(error_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ error_ptr_ra.x(),
+ if (lo12 > 0) error_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(error_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ try error_union_ptr_mat.finish(isel);
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .errunion_payload_ptr_set => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
+ defer payload_ptr_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const payload_ty = ty_op.ty.toType().childType(zcu);
+ const error_union_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
+ const error_set_size = error_union_ty.errorUnionSet(zcu).abiSize(zcu);
+ const error_union_ptr_vi = try isel.use(ty_op.operand);
+ const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
+ if (error_set_size > 0) try isel.storeReg(
+ .zr,
+ error_set_size,
+ error_union_ptr_mat.ra,
+ codegen.errUnionErrorOffset(payload_ty, zcu),
+ );
+ switch (codegen.errUnionPayloadOffset(payload_ty, zcu)) {
+ 0 => {
+ try error_union_ptr_mat.finish(isel);
+ try payload_ptr_vi.value.move(isel, ty_op.operand);
+ },
+ else => |payload_offset| {
+ const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
+ const lo12: u12 = @truncate(payload_offset >> 0);
+ const hi12: u12 = @intCast(payload_offset >> 12);
+ if (hi12 > 0) try isel.emit(.add(
+ payload_ptr_ra.x(),
+ if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
+ .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
+ ));
+ if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
+ try error_union_ptr_mat.finish(isel);
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .wrap_errunion_payload => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |error_union_vi| {
+ defer error_union_vi.value.deref(isel);
+
+ 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;
+ },
+ .set_union_tag => {
+ const bin_op = air.data(air.inst_index).bin_op;
+ const union_ty = isel.air.typeOf(bin_op.lhs, ip).childType(zcu);
+ const union_layout = union_ty.unionGetLayout(zcu);
+ const tag_vi = try isel.use(bin_op.rhs);
+ const union_ptr_vi = try isel.use(bin_op.lhs);
+ const union_ptr_mat = try union_ptr_vi.matReg(isel);
+ try tag_vi.store(isel, isel.air.typeOf(bin_op.rhs, ip), union_ptr_mat.ra, .{
+ .offset = union_layout.tagOffset(),
+ });
+ try union_ptr_mat.finish(isel);
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .get_union_tag => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |tag_vi| {
+ defer tag_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const union_ty = isel.air.typeOf(ty_op.operand, ip);
+ const union_layout = union_ty.unionGetLayout(zcu);
+ const union_vi = try isel.use(ty_op.operand);
+ var tag_part_it = union_vi.field(union_ty, union_layout.tagOffset(), union_layout.tag_size);
+ const tag_part_vi = try tag_part_it.only(isel);
+ try tag_vi.value.copy(isel, ty_op.ty.toType(), tag_part_vi.?);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .slice => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {
+ defer slice_vi.value.deref(isel);
+ 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 = 1 },
+ } }) 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 => if (elem_is_vector) .ldr(elem_ra.q(), .{ .extended_register = .{
+ .base = base_mat.ra.x(),
+ .index = index_mat.ra.x(),
+ .extend = .{ .lsl = 4 },
+ } }) else unreachable,
+ });
+ 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, 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);
+ 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 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 => 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 = 1 },
+ } }) 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 => if (elem_is_vector) .ldr(elem_ra.q(), .{ .extended_register = .{
+ .base = base_mat.ra.x(),
+ .index = index_mat.ra.x(),
+ .extend = .{ .lsl = 4 },
+ } }) else unreachable,
+ });
+ 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, .{
+ .name = 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, .{
+ .name = 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, .memset_safe => |air_tag| {
+ const bin_op = air.data(air.inst_index).bin_op;
+ const dst_ty = isel.air.typeOf(bin_op.lhs, ip);
+ const dst_info = dst_ty.ptrInfo(zcu);
+ const fill_byte: union(enum) { constant: u8, value: Air.Inst.Ref } = fill_byte: {
+ if (bin_op.rhs.toInterned()) |fill_val| {
+ if (ip.isUndef(fill_val)) switch (air_tag) {
+ else => unreachable,
+ .memset => break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
+ .memset_safe => break :fill_byte .{ .constant = 0xaa },
+ };
+ if (try isel.hasRepeatedByteRepr(.fromInterned(fill_val))) |fill_byte|
+ break :fill_byte .{ .constant = fill_byte };
+ }
+ switch (dst_ty.elemType2(zcu).abiSize(zcu)) {
+ 0 => unreachable,
+ 1 => break :fill_byte .{ .value = bin_op.rhs },
+ 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,
+ }
+
+ break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty) }),
+ }
+ };
+
+ try call.prepareReturn(isel);
+ try call.finishReturn(isel);
+
+ try call.prepareCallee(isel);
+ try isel.global_relocs.append(gpa, .{
+ .name = "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, .{
+ .name = @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;
+ },
+ .error_name => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |name_vi| unused: {
+ defer name_vi.value.deref(isel);
+ var ptr_part_it = name_vi.value.field(.slice_const_u8_sentinel_0, 0, 8);
+ const ptr_part_vi = try ptr_part_it.only(isel);
+ const ptr_part_ra = try ptr_part_vi.?.defReg(isel);
+ var len_part_it = name_vi.value.field(.slice_const_u8_sentinel_0, 8, 8);
+ const len_part_vi = try len_part_it.only(isel);
+ const len_part_ra = try len_part_vi.?.defReg(isel);
+ if (ptr_part_ra == null and len_part_ra == null) break :unused;
+
+ const un_op = air.data(air.inst_index).un_op;
+ const error_vi = try isel.use(un_op);
+ const error_mat = try error_vi.matReg(isel);
+ const ptr_ra = try isel.allocIntReg();
+ defer isel.freeReg(ptr_ra);
+ const start_ra, const end_ra = range_ras: {
+ const name_lock: RegLock = if (len_part_ra != null) if (ptr_part_ra) |name_ptr_ra|
+ isel.tryLockReg(name_ptr_ra)
+ else
+ .empty else .empty;
+ defer name_lock.unlock(isel);
+ break :range_ras .{ try isel.allocIntReg(), try isel.allocIntReg() };
+ };
+ defer {
+ isel.freeReg(start_ra);
+ isel.freeReg(end_ra);
+ }
+ if (len_part_ra) |name_len_ra| try isel.emit(.sub(
+ name_len_ra.w(),
+ end_ra.w(),
+ .{ .register = start_ra.w() },
+ ));
+ if (ptr_part_ra) |name_ptr_ra| try isel.emit(.add(
+ name_ptr_ra.x(),
+ ptr_ra.x(),
+ .{ .extended_register = .{
+ .register = start_ra.w(),
+ .extend = .{ .uxtw = 0 },
+ } },
+ ));
+ if (len_part_ra) |_| try isel.emit(.sub(end_ra.w(), end_ra.w(), .{ .immediate = 1 }));
+ try isel.emit(.ldp(start_ra.w(), end_ra.w(), .{ .base = start_ra.x() }));
+ try isel.emit(.add(start_ra.x(), ptr_ra.x(), .{ .extended_register = .{
+ .register = error_mat.ra.w(),
+ .extend = switch (zcu.errorSetBits()) {
+ else => unreachable,
+ 1...8 => .{ .uxtb = 2 },
+ 9...16 => .{ .uxth = 2 },
+ 17...32 => .{ .uxtw = 2 },
+ },
+ } }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.adrp(ptr_ra.x(), 0));
+ try error_mat.finish(isel);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .aggregate_init => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |agg_vi| {
+ defer agg_vi.value.deref(isel);
+
+ 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(loaded_struct.flagsUnordered(ip).alignment.forward(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 tuple_align: InternPool.Alignment = .@"1";
+ 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);
+ const field_align = field_ty.abiAlignment(zcu);
+ tuple_align = tuple_align.maxStrict(field_align);
+ field_offset = field_align.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(tuple_align.forward(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, .{
+ .name = "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, .{
+ .name = 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;
+ },
+ .wasm_memory_size, .wasm_memory_grow => unreachable,
+ .cmp_lt_errors_len => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |is_vi| unused: {
+ defer is_vi.value.deref(isel);
+ const is_ra = try is_vi.value.defReg(isel) orelse break :unused;
+ try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(.ls)));
+
+ const un_op = air.data(air.inst_index).un_op;
+ const error_vi = try isel.use(un_op);
+ const error_mat = try error_vi.matReg(isel);
+ const ptr_ra = try isel.allocIntReg();
+ defer isel.freeReg(ptr_ra);
+ try isel.emit(.subs(.wzr, error_mat.ra.w(), .{ .register = ptr_ra.w() }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.ldr(ptr_ra.w(), .{ .base = ptr_ra.x() }));
+ try isel.lazy_relocs.append(gpa, .{
+ .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.adrp(ptr_ra.x(), 0));
+ try error_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(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(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(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;
+ },
+ .c_va_arg => {
+ const maybe_arg_vi = isel.live_values.fetchRemove(air.inst_index);
+ defer if (maybe_arg_vi) |arg_vi| arg_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const ty = ty_op.ty.toType();
+ var param_it: CallAbiIterator = .init;
+ const param_vi = try param_it.param(isel, ty);
+ defer param_vi.?.deref(isel);
+ const passed_vi = switch (param_vi.?.parent(isel)) {
+ .unallocated => param_vi.?,
+ .stack_slot, .value, .constant => unreachable,
+ .address => |address_vi| address_vi,
+ };
+ const passed_size: u5 = @intCast(passed_vi.alignment(isel).forward(passed_vi.size(isel)));
+ const passed_is_vector = passed_vi.isVector(isel);
+
+ const va_list_ptr_vi = try isel.use(ty_op.operand);
+ const va_list_ptr_mat = try va_list_ptr_vi.matReg(isel);
+ const offs_ra = try isel.allocIntReg();
+ defer isel.freeReg(offs_ra);
+ const stack_ra = try isel.allocIntReg();
+ defer isel.freeReg(stack_ra);
+
+ var part_vis: [2]Value.Index = undefined;
+ var arg_part_ras: [2]?Register.Alias = @splat(null);
+ const parts_len = parts_len: {
+ var parts_len: u2 = 0;
+ var part_it = passed_vi.parts(isel);
+ while (part_it.next()) |part_vi| : (parts_len += 1) {
+ part_vis[parts_len] = part_vi;
+ const arg_vi = maybe_arg_vi orelse continue;
+ const part_offset, const part_size = part_vi.position(isel);
+ var arg_part_it = arg_vi.value.field(ty, part_offset, part_size);
+ const arg_part_vi = try arg_part_it.only(isel);
+ arg_part_ras[parts_len] = try arg_part_vi.?.defReg(isel);
+ }
+ break :parts_len parts_len;
+ };
+
+ const done_label = isel.instructions.items.len;
+ try isel.emit(.str(stack_ra.x(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = 0,
+ } }));
+ try isel.emit(switch (parts_len) {
+ else => unreachable,
+ 1 => if (arg_part_ras[0]) |arg_part_ra| switch (part_vis[0].size(isel)) {
+ else => unreachable,
+ 1 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.b(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsb(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ .unsigned => .ldrb(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ },
+ 2 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.h(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsh(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ .unsigned => .ldrh(arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ },
+ 4 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.s() else arg_part_ra.w(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ 8 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.d() else arg_part_ra.x(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ 16 => .ldr(arg_part_ra.q(), .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } }),
+ } else .add(stack_ra.x(), stack_ra.x(), .{ .immediate = passed_size }),
+ 2 => if (arg_part_ras[0] != null or arg_part_ras[1] != null) .ldp(
+ @as(Register.Alias, arg_part_ras[0] orelse .zr).x(),
+ @as(Register.Alias, arg_part_ras[1] orelse .zr).x(),
+ .{ .post_index = .{
+ .base = stack_ra.x(),
+ .index = passed_size,
+ } },
+ ) else .add(stack_ra.x(), stack_ra.x(), .{ .immediate = passed_size }),
+ });
+ try isel.emit(.ldr(stack_ra.x(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = 0,
+ } }));
+ switch (isel.va_list) {
+ .other => {},
+ .sysv => {
+ const stack_label = isel.instructions.items.len;
+ try isel.emit(.b(
+ @intCast((isel.instructions.items.len + 1 - done_label) << 2),
+ ));
+ switch (parts_len) {
+ else => unreachable,
+ 1 => if (arg_part_ras[0]) |arg_part_ra| try isel.emit(switch (part_vis[0].size(isel)) {
+ else => unreachable,
+ 1 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.b(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsb(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ .unsigned => .ldrb(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ },
+ 2 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.h(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }) else switch (part_vis[0].signedness(isel)) {
+ .signed => .ldrsh(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ .unsigned => .ldrh(arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ },
+ 4 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.s() else arg_part_ra.w(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ 8 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.d() else arg_part_ra.x(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ 16 => .ldr(arg_part_ra.q(), .{ .extended_register = .{
+ .base = stack_ra.x(),
+ .index = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }),
+ }),
+ 2 => if (arg_part_ras[0] != null or arg_part_ras[1] != null) {
+ try isel.emit(.ldp(
+ @as(Register.Alias, arg_part_ras[0] orelse .zr).x(),
+ @as(Register.Alias, arg_part_ras[1] orelse .zr).x(),
+ .{ .base = stack_ra.x() },
+ ));
+ try isel.emit(.add(stack_ra.x(), stack_ra.x(), .{ .extended_register = .{
+ .register = offs_ra.w(),
+ .extend = .{ .sxtw = 0 },
+ } }));
+ },
+ }
+ try isel.emit(.ldr(stack_ra.x(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = if (passed_is_vector) 16 else 8,
+ } }));
+ try isel.emit(.@"b."(
+ .gt,
+ @intCast((isel.instructions.items.len + 1 - stack_label) << 2),
+ ));
+ try isel.emit(.str(stack_ra.w(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = if (passed_is_vector) 28 else 24,
+ } }));
+ try isel.emit(.adds(stack_ra.w(), offs_ra.w(), .{ .immediate = passed_size }));
+ try isel.emit(.tbz(
+ offs_ra.w(),
+ 31,
+ @intCast((isel.instructions.items.len + 1 - stack_label) << 2),
+ ));
+ try isel.emit(.ldr(offs_ra.w(), .{ .unsigned_offset = .{
+ .base = va_list_ptr_mat.ra.x(),
+ .offset = if (passed_is_vector) 28 else 24,
+ } }));
+ },
+ }
+ try va_list_ptr_mat.finish(isel);
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .c_va_copy => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |va_list_vi| {
+ defer va_list_vi.value.deref(isel);
+ const ty_op = air.data(air.inst_index).ty_op;
+ const va_list_ptr_vi = try isel.use(ty_op.operand);
+ const va_list_ptr_mat = try va_list_ptr_vi.matReg(isel);
+ _ = try va_list_vi.value.load(isel, ty_op.ty.toType(), va_list_ptr_mat.ra, .{});
+ try va_list_ptr_mat.finish(isel);
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .c_va_end => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
+ .c_va_start => {
+ if (isel.live_values.fetchRemove(air.inst_index)) |va_list_vi| {
+ defer va_list_vi.value.deref(isel);
+ const ty = air.data(air.inst_index).ty;
+ switch (isel.va_list) {
+ .other => |va_list| if (try va_list_vi.value.defReg(isel)) |va_list_ra| try isel.emit(.add(
+ va_list_ra.x(),
+ va_list.base.x(),
+ .{ .immediate = @intCast(va_list.offset) },
+ )),
+ .sysv => |va_list| {
+ var vr_offs_it = va_list_vi.value.field(ty, 28, 4);
+ const vr_offs_vi = try vr_offs_it.only(isel);
+ if (try vr_offs_vi.?.defReg(isel)) |vr_offs_ra| try isel.movImmediate(
+ vr_offs_ra.w(),
+ @as(u32, @bitCast(va_list.__vr_offs)),
+ );
+ var gr_offs_it = va_list_vi.value.field(ty, 24, 4);
+ const gr_offs_vi = try gr_offs_it.only(isel);
+ if (try gr_offs_vi.?.defReg(isel)) |gr_offs_ra| try isel.movImmediate(
+ gr_offs_ra.w(),
+ @as(u32, @bitCast(va_list.__gr_offs)),
+ );
+ var vr_top_it = va_list_vi.value.field(ty, 16, 8);
+ const vr_top_vi = try vr_top_it.only(isel);
+ if (try vr_top_vi.?.defReg(isel)) |vr_top_ra| try isel.emit(.add(
+ vr_top_ra.x(),
+ va_list.__vr_top.base.x(),
+ .{ .immediate = @intCast(va_list.__vr_top.offset) },
+ ));
+ var gr_top_it = va_list_vi.value.field(ty, 8, 8);
+ const gr_top_vi = try gr_top_it.only(isel);
+ if (try gr_top_vi.?.defReg(isel)) |gr_top_ra| try isel.emit(.add(
+ gr_top_ra.x(),
+ va_list.__gr_top.base.x(),
+ .{ .immediate = @intCast(va_list.__gr_top.offset) },
+ ));
+ var stack_it = va_list_vi.value.field(ty, 0, 8);
+ const stack_vi = try stack_it.only(isel);
+ if (try stack_vi.?.defReg(isel)) |stack_ra| try isel.emit(.add(
+ stack_ra.x(),
+ va_list.__stack.base.x(),
+ .{ .immediate = @intCast(va_list.__stack.offset) },
+ ));
+ },
+ }
+ }
+ if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
+ },
+ .work_item_id, .work_group_size, .work_group_id => unreachable,
+ }
+ assert(air.body_index == 0);
+}
+
+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,
+ is_sysv_var_args: 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}<body>:\n", .{nav.fqn.fmt(ip)});
+
+ const stack_size: u24 = @intCast(InternPool.Alignment.@"16".forward(isel.stack_size));
+
+ 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 (is_sysv_var_args) CallAbiIterator.nsrn_end else incoming.nsrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
+ saves_size = std.mem.alignForward(u10, saves_size, 16);
+ saves_buf[saves_len] = .{
+ .class = .vector,
+ .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;
+ } else switch (@as(u1, @truncate(saved_gra_len))) {
+ 0 => {},
+ 1 => saves_size += 8,
+ }
+ save_ra = if (mod.strip) incoming.ngrn else CallAbiIterator.ngrn_start;
+ while (save_ra != if (is_sysv_var_args) CallAbiIterator.ngrn_end else incoming.ngrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
+ saves_size = std.mem.alignForward(u10, saves_size, 8);
+ saves_buf[saves_len] = .{
+ .class = .integer,
+ .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}<prologue>:", .{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].size == saves[save_index + 1].size and
+ saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset)
+ {
+ try isel.emit(.stp(
+ saves[save_index + 0].register,
+ saves[save_index + 1].register,
+ switch (saves[save_index + 0].offset) {
+ 0 => .{ .pre_index = .{
+ .base = .sp,
+ .index = @intCast(-@as(i11, saves_size)),
+ } },
+ else => |offset| .{ .signed_offset = .{
+ .base = .sp,
+ .offset = @intCast(offset),
+ } },
+ },
+ ));
+ save_index += 2;
+ } else {
+ try isel.emit(.str(
+ saves[save_index].register,
+ switch (saves[save_index].offset) {
+ 0 => .{ .pre_index = .{
+ .base = .sp,
+ .index = @intCast(-@as(i11, saves_size)),
+ } },
+ else => |offset| .{ .unsigned_offset = .{
+ .base = .sp,
+ .offset = @intCast(offset),
+ } },
+ },
+ ));
+ save_index += 1;
+ };
+
+ try isel.emit(.add(.fp, .sp, .{ .immediate = frame_record_offset }));
+ const scratch_reg: Register = if (isel.stack_align == .@"16")
+ .sp
+ else if (stack_size == 0 and frame_record_offset == 0)
+ .fp
+ else
+ .ip0;
+ const stack_size_lo: u12 = @truncate(stack_size >> 0);
+ const stack_size_hi: u12 = @truncate(stack_size >> 12);
+ if (mod.stack_check) {
+ if (stack_size_hi > 2) {
+ try isel.movImmediate(.ip1, stack_size_hi);
+ const loop_label = isel.instructions.items.len;
+ try isel.emit(.sub(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = 1, .lsl = .@"12" },
+ }));
+ try isel.emit(.sub(.ip1, .ip1, .{ .immediate = 1 }));
+ try isel.emit(.ldr(.xzr, .{ .base = .sp }));
+ try isel.emit(.cbnz(.ip1, -@as(i21, @intCast(
+ (isel.instructions.items.len - loop_label) << 2,
+ ))));
+ } else for (0..stack_size_hi) |_| {
+ try isel.emit(.sub(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = 1, .lsl = .@"12" },
+ }));
+ try isel.emit(.ldr(.xzr, .{ .base = .sp }));
+ }
+ if (stack_size_lo > 0) try isel.emit(.sub(
+ scratch_reg,
+ .sp,
+ .{ .immediate = stack_size_lo },
+ )) else if (scratch_reg.alias == Register.Alias.ip0)
+ try isel.emit(.add(scratch_reg, .sp, .{ .immediate = 0 }));
+ } else {
+ if (stack_size_hi > 0) try isel.emit(.sub(scratch_reg, .sp, .{
+ .shifted_immediate = .{ .immediate = stack_size_hi, .lsl = .@"12" },
+ }));
+ if (stack_size_lo > 0) try isel.emit(.sub(
+ scratch_reg,
+ if (stack_size_hi > 0) scratch_reg else .sp,
+ .{ .immediate = stack_size_lo },
+ )) else if (scratch_reg.alias == Register.Alias.ip0 and stack_size_hi == 0)
+ try isel.emit(.add(scratch_reg, .sp, .{ .immediate = 0 }));
+ }
+ if (isel.stack_align != .@"16") try isel.emit(.@"and"(.sp, scratch_reg, .{ .immediate = .{
+ .N = .doubleword,
+ .immr = -%isel.stack_align.toLog2Units(),
+ .imms = ~isel.stack_align.toLog2Units(),
+ } }));
+ wip_mir_log.debug("", .{});
+ }
+
+ const epilogue = isel.instructions.items.len;
+ if (isel.returns) {
+ try isel.emit(.ret(.lr));
+ var save_index: usize = 0;
+ var first_offset: ?u10 = null;
+ while (save_index < saves.len) {
+ if (save_index + 2 <= saves.len and saves[save_index + 1].needs_restore and
+ saves[save_index + 0].class == saves[save_index + 1].class and
+ 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,
+ if (first_offset) |offset| .{ .signed_offset = .{
+ .base = .sp,
+ .offset = @intCast(saves[save_index + 0].offset - offset),
+ } } else form: {
+ first_offset = @intCast(saves[save_index + 0].offset);
+ break :form .{ .post_index = .{
+ .base = .sp,
+ .index = @intCast(saves_size - first_offset.?),
+ } };
+ },
+ ));
+ save_index += 2;
+ } else if (saves[save_index].needs_restore) {
+ try isel.emit(.ldr(
+ saves[save_index].register,
+ if (first_offset) |offset| .{ .unsigned_offset = .{
+ .base = .sp,
+ .offset = saves[save_index + 0].offset - offset,
+ } } else form: {
+ const offset = saves[save_index + 0].offset;
+ first_offset = offset;
+ break :form .{ .post_index = .{
+ .base = .sp,
+ .index = @intCast(saves_size - offset),
+ } };
+ },
+ ));
+ save_index += 1;
+ } else save_index += 1;
+ }
+ const offset = stack_size + first_offset.?;
+ const offset_lo: u12 = @truncate(offset >> 0);
+ const offset_hi: u12 = @truncate(offset >> 12);
+ if (isel.stack_align != .@"16" or (offset_lo > 0 and offset_hi > 0)) {
+ const fp_offset = @as(i11, first_offset.?) - frame_record_offset;
+ try isel.emit(if (fp_offset >= 0)
+ .add(.sp, .fp, .{ .immediate = @intCast(fp_offset) })
+ else
+ .sub(.sp, .fp, .{ .immediate = @intCast(-fp_offset) }));
+ } else {
+ if (offset_hi > 0) try isel.emit(.add(.sp, .sp, .{
+ .shifted_immediate = .{ .immediate = offset_hi, .lsl = .@"12" },
+ }));
+ if (offset_lo > 0) try isel.emit(.add(.sp, .sp, .{
+ .immediate = offset_lo,
+ }));
+ }
+ wip_mir_log.debug("{f}<epilogue>:\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 block(
+ isel: *Select,
+ air_inst_index: Air.Inst.Index,
+ res_ty: ZigType,
+ air_body: []const Air.Inst.Index,
+) !void {
+ if (res_ty.toIntern() != .noreturn_type) {
+ isel.blocks.putAssumeCapacityNoClobber(air_inst_index, .{
+ .live_registers = isel.live_registers,
+ .target_label = @intCast(isel.instructions.items.len),
+ });
+ }
+ try isel.body(air_body);
+ if (res_ty.toIntern() != .noreturn_type) {
+ const block_entry = isel.blocks.pop().?;
+ assert(block_entry.key == air_inst_index);
+ if (isel.live_values.fetchRemove(air_inst_index)) |result_vi| result_vi.value.deref(isel);
+ }
+}
+
+fn emit(isel: *Select, instruction: codegen.aarch64.encoding.Instruction) !void {
+ wip_mir_log.debug(" | {f}", .{instruction});
+ try isel.instructions.append(isel.pt.zcu.gpa, instruction);
+}
+
+fn emitPanic(isel: *Select, panic_id: Zcu.SimplePanicId) !void {
+ const zcu = isel.pt.zcu;
+ try isel.nav_relocs.append(zcu.gpa, .{
+ .nav = switch (zcu.intern_pool.indexToKey(zcu.builtin_decl_values.get(panic_id.toBuiltin()))) {
+ else => unreachable,
+ inline .@"extern", .func => |func| func.owner_nav,
+ },
+ .reloc = .{ .label = @intCast(isel.instructions.items.len) },
+ });
+ try isel.emit(.bl(0));
+}
+
+fn emitLiteral(isel: *Select, bytes: []const u8) !void {
+ const words: []align(1) const u32 = @ptrCast(bytes);
+ const literals = try isel.literals.addManyAsSlice(isel.pt.zcu.gpa, words.len);
+ 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 loadReg(
+ isel: *Select,
+ ra: Register.Alias,
+ size: u64,
+ signedness: std.builtin.Signedness,
+ 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()) .ldr(
+ ra.b(),
+ .{ .unsigned_offset = .{
+ .base = base_ra.x(),
+ .offset = unsigned_offset,
+ } },
+ ) else switch (signedness) {
+ .signed => .ldrsb(ra.w(), .{ .unsigned_offset = .{
+ .base = base_ra.x(),
+ .offset = unsigned_offset,
+ } }),
+ .unsigned => .ldrb(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())
+ .ldur(ra.b(), base_ra.x(), signed_offset)
+ else switch (signedness) {
+ .signed => .ldursb(ra.w(), base_ra.x(), signed_offset),
+ .unsigned => .ldurb(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()) .ldr(
+ ra.h(),
+ .{ .unsigned_offset = .{
+ .base = base_ra.x(),
+ .offset = unsigned_offset,
+ } },
+ ) else switch (signedness) {
+ .signed => .ldrsh(
+ ra.w(),
+ .{ .unsigned_offset = .{
+ .base = base_ra.x(),
+ .offset = unsigned_offset,
+ } },
+ ),
+ .unsigned => .ldrh(
+ 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())
+ .ldur(ra.h(), base_ra.x(), signed_offset)
+ else switch (signedness) {
+ .signed => .ldursh(ra.w(), base_ra.x(), signed_offset),
+ .unsigned => .ldurh(ra.w(), base_ra.x(), signed_offset),
+ });
+ },
+ 3 => {
+ const lo16_ra = try isel.allocIntReg();
+ defer isel.freeReg(lo16_ra);
+ try isel.emit(.orr(ra.w(), lo16_ra.w(), .{ .shifted_register = .{
+ .register = ra.w(),
+ .shift = .{ .lsl = 16 },
+ } }));
+ try isel.loadReg(ra, 1, signedness, base_ra, offset + 2);
+ return isel.loadReg(lo16_ra, 2, .unsigned, base_ra, offset);
+ },
+ 4 => {
+ if (std.math.cast(u14, offset)) |unsigned_offset| if (unsigned_offset % 4 == 0) return isel.emit(.ldr(
+ 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(.ldur(
+ if (ra.isVector()) ra.s() else ra.w(),
+ base_ra.x(),
+ signed_offset,
+ ));
+ },
+ 5, 6 => {
+ const lo32_ra = try isel.allocIntReg();
+ defer isel.freeReg(lo32_ra);
+ try isel.emit(.orr(ra.x(), lo32_ra.x(), .{ .shifted_register = .{
+ .register = ra.x(),
+ .shift = .{ .lsl = 32 },
+ } }));
+ try isel.loadReg(ra, size - 4, signedness, base_ra, offset + 4);
+ return isel.loadReg(lo32_ra, 4, .unsigned, base_ra, 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(ra.x(), lo48_ra.x(), .{ .shifted_register = .{
+ .register = ra.x(),
+ .shift = .{ .lsl = 32 + 16 },
+ } }));
+ try isel.loadReg(ra, 1, signedness, base_ra, offset + 4 + 2);
+ try isel.emit(.orr(lo48_ra.x(), lo32_ra.x(), .{ .shifted_register = .{
+ .register = lo48_ra.x(),
+ .shift = .{ .lsl = 32 },
+ } }));
+ try isel.loadReg(lo48_ra, 2, .unsigned, base_ra, offset + 4);
+ return isel.loadReg(lo32_ra, 4, .unsigned, base_ra, offset);
+ },
+ 8 => {
+ if (std.math.cast(u15, offset)) |unsigned_offset| if (unsigned_offset % 8 == 0) return isel.emit(.ldr(
+ 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(.ldur(
+ 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(.ldr(
+ ra.q(),
+ .{ .unsigned_offset = .{
+ .base = base_ra.x(),
+ .offset = unsigned_offset,
+ } },
+ ));
+ if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.ldur(ra.q(), base_ra.x(), signed_offset));
+ },
+ else => return isel.fail("bad load size: {d}", .{size}),
+ }
+ const ptr_ra = try isel.allocIntReg();
+ defer isel.freeReg(ptr_ra);
+ try isel.loadReg(ra, size, signedness, 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))));
+ }
+}
+
+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 changeStackSlot(vi: Value.Index, isel: *Select, new_stack_slot: Indirect) void {
+ const value = vi.get(isel);
+ assert(value.flags.parent_tag == .stack_slot);
+ value.flags.parent_tag = .unallocated;
+ vi.setParent(isel, .{ .stack_slot = new_stack_slot });
+ }
+
+ 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,
+ });
+ }
+ }
+
+ const AddOrSubtractOptions = struct {
+ overflow: Overflow,
+
+ const Overflow = union(enum) {
+ @"unreachable",
+ panic: Zcu.SimplePanicId,
+ wrap,
+ ra: Register.Alias,
+
+ fn defCond(overflow: Overflow, isel: *Select, cond: codegen.aarch64.encoding.ConditionCode) !void {
+ switch (overflow) {
+ .@"unreachable" => unreachable,
+ .panic => |panic_id| {
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(panic_id);
+ try isel.emit(.@"b."(
+ cond.invert(),
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ },
+ .wrap => {},
+ .ra => |overflow_ra| try isel.emit(.csinc(overflow_ra.w(), .wzr, .wzr, cond.invert())),
+ }
+ }
+ };
+ };
+ fn addOrSubtract(
+ res_vi: Value.Index,
+ isel: *Select,
+ ty: ZigType,
+ lhs_vi: Value.Index,
+ op: codegen.aarch64.encoding.Instruction.AddSubtractOp,
+ rhs_vi: Value.Index,
+ opts: AddOrSubtractOptions,
+ ) !void {
+ 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 = switch (opts.overflow) {
+ .@"unreachable" => false,
+ .panic, .wrap, .ra => true,
+ };
+ var need_carry = switch (opts.overflow) {
+ .@"unreachable", .wrap => false,
+ .panic, .ra => true,
+ };
+ while (part_offset > 0) : (need_wrap = false) {
+ const part_size = @min(part_offset, 8);
+ part_offset -= part_size;
+ 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) {
+ try opts.overflow.defCond(isel, switch (int_info.signedness) {
+ .signed => .vs,
+ .unsigned => switch (op) {
+ .add => .cs,
+ .sub => .cc,
+ },
+ });
+ break :unwrapped_res_part_ra wrapped_res_part_ra;
+ }
+ need_carry = false;
+ const wrapped_part_ra, const unwrapped_part_ra = part_ra: switch (opts.overflow) {
+ .@"unreachable" => unreachable,
+ .panic, .ra => switch (int_info.signedness) {
+ .signed => {
+ try opts.overflow.defCond(isel, .ne);
+ const wrapped_part_ra = switch (wrapped_res_part_ra) {
+ else => |res_part_ra| res_part_ra,
+ .zr => try isel.allocIntReg(),
+ };
+ errdefer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
+ const unwrapped_part_ra = unwrapped_part_ra: {
+ const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
+ else => |res_part_ra| isel.lockReg(res_part_ra),
+ .zr => .empty,
+ };
+ defer wrapped_res_part_lock.unlock(isel);
+ break :unwrapped_part_ra try isel.allocIntReg();
+ };
+ errdefer isel.freeReg(unwrapped_part_ra);
+ switch (part_size) {
+ else => unreachable,
+ 1...4 => try isel.emit(.subs(.wzr, wrapped_part_ra.w(), .{ .register = unwrapped_part_ra.w() })),
+ 5...8 => try isel.emit(.subs(.xzr, wrapped_part_ra.x(), .{ .register = unwrapped_part_ra.x() })),
+ }
+ break :part_ra .{ wrapped_part_ra, unwrapped_part_ra };
+ },
+ .unsigned => {
+ const unwrapped_part_ra = unwrapped_part_ra: {
+ const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
+ else => |res_part_ra| isel.lockReg(res_part_ra),
+ .zr => .empty,
+ };
+ defer wrapped_res_part_lock.unlock(isel);
+ break :unwrapped_part_ra try isel.allocIntReg();
+ };
+ errdefer isel.freeReg(unwrapped_part_ra);
+ const bit: u6 = @truncate(int_info.bits);
+ switch (opts.overflow) {
+ .@"unreachable", .wrap => unreachable,
+ .panic => |panic_id| {
+ const skip_label = isel.instructions.items.len;
+ try isel.emitPanic(panic_id);
+ try isel.emit(.tbz(
+ switch (bit) {
+ 0, 32 => unreachable,
+ 1...31 => unwrapped_part_ra.w(),
+ 33...63 => unwrapped_part_ra.x(),
+ },
+ bit,
+ @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
+ ));
+ },
+ .ra => |overflow_ra| try isel.emit(switch (bit) {
+ 0, 32 => unreachable,
+ 1...31 => .ubfm(overflow_ra.w(), unwrapped_part_ra.w(), .{
+ .N = .word,
+ .immr = bit,
+ .imms = bit,
+ }),
+ 33...63 => .ubfm(overflow_ra.x(), unwrapped_part_ra.x(), .{
+ .N = .doubleword,
+ .immr = bit,
+ .imms = bit,
+ }),
+ }),
+ }
+ break :part_ra .{ wrapped_res_part_ra, unwrapped_part_ra };
+ },
+ },
+ .wrap => .{ wrapped_res_part_ra, wrapped_res_part_ra },
+ };
+ defer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
+ errdefer if (unwrapped_part_ra != wrapped_res_part_ra) isel.freeReg(unwrapped_part_ra);
+ if (wrapped_part_ra != .zr) try isel.emit(switch (part_size) {
+ 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.loadReg(part_ra, part_size, part_vi.signedness(isel), base_ra, 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, const parent_vi = vi.valueParent(isel);
+ switch (parent_vi.parent(isel)) {
+ .unallocated => {},
+ .stack_slot => |stack_slot| if (stack_slot.base != Register.Alias.fp) try isel.storeReg(
+ src_ra,
+ vi.size(isel),
+ stack_slot.base,
+ @as(i65, stack_slot.offset) + offset_from_parent,
+ ),
+ else => unreachable,
+ }
+ try vi.spillReg(isel, src_ra, 0, expected_live_registers);
+ }
+
+ 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 peek(it: PartIterator) ?Value.Index {
+ var it_mut = it;
+ return it_mut.next();
+ }
+
+ pub fn only(it: PartIterator) ?Value.Index {
+ return if (it.remaining == 1) it.vi else null;
+ }
+ };
+
+ 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 loaded_struct = ip.loadStructType(ty.toIntern());
+ switch (loaded_struct.layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{
+ .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
+ },
+ }
+ const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
+ if (loaded_struct.field_types.len > Value.max_parts and
+ (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
+ 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);
+ }
+ },
+ .union_type => {
+ const loaded_union = ip.loadUnionType(ty.toIntern());
+ switch (loaded_union.flagsUnordered(ip).layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{ .int_type = .{
+ .signedness = .unsigned,
+ .bits = @intCast(ty.bitSize(zcu)),
+ } },
+ }
+ const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
+ if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
+ return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
+ const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
+ const alignment = vi.alignment(isel);
+ const tag_offset = union_layout.tagOffset();
+ const payload_offset = union_layout.payloadOffset();
+ const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness };
+ var parts: [2]Part = undefined;
+ var parts_len: Value.PartsLen = 0;
+ var field_end: u64 = 0;
+ for (0..2) |field_index| {
+ const field: enum { tag, payload } = switch (field_index) {
+ 0 => if (tag_offset < payload_offset) .tag else .payload,
+ 1 => if (tag_offset < payload_offset) .payload else .tag,
+ else => unreachable,
+ };
+ const field_size, const field_begin = switch (field) {
+ .tag => .{ union_layout.tag_size, tag_offset },
+ .payload => .{ union_layout.payload_size, payload_offset },
+ };
+ if (field_begin >= offset + size) break;
+ if (field_size == 0) continue;
+ field_end = field_begin + field_size;
+ if (field_end <= offset) continue;
+ const field_signedness = field_signedness: switch (field) {
+ .tag => {
+ if (offset >= field_begin and offset + size <= field_begin + field_size) {
+ ty = .fromInterned(loaded_union.enum_tag_ty);
+ ty_size = field_size;
+ offset -= field_begin;
+ continue :type_key ip.indexToKey(loaded_union.enum_tag_ty);
+ }
+ break :field_signedness ip.indexToKey(loaded_union.loadTagType(ip).tag_ty).int_type.signedness;
+ },
+ .payload => null,
+ };
+ if (parts_len > 0) combine: {
+ const prev_part = &parts[parts_len - 1];
+ const combined_size = field_end - prev_part.offset;
+ if (combined_size > @as(u64, 1) << @min(
+ min_part_log2_stride,
+ alignment.toLog2Units(),
+ @ctz(prev_part.offset),
+ )) break :combine;
+ prev_part.size = combined_size;
+ prev_part.signedness = null;
+ continue;
+ }
+ parts[parts_len] = .{
+ .offset = field_begin,
+ .size = field_size,
+ .signedness = field_signedness,
+ };
+ parts_len += 1;
+ }
+ vi.setParts(isel, parts_len);
+ for (parts[0..parts_len]) |part| {
+ const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
+ if (part.signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
+ }
+ },
+ .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
+ .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
+ .error_set_type,
+ .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: u64 = 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| break :free try isel.loadReg(
+ mat.ra,
+ size,
+ mat.vi.signedness(isel),
+ stack_slot.base,
+ @as(i65, stack_slot.offset) + offset,
+ ),
+ .address => |base_vi| {
+ const base_mat = try base_vi.matReg(isel);
+ try isel.loadReg(mat.ra, size, mat.vi.signedness(isel), base_mat.ra, 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 error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
+ const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
+ const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
+ const error_set_size = error_set_ty.abiSize(zcu);
+ if (offset >= error_set_offset and offset + size <= error_set_offset + error_set_size) {
+ offset -= error_set_offset;
+ continue :constant_key switch (error_union.val) {
+ .err_name => |err_name| .{ .err = .{
+ .ty = error_union_type.error_set_type,
+ .name = err_name,
+ } },
+ .payload => .{ .int = .{
+ .ty = error_union_type.error_set_type,
+ .storage = .{ .u64 = 0 },
+ } },
+ };
+ }
+ const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
+ const payload_size = payload_ty.abiSize(zcu);
+ if (offset >= payload_offset and offset + size <= payload_offset + payload_size) {
+ offset -= payload_offset;
+ switch (error_union.val) {
+ .err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
+ .payload => |payload| {
+ constant = payload;
+ constant_key = ip.indexToKey(payload);
+ continue :constant_key constant_key;
+ },
+ }
+ }
+ },
+ .enum_tag => |enum_tag| continue :constant_key .{ .int = ip.indexToKey(enum_tag.int).int },
+ .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) + ptr.byte_offset;
+ continue :constant_key .{ .ptr = base_ptr };
+ },
+ .opt_payload => |base| {
+ var base_ptr = ip.indexToKey(base).ptr;
+ base_ptr.byte_offset += ptr.byte_offset;
+ continue :constant_key .{ .ptr = base_ptr };
+ },
+ .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) + ptr.byte_offset;
+ continue :constant_key .{ .ptr = base_ptr };
+ },
+ .comptime_alloc, .comptime_field, .arr_elem => unreachable,
+ };
+ },
+ .slice => |slice| switch (offset) {
+ 0 => continue :constant_key switch (ip.indexToKey(slice.ptr)) {
+ else => unreachable,
+ .undef => |undef| .{ .undef = undef },
+ .ptr => |ptr| .{ .ptr = ptr },
+ },
+ else => {
+ assert(offset == @divExact(isel.target.ptrBitWidth(), 8));
+ offset = 0;
+ 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;
+ if (try isel.writeKeyToMemory(ip.indexToKey(constant.toIntern()), buffer)) return true;
+ constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false,
+ };
+ return true;
+}
+fn writeKeyToMemory(isel: *Select, constant_key: InternPool.Key, buffer: []u8) error{OutOfMemory}!bool {
+ const zcu = isel.pt.zcu;
+ const ip = &zcu.intern_pool;
+ switch (constant_key) {
+ .int_type,
+ .ptr_type,
+ .array_type,
+ .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
+ .err => |err| {
+ const error_int = ip.getErrorValueIfExists(err.name).?;
+ switch (buffer.len) {
+ else => unreachable,
+ inline 1...4 => |size| std.mem.writeInt(
+ @Type(.{ .int = .{ .signedness = .unsigned, .bits = 8 * size } }),
+ buffer[0..size],
+ @intCast(error_int),
+ isel.target.cpu.arch.endian(),
+ ),
+ }
+ },
+ .error_union => |error_union| {
+ const error_union_type = ip.indexToKey(error_union.ty).error_union_type;
+ const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
+ const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
+ const error_set = buffer[@intCast(codegen.errUnionErrorOffset(payload_ty, zcu))..][0..@intCast(error_set_ty.abiSize(zcu))];
+ switch (error_union.val) {
+ .err_name => |err_name| if (!try isel.writeKeyToMemory(.{ .err = .{
+ .ty = error_set_ty.toIntern(),
+ .name = err_name,
+ } }, error_set)) return false,
+ .payload => |payload| {
+ if (!try isel.writeToMemory(
+ .fromInterned(payload),
+ buffer[@intCast(codegen.errUnionPayloadOffset(payload_ty, zcu))..][0..@intCast(payload_ty.abiSize(zcu))],
+ )) return false;
+ @memset(error_set, 0);
+ },
+ }
+ },
+ .opt => |opt| {
+ const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu));
+ switch (opt.val) {
+ .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);
+ },
+ }
+ },
+ .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;
+ },
+ }
+ },
+ .vector_type => return false,
+ .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;
+ }
+ },
+ .@"extern", .@"packed" => return false,
+ }
+ },
+ .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;
+ }
+ },
+ },
+ else => 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 end = offset % 8 + error_set_ty.abiSize(zcu);
+ const part_index: usize = @intCast(offset / 8);
+ sizes[part_index] = @max(sizes[part_index], @min(end, 8));
+ if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
+ }
+ {
+ const offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
+ const end = offset % 8 + payload_ty.abiSize(zcu);
+ const part_index: usize = @intCast(offset / 8);
+ sizes[part_index] = @max(sizes[part_index], @min(end, 8));
+ if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
+ }
+ 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 loaded_struct = ip.loadStructType(ty.toIntern());
+ switch (loaded_struct.layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{
+ .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
+ },
+ }
+ const size = wip_vi.size(isel);
+ if (size <= 16 * 4) homogeneous_aggregate: {
+ const fdt = homogeneousStructBaseType(zcu, &loaded_struct) orelse break :homogeneous_aggregate;
+ const parts_len = @shrExact(size, fdt.log2Size());
+ 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),
+ }
+ },
+ .union_type => {
+ const loaded_union = ip.loadUnionType(ty.toIntern());
+ switch (loaded_union.flagsUnordered(ip).layout) {
+ .auto, .@"extern" => {},
+ .@"packed" => continue :type_key .{ .int_type = .{
+ .signedness = .unsigned,
+ .bits = @intCast(ty.bitSize(zcu)),
+ } },
+ }
+ switch (wip_vi.size(isel)) {
+ 0 => unreachable,
+ 1...8 => it.integer(isel, wip_vi),
+ 9...16 => {
+ const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
+ var sizes: [2]u64 = @splat(0);
+ {
+ const offset = union_layout.tagOffset();
+ const end = offset % 8 + union_layout.tag_size;
+ const part_index: usize = @intCast(offset / 8);
+ sizes[part_index] = @max(sizes[part_index], @min(end, 8));
+ if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
+ }
+ {
+ const offset = union_layout.payloadOffset();
+ const end = offset % 8 + union_layout.payload_size;
+ const part_index: usize = @intCast(offset / 8);
+ sizes[part_index] = @max(sizes[part_index], @min(end, 8));
+ if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
+ }
+ it.integers(isel, wip_vi, sizes);
+ },
+ else => it.indirect(isel, wip_vi),
+ }
+ },
+ .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
+ .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
+ .error_set_type,
+ .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 nonSysvVarArg(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
+ const ngrn = it.ngrn;
+ defer it.ngrn = ngrn;
+ it.ngrn = ngrn_end;
+ const nsrn = it.nsrn;
+ defer it.nsrn = nsrn;
+ it.nsrn = nsrn_end;
+ return it.param(isel, ty);
+ }
+
+ pub fn ret(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
+ const wip_vi = try it.param(isel, ty) orelse return null;
+ switch (wip_vi.parent(isel)) {
+ .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..6ddf46b625
--- /dev/null
+++ b/src/codegen/aarch64/encoding.zig
@@ -0,0 +1,12194 @@
+/// 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 ip = x16;
+ 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 ip: Alias = .r16;
+ 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 (std.ascii.toLower(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 (toLowerEqlAssertLower(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 (toLowerEqlAssertLower(reg, "wzr"))
+ .wzr
+ else if (toLowerEqlAssertLower(reg, "wsp"))
+ .wsp
+ else
+ null,
+ 'i' => return if (toLowerEqlAssertLower(reg, "ip") or toLowerEqlAssertLower(reg, "ip0"))
+ .ip0
+ else if (toLowerEqlAssertLower(reg, "ip1"))
+ .ip1
+ else
+ null,
+ 'f' => return if (toLowerEqlAssertLower(reg, "fp")) .fp else null,
+ 'p' => return if (toLowerEqlAssertLower(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 (toLowerEqlAssertLower(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 };
+ }
+
+ pub const System = packed struct(u16) {
+ op2: u3,
+ CRm: u4,
+ CRn: u4,
+ op1: u3,
+ op0: u2,
+
+ // D19.2 General system control registers
+ /// D19.2.1 ACCDATA_EL1, Accelerator Data
+ pub const accdata_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b101 };
+ /// D19.2.2 ACTLR_EL1, Auxiliary Control Register (EL1)
+ pub const actlr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.3 ACTLR_EL2, Auxiliary Control Register (EL2)
+ pub const actlr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.4 ACTLR_EL3, Auxiliary Control Register (EL3)
+ pub const actlr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.5 AFSR0_EL1, Auxiliary Fault Status Register 0 (EL1)
+ pub const afsr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.5 AFSR0_EL12, Auxiliary Fault Status Register 0 (EL12)
+ pub const afsr0_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.6 AFSR0_EL2, Auxiliary Fault Status Register 0 (EL2)
+ pub const afsr0_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.7 AFSR0_EL3, Auxiliary Fault Status Register 0 (EL3)
+ pub const afsr0_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.8 AFSR1_EL1, Auxiliary Fault Status Register 1 (EL1)
+ pub const afsr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b001 };
+ /// D19.2.8 AFSR1_EL12, Auxiliary Fault Status Register 1 (EL12)
+ pub const afsr1_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b001 };
+ /// D19.2.9 AFSR1_EL2, Auxiliary Fault Status Register 1 (EL2)
+ pub const afsr1_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b001 };
+ /// D19.2.10 AFSR1_EL3, Auxiliary Fault Status Register 1 (EL3)
+ pub const afsr1_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0101, .CRm = 0b0001, .op2 = 0b001 };
+ /// D19.2.11 AIDR_EL1, Auxiliary ID Register
+ pub const aidr_el1: System = .{ .op0 = 0b11, .op1 = 0b001, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.12 AMAIR_EL1, Auxiliary Memory Attribute Indirection Register (EL1)
+ pub const amair_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.12 AMAIR_EL12, Auxiliary Memory Attribute Indirection Register (EL12)
+ pub const amair_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b1010, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.13 AMAIR_EL2, Auxiliary Memory Attribute Indirection Register (EL2)
+ pub const amair_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1010, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.14 AMAIR_EL3, Auxiliary Memory Attribute Indirection Register (EL3)
+ pub const amair_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1010, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.15 APDAKeyHi_EL1, Pointer Authentication Key A for Data (bits[127:64])
+ pub const apdakeyhi_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0010, .op2 = 0b001 };
+ /// D19.2.16 APDAKeyLo_EL1, Pointer Authentication Key A for Data (bits[63:0])
+ pub const apdakeylo_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.17 APDBKeyHi_EL1, Pointer Authentication Key B for Data (bits[127:64])
+ pub const apdbkeyhi_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0010, .op2 = 0b011 };
+ /// D19.2.18 APDAKeyHi_EL1, Pointer Authentication Key B for Data (bits[63:0])
+ pub const apdbkeylo_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0010, .op2 = 0b010 };
+ /// D19.2.19 APGAKeyHi_EL1, Pointer Authentication Key A for Code (bits[127:64])
+ pub const apgakeyhi_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0011, .op2 = 0b001 };
+ /// D19.2.20 APGAKeyLo_EL1, Pointer Authentication Key A for Code (bits[63:0])
+ pub const apgakeylo_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.21 APIAKeyHi_EL1, Pointer Authentication Key A for Instruction (bits[127:64])
+ pub const apiakeyhi_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0001, .op2 = 0b001 };
+ /// D19.2.22 APIAKeyLo_EL1, Pointer Authentication Key A for Instruction (bits[63:0])
+ pub const apiakeylo_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.23 APIBKeyHi_EL1, Pointer Authentication Key B for Instruction (bits[127:64])
+ pub const apibkeyhi_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0001, .op2 = 0b011 };
+ /// D19.2.24 APIBKeyLo_EL1, Pointer Authentication Key B for Instruction (bits[63:0])
+ pub const apibkeylo_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0001, .op2 = 0b010 };
+ /// D19.2.25 CCSIDR2_EL1, Current Cache Size ID Register 2
+ pub const ccsidr2_el1: System = .{ .op0 = 0b11, .op1 = 0b001, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.26 CCSIDR_EL1, Current Cache Size ID Register
+ pub const ccsidr_el1: System = .{ .op0 = 0b11, .op1 = 0b001, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.27 CLIDR_EL1, Cache Level ID Register
+ pub const clidr_el1: System = .{ .op0 = 0b11, .op1 = 0b001, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.28 CONTEXTIDR_EL1, Context ID Register (EL1)
+ pub const contextidr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.28 CONTEXTIDR_EL12, Context ID Register (EL12)
+ pub const contextidr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.29 CONTEXTIDR_EL2, Context ID Register (EL2)
+ pub const contextidr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.30 CPACR_EL1, Architectural Feature Access Control Register
+ pub const cpacr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.30 CPACR_EL12, Architectural Feature Access Control Register
+ pub const cpacr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.31 CPACR_EL2, Architectural Feature Trap Register (EL2)
+ pub const cptr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b010 };
+ /// D19.2.32 CPACR_EL3, Architectural Feature Trap Register (EL3)
+ pub const cptr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b010 };
+ /// D19.2.33 CSSELR_EL1, Cache Size Selection Register
+ pub const csselr_el1: System = .{ .op0 = 0b11, .op1 = 0b010, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.34 CTR_EL0, Cache Type Register
+ pub const ctr_el0: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.35 DACR32_EL2, Domain Access Control Register
+ pub const dacr32_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0011, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.36 DCZID_EL0, Data Cache Zero ID Register
+ pub const dczid_el0: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.37 ESR_EL1, Exception Syndrome Register (EL1)
+ pub const esr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0101, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.37 ESR_EL12, Exception Syndrome Register (EL12)
+ pub const esr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0101, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.38 ESR_EL2, Exception Syndrome Register (EL2)
+ pub const esr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0101, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.39 ESR_EL3, Exception Syndrome Register (EL3)
+ pub const esr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0101, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.40 FAR_EL1, Fault Address Register (EL1)
+ pub const far_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0110, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.40 FAR_EL12, Fault Address Register (EL12)
+ pub const far_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0110, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.41 FAR_EL2, Fault Address Register (EL2)
+ pub const far_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0110, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.42 FAR_EL3, Fault Address Register (EL3)
+ pub const far_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0110, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.43 FPEXC32_EL2, Floating-Point Exception Control Register
+ pub const fpexc32_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0101, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.44 GCR_EL1, Tag Control Register
+ pub const gcr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b110 };
+ /// D19.2.45 GMID_EL1, Tag Control Register
+ pub const gmid_el1: System = .{ .op0 = 0b11, .op1 = 0b001, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b100 };
+ /// D19.2.46 HACR_EL2, Hypervisor Auxiliary Control Register
+ pub const hacr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b111 };
+ /// D19.2.47 HAFGRTR_EL2, Hypervisor Activity Monitors Fine-Grained Read Trap Register
+ pub const hafgrtr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0011, .CRm = 0b0001, .op2 = 0b110 };
+ /// D19.2.48 HCR_EL2, Hypervisor Configuration Register
+ pub const hcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.49 HCRX_EL2, Extended Hypervisor Configuration Register
+ pub const hcrx_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b010 };
+ /// D19.2.50 HDFGRTR_EL2, Hypervisor Debug Fine-Grained Read Trap Register
+ pub const hdfgrtr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0011, .CRm = 0b0001, .op2 = 0b100 };
+ /// D19.2.51 HDFGWTR_EL2, Hypervisor Debug Fine-Grained Write Trap Register
+ pub const hdfgwtr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0011, .CRm = 0b0001, .op2 = 0b101 };
+ /// D19.2.52 HFGITR_EL2, Hypervisor Fine-Grained Instruction Trap Register
+ pub const hfgitr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b110 };
+ /// D19.2.53 HFGRTR_EL2, Hypervisor Fine-Grained Read Trap Register
+ pub const hfgrtr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b100 };
+ /// D19.2.54 HFGWTR_EL2, Hypervisor Fine-Grained Write Trap Register
+ pub const hfgwtr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b101 };
+ /// D19.2.55 HPFAR_EL2, Hypervisor IPA Fault Address Register
+ pub const hpfar_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0110, .CRm = 0b0000, .op2 = 0b100 };
+ /// D19.2.56 HSTR_EL2, Hypervisor System Trap Register
+ pub const hstr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b011 };
+ /// D19.2.57 ID_AA64AFR0_EL1, AArch64 Auxiliary Feature Register 0
+ pub const id_aa64afr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0101, .op2 = 0b100 };
+ /// D19.2.58 ID_AA64AFR1_EL1, AArch64 Auxiliary Feature Register 1
+ pub const id_aa64afr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0101, .op2 = 0b101 };
+ /// D19.2.59 ID_AA64DFR0_EL1, AArch64 Debug Feature Register 0
+ pub const id_aa64dfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0101, .op2 = 0b000 };
+ /// D19.2.60 ID_AA64DFR1_EL1, AArch64 Debug Feature Register 1
+ pub const id_aa64dfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0101, .op2 = 0b001 };
+ /// D19.2.61 ID_AA64ISAR0_EL1, AArch64 Instruction Set Attribute Register 0
+ pub const id_aa64isar0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0110, .op2 = 0b000 };
+ /// D19.2.62 ID_AA64ISAR1_EL1, AArch64 Instruction Set Attribute Register 1
+ pub const id_aa64isar1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0110, .op2 = 0b001 };
+ /// D19.2.63 ID_AA64ISAR2_EL1, AArch64 Instruction Set Attribute Register 2
+ pub const id_aa64isar2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0110, .op2 = 0b010 };
+ /// D19.2.64 ID_AA64MMFR0_EL1, AArch64 Memory Model Feature Register 0
+ pub const id_aa64mmfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0111, .op2 = 0b000 };
+ /// D19.2.65 ID_AA64MMFR1_EL1, AArch64 Memory Model Feature Register 1
+ pub const id_aa64mmfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0111, .op2 = 0b001 };
+ /// D19.2.66 ID_AA64MMFR2_EL1, AArch64 Memory Model Feature Register 2
+ pub const id_aa64mmfr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0111, .op2 = 0b010 };
+ /// D19.2.67 ID_AA64MMFR3_EL1, AArch64 Memory Model Feature Register 3
+ pub const id_aa64mmfr3_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0111, .op2 = 0b011 };
+ /// D19.2.68 ID_AA64MMFR4_EL1, AArch64 Memory Model Feature Register 4
+ pub const id_aa64mmfr4_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0111, .op2 = 0b100 };
+ /// D19.2.69 ID_AA64PFR0_EL1, AArch64 Processor Feature Register 0
+ pub const id_aa64pfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0100, .op2 = 0b000 };
+ /// D19.2.70 ID_AA64PFR1_EL1, AArch64 Processor Feature Register 1
+ pub const id_aa64pfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0100, .op2 = 0b001 };
+ /// D19.2.71 ID_AA64PFR2_EL1, AArch64 Processor Feature Register 2
+ pub const id_aa64pfr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0100, .op2 = 0b010 };
+ /// D19.2.72 ID_AA64SMFR0_EL1, SME Feature ID Register 0
+ pub const id_aa64smfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0100, .op2 = 0b101 };
+ /// D19.2.73 ID_AA64ZFR0_EL1, SVE Feature ID Register 0
+ pub const id_aa64zfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0100, .op2 = 0b100 };
+ /// D19.2.74 ID_AFR0_EL1, AArch32 Auxiliary Feature Register 0
+ pub const id_afr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b011 };
+ /// D19.2.75 ID_DFR0_EL1, AArch32 Debug Feature Register 0
+ pub const id_dfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b010 };
+ /// D19.2.76 ID_DFR1_EL1, AArch32 Debug Feature Register 1
+ pub const id_dfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0011, .op2 = 0b101 };
+ /// D19.2.77 ID_ISAR0_EL1, AArch32 Instruction Set Attribute Register 0
+ pub const id_isar0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.78 ID_ISAR1_EL1, AArch32 Instruction Set Attribute Register 1
+ pub const id_isar1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b001 };
+ /// D19.2.79 ID_ISAR2_EL1, AArch32 Instruction Set Attribute Register 2
+ pub const id_isar2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b010 };
+ /// D19.2.80 ID_ISAR3_EL1, AArch32 Instruction Set Attribute Register 3
+ pub const id_isar3_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b011 };
+ /// D19.2.81 ID_ISAR4_EL1, AArch32 Instruction Set Attribute Register 4
+ pub const id_isar4_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b100 };
+ /// D19.2.82 ID_ISAR5_EL1, AArch32 Instruction Set Attribute Register 5
+ pub const id_isar5_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b101 };
+ /// D19.2.83 ID_ISAR6_EL1, AArch32 Instruction Set Attribute Register 6
+ pub const id_isar6_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b111 };
+ /// D19.2.84 ID_MMFR0_EL1, AArch32 Memory Model Feature Register 0
+ pub const id_mmfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b100 };
+ /// D19.2.85 ID_MMFR1_EL1, AArch32 Memory Model Feature Register 1
+ pub const id_mmfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b101 };
+ /// D19.2.86 ID_MMFR2_EL1, AArch32 Memory Model Feature Register 2
+ pub const id_mmfr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b110 };
+ /// D19.2.87 ID_MMFR3_EL1, AArch32 Memory Model Feature Register 3
+ pub const id_mmfr3_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b111 };
+ /// D19.2.88 ID_MMFR4_EL1, AArch32 Memory Model Feature Register 4
+ pub const id_mmfr4_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0010, .op2 = 0b110 };
+ /// D19.2.89 ID_MMFR5_EL1, AArch32 Memory Model Feature Register 5
+ pub const id_mmfr5_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0011, .op2 = 0b110 };
+ /// D19.2.90 ID_PFR0_EL1, AArch32 Processor Feature Register 0
+ pub const id_pfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.91 ID_PFR1_EL1, AArch32 Processor Feature Register 1
+ pub const id_pfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0001, .op2 = 0b001 };
+ /// D19.2.92 ID_PFR2_EL1, AArch32 Processor Feature Register 2
+ pub const id_pfr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0011, .op2 = 0b100 };
+ /// D19.2.93 IFSR32_EL2, Instruction Fault Status Register (EL2)
+ pub const ifsr32_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0101, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.94 ISR_EL1, Interrupt Status Register
+ pub const isr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1100, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.95 LORC_EL1, LORegion Control (EL1)
+ pub const lorc_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0100, .op2 = 0b011 };
+ /// D19.2.96 LOREA_EL1, LORegion End Address (EL1)
+ pub const lorea_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0100, .op2 = 0b001 };
+ /// D19.2.97 SORID_EL1, LORegionID (EL1)
+ pub const lorid_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0100, .op2 = 0b111 };
+ /// D19.2.98 LORN_EL1, LORegion Number (EL1)
+ pub const lorn_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0100, .op2 = 0b010 };
+ /// D19.2.99 LORSA_EL1, LORegion Start Address (EL1)
+ pub const lorsa_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0100, .op2 = 0b000 };
+ /// D19.2.100 MAIR_EL1, Memory Attribute Indirection Register (EL1)
+ pub const mair_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1010, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.100 MAIR_EL12, Memory Attribute Indirection Register (EL12)
+ pub const mair_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b1010, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.101 MAIR_EL2, Memory Attribute Indirection Register (EL2)
+ pub const mair_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1010, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.102 MAIR_EL3, Memory Attribute Indirection Register (EL3)
+ pub const mair_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1010, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.103 MIDR_EL1, Main ID Register
+ pub const midr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.104 MPIDR_EL1, Multiprocessor Affinity Register
+ pub const mpidr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b101 };
+ /// D19.2.105 MVFR0_EL1, AArch32 Media and VFP Feature Register 0
+ pub const mvfr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0011, .op2 = 0b000 };
+ /// D19.2.106 MVFR1_EL1, AArch32 Media and VFP Feature Register 1
+ pub const mvfr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0011, .op2 = 0b001 };
+ /// D19.2.107 MVFR2_EL1, AArch32 Media and VFP Feature Register 2
+ pub const mvfr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0011, .op2 = 0b010 };
+ /// D19.2.108 PAR_EL1, Physical Address Register
+ pub const par_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0111, .CRm = 0b0100, .op2 = 0b000 };
+ /// D19.2.109 REVIDR_EL1, Revision ID Register
+ pub const revidr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b110 };
+ /// D19.2.110 RGSR_EL1, Random Allocation Tag Seed Register
+ pub const rgsr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b101 };
+ /// D19.2.111 RMR_EL1, Reset Management Register (EL1)
+ pub const rmr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.112 RMR_EL2, Reset Management Register (EL2)
+ pub const rmr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.113 RMR_EL3, Reset Management Register (EL3)
+ pub const rmr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.114 RNDR, Random Number
+ pub const rndr: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b0010, .CRm = 0b0100, .op2 = 0b000 };
+ /// D19.2.115 RNDRRS, Reseeded Random Number
+ pub const rndrrs: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b0010, .CRm = 0b0100, .op2 = 0b001 };
+ /// D19.2.116 RVBAR_EL1, Reset Vector Base Address Register (if EL2 and EL3 not implemented)
+ pub const rvbar_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.117 RVBAR_EL2, Reset Vector Base Address Register (if EL3 not implemented)
+ pub const rvbar_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.118 RVBAR_EL3, Reset Vector Base Address Register (if EL3 implemented)
+ pub const rvbar_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.120 SCR_EL3, Secure Configuration Register
+ pub const scr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.121 SCTLR2_EL1, System Control Register (EL1)
+ pub const sctlr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.121 SCTLR2_EL12, System Control Register (EL12)
+ pub const sctlr2_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.122 SCTLR2_EL2, System Control Register (EL2)
+ pub const sctlr2_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.123 SCTLR2_EL3, System Control Register (EL3)
+ pub const sctlr2_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.124 SCTLR_EL1, System Control Register (EL1)
+ pub const sctlr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.124 SCTLR_EL12, System Control Register (EL12)
+ pub const sctlr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.125 SCTLR_EL2, System Control Register (EL2)
+ pub const sctlr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.126 SCTLR_EL3, System Control Register (EL3)
+ pub const sctlr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.127 SCXTNUM_EL0, EL0 Read/Write Software Context Number
+ pub const scxtnum_el0: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.128 SCXTNUM_EL1, EL1 Read/Write Software Context Number
+ pub const scxtnum_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.128 SCXTNUM_EL12, EL12 Read/Write Software Context Number
+ pub const scxtnum_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.129 SCXTNUM_EL2, EL2 Read/Write Software Context Number
+ pub const scxtnum_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.130 SCXTNUM_EL3, EL3 Read/Write Software Context Number
+ pub const scxtnum_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b111 };
+ /// D19.2.131 SMCR_EL1, SME Control Register (EL1)
+ pub const smcr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b110 };
+ /// D19.2.131 SMCR_EL12, SME Control Register (EL12)
+ pub const smcr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b110 };
+ /// D19.2.132 SMCR_EL2, SME Control Register (EL2)
+ pub const smcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b110 };
+ /// D19.2.133 SMCR_EL3, SME Control Register (EL3)
+ pub const smcr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b110 };
+ /// D19.2.134 SMIDR_EL1, Streaming Mode Identification Register
+ pub const smidr_el1: System = .{ .op0 = 0b11, .op1 = 0b001, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b110 };
+ /// D19.2.135 SMPRIMAP_EL2, Streaming Mode Priority Mapping Register
+ pub const smprimap_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b101 };
+ /// D19.2.136 SMPRI_EL1, Streaming Mode Priority Register
+ pub const smpri_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b100 };
+ /// D19.2.137 TCR2_EL1, Extended Translation Control Register (EL1)
+ pub const tcr2_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.137 TCR2_EL12, Extended Translation Control Register (EL12)
+ pub const tcr2_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.138 TCR2_EL2, Extended Translation Control Register (EL2)
+ pub const tcr2_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.139 TCR_EL1, Translation Control Register (EL1)
+ pub const tcr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.139 TCR_EL12, Translation Control Register (EL12)
+ pub const tcr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.140 TCR_EL2, Translation Control Register (EL2)
+ pub const tcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.141 TCR_EL3, Translation Control Register (EL3)
+ pub const tcr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.142 TFSRE0_EL1, Tag Fault Status Register (EL0)
+ pub const tfsre0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0101, .CRm = 0b0110, .op2 = 0b001 };
+ /// D19.2.143 TFSR_EL1, Tag Fault Status Register (EL1)
+ pub const tfsr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0101, .CRm = 0b0110, .op2 = 0b000 };
+ /// D19.2.143 TFSR_EL12, Tag Fault Status Register (EL12)
+ pub const tfsr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0101, .CRm = 0b0110, .op2 = 0b000 };
+ /// D19.2.144 TFSR_EL2, Tag Fault Status Register (EL2)
+ pub const tfsr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0101, .CRm = 0b0110, .op2 = 0b000 };
+ /// D19.2.145 TFSR_EL3, Tag Fault Status Register (EL3)
+ pub const tfsr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0101, .CRm = 0b0110, .op2 = 0b000 };
+ /// D19.2.146 TPIDR2_EL0, EL0 Read/Write Software Thread ID Register 2
+ pub const tpidr2_el0: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b101 };
+ /// D19.2.147 TPIDR_EL0, EL0 Read/Write Software Thread ID Register
+ pub const tpidr_el0: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.148 TPIDR_EL1, EL1 Read/Write Software Thread ID Register
+ pub const tpidr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b100 };
+ /// D19.2.149 TPIDR_EL2, EL2 Read/Write Software Thread ID Register
+ pub const tpidr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.150 TPIDR_EL3, EL3 Read/Write Software Thread ID Register
+ pub const tpidr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b010 };
+ /// D19.2.151 TPIDRRO_EL0, EL0 Read-Only Software Thread ID Register
+ pub const tpidrro_el3: System = .{ .op0 = 0b11, .op1 = 0b011, .CRn = 0b1101, .CRm = 0b0000, .op2 = 0b011 };
+ /// D19.2.152 TTBR0_EL1, Translation Table Base Register 0 (EL1)
+ pub const ttbr0_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.152 TTBR0_EL12, Translation Table Base Register 0 (EL12)
+ pub const ttbr0_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.153 TTBR0_EL2, Translation Table Base Register 0 (EL2)
+ pub const ttbr0_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.154 TTBR0_EL3, Translation Table Base Register 0 (EL3)
+ pub const ttbr0_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.155 TTBR1_EL1, Translation Table Base Register 1 (EL1)
+ pub const ttbr1_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.155 TTBR1_EL12, Translation Table Base Register 1 (EL12)
+ pub const ttbr1_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.156 TTBR1_EL2, Translation Table Base Register 1 (EL2)
+ pub const ttbr1_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0000, .op2 = 0b001 };
+ /// D19.2.157 VBAR_EL1, Vector Base Address Register (EL1)
+ pub const vbar_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.157 VBAR_EL12, Vector Base Address Register (EL12)
+ pub const vbar_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.158 VBAR_EL2, Vector Base Address Register (EL2)
+ pub const vbar_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.159 VBAR_EL3, Vector Base Address Register (EL3)
+ pub const vbar_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b1100, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.160 VMPIDR_EL2, Virtualization Multiprocessor ID Register
+ pub const vmpidr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b101 };
+ /// D19.2.161 VNCR_EL2, Virtual Nested Control Register
+ pub const nvcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.162 VPIDR_EL2, Virtualization Processor ID Register
+ pub const vpidr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0000, .CRm = 0b0000, .op2 = 0b000 };
+ /// D19.2.163 VSTCR_EL2, Virtualization Secure Translation Control Register
+ pub const vstcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0110, .op2 = 0b010 };
+ /// D19.2.164 VSTTBR_EL2, Virtualization Secure Translation Table Base Register
+ pub const vsttbr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0110, .op2 = 0b000 };
+ /// D19.2.165 VTCR_EL2, Virtualization Translation Control Register
+ pub const vtcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0001, .op2 = 0b010 };
+ /// D19.2.166 VTTBR_EL2, Virtualization Translation Table Base Register
+ pub const vttbr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0010, .CRm = 0b0001, .op2 = 0b000 };
+ /// D19.2.167 ZCR_EL1, SVE Control Register (EL1)
+ pub const zcr_el1: System = .{ .op0 = 0b11, .op1 = 0b000, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.167 ZCR_EL12, SVE Control Register (EL12)
+ pub const zcr_el12: System = .{ .op0 = 0b11, .op1 = 0b101, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.168 ZCR_EL2, SVE Control Register (EL2)
+ pub const zcr_el2: System = .{ .op0 = 0b11, .op1 = 0b100, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b000 };
+ /// D19.2.169 ZCR_EL3, SVE Control Register (EL3)
+ pub const zcr_el3: System = .{ .op0 = 0b11, .op1 = 0b110, .CRn = 0b0001, .CRm = 0b0010, .op2 = 0b000 };
+
+ pub fn parse(reg: []const u8) ?System {
+ if (reg.len >= 10 and std.ascii.toLower(reg[0]) == 's') encoded: {
+ var symbol_it = std.mem.splitScalar(u8, reg[1..], '_');
+ const op0 = std.fmt.parseInt(u2, symbol_it.next() orelse break :encoded, 10) catch break :encoded;
+ if (op0 < 0b10) break :encoded;
+ const op1 = std.fmt.parseInt(u3, symbol_it.next() orelse break :encoded, 10) catch break :encoded;
+ const n = symbol_it.next() orelse break :encoded;
+ if (n.len == 0 or std.ascii.toLower(n[0]) != 'c') break :encoded;
+ const CRn = std.fmt.parseInt(u4, n[1..], 10) catch break :encoded;
+ const m = symbol_it.next() orelse break :encoded;
+ if (m.len == 0 or std.ascii.toLower(m[0]) != 'c') break :encoded;
+ const CRm = std.fmt.parseInt(u4, m[1..], 10) catch break :encoded;
+ const op2 = std.fmt.parseInt(u3, symbol_it.next() orelse break :encoded, 10) catch break :encoded;
+ if (symbol_it.next() != null) break :encoded;
+ return .{ .op0 = op0, .op1 = op1, .CRn = CRn, .CRm = CRm, .op2 = op2 };
+ }
+ inline for (@typeInfo(System).@"struct".decls) |decl| {
+ if (@TypeOf(@field(System, decl.name)) != System) continue;
+ if (toLowerEqlAssertLower(reg, decl.name)) return @field(System, decl.name);
+ }
+ return null;
+ }
+ };
+
+ fn toLowerEqlAssertLower(lhs: []const u8, rhs: []const u8) bool {
+ if (lhs.len != rhs.len) return false;
+ for (lhs, rhs) |l, r| {
+ assert(!std.ascii.isUpper(r));
+ if (std.ascii.toLower(l) != r) return false;
+ }
+ return true;
+ }
+};
+
+/// 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,
+ systemreg: Register.System,
+ L: L,
+ decoded22: u10 = 0b1101010100,
+ };
+
+ /// C6.2.230 MSR (register)
+ pub const Msr = packed struct {
+ Rt: Register.Encoded,
+ systemreg: Register.System,
+ L: L = .msr,
+ decoded22: u10 = 0b1101010100,
+ };
+
+ /// C6.2.228 MRS
+ pub const Mrs = packed struct {
+ Rt: Register.Encoded,
+ systemreg: Register.System,
+ 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,
+ },
+ } } } };
+ },
+ .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,
+ },
+ } } } };
+ },
+ .signed_offset => |signed_offset| {
+ assert(signed_offset.base.format.integer == .doubleword);
+ return .{ .load_store = .{ .register_pair_offset = .{ .integer = .{
+ .ldp = .{
+ .Rt = t1.alias.encode(.{}),
+ .Rn = signed_offset.base.alias.encode(.{ .sp = true }),
+ .Rt2 = t2.alias.encode(.{}),
+ .imm7 = @intCast(@shrExact(signed_offset.offset, @as(u2, 2) + @intFromEnum(sf))),
+ .sf = sf,
+ },
+ } } } };
+ },
+ .base => |base| continue :form .{ .signed_offset = .{ .base = base } },
+ }
+ },
+ .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, systemreg: Register.System) Instruction {
+ assert(t.format.integer == .doubleword and systemreg.op0 >= 0b10);
+ return .{ .branch_exception_generating_system = .{ .system_register_move = .{
+ .mrs = .{
+ .Rt = t.alias.encode(.{}),
+ .systemreg = systemreg,
+ },
+ } } };
+ }
+ /// C6.2.230 MSR (register)
+ pub fn msr(systemreg: Register.System, t: Register) Instruction {
+ assert(systemreg.op0 >= 0b10 and t.format.integer == .doubleword);
+ return .{ .branch_exception_generating_system = .{ .system_register_move = .{
+ .msr = .{
+ .Rt = t.alias.encode(.{}),
+ .systemreg = systemreg,
+ },
+ } } };
+ }
+ /// 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,
+ },
+ } } } };
+ },
+ .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,
+ },
+ } } } };
+ },
+ .signed_offset => |signed_offset| {
+ assert(signed_offset.base.format.integer == .doubleword);
+ return .{ .load_store = .{ .register_pair_offset = .{ .integer = .{
+ .stp = .{
+ .Rt = t1.alias.encode(.{}),
+ .Rn = signed_offset.base.alias.encode(.{ .sp = true }),
+ .Rt2 = t2.alias.encode(.{}),
+ .imm7 = @intCast(@shrExact(signed_offset.offset, @as(u2, 2) + @intFromEnum(sf))),
+ .sf = sf,
+ },
+ } } } };
+ },
+ .base => |base| continue :form .{ .signed_offset = .{ .base = base } },
+ }
+ },
+ .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..48b8eaa21f
--- /dev/null
+++ b/src/codegen/aarch64/instructions.zon
@@ -0,0 +1,1543 @@
+.{
+ // C6.2.3 ADD (extended register)
+ .{
+ .pattern = "ADD <Wd|WSP>, <Wn|WSP>, <Wm>",
+ .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 <Wd|WSP>, <Wn|WSP>, <Wm>, <extend> #<amount>",
+ .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 <Xd|SP>, <Xn|SP>, <Xm>",
+ .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 <Xd|SP>, <Xn|SP>, <Wm>, <extend> #<amount>",
+ .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 <Xd|SP>, <Xn|SP>, <Xm>, <extend> #<amount>",
+ .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 <Wd|WSP>, <Wn|WSP>, #<imm>",
+ .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 <Wd|WSP>, <Wn|WSP>, #<imm>, LSL #<shift>",
+ .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 <Xd|SP>, <Xn|SP>, #<imm>",
+ .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 <Xd|SP>, <Xn|SP>, #<imm>, LSL #<shift>",
+ .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 <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .add, .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "ADD <Wd>, <Wn>, <Wm>, <shift> #<amount>",
+ .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 <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .add, .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "ADD <Xd>, <Xn>, <Xm>, <shift> #<amount>",
+ .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 <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .@"and", .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "AND <Wd>, <Wn>, <Wm>, <shift> #<amount>",
+ .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 <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .@"and", .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "AND <Xd>, <Xn>, <Xm>, <shift> #<amount>",
+ .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 <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .ands, .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "ANDS <Wd>, <Wn>, <Wm>, <shift> #<amount>",
+ .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 <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .ands, .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "ANDS <Xd>, <Xn>, <Xm>, <shift> #<amount>",
+ .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.16 ASR (register)
+ .{
+ .pattern = "ASR <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .asrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "ASR <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .asrv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.17 ASR (immediate)
+ .{
+ .pattern = "ASR <Wd>, <Wn>, #<shift>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } },
+ },
+ .encode = .{ .sbfm, .Wd, .Wn, .{ .N = .word, .immr = .shift, .imms = 31 } },
+ },
+ .{
+ .pattern = "ASR <Xd>, <Xn>, #<shift>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } },
+ },
+ .encode = .{ .sbfm, .Xd, .Xn, .{ .N = .doubleword, .immr = .shift, .imms = 63 } },
+ },
+ // C6.2.18 ASRV
+ .{
+ .pattern = "ASRV <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .asrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "ASRV <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .asrv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.35 BLR
+ .{
+ .pattern = "BLR <Xn>",
+ .symbols = .{
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .blr, .Xn },
+ },
+ // C6.2.30 BFM
+ .{
+ .pattern = "BFM <Wd>, <Wn>, #<immr>, #<imms>",
+ .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 <Xd>, <Xn>, #<immr>, #<imms>",
+ .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 <Xn>",
+ .symbols = .{
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .br, .Xn },
+ },
+ // C6.2.40 BRK
+ .{
+ .pattern = "BRK #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .brk, .imm },
+ },
+ // C6.2.56 CLREX
+ .{
+ .pattern = "CLREX",
+ .symbols = .{},
+ .encode = .{ .clrex, 0b1111 },
+ },
+ .{
+ .pattern = "CLREX #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 4 } } },
+ },
+ .encode = .{ .clrex, .imm },
+ },
+ // C6.2.109 DC
+ .{
+ .pattern = "DC IVAC, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b0110, 0b001, .Xt },
+ },
+ .{
+ .pattern = "DC ISW, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b0110, 0b010, .Xt },
+ },
+ .{
+ .pattern = "DC CSW, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b1010, 0b010, .Xt },
+ },
+ .{
+ .pattern = "DC CISW, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b1110, 0b010, .Xt },
+ },
+ .{
+ .pattern = "DC ZVA, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b011, 0b0111, 0b0100, 0b001, .Xt },
+ },
+ .{
+ .pattern = "DC CVAC, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b011, 0b0111, 0b1010, 0b001, .Xt },
+ },
+ .{
+ .pattern = "DC CVAU, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b011, 0b0111, 0b1011, 0b001, .Xt },
+ },
+ .{
+ .pattern = "DC CIVAC, <Xt>",
+ .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 #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .dcps1, .imm },
+ },
+ // C6.2.111 DCPS2
+ .{
+ .pattern = "DCPS2",
+ .symbols = .{},
+ .encode = .{ .dcps2, 0 },
+ },
+ .{
+ .pattern = "DCPS2 #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .dcps2, .imm },
+ },
+ // C6.2.112 DCPS3
+ .{
+ .pattern = "DCPS3",
+ .symbols = .{},
+ .encode = .{ .dcps3, 0 },
+ },
+ .{
+ .pattern = "DCPS3 #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .dcps3, .imm },
+ },
+ // C6.2.116 DSB
+ .{
+ .pattern = "DSB <option>",
+ .symbols = .{
+ .option = .{ .barrier = .{} },
+ },
+ .encode = .{ .dsb, .option },
+ },
+ .{
+ .pattern = "DSB #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 4 } } },
+ },
+ .encode = .{ .dsb, .imm },
+ },
+ // C6.2.120 EOR (shifted register)
+ .{
+ .pattern = "EOR <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .eor, .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "EOR <Wd>, <Wn>, <Wm>, <shift> #<amount>",
+ .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 = .{ .eor, .Wd, .Wn, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } },
+ },
+ .{
+ .pattern = "EOR <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .eor, .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "EOR <Xd>, <Xn>, <Xm>, <shift> #<amount>",
+ .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 = .{ .eor, .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } },
+ },
+ // C6.2.124 EXTR
+ .{
+ .pattern = "EXTR <Wd>, <Wn>, <Wm>, #<lsb>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .lsb = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } },
+ },
+ .encode = .{ .extr, .Wd, .Wn, .Wm, .lsb },
+ },
+ .{
+ .pattern = "EXTR <Xd>, <Xn>, <Xm>, #<lsb>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .lsb = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } },
+ },
+ .encode = .{ .extr, .Xd, .Xn, .Xm, .lsb },
+ },
+ // C6.2.126 HINT
+ .{
+ .pattern = "HINT #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 7 } } },
+ },
+ .encode = .{ .hint, .imm },
+ },
+ // C6.2.127 HLT
+ .{
+ .pattern = "HLT #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .hlt, .imm },
+ },
+ // C6.2.128 HVC
+ .{
+ .pattern = "HVC #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .hvc, .imm },
+ },
+ // C6.2.129 IC
+ .{
+ .pattern = "IC IALLUIS",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b0001, 0b000, .xzr },
+ },
+ .{
+ .pattern = "IC IALLUIS, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b0001, 0b000, .Xt },
+ },
+ .{
+ .pattern = "IC IALLU",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b0101, 0b000, .xzr },
+ },
+ .{
+ .pattern = "IC IALLU, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b000, 0b0111, 0b0101, 0b000, .Xt },
+ },
+ .{
+ .pattern = "IC IVAU",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b011, 0b0111, 0b0101, 0b001, .xzr },
+ },
+ .{
+ .pattern = "IC IVAU, <Xt>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sys, 0b011, 0b0111, 0b0101, 0b001, .Xt },
+ },
+ // C6.2.131 ISB
+ .{
+ .pattern = "ISB",
+ .symbols = .{},
+ .encode = .{ .isb, .sy },
+ },
+ .{
+ .pattern = "ISB <option>",
+ .symbols = .{
+ .option = .{ .barrier = .{ .only_sy = true } },
+ },
+ .encode = .{ .isb, .option },
+ },
+ .{
+ .pattern = "ISB #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 4 } } },
+ },
+ .encode = .{ .isb, .imm },
+ },
+ // C6.2.164 LDP
+ .{
+ .pattern = "LDP <Wt1>, <Wt2>, [<Xn|SP>], #<imm>",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .ldp, .Wt1, .Wt2, .{ .post_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "LDP <Xt1>, <Xt2>, [<Xn|SP>], #<imm>",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 10 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .ldp, .Xt1, .Xt2, .{ .post_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "LDP <Wt1>, <Wt2>, [<Xn|SP>, #<imm>]!",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .ldp, .Wt1, .Wt2, .{ .pre_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "LDP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]!",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 10 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .ldp, .Xt1, .Xt2, .{ .pre_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "LDP <Wt1>, <Wt2>, [<Xn|SP>]",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .ldp, .Wt1, .Wt2, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "LDP <Wt1>, <Wt2>, [<Xn|SP>, #<imm>]",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .ldp, .Wt1, .Wt2, .{ .signed_offset = .{ .base = .Xn, .offset = .imm } } },
+ },
+ .{
+ .pattern = "LDP <Xt1>, <Xt2>, [<Xn|SP>]",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .ldp, .Xt1, .Xt2, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "LDP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 10 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .ldp, .Xt1, .Xt2, .{ .signed_offset = .{ .base = .Xn, .offset = .imm } } },
+ },
+ // C6.2.166 LDR (immediate)
+ .{
+ .pattern = "LDR <Wt>, [<Xn|SP>], #<simm>",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .ldr, .Wt, .{ .post_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "LDR <Xt>, [<Xn|SP>], #<simm>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .ldr, .Xt, .{ .post_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "LDR <Wt>, [<Xn|SP>, #<simm>]!",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .ldr, .Wt, .{ .pre_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "LDR <Xt>, [<Xn|SP>, #<simm>]!",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .ldr, .Xt, .{ .pre_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "LDR <Wt>, [<Xn|SP>]",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .ldr, .Wt, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "LDR <Wt>, [<Xn|SP>, #<pimm>]",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .pimm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 14 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .ldr, .Wt, .{ .unsigned_offset = .{ .base = .Xn, .offset = .pimm } } },
+ },
+ .{
+ .pattern = "LDR <Xt>, [<Xn|SP>]",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .ldr, .Xt, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "LDR <Xt>, [<Xn|SP>, #<pimm>]",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .pimm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 15 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .ldr, .Xt, .{ .unsigned_offset = .{ .base = .Xn, .offset = .pimm } } },
+ },
+ // C6.2.212 LSL (register)
+ .{
+ .pattern = "LSL <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lslv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSL <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lslv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.214 LSLV
+ .{
+ .pattern = "LSLV <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lslv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSLV <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lslv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.215 LSR (register)
+ .{
+ .pattern = "LSR <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lsrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSR <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lsrv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.217 LSRV
+ .{
+ .pattern = "LSRV <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .lsrv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "LSRV <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .lsrv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.220 MOV (to/from SP)
+ .{
+ .pattern = "MOV WSP, <Wn|WSP>",
+ .symbols = .{
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } },
+ },
+ .encode = .{ .add, .wsp, .Wn, .{ .immediate = 0 } },
+ },
+ .{
+ .pattern = "MOV <Wd|WSP>, WSP",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } },
+ },
+ .encode = .{ .add, .Wd, .wsp, .{ .immediate = 0 } },
+ },
+ .{
+ .pattern = "MOV SP, <Xn|SP>",
+ .symbols = .{
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .add, .sp, .Xn, .{ .immediate = 0 } },
+ },
+ .{
+ .pattern = "MOV <Xd|SP>, SP",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .add, .Xd, .sp, .{ .immediate = 0 } },
+ },
+ // C6.2.222 MOV (wide immediate)
+ .{
+ .pattern = "MOV <Wd>, #<imm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movz, .Wd, .imm, .{ .lsl = .@"0" } },
+ },
+ .{
+ .pattern = "MOV <Xd>, #<imm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movz, .Xd, .imm, .{ .lsl = .@"0" } },
+ },
+ // C6.2.224 MOV (register)
+ .{
+ .pattern = "MOV <Wd>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .orr, .Wd, .wzr, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "MOV <Xd>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .orr, .Xd, .xzr, .{ .register = .Xm } },
+ },
+ // C6.2.225 MOVK
+ .{
+ .pattern = "MOVK <Wd>, #<imm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movk, .Wd, .imm, .{} },
+ },
+ .{
+ .pattern = "MOVK <Wd>, #<imm>, LSL #<shift>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 }, .multiple_of = 16 } },
+ },
+ .encode = .{ .movk, .Wd, .imm, .{ .lsl = .shift } },
+ },
+ .{
+ .pattern = "MOVK <Xd>, #<imm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movk, .Xd, .imm, .{} },
+ },
+ .{
+ .pattern = "MOVK <Xd>, #<imm>, LSL #<shift>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 }, .multiple_of = 16 } },
+ },
+ .encode = .{ .movk, .Xd, .imm, .{ .lsl = .shift } },
+ },
+ // C6.2.226 MOVN
+ .{
+ .pattern = "MOVN <Wd>, #<imm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movn, .Wd, .imm, .{} },
+ },
+ .{
+ .pattern = "MOVN <Wd>, #<imm>, LSL #<shift>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 }, .multiple_of = 16 } },
+ },
+ .encode = .{ .movn, .Wd, .imm, .{ .lsl = .shift } },
+ },
+ .{
+ .pattern = "MOVN <Xd>, #<imm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movn, .Xd, .imm, .{} },
+ },
+ .{
+ .pattern = "MOVN <Xd>, #<imm>, LSL #<shift>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 }, .multiple_of = 16 } },
+ },
+ .encode = .{ .movn, .Xd, .imm, .{ .lsl = .shift } },
+ },
+ // C6.2.227 MOVZ
+ .{
+ .pattern = "MOVZ <Wd>, #<imm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movz, .Wd, .imm, .{} },
+ },
+ .{
+ .pattern = "MOVZ <Wd>, #<imm>, LSL #<shift>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 }, .multiple_of = 16 } },
+ },
+ .encode = .{ .movz, .Wd, .imm, .{ .lsl = .shift } },
+ },
+ .{
+ .pattern = "MOVZ <Xd>, #<imm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .movz, .Xd, .imm, .{} },
+ },
+ .{
+ .pattern = "MOVZ <Xd>, #<imm>, LSL #<shift>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 }, .multiple_of = 16 } },
+ },
+ .encode = .{ .movz, .Xd, .imm, .{ .lsl = .shift } },
+ },
+ // C6.2.228 MRS
+ .{
+ .pattern = "MRS <Xt>, <systemreg>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .systemreg = .systemreg,
+ },
+ .encode = .{ .mrs, .Xt, .systemreg },
+ },
+ // C6.2.230 MSR (register)
+ .{
+ .pattern = "MSR <systemreg>, <Xt>",
+ .symbols = .{
+ .systemreg = .systemreg,
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .msr, .systemreg, .Xt },
+ },
+ // C6.2.234 NEG
+ .{
+ .pattern = "NEG <Wd>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .sub, .Wd, .wzr, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "NEG <Wd>, <Wm>, <shift> #<amount>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .shift = .{ .shift = .{ .allow_ror = false } },
+ .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } },
+ },
+ .encode = .{ .sub, .Wd, .wzr, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } },
+ },
+ .{
+ .pattern = "NEG <Xd>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sub, .Xd, .xzr, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "NEG <Xd>, <Xm>, <shift> #<amount>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .shift = .{ .shift = .{ .allow_ror = false } },
+ .amount = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } },
+ },
+ .encode = .{ .sub, .Xd, .xzr, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } },
+ },
+ // C6.2.238 NOP
+ .{
+ .pattern = "NOP",
+ .symbols = .{},
+ .encode = .{.nop},
+ },
+ // C6.2.241 ORR (shifted register)
+ .{
+ .pattern = "ORR <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .orr, .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "ORR <Wd>, <Wn>, <Wm>, <shift> #<amount>",
+ .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 = .{ .orr, .Wd, .Wn, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } },
+ },
+ .{
+ .pattern = "ORR <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .orr, .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "ORR <Xd>, <Xn>, <Xm>, <shift> #<amount>",
+ .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 = .{ .orr, .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } },
+ },
+ // C6.2.254 RET
+ .{
+ .pattern = "RET",
+ .symbols = .{},
+ .encode = .{ .ret, .x30 },
+ },
+ .{
+ .pattern = "RET <Xn>",
+ .symbols = .{
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .ret, .Xn },
+ },
+ // C6.2.261 ROR (immediate)
+ .{
+ .pattern = "ROR <Wd>, <Ws>, #<shift>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Ws = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 5 } } },
+ },
+ .encode = .{ .extr, .Wd, .Ws, .Ws, .shift },
+ },
+ .{
+ .pattern = "ROR <Xd>, <Xs>, #<shift>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xs = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .shift = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 6 } } },
+ },
+ .encode = .{ .extr, .Xd, .Xs, .Xs, .shift },
+ },
+ // C6.2.262 ROR (register)
+ .{
+ .pattern = "ROR <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .rorv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "ROR <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .rorv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.263 RORV
+ .{
+ .pattern = "RORV <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .rorv, .Wd, .Wn, .Wm },
+ },
+ .{
+ .pattern = "RORV <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .rorv, .Xd, .Xn, .Xm },
+ },
+ // C6.2.268 SBFM
+ .{
+ .pattern = "SBFM <Wd>, <Wn>, #<immr>, #<imms>",
+ .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 = .{ .sbfm, .Wd, .Wn, .{ .N = .word, .immr = .immr, .imms = .imms } },
+ },
+ .{
+ .pattern = "SBFM <Xd>, <Xn>, #<immr>, #<imms>",
+ .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 = .{ .sbfm, .Xd, .Xn, .{ .N = .doubleword, .immr = .immr, .imms = .imms } },
+ },
+ // C6.2.280 SEV
+ .{
+ .pattern = "SEV",
+ .symbols = .{},
+ .encode = .{.sev},
+ },
+ // C6.2.281 SEVL
+ .{
+ .pattern = "SEVL",
+ .symbols = .{},
+ .encode = .{.sevl},
+ },
+ // C6.2.283 SMC
+ .{
+ .pattern = "SMC #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .smc, .imm },
+ },
+ // C6.2.321 STP
+ .{
+ .pattern = "STP <Wt1>, <Wt2>, [<Xn|SP>], #<imm>",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .stp, .Wt1, .Wt2, .{ .post_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "STP <Xt1>, <Xt2>, [<Xn|SP>], #<imm>",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 10 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .stp, .Xt1, .Xt2, .{ .post_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "STP <Wt1>, <Wt2>, [<Xn|SP>, #<imm>]!",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .stp, .Wt1, .Wt2, .{ .pre_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "STP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]!",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 10 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .stp, .Xt1, .Xt2, .{ .pre_index = .{ .base = .Xn, .index = .imm } } },
+ },
+ .{
+ .pattern = "STP <Wt1>, <Wt2>, [<Xn|SP>]",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .stp, .Wt1, .Wt2, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "STP <Wt1>, <Wt2>, [<Xn|SP>, #<imm>]",
+ .symbols = .{
+ .Wt1 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wt2 = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .stp, .Wt1, .Wt2, .{ .signed_offset = .{ .base = .Xn, .offset = .imm } } },
+ },
+ .{
+ .pattern = "STP <Xt1>, <Xt2>, [<Xn|SP>]",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .stp, .Xt1, .Xt2, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "STP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]",
+ .symbols = .{
+ .Xt1 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xt2 = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .imm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 10 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .stp, .Xt1, .Xt2, .{ .signed_offset = .{ .base = .Xn, .offset = .imm } } },
+ },
+ // C6.2.322 STR (immediate)
+ .{
+ .pattern = "STR <Wt>, [<Xn|SP>], #<simm>",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .str, .Wt, .{ .post_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "STR <Xt>, [<Xn|SP>], #<simm>",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .str, .Xt, .{ .post_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "STR <Wt>, [<Xn|SP>, #<simm>]!",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .str, .Wt, .{ .pre_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "STR <Xt>, [<Xn|SP>, #<simm>]!",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .simm = .{ .imm = .{ .type = .{ .signedness = .signed, .bits = 9 } } },
+ },
+ .encode = .{ .str, .Xt, .{ .pre_index = .{ .base = .Xn, .index = .simm } } },
+ },
+ .{
+ .pattern = "STR <Wt>, [<Xn|SP>]",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .str, .Wt, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "STR <Wt>, [<Xn|SP>, #<pimm>]",
+ .symbols = .{
+ .Wt = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .pimm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 14 }, .multiple_of = 4 } },
+ },
+ .encode = .{ .str, .Wt, .{ .unsigned_offset = .{ .base = .Xn, .offset = .pimm } } },
+ },
+ .{
+ .pattern = "STR <Xt>, [<Xn|SP>]",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ },
+ .encode = .{ .str, .Xt, .{ .base = .Xn } },
+ },
+ .{
+ .pattern = "STR <Xt>, [<Xn|SP>, #<pimm>]",
+ .symbols = .{
+ .Xt = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .pimm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 15 }, .multiple_of = 8 } },
+ },
+ .encode = .{ .str, .Xt, .{ .unsigned_offset = .{ .base = .Xn, .offset = .pimm } } },
+ },
+ // C6.2.356 SUB (extended register)
+ .{
+ .pattern = "SUB <Wd|WSP>, <Wn|WSP>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word }, .allow_sp = true } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .sub, .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "SUB <Wd|WSP>, <Wn|WSP>, <Wm>, <extend> #<amount>",
+ .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 = .{ .sub, .Wd, .Wn, .{ .extended_register_explicit = .{ .register = .Wm, .option = .extend, .amount = .amount } } },
+ },
+ .{
+ .pattern = "SUB <Xd|SP>, <Xn|SP>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword }, .allow_sp = true } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sub, .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "SUB <Xd|SP>, <Xn|SP>, <Wm>, <extend> #<amount>",
+ .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 = .{ .sub, .Xd, .Xn, .{ .extended_register_explicit = .{ .register = .Wm, .option = .extend, .amount = .amount } } },
+ },
+ .{
+ .pattern = "SUB <Xd|SP>, <Xn|SP>, <Xm>, <extend> #<amount>",
+ .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 = .{ .sub, .Xd, .Xn, .{ .extended_register_explicit = .{ .register = .Xm, .option = .extend, .amount = .amount } } },
+ },
+ // C6.2.357 SUB (immediate)
+ .{
+ .pattern = "SUB <Wd|WSP>, <Wn|WSP>, #<imm>",
+ .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 = .{ .sub, .Wd, .Wn, .{ .immediate = .imm } },
+ },
+ .{
+ .pattern = "SUB <Wd|WSP>, <Wn|WSP>, #<imm>, LSL #<shift>",
+ .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 = .{ .sub, .Wd, .Wn, .{ .shifted_immediate = .{ .immediate = .imm, .lsl = .shift } } },
+ },
+ .{
+ .pattern = "SUB <Xd|SP>, <Xn|SP>, #<imm>",
+ .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 = .{ .sub, .Xd, .Xn, .{ .immediate = .imm } },
+ },
+ .{
+ .pattern = "SUB <Xd|SP>, <Xn|SP>, #<imm>, LSL #<shift>",
+ .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 = .{ .sub, .Xd, .Xn, .{ .shifted_immediate = .{ .immediate = .imm, .lsl = .shift } } },
+ },
+ // C6.2.358 SUB (shifted register)
+ .{
+ .pattern = "SUB <Wd>, <Wn>, <Wm>",
+ .symbols = .{
+ .Wd = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wn = .{ .reg = .{ .format = .{ .integer = .word } } },
+ .Wm = .{ .reg = .{ .format = .{ .integer = .word } } },
+ },
+ .encode = .{ .sub, .Wd, .Wn, .{ .register = .Wm } },
+ },
+ .{
+ .pattern = "SUB <Wd>, <Wn>, <Wm>, <shift> #<amount>",
+ .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 = .{ .sub, .Wd, .Wn, .{ .shifted_register_explicit = .{ .register = .Wm, .shift = .shift, .amount = .amount } } },
+ },
+ .{
+ .pattern = "SUB <Xd>, <Xn>, <Xm>",
+ .symbols = .{
+ .Xd = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xn = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ .Xm = .{ .reg = .{ .format = .{ .integer = .doubleword } } },
+ },
+ .encode = .{ .sub, .Xd, .Xn, .{ .register = .Xm } },
+ },
+ .{
+ .pattern = "SUB <Xd>, <Xn>, <Xm>, <shift> #<amount>",
+ .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 = .{ .sub, .Xd, .Xn, .{ .shifted_register_explicit = .{ .register = .Xm, .shift = .shift, .amount = .amount } } },
+ },
+ // C6.2.365 SVC
+ .{
+ .pattern = "SVC #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .svc, .imm },
+ },
+ // C6.2.376 TCANCEL
+ .{
+ .pattern = "TCANCEL #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .tcancel, .imm },
+ },
+ // C6.2.385 UBFM
+ .{
+ .pattern = "UBFM <Wd>, <Wn>, #<immr>, #<imms>",
+ .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 = .{ .ubfm, .Wd, .Wn, .{ .N = .word, .immr = .immr, .imms = .imms } },
+ },
+ .{
+ .pattern = "UBFM <Xd>, <Xn>, #<immr>, #<imms>",
+ .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 = .{ .ubfm, .Xd, .Xn, .{ .N = .doubleword, .immr = .immr, .imms = .imms } },
+ },
+ // C6.2.387 UDF
+ .{
+ .pattern = "UDF #<imm>",
+ .symbols = .{
+ .imm = .{ .imm = .{ .type = .{ .signedness = .unsigned, .bits = 16 } } },
+ },
+ .encode = .{ .udf, .imm },
+ },
+ // C6.2.396 WFE
+ .{
+ .pattern = "WFE",
+ .symbols = .{},
+ .encode = .{.wfe},
+ },
+ // C6.2.398 WFI
+ .{
+ .pattern = "WFI",
+ .symbols = .{},
+ .encode = .{.wfi},
+ },
+ // C6.2.402 YIELD
+ .{
+ .pattern = "YIELD",
+ .symbols = .{},
+ .encode = .{.yield},
+ },
+}
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index c726c05e1b..832c8b2ea5 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -449,14 +449,15 @@ pub const Function = struct {
if (gop.found_existing) return gop.value_ptr.*;
const pt = f.object.dg.pt;
+ const zcu = pt.zcu;
const val = (try f.air.value(ref, pt)).?;
const ty = f.typeOf(ref);
- const result: CValue = if (lowersToArray(ty, pt)) result: {
+ const result: CValue = if (lowersToArray(ty, zcu)) result: {
const ch = &f.object.code_header.writer;
const decl_c_value = try f.allocLocalValue(.{
.ctype = try f.ctypeFromType(ty, .complete),
- .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)),
+ .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(zcu)),
});
const gpa = f.object.dg.gpa;
try f.allocs.put(gpa, decl_c_value.new_local, false);
@@ -916,7 +917,7 @@ pub const DeclGen = struct {
// Ensure complete type definition is available before accessing fields.
_ = try dg.ctypeFromType(parent_ptr_ty.childType(zcu), .complete);
- switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, pt)) {
+ switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, zcu)) {
.begin => {
const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
try w.writeByte('(');
@@ -3008,7 +3009,7 @@ pub fn generate(
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) @import("../codegen.zig").CodeGenError!Mir {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3021,7 +3022,7 @@ pub fn generate(
var function: Function = .{
.value_map = .init(gpa),
.air = air.*,
- .liveness = liveness.*,
+ .liveness = liveness.*.?,
.func_index = func_index,
.object = .{
.dg = .{
@@ -3961,7 +3962,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
else
true;
- const is_array = lowersToArray(src_ty, pt);
+ const is_array = lowersToArray(src_ty, zcu);
const need_memcpy = !is_aligned or is_array;
const w = &f.object.code.writer;
@@ -4044,7 +4045,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
const operand = try f.resolveInst(un_op);
try reap(f, inst, &.{un_op});
var deref = is_ptr;
- const is_array = lowersToArray(ret_ty, pt);
+ const is_array = lowersToArray(ret_ty, zcu);
const ret_val = if (is_array) ret_val: {
const array_local = try f.allocAlignedLocal(inst, .{
.ctype = ret_ctype,
@@ -4228,7 +4229,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
else
true;
- const is_array = lowersToArray(.fromInterned(ptr_info.child), pt);
+ const is_array = lowersToArray(.fromInterned(ptr_info.child), zcu);
const need_memcpy = !is_aligned or is_array;
const src_val = try f.resolveInst(bin_op.rhs);
@@ -4873,7 +4874,7 @@ fn airCall(
}
const result = result: {
- if (result_local == .none or !lowersToArray(ret_ty, pt))
+ if (result_local == .none or !lowersToArray(ret_ty, zcu))
break :result result_local;
const array_local = try f.allocLocal(inst, ret_ty);
@@ -5971,13 +5972,12 @@ fn fieldLocation(
container_ptr_ty: Type,
field_ptr_ty: Type,
field_index: u32,
- pt: Zcu.PerThread,
+ zcu: *Zcu,
) union(enum) {
begin: void,
field: CValue,
byte_offset: u64,
} {
- const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const container_ty: Type = .fromInterned(ip.indexToKey(container_ptr_ty.toIntern()).ptr_type.child);
switch (ip.indexToKey(container_ty.toIntern())) {
@@ -5994,7 +5994,7 @@ fn fieldLocation(
else
.{ .field = field_index } },
.@"packed" => if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0)
- .{ .byte_offset = @divExact(pt.structPackedFieldBitOffset(loaded_struct, field_index) +
+ .{ .byte_offset = @divExact(zcu.structPackedFieldBitOffset(loaded_struct, field_index) +
container_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset, 8) }
else
.begin,
@@ -6076,7 +6076,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
try f.renderType(w, container_ptr_ty);
try w.writeByte(')');
- switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, pt)) {
+ switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, zcu)) {
.begin => try f.writeCValue(w, field_ptr_val, .Other),
.field => |field| {
const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
@@ -6131,7 +6131,7 @@ fn fieldPtr(
try f.renderType(w, field_ptr_ty);
try w.writeByte(')');
- switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, pt)) {
+ switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, zcu)) {
.begin => try f.writeCValue(w, container_ptr_val, .Other),
.field => |field| {
try w.writeByte('&');
@@ -6189,7 +6189,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
const bit_offset_ty = try pt.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
- const bit_offset = pt.structPackedFieldBitOffset(loaded_struct, extra.field_index);
+ const bit_offset = zcu.structPackedFieldBitOffset(loaded_struct, extra.field_index);
const field_int_signedness = if (inst_ty.isAbiInt(zcu))
inst_ty.intInfo(zcu).signedness
@@ -8573,8 +8573,7 @@ const Vectorize = struct {
}
};
-fn lowersToArray(ty: Type, pt: Zcu.PerThread) bool {
- const zcu = pt.zcu;
+fn lowersToArray(ty: Type, zcu: *Zcu) bool {
return switch (ty.zigTypeTag(zcu)) {
.array, .vector => return true,
else => return ty.isAbiInt(zcu) and toCIntBits(@as(u32, @intCast(ty.bitSize(zcu)))) == null,
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index a570dd5ec0..111fc6ec14 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -20,6 +20,7 @@ const Package = @import("../Package.zig");
const Air = @import("../Air.zig");
const Value = @import("../Value.zig");
const Type = @import("../Type.zig");
+const codegen = @import("../codegen.zig");
const x86_64_abi = @import("../arch/x86_64/abi.zig");
const wasm_c_abi = @import("wasm/abi.zig");
const aarch64_c_abi = @import("aarch64/abi.zig");
@@ -1131,7 +1132,7 @@ pub const Object = struct {
pt: Zcu.PerThread,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) !void {
const zcu = pt.zcu;
const comp = zcu.comp;
@@ -1489,7 +1490,7 @@ pub const Object = struct {
var fg: FuncGen = .{
.gpa = gpa,
.air = air.*,
- .liveness = liveness.*,
+ .liveness = liveness.*.?,
.ng = &ng,
.wip = wip,
.is_naked = fn_info.cc == .naked,
@@ -4210,7 +4211,7 @@ pub const Object = struct {
.eu_payload => |eu_ptr| try o.lowerPtr(
pt,
eu_ptr,
- offset + @import("../codegen.zig").errUnionPayloadOffset(
+ offset + codegen.errUnionPayloadOffset(
Value.fromInterned(eu_ptr).typeOf(zcu).childType(zcu),
zcu,
),
@@ -6050,10 +6051,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(
@@ -6969,7 +6970,7 @@ pub const FuncGen = struct {
.@"struct" => switch (struct_ty.containerLayout(zcu)) {
.@"packed" => {
const struct_type = zcu.typeToStruct(struct_ty).?;
- const bit_offset = pt.structPackedFieldBitOffset(struct_type, field_index);
+ const bit_offset = zcu.structPackedFieldBitOffset(struct_type, field_index);
const containing_int = struct_llvm_val;
const shift_amt =
try o.builder.intValue(containing_int.typeOfWip(&self.wip), bit_offset);
@@ -11364,7 +11365,7 @@ pub const FuncGen = struct {
// We have a pointer to a packed struct field that happens to be byte-aligned.
// Offset our operand pointer by the correct number of bytes.
- const byte_offset = @divExact(pt.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8);
+ const byte_offset = @divExact(zcu.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8);
if (byte_offset == 0) return struct_ptr;
const usize_ty = try o.lowerType(pt, Type.usize);
const llvm_index = try o.builder.intValue(usize_ty, byte_offset);
diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig
index 292f5a62fc..17fa62d18f 100644
--- a/src/codegen/spirv.zig
+++ b/src/codegen/spirv.zig
@@ -251,11 +251,11 @@ pub const Object = struct {
pt: Zcu.PerThread,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) !void {
const nav = pt.zcu.funcInfo(func_index).owner_nav;
// TODO: Separate types for generating decls and functions?
- try self.genNav(pt, nav, air.*, liveness.*, true);
+ try self.genNav(pt, nav, air.*, liveness.*.?, true);
}
pub fn updateNav(
@@ -5134,7 +5134,7 @@ const NavGen = struct {
.@"struct" => switch (object_ty.containerLayout(zcu)) {
.@"packed" => {
const struct_ty = zcu.typeToPackedStruct(object_ty).?;
- const bit_offset = pt.structPackedFieldBitOffset(struct_ty, field_index);
+ const bit_offset = zcu.structPackedFieldBitOffset(struct_ty, field_index);
const bit_offset_id = try self.constInt(.u16, bit_offset);
const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;
const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));