master
    1pt: Zcu.PerThread,
    2target: *const std.Target,
    3air: Air,
    4nav_index: InternPool.Nav.Index,
    5
    6// Blocks
    7def_order: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, void),
    8blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, Block),
    9loops: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, Loop),
   10active_loops: std.ArrayList(Loop.Index),
   11loop_live: struct {
   12    set: std.AutoArrayHashMapUnmanaged(struct { Loop.Index, Air.Inst.Index }, void),
   13    list: std.ArrayList(Air.Inst.Index),
   14},
   15dom_start: u32,
   16dom_len: u32,
   17dom: std.ArrayList(DomInt),
   18
   19// Wip Mir
   20saved_registers: std.enums.EnumSet(Register.Alias),
   21instructions: std.ArrayList(codegen.aarch64.encoding.Instruction),
   22literals: std.ArrayList(u32),
   23nav_relocs: std.ArrayList(codegen.aarch64.Mir.Reloc.Nav),
   24uav_relocs: std.ArrayList(codegen.aarch64.Mir.Reloc.Uav),
   25lazy_relocs: std.ArrayList(codegen.aarch64.Mir.Reloc.Lazy),
   26global_relocs: std.ArrayList(codegen.aarch64.Mir.Reloc.Global),
   27literal_relocs: std.ArrayList(codegen.aarch64.Mir.Reloc.Literal),
   28
   29// Stack Frame
   30returns: bool,
   31va_list: union(enum) {
   32    other: Value.Indirect,
   33    sysv: struct {
   34        __stack: Value.Indirect,
   35        __gr_top: Value.Indirect,
   36        __vr_top: Value.Indirect,
   37        __gr_offs: i32,
   38        __vr_offs: i32,
   39    },
   40},
   41stack_size: u24,
   42stack_align: InternPool.Alignment,
   43
   44// Value Tracking
   45live_registers: LiveRegisters,
   46live_values: std.AutoHashMapUnmanaged(Air.Inst.Index, Value.Index),
   47values: std.ArrayList(Value),
   48
   49pub const LiveRegisters = std.enums.EnumArray(Register.Alias, Value.Index);
   50
   51pub const Block = struct {
   52    live_registers: LiveRegisters,
   53    target_label: u32,
   54
   55    pub const main: Air.Inst.Index = @enumFromInt(
   56        std.math.maxInt(@typeInfo(Air.Inst.Index).@"enum".tag_type),
   57    );
   58
   59    fn branch(target_block: *const Block, isel: *Select) !void {
   60        if (isel.instructions.items.len > target_block.target_label) {
   61            try isel.emit(.b(@intCast((isel.instructions.items.len + 1 - target_block.target_label) << 2)));
   62        }
   63        try isel.merge(&target_block.live_registers, .{});
   64    }
   65};
   66
   67pub const Loop = struct {
   68    def_order: u32,
   69    dom: u32,
   70    depth: u32,
   71    live: u32,
   72    live_registers: LiveRegisters,
   73    repeat_list: u32,
   74
   75    pub const invalid: Air.Inst.Index = @enumFromInt(
   76        std.math.maxInt(@typeInfo(Air.Inst.Index).@"enum".tag_type),
   77    );
   78
   79    pub const Index = enum(u32) {
   80        _,
   81
   82        fn inst(li: Loop.Index, isel: *Select) Air.Inst.Index {
   83            return isel.loops.keys()[@intFromEnum(li)];
   84        }
   85
   86        fn get(li: Loop.Index, isel: *Select) *Loop {
   87            return &isel.loops.values()[@intFromEnum(li)];
   88        }
   89    };
   90
   91    pub const empty_list: u32 = std.math.maxInt(u32);
   92
   93    fn branch(target_loop: *Loop, isel: *Select) !void {
   94        try isel.instructions.ensureUnusedCapacity(isel.pt.zcu.gpa, 1);
   95        const repeat_list_tail = target_loop.repeat_list;
   96        target_loop.repeat_list = @intCast(isel.instructions.items.len);
   97        isel.instructions.appendAssumeCapacity(@bitCast(repeat_list_tail));
   98        try isel.merge(&target_loop.live_registers, .{});
   99    }
  100};
  101
  102pub fn deinit(isel: *Select) void {
  103    const gpa = isel.pt.zcu.gpa;
  104
  105    isel.def_order.deinit(gpa);
  106    isel.blocks.deinit(gpa);
  107    isel.loops.deinit(gpa);
  108    isel.active_loops.deinit(gpa);
  109    isel.loop_live.set.deinit(gpa);
  110    isel.loop_live.list.deinit(gpa);
  111    isel.dom.deinit(gpa);
  112
  113    isel.instructions.deinit(gpa);
  114    isel.literals.deinit(gpa);
  115    isel.nav_relocs.deinit(gpa);
  116    isel.uav_relocs.deinit(gpa);
  117    isel.lazy_relocs.deinit(gpa);
  118    isel.global_relocs.deinit(gpa);
  119    isel.literal_relocs.deinit(gpa);
  120
  121    isel.live_values.deinit(gpa);
  122    isel.values.deinit(gpa);
  123
  124    isel.* = undefined;
  125}
  126
  127pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
  128    const zcu = isel.pt.zcu;
  129    const ip = &zcu.intern_pool;
  130    const gpa = zcu.gpa;
  131    const air_tags = isel.air.instructions.items(.tag);
  132    const air_data = isel.air.instructions.items(.data);
  133    var air_body_index: usize = 0;
  134    var air_inst_index = air_body[air_body_index];
  135    const initial_def_order_len = isel.def_order.count();
  136    air_tag: switch (air_tags[@intFromEnum(air_inst_index)]) {
  137        // No "scalarize" legalizations are enabled, so these instructions never appear.
  138        .legalize_vec_elem_val => unreachable,
  139        .legalize_vec_store_elem => unreachable,
  140        // No soft float legalizations are enabled.
  141        .legalize_compiler_rt_call => unreachable,
  142
  143        .arg,
  144        .ret_addr,
  145        .frame_addr,
  146        .err_return_trace,
  147        .save_err_return_trace_index,
  148        .runtime_nav_ptr,
  149        .c_va_start,
  150        => {
  151            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  152
  153            air_body_index += 1;
  154            air_inst_index = air_body[air_body_index];
  155            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  156        },
  157        .add,
  158        .add_safe,
  159        .add_optimized,
  160        .add_wrap,
  161        .add_sat,
  162        .sub,
  163        .sub_safe,
  164        .sub_optimized,
  165        .sub_wrap,
  166        .sub_sat,
  167        .mul,
  168        .mul_safe,
  169        .mul_optimized,
  170        .mul_wrap,
  171        .mul_sat,
  172        .div_float,
  173        .div_float_optimized,
  174        .div_trunc,
  175        .div_trunc_optimized,
  176        .div_floor,
  177        .div_floor_optimized,
  178        .div_exact,
  179        .div_exact_optimized,
  180        .rem,
  181        .rem_optimized,
  182        .mod,
  183        .mod_optimized,
  184        .max,
  185        .min,
  186        .bit_and,
  187        .bit_or,
  188        .shr,
  189        .shr_exact,
  190        .shl,
  191        .shl_exact,
  192        .shl_sat,
  193        .xor,
  194        .cmp_lt,
  195        .cmp_lt_optimized,
  196        .cmp_lte,
  197        .cmp_lte_optimized,
  198        .cmp_eq,
  199        .cmp_eq_optimized,
  200        .cmp_gte,
  201        .cmp_gte_optimized,
  202        .cmp_gt,
  203        .cmp_gt_optimized,
  204        .cmp_neq,
  205        .cmp_neq_optimized,
  206        .bool_and,
  207        .bool_or,
  208        .array_elem_val,
  209        .slice_elem_val,
  210        .ptr_elem_val,
  211        => {
  212            const bin_op = air_data[@intFromEnum(air_inst_index)].bin_op;
  213
  214            try isel.analyzeUse(bin_op.lhs);
  215            try isel.analyzeUse(bin_op.rhs);
  216            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  217
  218            air_body_index += 1;
  219            air_inst_index = air_body[air_body_index];
  220            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  221        },
  222        .ptr_add,
  223        .ptr_sub,
  224        .add_with_overflow,
  225        .sub_with_overflow,
  226        .mul_with_overflow,
  227        .shl_with_overflow,
  228        .slice,
  229        .slice_elem_ptr,
  230        .ptr_elem_ptr,
  231        => {
  232            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  233            const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
  234
  235            try isel.analyzeUse(bin_op.lhs);
  236            try isel.analyzeUse(bin_op.rhs);
  237            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  238
  239            air_body_index += 1;
  240            air_inst_index = air_body[air_body_index];
  241            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  242        },
  243        .alloc => {
  244            const ty = air_data[@intFromEnum(air_inst_index)].ty;
  245
  246            isel.stack_align = isel.stack_align.maxStrict(ty.ptrAlignment(zcu));
  247            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  248
  249            air_body_index += 1;
  250            air_inst_index = air_body[air_body_index];
  251            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  252        },
  253        .inferred_alloc,
  254        .inferred_alloc_comptime,
  255        .wasm_memory_size,
  256        .wasm_memory_grow,
  257        .work_item_id,
  258        .work_group_size,
  259        .work_group_id,
  260        => unreachable,
  261        .ret_ptr => {
  262            const ty = air_data[@intFromEnum(air_inst_index)].ty;
  263
  264            if (isel.live_values.get(Block.main)) |ret_vi| switch (ret_vi.parent(isel)) {
  265                .unallocated, .stack_slot => isel.stack_align = isel.stack_align.maxStrict(ty.ptrAlignment(zcu)),
  266                .value, .constant => unreachable,
  267                .address => |address_vi| try isel.live_values.putNoClobber(gpa, air_inst_index, address_vi.ref(isel)),
  268            };
  269            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  270
  271            air_body_index += 1;
  272            air_inst_index = air_body[air_body_index];
  273            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  274        },
  275        .assembly => {
  276            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  277            const extra = isel.air.extraData(Air.Asm, ty_pl.payload);
  278            const operands: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0 .. extra.data.flags.outputs_len + extra.data.inputs_len]);
  279
  280            for (operands) |operand| if (operand != .none) try isel.analyzeUse(operand);
  281            if (ty_pl.ty != .void_type) try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  282
  283            air_body_index += 1;
  284            air_inst_index = air_body[air_body_index];
  285            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  286        },
  287        .not,
  288        .clz,
  289        .ctz,
  290        .popcount,
  291        .byte_swap,
  292        .bit_reverse,
  293        .abs,
  294        .load,
  295        .fptrunc,
  296        .fpext,
  297        .intcast,
  298        .intcast_safe,
  299        .trunc,
  300        .optional_payload,
  301        .optional_payload_ptr,
  302        .optional_payload_ptr_set,
  303        .wrap_optional,
  304        .unwrap_errunion_payload,
  305        .unwrap_errunion_err,
  306        .unwrap_errunion_payload_ptr,
  307        .unwrap_errunion_err_ptr,
  308        .errunion_payload_ptr_set,
  309        .wrap_errunion_payload,
  310        .wrap_errunion_err,
  311        .struct_field_ptr_index_0,
  312        .struct_field_ptr_index_1,
  313        .struct_field_ptr_index_2,
  314        .struct_field_ptr_index_3,
  315        .get_union_tag,
  316        .ptr_slice_len_ptr,
  317        .ptr_slice_ptr_ptr,
  318        .array_to_slice,
  319        .int_from_float,
  320        .int_from_float_optimized,
  321        .int_from_float_safe,
  322        .int_from_float_optimized_safe,
  323        .float_from_int,
  324        .splat,
  325        .error_set_has_value,
  326        .addrspace_cast,
  327        .c_va_arg,
  328        .c_va_copy,
  329        => {
  330            const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op;
  331
  332            try isel.analyzeUse(ty_op.operand);
  333            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  334
  335            air_body_index += 1;
  336            air_inst_index = air_body[air_body_index];
  337            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  338        },
  339        .bitcast => {
  340            const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op;
  341            maybe_noop: {
  342                if (ty_op.ty.toInterned().? != isel.air.typeOf(ty_op.operand, ip).toIntern()) break :maybe_noop;
  343                if (true) break :maybe_noop;
  344                if (ty_op.operand.toIndex()) |src_air_inst_index| {
  345                    if (isel.hints.get(src_air_inst_index)) |hint_vpsi| {
  346                        try isel.hints.putNoClobber(gpa, air_inst_index, hint_vpsi);
  347                    }
  348                }
  349            }
  350            try isel.analyzeUse(ty_op.operand);
  351            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  352
  353            air_body_index += 1;
  354            air_inst_index = air_body[air_body_index];
  355            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  356        },
  357        inline .block, .dbg_inline_block => |air_tag| {
  358            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  359            const extra = isel.air.extraData(switch (air_tag) {
  360                else => comptime unreachable,
  361                .block => Air.Block,
  362                .dbg_inline_block => Air.DbgInlineBlock,
  363            }, ty_pl.payload);
  364            const result_ty = ty_pl.ty.toInterned().?;
  365
  366            if (result_ty == .noreturn_type) {
  367                try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
  368
  369                air_body_index += 1;
  370                break :air_tag;
  371            }
  372
  373            assert(!(try isel.blocks.getOrPut(gpa, air_inst_index)).found_existing);
  374            try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
  375            const block_entry = isel.blocks.pop().?;
  376            assert(block_entry.key == air_inst_index);
  377
  378            if (result_ty != .void_type) try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  379
  380            air_body_index += 1;
  381            air_inst_index = air_body[air_body_index];
  382            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  383        },
  384        .loop => {
  385            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  386            const extra = isel.air.extraData(Air.Block, ty_pl.payload);
  387
  388            const initial_dom_start = isel.dom_start;
  389            const initial_dom_len = isel.dom_len;
  390            isel.dom_start = @intCast(isel.dom.items.len);
  391            isel.dom_len = @intCast(isel.blocks.count());
  392            try isel.active_loops.append(gpa, @enumFromInt(isel.loops.count()));
  393            try isel.loops.putNoClobber(gpa, air_inst_index, .{
  394                .def_order = @intCast(isel.def_order.count()),
  395                .dom = isel.dom_start,
  396                .depth = isel.dom_len,
  397                .live = 0,
  398                .live_registers = undefined,
  399                .repeat_list = undefined,
  400            });
  401            try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable);
  402            try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
  403            for (
  404                isel.dom.items[initial_dom_start..].ptr,
  405                isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable],
  406            ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
  407            isel.dom_start = initial_dom_start;
  408            isel.dom_len = initial_dom_len;
  409            assert(isel.active_loops.pop().?.inst(isel) == air_inst_index);
  410
  411            air_body_index += 1;
  412        },
  413        .repeat, .trap, .unreach => air_body_index += 1,
  414        .br => {
  415            const br = air_data[@intFromEnum(air_inst_index)].br;
  416            const block_index = isel.blocks.getIndex(br.block_inst).?;
  417            if (block_index < isel.dom_len) isel.dom.items[isel.dom_start + block_index / @bitSizeOf(DomInt)] |= @as(DomInt, 1) << @truncate(block_index);
  418            try isel.analyzeUse(br.operand);
  419
  420            air_body_index += 1;
  421        },
  422        .breakpoint, .dbg_stmt, .dbg_empty_stmt, .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline, .c_va_end => {
  423            air_body_index += 1;
  424            air_inst_index = air_body[air_body_index];
  425            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  426        },
  427        .call,
  428        .call_always_tail,
  429        .call_never_tail,
  430        .call_never_inline,
  431        => {
  432            const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
  433            const extra = isel.air.extraData(Air.Call, pl_op.payload);
  434            const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]);
  435            isel.saved_registers.insert(.lr);
  436            const callee_ty = isel.air.typeOf(pl_op.operand, ip);
  437            const func_info = switch (ip.indexToKey(callee_ty.toIntern())) {
  438                else => unreachable,
  439                .func_type => |func_type| func_type,
  440                .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type,
  441            };
  442
  443            try isel.analyzeUse(pl_op.operand);
  444            var param_it: CallAbiIterator = .init;
  445            for (args, 0..) |arg, arg_index| {
  446                const restore_values_len = isel.values.items.len;
  447                defer isel.values.shrinkRetainingCapacity(restore_values_len);
  448                const param_vi = param_vi: {
  449                    const param_ty = isel.air.typeOf(arg, ip);
  450                    if (arg_index >= func_info.param_types.len) {
  451                        assert(func_info.is_var_args);
  452                        switch (isel.va_list) {
  453                            .other => break :param_vi try param_it.nonSysvVarArg(isel, param_ty),
  454                            .sysv => {},
  455                        }
  456                    }
  457                    break :param_vi try param_it.param(isel, param_ty);
  458                } orelse continue;
  459                defer param_vi.deref(isel);
  460                const passed_vi = switch (param_vi.parent(isel)) {
  461                    .unallocated, .stack_slot => param_vi,
  462                    .value, .constant => unreachable,
  463                    .address => |address_vi| address_vi,
  464                };
  465                switch (passed_vi.parent(isel)) {
  466                    .unallocated => {},
  467                    .stack_slot => |stack_slot| {
  468                        assert(stack_slot.base == .sp);
  469                        isel.stack_size = @max(
  470                            isel.stack_size,
  471                            stack_slot.offset + @as(u24, @intCast(passed_vi.size(isel))),
  472                        );
  473                    },
  474                    .value, .constant, .address => unreachable,
  475                }
  476
  477                try isel.analyzeUse(arg);
  478            }
  479
  480            var ret_it: CallAbiIterator = .init;
  481            if (try ret_it.ret(isel, isel.air.typeOfIndex(air_inst_index, ip))) |ret_vi| {
  482                tracking_log.debug("${d} <- %{d}", .{ @intFromEnum(ret_vi), @intFromEnum(air_inst_index) });
  483                switch (ret_vi.parent(isel)) {
  484                    .unallocated, .stack_slot => {},
  485                    .value, .constant => unreachable,
  486                    .address => |address_vi| {
  487                        defer address_vi.deref(isel);
  488                        const ret_value = ret_vi.get(isel);
  489                        ret_value.flags.parent_tag = .unallocated;
  490                        ret_value.parent_payload = .{ .unallocated = {} };
  491                    },
  492                }
  493                try isel.live_values.putNoClobber(gpa, air_inst_index, ret_vi);
  494
  495                try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  496            }
  497
  498            air_body_index += 1;
  499            air_inst_index = air_body[air_body_index];
  500            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  501        },
  502        .sqrt,
  503        .sin,
  504        .cos,
  505        .tan,
  506        .exp,
  507        .exp2,
  508        .log,
  509        .log2,
  510        .log10,
  511        .floor,
  512        .ceil,
  513        .round,
  514        .trunc_float,
  515        .neg,
  516        .neg_optimized,
  517        .is_null,
  518        .is_non_null,
  519        .is_null_ptr,
  520        .is_non_null_ptr,
  521        .is_err,
  522        .is_non_err,
  523        .is_err_ptr,
  524        .is_non_err_ptr,
  525        .is_named_enum_value,
  526        .tag_name,
  527        .error_name,
  528        .cmp_lt_errors_len,
  529        => {
  530            const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
  531
  532            try isel.analyzeUse(un_op);
  533            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  534
  535            air_body_index += 1;
  536            air_inst_index = air_body[air_body_index];
  537            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  538        },
  539        .cmp_vector, .cmp_vector_optimized => {
  540            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  541            const extra = isel.air.extraData(Air.VectorCmp, ty_pl.payload).data;
  542
  543            try isel.analyzeUse(extra.lhs);
  544            try isel.analyzeUse(extra.rhs);
  545            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  546
  547            air_body_index += 1;
  548            air_inst_index = air_body[air_body_index];
  549            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  550        },
  551        .cond_br => {
  552            const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
  553            const extra = isel.air.extraData(Air.CondBr, pl_op.payload);
  554
  555            try isel.analyzeUse(pl_op.operand);
  556
  557            try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.then_body_len]));
  558            try isel.analyze(@ptrCast(isel.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]));
  559
  560            air_body_index += 1;
  561        },
  562        .switch_br => {
  563            const switch_br = isel.air.unwrapSwitch(air_inst_index);
  564
  565            try isel.analyzeUse(switch_br.operand);
  566
  567            var cases_it = switch_br.iterateCases();
  568            while (cases_it.next()) |case| try isel.analyze(case.body);
  569            if (switch_br.else_body_len > 0) try isel.analyze(cases_it.elseBody());
  570
  571            air_body_index += 1;
  572        },
  573        .loop_switch_br => {
  574            const switch_br = isel.air.unwrapSwitch(air_inst_index);
  575
  576            const initial_dom_start = isel.dom_start;
  577            const initial_dom_len = isel.dom_len;
  578            isel.dom_start = @intCast(isel.dom.items.len);
  579            isel.dom_len = @intCast(isel.blocks.count());
  580            try isel.active_loops.append(gpa, @enumFromInt(isel.loops.count()));
  581            try isel.loops.putNoClobber(gpa, air_inst_index, .{
  582                .def_order = @intCast(isel.def_order.count()),
  583                .dom = isel.dom_start,
  584                .depth = isel.dom_len,
  585                .live = 0,
  586                .live_registers = undefined,
  587                .repeat_list = undefined,
  588            });
  589            try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable);
  590
  591            var cases_it = switch_br.iterateCases();
  592            while (cases_it.next()) |case| try isel.analyze(case.body);
  593            if (switch_br.else_body_len > 0) try isel.analyze(cases_it.elseBody());
  594
  595            for (
  596                isel.dom.items[initial_dom_start..].ptr,
  597                isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable],
  598            ) |*initial_dom, loop_dom| initial_dom.* |= loop_dom;
  599            isel.dom_start = initial_dom_start;
  600            isel.dom_len = initial_dom_len;
  601            assert(isel.active_loops.pop().?.inst(isel) == air_inst_index);
  602
  603            air_body_index += 1;
  604        },
  605        .switch_dispatch => {
  606            const br = air_data[@intFromEnum(air_inst_index)].br;
  607
  608            try isel.analyzeUse(br.operand);
  609
  610            air_body_index += 1;
  611        },
  612        .@"try", .try_cold => {
  613            const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
  614            const extra = isel.air.extraData(Air.Try, pl_op.payload);
  615
  616            try isel.analyzeUse(pl_op.operand);
  617            try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
  618            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  619
  620            air_body_index += 1;
  621            air_inst_index = air_body[air_body_index];
  622            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  623        },
  624        .try_ptr, .try_ptr_cold => {
  625            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  626            const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
  627
  628            try isel.analyzeUse(extra.data.ptr);
  629            try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
  630            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  631
  632            air_body_index += 1;
  633            air_inst_index = air_body[air_body_index];
  634            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  635        },
  636        .ret, .ret_safe, .ret_load => {
  637            const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
  638            isel.returns = true;
  639
  640            const block_index = 0;
  641            assert(isel.blocks.keys()[block_index] == Block.main);
  642            if (isel.dom_len > 0) isel.dom.items[isel.dom_start] |= 1 << block_index;
  643
  644            try isel.analyzeUse(un_op);
  645
  646            air_body_index += 1;
  647        },
  648        .store,
  649        .store_safe,
  650        .set_union_tag,
  651        .memset,
  652        .memset_safe,
  653        .memcpy,
  654        .memmove,
  655        .atomic_store_unordered,
  656        .atomic_store_monotonic,
  657        .atomic_store_release,
  658        .atomic_store_seq_cst,
  659        => {
  660            const bin_op = air_data[@intFromEnum(air_inst_index)].bin_op;
  661
  662            try isel.analyzeUse(bin_op.lhs);
  663            try isel.analyzeUse(bin_op.rhs);
  664
  665            air_body_index += 1;
  666            air_inst_index = air_body[air_body_index];
  667            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  668        },
  669        .struct_field_ptr, .struct_field_val => {
  670            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  671            const extra = isel.air.extraData(Air.StructField, ty_pl.payload).data;
  672
  673            try isel.analyzeUse(extra.struct_operand);
  674            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  675
  676            air_body_index += 1;
  677            air_inst_index = air_body[air_body_index];
  678            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  679        },
  680        .slice_len => {
  681            const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op;
  682
  683            try isel.analyzeUse(ty_op.operand);
  684            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  685
  686            const slice_vi = try isel.use(ty_op.operand);
  687            var len_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 8, 8);
  688            if (try len_part_it.only(isel)) |len_part_vi|
  689                try isel.live_values.putNoClobber(gpa, air_inst_index, len_part_vi.ref(isel));
  690
  691            air_body_index += 1;
  692            air_inst_index = air_body[air_body_index];
  693            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  694        },
  695        .slice_ptr => {
  696            const ty_op = air_data[@intFromEnum(air_inst_index)].ty_op;
  697
  698            try isel.analyzeUse(ty_op.operand);
  699            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  700
  701            const slice_vi = try isel.use(ty_op.operand);
  702            var ptr_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 0, 8);
  703            if (try ptr_part_it.only(isel)) |ptr_part_vi|
  704                try isel.live_values.putNoClobber(gpa, air_inst_index, ptr_part_vi.ref(isel));
  705
  706            air_body_index += 1;
  707            air_inst_index = air_body[air_body_index];
  708            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  709        },
  710        .reduce, .reduce_optimized => {
  711            const reduce = air_data[@intFromEnum(air_inst_index)].reduce;
  712
  713            try isel.analyzeUse(reduce.operand);
  714            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  715
  716            air_body_index += 1;
  717            air_inst_index = air_body[air_body_index];
  718            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  719        },
  720        .shuffle_one => {
  721            const extra = isel.air.unwrapShuffleOne(zcu, air_inst_index);
  722
  723            try isel.analyzeUse(extra.operand);
  724            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  725
  726            air_body_index += 1;
  727            air_inst_index = air_body[air_body_index];
  728            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  729        },
  730        .shuffle_two => {
  731            const extra = isel.air.unwrapShuffleTwo(zcu, air_inst_index);
  732
  733            try isel.analyzeUse(extra.operand_a);
  734            try isel.analyzeUse(extra.operand_b);
  735            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  736
  737            air_body_index += 1;
  738            air_inst_index = air_body[air_body_index];
  739            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  740        },
  741        .select, .mul_add => {
  742            const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
  743            const bin_op = isel.air.extraData(Air.Bin, pl_op.payload).data;
  744
  745            try isel.analyzeUse(pl_op.operand);
  746            try isel.analyzeUse(bin_op.lhs);
  747            try isel.analyzeUse(bin_op.rhs);
  748            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  749
  750            air_body_index += 1;
  751            air_inst_index = air_body[air_body_index];
  752            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  753        },
  754        .cmpxchg_weak, .cmpxchg_strong => {
  755            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  756            const extra = isel.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
  757
  758            try isel.analyzeUse(extra.ptr);
  759            try isel.analyzeUse(extra.expected_value);
  760            try isel.analyzeUse(extra.new_value);
  761            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  762
  763            air_body_index += 1;
  764            air_inst_index = air_body[air_body_index];
  765            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  766        },
  767        .atomic_load => {
  768            const atomic_load = air_data[@intFromEnum(air_inst_index)].atomic_load;
  769
  770            try isel.analyzeUse(atomic_load.ptr);
  771            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  772
  773            air_body_index += 1;
  774            air_inst_index = air_body[air_body_index];
  775            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  776        },
  777        .atomic_rmw => {
  778            const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
  779            const extra = isel.air.extraData(Air.AtomicRmw, pl_op.payload).data;
  780
  781            try isel.analyzeUse(extra.operand);
  782            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  783
  784            air_body_index += 1;
  785            air_inst_index = air_body[air_body_index];
  786            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  787        },
  788        .aggregate_init => {
  789            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  790            const elements: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[ty_pl.payload..][0..@intCast(ty_pl.ty.toType().arrayLen(zcu))]);
  791
  792            for (elements) |element| try isel.analyzeUse(element);
  793            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  794
  795            air_body_index += 1;
  796            air_inst_index = air_body[air_body_index];
  797            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  798        },
  799        .union_init => {
  800            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  801            const extra = isel.air.extraData(Air.UnionInit, ty_pl.payload).data;
  802
  803            try isel.analyzeUse(extra.init);
  804            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  805
  806            air_body_index += 1;
  807            air_inst_index = air_body[air_body_index];
  808            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  809        },
  810        .prefetch => {
  811            const prefetch = air_data[@intFromEnum(air_inst_index)].prefetch;
  812
  813            try isel.analyzeUse(prefetch.ptr);
  814
  815            air_body_index += 1;
  816            air_inst_index = air_body[air_body_index];
  817            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  818        },
  819        .field_parent_ptr => {
  820            const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
  821            const extra = isel.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
  822
  823            try isel.analyzeUse(extra.field_ptr);
  824            try isel.def_order.putNoClobber(gpa, air_inst_index, {});
  825
  826            air_body_index += 1;
  827            air_inst_index = air_body[air_body_index];
  828            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  829        },
  830        .set_err_return_trace => {
  831            const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
  832
  833            try isel.analyzeUse(un_op);
  834
  835            air_body_index += 1;
  836            air_inst_index = air_body[air_body_index];
  837            continue :air_tag air_tags[@intFromEnum(air_inst_index)];
  838        },
  839    }
  840    assert(air_body_index == air_body.len);
  841    isel.def_order.shrinkRetainingCapacity(initial_def_order_len);
  842}
  843
  844fn analyzeUse(isel: *Select, air_ref: Air.Inst.Ref) !void {
  845    const air_inst_index = air_ref.toIndex() orelse return;
  846    const def_order_index = isel.def_order.getIndex(air_inst_index).?;
  847
  848    // Loop liveness
  849    var active_loop_index = isel.active_loops.items.len;
  850    while (active_loop_index > 0) {
  851        const prev_active_loop_index = active_loop_index - 1;
  852        const active_loop = isel.active_loops.items[prev_active_loop_index];
  853        if (def_order_index >= active_loop.get(isel).def_order) break;
  854        active_loop_index = prev_active_loop_index;
  855    }
  856    if (active_loop_index < isel.active_loops.items.len) {
  857        const active_loop = isel.active_loops.items[active_loop_index];
  858        const loop_live_gop =
  859            try isel.loop_live.set.getOrPut(isel.pt.zcu.gpa, .{ active_loop, air_inst_index });
  860        if (!loop_live_gop.found_existing) active_loop.get(isel).live += 1;
  861    }
  862}
  863
  864pub fn finishAnalysis(isel: *Select) !void {
  865    const gpa = isel.pt.zcu.gpa;
  866
  867    // Loop Liveness
  868    if (isel.loops.count() > 0) {
  869        try isel.loops.ensureUnusedCapacity(gpa, 1);
  870
  871        const loop_live_len: u32 = @intCast(isel.loop_live.set.count());
  872        if (loop_live_len > 0) {
  873            try isel.loop_live.list.resize(gpa, loop_live_len);
  874
  875            const loops = isel.loops.values();
  876            for (loops[1..], loops[0 .. loops.len - 1]) |*loop, prev_loop| loop.live += prev_loop.live;
  877            assert(loops[loops.len - 1].live == loop_live_len);
  878
  879            for (isel.loop_live.set.keys()) |entry| {
  880                const loop, const inst = entry;
  881                const loop_live = &loop.get(isel).live;
  882                loop_live.* -= 1;
  883                isel.loop_live.list.items[loop_live.*] = inst;
  884            }
  885            assert(loops[0].live == 0);
  886        }
  887
  888        const invalid_gop = isel.loops.getOrPutAssumeCapacity(Loop.invalid);
  889        assert(!invalid_gop.found_existing);
  890        invalid_gop.value_ptr.live = loop_live_len;
  891    }
  892}
  893
  894pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
  895    const zcu = isel.pt.zcu;
  896    const ip = &zcu.intern_pool;
  897    const gpa = zcu.gpa;
  898
  899    {
  900        var live_reg_it = isel.live_registers.iterator();
  901        while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) {
  902            _ => {
  903                const ra = &live_reg_entry.value.get(isel).location_payload.small.register;
  904                assert(ra.* == live_reg_entry.key);
  905                ra.* = .zr;
  906                live_reg_entry.value.* = .free;
  907            },
  908            .allocating => live_reg_entry.value.* = .free,
  909            .free => {},
  910        };
  911    }
  912
  913    var air: struct {
  914        isel: *Select,
  915        tag_items: []const Air.Inst.Tag,
  916        data_items: []const Air.Inst.Data,
  917        body: []const Air.Inst.Index,
  918        body_index: u32,
  919        inst_index: Air.Inst.Index,
  920
  921        fn tag(it: *@This(), inst_index: Air.Inst.Index) Air.Inst.Tag {
  922            return it.tag_items[@intFromEnum(inst_index)];
  923        }
  924
  925        fn data(it: *@This(), inst_index: Air.Inst.Index) Air.Inst.Data {
  926            return it.data_items[@intFromEnum(inst_index)];
  927        }
  928
  929        fn next(it: *@This()) ?Air.Inst.Tag {
  930            if (it.body_index == 0) {
  931                @branchHint(.unlikely);
  932                return null;
  933            }
  934            it.body_index -= 1;
  935            it.inst_index = it.body[it.body_index];
  936            wip_mir_log.debug("{f}", .{it.fmtAir(it.inst_index)});
  937            return it.tag(it.inst_index);
  938        }
  939
  940        fn fmtAir(it: @This(), inst: Air.Inst.Index) struct {
  941            isel: *Select,
  942            inst: Air.Inst.Index,
  943            pub fn format(fmt_air: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
  944                fmt_air.isel.air.writeInst(writer, fmt_air.inst, fmt_air.isel.pt, null);
  945            }
  946        } {
  947            return .{ .isel = it.isel, .inst = inst };
  948        }
  949    } = .{
  950        .isel = isel,
  951        .tag_items = isel.air.instructions.items(.tag),
  952        .data_items = isel.air.instructions.items(.data),
  953        .body = air_body,
  954        .body_index = @intCast(air_body.len),
  955        .inst_index = undefined,
  956    };
  957    air_tag: switch (air.next().?) {
  958        else => |air_tag| return isel.fail("unimplemented {t}", .{air_tag}),
  959
  960        // No "scalarize" legalizations are enabled, so these instructions never appear.
  961        .legalize_vec_elem_val => unreachable,
  962        .legalize_vec_store_elem => unreachable,
  963
  964        .arg => {
  965            const arg_vi = isel.live_values.fetchRemove(air.inst_index).?.value;
  966            defer arg_vi.deref(isel);
  967            switch (arg_vi.parent(isel)) {
  968                .unallocated, .stack_slot => if (arg_vi.hint(isel)) |arg_ra| {
  969                    try arg_vi.defLiveIn(isel, arg_ra, comptime &.initFill(.free));
  970                } else {
  971                    var arg_part_it = arg_vi.parts(isel);
  972                    while (arg_part_it.next()) |arg_part| {
  973                        try arg_part.defLiveIn(isel, arg_part.hint(isel).?, comptime &.initFill(.free));
  974                    }
  975                },
  976                .value, .constant => unreachable,
  977                .address => |address_vi| try address_vi.defLiveIn(isel, address_vi.hint(isel).?, comptime &.initFill(.free)),
  978            }
  979            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
  980        },
  981        .add, .add_safe, .add_optimized, .add_wrap, .sub, .sub_safe, .sub_optimized, .sub_wrap => |air_tag| {
  982            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
  983                defer res_vi.value.deref(isel);
  984
  985                const bin_op = air.data(air.inst_index).bin_op;
  986                const ty = isel.air.typeOf(bin_op.lhs, ip);
  987                if (!ty.isRuntimeFloat()) try res_vi.value.addOrSubtract(isel, ty, try isel.use(bin_op.lhs), switch (air_tag) {
  988                    else => unreachable,
  989                    .add, .add_safe, .add_wrap => .add,
  990                    .sub, .sub_safe, .sub_wrap => .sub,
  991                }, try isel.use(bin_op.rhs), .{
  992                    .overflow = switch (air_tag) {
  993                        else => unreachable,
  994                        .add, .sub => .@"unreachable",
  995                        .add_safe, .sub_safe => .{ .panic = .integer_overflow },
  996                        .add_wrap, .sub_wrap => .wrap,
  997                    },
  998                }) else switch (ty.floatBits(isel.target)) {
  999                    else => unreachable,
 1000                    16, 32, 64 => |bits| {
 1001                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1002                        const need_fcvt = switch (bits) {
 1003                            else => unreachable,
 1004                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 1005                            32, 64 => false,
 1006                        };
 1007                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 1008                        const lhs_vi = try isel.use(bin_op.lhs);
 1009                        const rhs_vi = try isel.use(bin_op.rhs);
 1010                        const lhs_mat = try lhs_vi.matReg(isel);
 1011                        const rhs_mat = try rhs_vi.matReg(isel);
 1012                        const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 1013                        defer if (need_fcvt) isel.freeReg(lhs_ra);
 1014                        const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 1015                        defer if (need_fcvt) isel.freeReg(rhs_ra);
 1016                        try isel.emit(bits: switch (bits) {
 1017                            else => unreachable,
 1018                            16 => if (need_fcvt) continue :bits 32 else switch (air_tag) {
 1019                                else => unreachable,
 1020                                .add, .add_optimized => .fadd(res_ra.h(), lhs_ra.h(), rhs_ra.h()),
 1021                                .sub, .sub_optimized => .fsub(res_ra.h(), lhs_ra.h(), rhs_ra.h()),
 1022                            },
 1023                            32 => switch (air_tag) {
 1024                                else => unreachable,
 1025                                .add, .add_optimized => .fadd(res_ra.s(), lhs_ra.s(), rhs_ra.s()),
 1026                                .sub, .sub_optimized => .fsub(res_ra.s(), lhs_ra.s(), rhs_ra.s()),
 1027                            },
 1028                            64 => switch (air_tag) {
 1029                                else => unreachable,
 1030                                .add, .add_optimized => .fadd(res_ra.d(), lhs_ra.d(), rhs_ra.d()),
 1031                                .sub, .sub_optimized => .fsub(res_ra.d(), lhs_ra.d(), rhs_ra.d()),
 1032                            },
 1033                        });
 1034                        if (need_fcvt) {
 1035                            try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 1036                            try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 1037                        }
 1038                        try rhs_mat.finish(isel);
 1039                        try lhs_mat.finish(isel);
 1040                    },
 1041                    80, 128 => |bits| {
 1042                        try call.prepareReturn(isel);
 1043                        switch (bits) {
 1044                            else => unreachable,
 1045                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 1046                            80 => {
 1047                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 1048                                const res_hi16_vi = try res_hi16_it.only(isel);
 1049                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 1050                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 1051                                const res_lo64_vi = try res_lo64_it.only(isel);
 1052                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 1053                            },
 1054                        }
 1055                        try call.finishReturn(isel);
 1056
 1057                        try call.prepareCallee(isel);
 1058                        try isel.global_relocs.append(gpa, .{
 1059                            .name = switch (air_tag) {
 1060                                else => unreachable,
 1061                                .add, .add_optimized => switch (bits) {
 1062                                    else => unreachable,
 1063                                    16 => "__addhf3",
 1064                                    32 => "__addsf3",
 1065                                    64 => "__adddf3",
 1066                                    80 => "__addxf3",
 1067                                    128 => "__addtf3",
 1068                                },
 1069                                .sub, .sub_optimized => switch (bits) {
 1070                                    else => unreachable,
 1071                                    16 => "__subhf3",
 1072                                    32 => "__subsf3",
 1073                                    64 => "__subdf3",
 1074                                    80 => "__subxf3",
 1075                                    128 => "__subtf3",
 1076                                },
 1077                            },
 1078                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 1079                        });
 1080                        try isel.emit(.bl(0));
 1081                        try call.finishCallee(isel);
 1082
 1083                        try call.prepareParams(isel);
 1084                        const lhs_vi = try isel.use(bin_op.lhs);
 1085                        const rhs_vi = try isel.use(bin_op.rhs);
 1086                        switch (bits) {
 1087                            else => unreachable,
 1088                            16, 32, 64, 128 => {
 1089                                try call.paramLiveOut(isel, rhs_vi, .v1);
 1090                                try call.paramLiveOut(isel, lhs_vi, .v0);
 1091                            },
 1092                            80 => {
 1093                                var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 1094                                const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 1095                                try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 1096                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 1097                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 1098                                try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 1099                                var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 1100                                const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 1101                                try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 1102                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 1103                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 1104                                try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 1105                            },
 1106                        }
 1107                        try call.finishParams(isel);
 1108                    },
 1109                }
 1110            }
 1111            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 1112        },
 1113        .add_sat, .sub_sat => |air_tag| {
 1114            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 1115                defer res_vi.value.deref(isel);
 1116
 1117                const bin_op = air.data(air.inst_index).bin_op;
 1118                const ty = isel.air.typeOf(bin_op.lhs, ip);
 1119                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 1120                const int_info = ty.intInfo(zcu);
 1121                switch (int_info.bits) {
 1122                    0 => unreachable,
 1123                    32, 64 => |bits| switch (int_info.signedness) {
 1124                        .signed => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1125                        .unsigned => {
 1126                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1127                            const lhs_vi = try isel.use(bin_op.lhs);
 1128                            const rhs_vi = try isel.use(bin_op.rhs);
 1129                            const lhs_mat = try lhs_vi.matReg(isel);
 1130                            const rhs_mat = try rhs_vi.matReg(isel);
 1131                            const unsat_res_ra = try isel.allocIntReg();
 1132                            defer isel.freeReg(unsat_res_ra);
 1133                            switch (air_tag) {
 1134                                else => unreachable,
 1135                                .add_sat => switch (bits) {
 1136                                    else => unreachable,
 1137                                    32 => {
 1138                                        try isel.emit(.csinv(res_ra.w(), unsat_res_ra.w(), .wzr, .invert(.cs)));
 1139                                        try isel.emit(.adds(unsat_res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1140                                    },
 1141                                    64 => {
 1142                                        try isel.emit(.csinv(res_ra.x(), unsat_res_ra.x(), .xzr, .invert(.cs)));
 1143                                        try isel.emit(.adds(unsat_res_ra.x(), lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() }));
 1144                                    },
 1145                                },
 1146                                .sub_sat => switch (bits) {
 1147                                    else => unreachable,
 1148                                    32 => {
 1149                                        try isel.emit(.csel(res_ra.w(), unsat_res_ra.w(), .wzr, .invert(.cc)));
 1150                                        try isel.emit(.subs(unsat_res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1151                                    },
 1152                                    64 => {
 1153                                        try isel.emit(.csel(res_ra.x(), unsat_res_ra.x(), .xzr, .invert(.cc)));
 1154                                        try isel.emit(.subs(unsat_res_ra.x(), lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() }));
 1155                                    },
 1156                                },
 1157                            }
 1158                            try rhs_mat.finish(isel);
 1159                            try lhs_mat.finish(isel);
 1160                        },
 1161                    },
 1162                    else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1163                }
 1164            }
 1165            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 1166        },
 1167        .mul, .mul_optimized, .mul_wrap => |air_tag| {
 1168            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 1169                defer res_vi.value.deref(isel);
 1170
 1171                const bin_op = air.data(air.inst_index).bin_op;
 1172                const ty = isel.air.typeOf(bin_op.lhs, ip);
 1173                if (!ty.isRuntimeFloat()) {
 1174                    if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 1175                    const int_info = ty.intInfo(zcu);
 1176                    switch (int_info.bits) {
 1177                        0 => unreachable,
 1178                        1 => {
 1179                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1180                            switch (int_info.signedness) {
 1181                                .signed => switch (air_tag) {
 1182                                    else => unreachable,
 1183                                    .mul => break :unused try isel.emit(.orr(res_ra.w(), .wzr, .{ .register = .wzr })),
 1184                                    .mul_wrap => {},
 1185                                },
 1186                                .unsigned => {},
 1187                            }
 1188                            const lhs_vi = try isel.use(bin_op.lhs);
 1189                            const rhs_vi = try isel.use(bin_op.rhs);
 1190                            const lhs_mat = try lhs_vi.matReg(isel);
 1191                            const rhs_mat = try rhs_vi.matReg(isel);
 1192                            try isel.emit(.@"and"(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1193                            try rhs_mat.finish(isel);
 1194                            try lhs_mat.finish(isel);
 1195                        },
 1196                        2...32 => |bits| {
 1197                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1198                            switch (air_tag) {
 1199                                else => unreachable,
 1200                                .mul => {},
 1201                                .mul_wrap => switch (bits) {
 1202                                    else => unreachable,
 1203                                    1...31 => try isel.emit(switch (int_info.signedness) {
 1204                                        .signed => .sbfm(res_ra.w(), res_ra.w(), .{
 1205                                            .N = .word,
 1206                                            .immr = 0,
 1207                                            .imms = @intCast(bits - 1),
 1208                                        }),
 1209                                        .unsigned => .ubfm(res_ra.w(), res_ra.w(), .{
 1210                                            .N = .word,
 1211                                            .immr = 0,
 1212                                            .imms = @intCast(bits - 1),
 1213                                        }),
 1214                                    }),
 1215                                    32 => {},
 1216                                },
 1217                            }
 1218                            const lhs_vi = try isel.use(bin_op.lhs);
 1219                            const rhs_vi = try isel.use(bin_op.rhs);
 1220                            const lhs_mat = try lhs_vi.matReg(isel);
 1221                            const rhs_mat = try rhs_vi.matReg(isel);
 1222                            try isel.emit(.madd(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr));
 1223                            try rhs_mat.finish(isel);
 1224                            try lhs_mat.finish(isel);
 1225                        },
 1226                        33...64 => |bits| {
 1227                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1228                            switch (air_tag) {
 1229                                else => unreachable,
 1230                                .mul => {},
 1231                                .mul_wrap => switch (bits) {
 1232                                    else => unreachable,
 1233                                    33...63 => try isel.emit(switch (int_info.signedness) {
 1234                                        .signed => .sbfm(res_ra.x(), res_ra.x(), .{
 1235                                            .N = .doubleword,
 1236                                            .immr = 0,
 1237                                            .imms = @intCast(bits - 1),
 1238                                        }),
 1239                                        .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{
 1240                                            .N = .doubleword,
 1241                                            .immr = 0,
 1242                                            .imms = @intCast(bits - 1),
 1243                                        }),
 1244                                    }),
 1245                                    64 => {},
 1246                                },
 1247                            }
 1248                            const lhs_vi = try isel.use(bin_op.lhs);
 1249                            const rhs_vi = try isel.use(bin_op.rhs);
 1250                            const lhs_mat = try lhs_vi.matReg(isel);
 1251                            const rhs_mat = try rhs_vi.matReg(isel);
 1252                            try isel.emit(.madd(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
 1253                            try rhs_mat.finish(isel);
 1254                            try lhs_mat.finish(isel);
 1255                        },
 1256                        65...128 => |bits| {
 1257                            var res_hi64_it = res_vi.value.field(ty, 8, 8);
 1258                            const res_hi64_vi = try res_hi64_it.only(isel);
 1259                            const res_hi64_ra = try res_hi64_vi.?.defReg(isel);
 1260                            var res_lo64_it = res_vi.value.field(ty, 0, 8);
 1261                            const res_lo64_vi = try res_lo64_it.only(isel);
 1262                            const res_lo64_ra = try res_lo64_vi.?.defReg(isel);
 1263                            if (res_hi64_ra == null and res_lo64_ra == null) break :unused;
 1264                            if (res_hi64_ra) |res_ra| switch (air_tag) {
 1265                                else => unreachable,
 1266                                .mul => {},
 1267                                .mul_wrap => switch (bits) {
 1268                                    else => unreachable,
 1269                                    65...127 => try isel.emit(switch (int_info.signedness) {
 1270                                        .signed => .sbfm(res_ra.x(), res_ra.x(), .{
 1271                                            .N = .doubleword,
 1272                                            .immr = 0,
 1273                                            .imms = @intCast(bits - 1),
 1274                                        }),
 1275                                        .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{
 1276                                            .N = .doubleword,
 1277                                            .immr = 0,
 1278                                            .imms = @intCast(bits - 1),
 1279                                        }),
 1280                                    }),
 1281                                    128 => {},
 1282                                },
 1283                            };
 1284                            const lhs_vi = try isel.use(bin_op.lhs);
 1285                            const rhs_vi = try isel.use(bin_op.rhs);
 1286                            const lhs_lo64_mat, const rhs_lo64_mat = lo64_mat: {
 1287                                const res_hi64_lock: RegLock = if (res_hi64_ra != null and res_lo64_ra != null)
 1288                                    isel.lockReg(res_hi64_ra.?)
 1289                                else
 1290                                    .empty;
 1291                                defer res_hi64_lock.unlock(isel);
 1292
 1293                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 1294                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 1295                                const rhs_lo64_mat = try rhs_lo64_vi.?.matReg(isel);
 1296                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 1297                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 1298                                const lhs_lo64_mat = try lhs_lo64_vi.?.matReg(isel);
 1299                                break :lo64_mat .{ lhs_lo64_mat, rhs_lo64_mat };
 1300                            };
 1301                            if (res_lo64_ra) |res_ra| try isel.emit(.madd(res_ra.x(), lhs_lo64_mat.ra.x(), rhs_lo64_mat.ra.x(), .xzr));
 1302                            if (res_hi64_ra) |res_ra| {
 1303                                var rhs_hi64_it = rhs_vi.field(ty, 8, 8);
 1304                                const rhs_hi64_vi = try rhs_hi64_it.only(isel);
 1305                                const rhs_hi64_mat = try rhs_hi64_vi.?.matReg(isel);
 1306                                var lhs_hi64_it = lhs_vi.field(ty, 8, 8);
 1307                                const lhs_hi64_vi = try lhs_hi64_it.only(isel);
 1308                                const lhs_hi64_mat = try lhs_hi64_vi.?.matReg(isel);
 1309                                const acc_ra = try isel.allocIntReg();
 1310                                defer isel.freeReg(acc_ra);
 1311                                try isel.emit(.madd(res_ra.x(), lhs_hi64_mat.ra.x(), rhs_lo64_mat.ra.x(), acc_ra.x()));
 1312                                try isel.emit(.madd(acc_ra.x(), lhs_lo64_mat.ra.x(), rhs_hi64_mat.ra.x(), acc_ra.x()));
 1313                                try isel.emit(.umulh(acc_ra.x(), lhs_lo64_mat.ra.x(), rhs_lo64_mat.ra.x()));
 1314                                try rhs_hi64_mat.finish(isel);
 1315                                try lhs_hi64_mat.finish(isel);
 1316                            }
 1317                            try rhs_lo64_mat.finish(isel);
 1318                            try lhs_lo64_mat.finish(isel);
 1319                        },
 1320                        else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1321                    }
 1322                } else switch (ty.floatBits(isel.target)) {
 1323                    else => unreachable,
 1324                    16, 32, 64 => |bits| {
 1325                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1326                        const need_fcvt = switch (bits) {
 1327                            else => unreachable,
 1328                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 1329                            32, 64 => false,
 1330                        };
 1331                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 1332                        const lhs_vi = try isel.use(bin_op.lhs);
 1333                        const rhs_vi = try isel.use(bin_op.rhs);
 1334                        const lhs_mat = try lhs_vi.matReg(isel);
 1335                        const rhs_mat = try rhs_vi.matReg(isel);
 1336                        const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 1337                        defer if (need_fcvt) isel.freeReg(lhs_ra);
 1338                        const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 1339                        defer if (need_fcvt) isel.freeReg(rhs_ra);
 1340                        try isel.emit(bits: switch (bits) {
 1341                            else => unreachable,
 1342                            16 => if (need_fcvt)
 1343                                continue :bits 32
 1344                            else
 1345                                .fmul(res_ra.h(), lhs_ra.h(), rhs_ra.h()),
 1346                            32 => .fmul(res_ra.s(), lhs_ra.s(), rhs_ra.s()),
 1347                            64 => .fmul(res_ra.d(), lhs_ra.d(), rhs_ra.d()),
 1348                        });
 1349                        if (need_fcvt) {
 1350                            try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 1351                            try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 1352                        }
 1353                        try rhs_mat.finish(isel);
 1354                        try lhs_mat.finish(isel);
 1355                    },
 1356                    80, 128 => |bits| {
 1357                        try call.prepareReturn(isel);
 1358                        switch (bits) {
 1359                            else => unreachable,
 1360                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 1361                            80 => {
 1362                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 1363                                const res_hi16_vi = try res_hi16_it.only(isel);
 1364                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 1365                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 1366                                const res_lo64_vi = try res_lo64_it.only(isel);
 1367                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 1368                            },
 1369                        }
 1370                        try call.finishReturn(isel);
 1371
 1372                        try call.prepareCallee(isel);
 1373                        try isel.global_relocs.append(gpa, .{
 1374                            .name = switch (bits) {
 1375                                else => unreachable,
 1376                                16 => "__mulhf3",
 1377                                32 => "__mulsf3",
 1378                                64 => "__muldf3",
 1379                                80 => "__mulxf3",
 1380                                128 => "__multf3",
 1381                            },
 1382                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 1383                        });
 1384                        try isel.emit(.bl(0));
 1385                        try call.finishCallee(isel);
 1386
 1387                        try call.prepareParams(isel);
 1388                        const lhs_vi = try isel.use(bin_op.lhs);
 1389                        const rhs_vi = try isel.use(bin_op.rhs);
 1390                        switch (bits) {
 1391                            else => unreachable,
 1392                            16, 32, 64, 128 => {
 1393                                try call.paramLiveOut(isel, rhs_vi, .v1);
 1394                                try call.paramLiveOut(isel, lhs_vi, .v0);
 1395                            },
 1396                            80 => {
 1397                                var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 1398                                const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 1399                                try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 1400                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 1401                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 1402                                try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 1403                                var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 1404                                const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 1405                                try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 1406                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 1407                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 1408                                try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 1409                            },
 1410                        }
 1411                        try call.finishParams(isel);
 1412                    },
 1413                }
 1414            }
 1415            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 1416        },
 1417        .mul_safe => |air_tag| {
 1418            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 1419                defer res_vi.value.deref(isel);
 1420
 1421                const bin_op = air.data(air.inst_index).bin_op;
 1422                const ty = isel.air.typeOf(bin_op.lhs, ip);
 1423                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 1424                const int_info = ty.intInfo(zcu);
 1425                switch (int_info.signedness) {
 1426                    .signed => switch (int_info.bits) {
 1427                        0 => unreachable,
 1428                        1 => {
 1429                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1430                            const lhs_vi = try isel.use(bin_op.lhs);
 1431                            const rhs_vi = try isel.use(bin_op.rhs);
 1432                            const lhs_mat = try lhs_vi.matReg(isel);
 1433                            const rhs_mat = try rhs_vi.matReg(isel);
 1434                            try isel.emit(.orr(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1435                            const skip_label = isel.instructions.items.len;
 1436                            try isel.emitPanic(.integer_overflow);
 1437                            try isel.emit(.@"b."(
 1438                                .invert(.ne),
 1439                                @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 1440                            ));
 1441                            try isel.emit(.ands(.wzr, lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1442                            try rhs_mat.finish(isel);
 1443                            try lhs_mat.finish(isel);
 1444                        },
 1445                        else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1446                    },
 1447                    .unsigned => switch (int_info.bits) {
 1448                        0 => unreachable,
 1449                        1 => {
 1450                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1451                            const lhs_vi = try isel.use(bin_op.lhs);
 1452                            const rhs_vi = try isel.use(bin_op.rhs);
 1453                            const lhs_mat = try lhs_vi.matReg(isel);
 1454                            const rhs_mat = try rhs_vi.matReg(isel);
 1455                            try isel.emit(.@"and"(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1456                            try rhs_mat.finish(isel);
 1457                            try lhs_mat.finish(isel);
 1458                        },
 1459                        2...16 => |bits| {
 1460                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1461                            const lhs_vi = try isel.use(bin_op.lhs);
 1462                            const rhs_vi = try isel.use(bin_op.rhs);
 1463                            const lhs_mat = try lhs_vi.matReg(isel);
 1464                            const rhs_mat = try rhs_vi.matReg(isel);
 1465                            const skip_label = isel.instructions.items.len;
 1466                            try isel.emitPanic(.integer_overflow);
 1467                            try isel.emit(.@"b."(
 1468                                .eq,
 1469                                @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 1470                            ));
 1471                            try isel.emit(.ands(.wzr, res_ra.w(), .{ .immediate = .{
 1472                                .N = .word,
 1473                                .immr = @intCast(32 - bits),
 1474                                .imms = @intCast(32 - bits - 1),
 1475                            } }));
 1476                            try isel.emit(.madd(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr));
 1477                            try rhs_mat.finish(isel);
 1478                            try lhs_mat.finish(isel);
 1479                        },
 1480                        17...32 => |bits| {
 1481                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1482                            const lhs_vi = try isel.use(bin_op.lhs);
 1483                            const rhs_vi = try isel.use(bin_op.rhs);
 1484                            const lhs_mat = try lhs_vi.matReg(isel);
 1485                            const rhs_mat = try rhs_vi.matReg(isel);
 1486                            const skip_label = isel.instructions.items.len;
 1487                            try isel.emitPanic(.integer_overflow);
 1488                            try isel.emit(.@"b."(
 1489                                .eq,
 1490                                @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 1491                            ));
 1492                            try isel.emit(.ands(.xzr, res_ra.x(), .{ .immediate = .{
 1493                                .N = .doubleword,
 1494                                .immr = @intCast(64 - bits),
 1495                                .imms = @intCast(64 - bits - 1),
 1496                            } }));
 1497                            try isel.emit(.umaddl(res_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr));
 1498                            try rhs_mat.finish(isel);
 1499                            try lhs_mat.finish(isel);
 1500                        },
 1501                        33...63 => |bits| {
 1502                            const lo64_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1503                            const lhs_vi = try isel.use(bin_op.lhs);
 1504                            const rhs_vi = try isel.use(bin_op.rhs);
 1505                            const lhs_mat = try lhs_vi.matReg(isel);
 1506                            const rhs_mat = try rhs_vi.matReg(isel);
 1507                            const hi64_ra = hi64_ra: {
 1508                                const lo64_lock = isel.tryLockReg(lo64_ra);
 1509                                defer lo64_lock.unlock(isel);
 1510                                break :hi64_ra try isel.allocIntReg();
 1511                            };
 1512                            defer isel.freeReg(hi64_ra);
 1513                            const skip_label = isel.instructions.items.len;
 1514                            try isel.emitPanic(.integer_overflow);
 1515                            try isel.emit(.cbz(
 1516                                hi64_ra.x(),
 1517                                @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 1518                            ));
 1519                            try isel.emit(.orr(hi64_ra.x(), hi64_ra.x(), .{ .shifted_register = .{
 1520                                .register = lo64_ra.x(),
 1521                                .shift = .{ .lsr = @intCast(bits) },
 1522                            } }));
 1523                            try isel.emit(.madd(lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
 1524                            try isel.emit(.umulh(hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
 1525                            try rhs_mat.finish(isel);
 1526                            try lhs_mat.finish(isel);
 1527                        },
 1528                        64 => {
 1529                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1530                            const lhs_vi = try isel.use(bin_op.lhs);
 1531                            const rhs_vi = try isel.use(bin_op.rhs);
 1532                            const lhs_mat = try lhs_vi.matReg(isel);
 1533                            const rhs_mat = try rhs_vi.matReg(isel);
 1534                            try isel.emit(.madd(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
 1535                            const hi64_ra = try isel.allocIntReg();
 1536                            defer isel.freeReg(hi64_ra);
 1537                            const skip_label = isel.instructions.items.len;
 1538                            try isel.emitPanic(.integer_overflow);
 1539                            try isel.emit(.cbz(
 1540                                hi64_ra.x(),
 1541                                @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 1542                            ));
 1543                            try isel.emit(.umulh(hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
 1544                            try rhs_mat.finish(isel);
 1545                            try lhs_mat.finish(isel);
 1546                        },
 1547                        65...128 => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1548                        else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1549                    },
 1550                }
 1551            }
 1552            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 1553        },
 1554        .mul_sat => |air_tag| {
 1555            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 1556                defer res_vi.value.deref(isel);
 1557
 1558                const bin_op = air.data(air.inst_index).bin_op;
 1559                const ty = isel.air.typeOf(bin_op.lhs, ip);
 1560                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 1561                const int_info = ty.intInfo(zcu);
 1562                switch (int_info.bits) {
 1563                    0 => unreachable,
 1564                    1 => {
 1565                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1566                        switch (int_info.signedness) {
 1567                            .signed => try isel.emit(.orr(res_ra.w(), .wzr, .{ .register = .wzr })),
 1568                            .unsigned => {
 1569                                const lhs_vi = try isel.use(bin_op.lhs);
 1570                                const rhs_vi = try isel.use(bin_op.rhs);
 1571                                const lhs_mat = try lhs_vi.matReg(isel);
 1572                                const rhs_mat = try rhs_vi.matReg(isel);
 1573                                try isel.emit(.@"and"(res_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1574                                try rhs_mat.finish(isel);
 1575                                try lhs_mat.finish(isel);
 1576                            },
 1577                        }
 1578                    },
 1579                    2...32 => |bits| {
 1580                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1581                        const saturated_ra = switch (int_info.signedness) {
 1582                            .signed => try isel.allocIntReg(),
 1583                            .unsigned => switch (bits) {
 1584                                else => unreachable,
 1585                                2...31 => try isel.allocIntReg(),
 1586                                32 => .zr,
 1587                            },
 1588                        };
 1589                        defer if (saturated_ra != .zr) isel.freeReg(saturated_ra);
 1590                        const unwrapped_ra = try isel.allocIntReg();
 1591                        defer isel.freeReg(unwrapped_ra);
 1592                        try isel.emit(switch (saturated_ra) {
 1593                            else => .csel(res_ra.w(), unwrapped_ra.w(), saturated_ra.w(), .eq),
 1594                            .zr => .csinv(res_ra.w(), unwrapped_ra.w(), saturated_ra.w(), .eq),
 1595                        });
 1596                        switch (bits) {
 1597                            else => unreachable,
 1598                            2...7, 9...15, 17...31 => switch (int_info.signedness) {
 1599                                .signed => {
 1600                                    const wrapped_ra = try isel.allocIntReg();
 1601                                    defer isel.freeReg(wrapped_ra);
 1602                                    switch (bits) {
 1603                                        else => unreachable,
 1604                                        1...7, 9...15 => {
 1605                                            try isel.emit(.subs(.wzr, unwrapped_ra.w(), .{ .register = wrapped_ra.w() }));
 1606                                            try isel.emit(.sbfm(wrapped_ra.w(), unwrapped_ra.w(), .{
 1607                                                .N = .word,
 1608                                                .immr = 0,
 1609                                                .imms = @intCast(bits - 1),
 1610                                            }));
 1611                                        },
 1612                                        17...31 => {
 1613                                            try isel.emit(.subs(.xzr, unwrapped_ra.x(), .{ .register = wrapped_ra.x() }));
 1614                                            try isel.emit(.sbfm(wrapped_ra.x(), unwrapped_ra.x(), .{
 1615                                                .N = .doubleword,
 1616                                                .immr = 0,
 1617                                                .imms = @intCast(bits - 1),
 1618                                            }));
 1619                                        },
 1620                                    }
 1621                                },
 1622                                .unsigned => switch (bits) {
 1623                                    else => unreachable,
 1624                                    1...7, 9...15 => try isel.emit(.ands(.wzr, unwrapped_ra.w(), .{ .immediate = .{
 1625                                        .N = .word,
 1626                                        .immr = @intCast(32 - bits),
 1627                                        .imms = @intCast(32 - bits - 1),
 1628                                    } })),
 1629                                    17...31 => try isel.emit(.ands(.xzr, unwrapped_ra.x(), .{ .immediate = .{
 1630                                        .N = .doubleword,
 1631                                        .immr = @intCast(64 - bits),
 1632                                        .imms = @intCast(64 - bits - 1),
 1633                                    } })),
 1634                                },
 1635                            },
 1636                            8 => try isel.emit(.subs(.wzr, unwrapped_ra.w(), .{ .extended_register = .{
 1637                                .register = unwrapped_ra.w(),
 1638                                .extend = switch (int_info.signedness) {
 1639                                    .signed => .{ .sxtb = 0 },
 1640                                    .unsigned => .{ .uxtb = 0 },
 1641                                },
 1642                            } })),
 1643                            16 => try isel.emit(.subs(.wzr, unwrapped_ra.w(), .{ .extended_register = .{
 1644                                .register = unwrapped_ra.w(),
 1645                                .extend = switch (int_info.signedness) {
 1646                                    .signed => .{ .sxth = 0 },
 1647                                    .unsigned => .{ .uxth = 0 },
 1648                                },
 1649                            } })),
 1650                            32 => try isel.emit(.subs(.xzr, unwrapped_ra.x(), .{ .extended_register = .{
 1651                                .register = unwrapped_ra.w(),
 1652                                .extend = switch (int_info.signedness) {
 1653                                    .signed => .{ .sxtw = 0 },
 1654                                    .unsigned => .{ .uxtw = 0 },
 1655                                },
 1656                            } })),
 1657                        }
 1658                        const lhs_vi = try isel.use(bin_op.lhs);
 1659                        const rhs_vi = try isel.use(bin_op.rhs);
 1660                        const lhs_mat = try lhs_vi.matReg(isel);
 1661                        const rhs_mat = try rhs_vi.matReg(isel);
 1662                        switch (int_info.signedness) {
 1663                            .signed => {
 1664                                try isel.emit(.eor(saturated_ra.w(), saturated_ra.w(), .{ .immediate = .{
 1665                                    .N = .word,
 1666                                    .immr = 0,
 1667                                    .imms = @intCast(bits - 1 - 1),
 1668                                } }));
 1669                                try isel.emit(.sbfm(saturated_ra.w(), saturated_ra.w(), .{
 1670                                    .N = .word,
 1671                                    .immr = @intCast(bits - 1),
 1672                                    .imms = @intCast(bits - 1 + 1 - 1),
 1673                                }));
 1674                                try isel.emit(.eor(saturated_ra.w(), lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 1675                            },
 1676                            .unsigned => switch (bits) {
 1677                                else => unreachable,
 1678                                2...31 => try isel.movImmediate(saturated_ra.w(), @as(u32, std.math.maxInt(u32)) >> @intCast(32 - bits)),
 1679                                32 => {},
 1680                            },
 1681                        }
 1682                        switch (bits) {
 1683                            else => unreachable,
 1684                            2...16 => try isel.emit(.madd(unwrapped_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), .wzr)),
 1685                            17...32 => switch (int_info.signedness) {
 1686                                .signed => try isel.emit(.smaddl(unwrapped_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr)),
 1687                                .unsigned => try isel.emit(.umaddl(unwrapped_ra.x(), lhs_mat.ra.w(), rhs_mat.ra.w(), .xzr)),
 1688                            },
 1689                        }
 1690                        try rhs_mat.finish(isel);
 1691                        try lhs_mat.finish(isel);
 1692                    },
 1693                    33...64 => |bits| {
 1694                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1695                        const saturated_ra = switch (int_info.signedness) {
 1696                            .signed => try isel.allocIntReg(),
 1697                            .unsigned => switch (bits) {
 1698                                else => unreachable,
 1699                                33...63 => try isel.allocIntReg(),
 1700                                64 => .zr,
 1701                            },
 1702                        };
 1703                        defer if (saturated_ra != .zr) isel.freeReg(saturated_ra);
 1704                        const unwrapped_lo64_ra = try isel.allocIntReg();
 1705                        defer isel.freeReg(unwrapped_lo64_ra);
 1706                        const unwrapped_hi64_ra = try isel.allocIntReg();
 1707                        defer isel.freeReg(unwrapped_hi64_ra);
 1708                        try isel.emit(switch (saturated_ra) {
 1709                            else => .csel(res_ra.x(), unwrapped_lo64_ra.x(), saturated_ra.x(), .eq),
 1710                            .zr => .csinv(res_ra.x(), unwrapped_lo64_ra.x(), saturated_ra.x(), .eq),
 1711                        });
 1712                        switch (int_info.signedness) {
 1713                            .signed => switch (bits) {
 1714                                else => unreachable,
 1715                                32...63 => {
 1716                                    const wrapped_lo64_ra = try isel.allocIntReg();
 1717                                    defer isel.freeReg(wrapped_lo64_ra);
 1718                                    try isel.emit(.ccmp(
 1719                                        unwrapped_lo64_ra.x(),
 1720                                        .{ .register = wrapped_lo64_ra.x() },
 1721                                        .{ .n = false, .z = false, .c = false, .v = false },
 1722                                        .eq,
 1723                                    ));
 1724                                    try isel.emit(.subs(.xzr, unwrapped_hi64_ra.x(), .{ .shifted_register = .{
 1725                                        .register = unwrapped_lo64_ra.x(),
 1726                                        .shift = .{ .asr = 63 },
 1727                                    } }));
 1728                                    try isel.emit(.sbfm(wrapped_lo64_ra.x(), unwrapped_lo64_ra.x(), .{
 1729                                        .N = .doubleword,
 1730                                        .immr = 0,
 1731                                        .imms = @intCast(bits - 1),
 1732                                    }));
 1733                                },
 1734                                64 => try isel.emit(.subs(.xzr, unwrapped_hi64_ra.x(), .{ .shifted_register = .{
 1735                                    .register = unwrapped_lo64_ra.x(),
 1736                                    .shift = .{ .asr = @intCast(bits - 1) },
 1737                                } })),
 1738                            },
 1739                            .unsigned => switch (bits) {
 1740                                else => unreachable,
 1741                                32...63 => {
 1742                                    const overflow_ra = try isel.allocIntReg();
 1743                                    defer isel.freeReg(overflow_ra);
 1744                                    try isel.emit(.subs(.xzr, overflow_ra.x(), .{ .immediate = 0 }));
 1745                                    try isel.emit(.orr(overflow_ra.x(), unwrapped_hi64_ra.x(), .{ .shifted_register = .{
 1746                                        .register = unwrapped_lo64_ra.x(),
 1747                                        .shift = .{ .lsr = @intCast(bits) },
 1748                                    } }));
 1749                                },
 1750                                64 => try isel.emit(.subs(.xzr, unwrapped_hi64_ra.x(), .{ .immediate = 0 })),
 1751                            },
 1752                        }
 1753                        const lhs_vi = try isel.use(bin_op.lhs);
 1754                        const rhs_vi = try isel.use(bin_op.rhs);
 1755                        const lhs_mat = try lhs_vi.matReg(isel);
 1756                        const rhs_mat = try rhs_vi.matReg(isel);
 1757                        switch (int_info.signedness) {
 1758                            .signed => {
 1759                                try isel.emit(.eor(saturated_ra.x(), saturated_ra.x(), .{ .immediate = .{
 1760                                    .N = .doubleword,
 1761                                    .immr = 0,
 1762                                    .imms = @intCast(bits - 1 - 1),
 1763                                } }));
 1764                                try isel.emit(.sbfm(saturated_ra.x(), saturated_ra.x(), .{
 1765                                    .N = .doubleword,
 1766                                    .immr = @intCast(bits - 1),
 1767                                    .imms = @intCast(bits - 1 + 1 - 1),
 1768                                }));
 1769                                try isel.emit(.eor(saturated_ra.x(), lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() }));
 1770                                try isel.emit(.madd(unwrapped_lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
 1771                                try isel.emit(.smulh(unwrapped_hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
 1772                            },
 1773                            .unsigned => {
 1774                                switch (bits) {
 1775                                    else => unreachable,
 1776                                    32...63 => try isel.movImmediate(saturated_ra.x(), @as(u64, std.math.maxInt(u64)) >> @intCast(64 - bits)),
 1777                                    64 => {},
 1778                                }
 1779                                try isel.emit(.madd(unwrapped_lo64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), .xzr));
 1780                                try isel.emit(.umulh(unwrapped_hi64_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()));
 1781                            },
 1782                        }
 1783                        try rhs_mat.finish(isel);
 1784                        try lhs_mat.finish(isel);
 1785                    },
 1786                    else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 1787                }
 1788            }
 1789            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 1790        },
 1791        .div_float, .div_float_optimized => {
 1792            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 1793                defer res_vi.value.deref(isel);
 1794
 1795                const bin_op = air.data(air.inst_index).bin_op;
 1796                const ty = isel.air.typeOf(bin_op.lhs, ip);
 1797                switch (ty.floatBits(isel.target)) {
 1798                    else => unreachable,
 1799                    16, 32, 64 => |bits| {
 1800                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1801                        const need_fcvt = switch (bits) {
 1802                            else => unreachable,
 1803                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 1804                            32, 64 => false,
 1805                        };
 1806                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 1807                        const lhs_vi = try isel.use(bin_op.lhs);
 1808                        const rhs_vi = try isel.use(bin_op.rhs);
 1809                        const lhs_mat = try lhs_vi.matReg(isel);
 1810                        const rhs_mat = try rhs_vi.matReg(isel);
 1811                        const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 1812                        defer if (need_fcvt) isel.freeReg(lhs_ra);
 1813                        const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 1814                        defer if (need_fcvt) isel.freeReg(rhs_ra);
 1815                        try isel.emit(bits: switch (bits) {
 1816                            else => unreachable,
 1817                            16 => if (need_fcvt)
 1818                                continue :bits 32
 1819                            else
 1820                                .fdiv(res_ra.h(), lhs_ra.h(), rhs_ra.h()),
 1821                            32 => .fdiv(res_ra.s(), lhs_ra.s(), rhs_ra.s()),
 1822                            64 => .fdiv(res_ra.d(), lhs_ra.d(), rhs_ra.d()),
 1823                        });
 1824                        if (need_fcvt) {
 1825                            try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 1826                            try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 1827                        }
 1828                        try rhs_mat.finish(isel);
 1829                        try lhs_mat.finish(isel);
 1830                    },
 1831                    80, 128 => |bits| {
 1832                        try call.prepareReturn(isel);
 1833                        switch (bits) {
 1834                            else => unreachable,
 1835                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 1836                            80 => {
 1837                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 1838                                const res_hi16_vi = try res_hi16_it.only(isel);
 1839                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 1840                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 1841                                const res_lo64_vi = try res_lo64_it.only(isel);
 1842                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 1843                            },
 1844                        }
 1845                        try call.finishReturn(isel);
 1846
 1847                        try call.prepareCallee(isel);
 1848                        try isel.global_relocs.append(gpa, .{
 1849                            .name = switch (bits) {
 1850                                else => unreachable,
 1851                                16 => "__divhf3",
 1852                                32 => "__divsf3",
 1853                                64 => "__divdf3",
 1854                                80 => "__divxf3",
 1855                                128 => "__divtf3",
 1856                            },
 1857                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 1858                        });
 1859                        try isel.emit(.bl(0));
 1860                        try call.finishCallee(isel);
 1861
 1862                        try call.prepareParams(isel);
 1863                        const lhs_vi = try isel.use(bin_op.lhs);
 1864                        const rhs_vi = try isel.use(bin_op.rhs);
 1865                        switch (bits) {
 1866                            else => unreachable,
 1867                            16, 32, 64, 128 => {
 1868                                try call.paramLiveOut(isel, rhs_vi, .v1);
 1869                                try call.paramLiveOut(isel, lhs_vi, .v0);
 1870                            },
 1871                            80 => {
 1872                                var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 1873                                const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 1874                                try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 1875                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 1876                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 1877                                try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 1878                                var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 1879                                const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 1880                                try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 1881                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 1882                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 1883                                try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 1884                            },
 1885                        }
 1886                        try call.finishParams(isel);
 1887                    },
 1888                }
 1889            }
 1890            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 1891        },
 1892        .div_trunc, .div_trunc_optimized, .div_floor, .div_floor_optimized, .div_exact, .div_exact_optimized => |air_tag| {
 1893            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 1894                defer res_vi.value.deref(isel);
 1895
 1896                const bin_op = air.data(air.inst_index).bin_op;
 1897                const ty = isel.air.typeOf(bin_op.lhs, ip);
 1898                if (!ty.isRuntimeFloat()) {
 1899                    if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 1900                    const int_info = ty.intInfo(zcu);
 1901                    switch (int_info.bits) {
 1902                        0 => unreachable,
 1903                        1...64 => |bits| {
 1904                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 1905                            const lhs_vi = try isel.use(bin_op.lhs);
 1906                            const rhs_vi = try isel.use(bin_op.rhs);
 1907                            const lhs_mat = try lhs_vi.matReg(isel);
 1908                            const rhs_mat = try rhs_vi.matReg(isel);
 1909                            const div_ra = div_ra: switch (air_tag) {
 1910                                else => unreachable,
 1911                                .div_trunc, .div_exact => res_ra,
 1912                                .div_floor => switch (int_info.signedness) {
 1913                                    .signed => {
 1914                                        const div_ra = try isel.allocIntReg();
 1915                                        errdefer isel.freeReg(div_ra);
 1916                                        const rem_ra = try isel.allocIntReg();
 1917                                        defer isel.freeReg(rem_ra);
 1918                                        switch (bits) {
 1919                                            else => unreachable,
 1920                                            1...32 => {
 1921                                                try isel.emit(.csel(res_ra.w(), div_ra.w(), rem_ra.w(), .pl));
 1922                                                try isel.emit(.sub(rem_ra.w(), div_ra.w(), .{ .immediate = 1 }));
 1923                                                try isel.emit(.ccmp(
 1924                                                    rem_ra.w(),
 1925                                                    .{ .immediate = 0 },
 1926                                                    .{ .n = false, .z = false, .c = false, .v = false },
 1927                                                    .ne,
 1928                                                ));
 1929                                                try isel.emit(.eor(rem_ra.w(), rem_ra.w(), .{ .register = rhs_mat.ra.w() }));
 1930                                                try isel.emit(.subs(.wzr, rem_ra.w(), .{ .immediate = 0 }));
 1931                                                try isel.emit(.msub(rem_ra.w(), div_ra.w(), rhs_mat.ra.w(), lhs_mat.ra.w()));
 1932                                            },
 1933                                            33...64 => {
 1934                                                try isel.emit(.csel(res_ra.x(), div_ra.x(), rem_ra.x(), .pl));
 1935                                                try isel.emit(.sub(rem_ra.x(), div_ra.x(), .{ .immediate = 1 }));
 1936                                                try isel.emit(.ccmp(
 1937                                                    rem_ra.x(),
 1938                                                    .{ .immediate = 0 },
 1939                                                    .{ .n = false, .z = false, .c = false, .v = false },
 1940                                                    .ne,
 1941                                                ));
 1942                                                try isel.emit(.eor(rem_ra.x(), rem_ra.x(), .{ .register = rhs_mat.ra.x() }));
 1943                                                try isel.emit(.subs(.xzr, rem_ra.x(), .{ .immediate = 0 }));
 1944                                                try isel.emit(.msub(rem_ra.x(), div_ra.x(), rhs_mat.ra.x(), lhs_mat.ra.x()));
 1945                                            },
 1946                                        }
 1947                                        break :div_ra div_ra;
 1948                                    },
 1949                                    .unsigned => res_ra,
 1950                                },
 1951                            };
 1952                            defer if (div_ra != res_ra) isel.freeReg(div_ra);
 1953                            try isel.emit(switch (bits) {
 1954                                else => unreachable,
 1955                                1...32 => switch (int_info.signedness) {
 1956                                    .signed => .sdiv(div_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 1957                                    .unsigned => .udiv(div_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 1958                                },
 1959                                33...64 => switch (int_info.signedness) {
 1960                                    .signed => .sdiv(div_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 1961                                    .unsigned => .udiv(div_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 1962                                },
 1963                            });
 1964                            try rhs_mat.finish(isel);
 1965                            try lhs_mat.finish(isel);
 1966                        },
 1967                        65...128 => {
 1968                            switch (air_tag) {
 1969                                else => unreachable,
 1970                                .div_trunc, .div_exact => {},
 1971                                .div_floor => switch (int_info.signedness) {
 1972                                    .signed => return isel.fail("unimplemented {t}", .{air_tag}),
 1973                                    .unsigned => {},
 1974                                },
 1975                            }
 1976
 1977                            try call.prepareReturn(isel);
 1978                            var res_hi64_it = res_vi.value.field(ty, 8, 8);
 1979                            const res_hi64_vi = try res_hi64_it.only(isel);
 1980                            try call.returnLiveIn(isel, res_hi64_vi.?, .r1);
 1981                            var res_lo64_it = res_vi.value.field(ty, 0, 8);
 1982                            const res_lo64_vi = try res_lo64_it.only(isel);
 1983                            try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 1984                            try call.finishReturn(isel);
 1985
 1986                            try call.prepareCallee(isel);
 1987                            try isel.global_relocs.append(gpa, .{
 1988                                .name = switch (int_info.signedness) {
 1989                                    .signed => "__divti3",
 1990                                    .unsigned => "__udivti3",
 1991                                },
 1992                                .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 1993                            });
 1994                            try isel.emit(.bl(0));
 1995                            try call.finishCallee(isel);
 1996
 1997                            try call.prepareParams(isel);
 1998                            const lhs_vi = try isel.use(bin_op.lhs);
 1999                            const rhs_vi = try isel.use(bin_op.rhs);
 2000                            var rhs_hi64_it = rhs_vi.field(ty, 8, 8);
 2001                            const rhs_hi64_vi = try rhs_hi64_it.only(isel);
 2002                            try call.paramLiveOut(isel, rhs_hi64_vi.?, .r3);
 2003                            var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 2004                            const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 2005                            try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 2006                            var lhs_hi64_it = lhs_vi.field(ty, 8, 8);
 2007                            const lhs_hi64_vi = try lhs_hi64_it.only(isel);
 2008                            try call.paramLiveOut(isel, lhs_hi64_vi.?, .r1);
 2009                            var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 2010                            const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 2011                            try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 2012                            try call.finishParams(isel);
 2013                        },
 2014                        else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 2015                    }
 2016                } else switch (ty.floatBits(isel.target)) {
 2017                    else => unreachable,
 2018                    16, 32, 64 => |bits| {
 2019                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2020                        const need_fcvt = switch (bits) {
 2021                            else => unreachable,
 2022                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 2023                            32, 64 => false,
 2024                        };
 2025                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 2026                        const lhs_vi = try isel.use(bin_op.lhs);
 2027                        const rhs_vi = try isel.use(bin_op.rhs);
 2028                        const lhs_mat = try lhs_vi.matReg(isel);
 2029                        const rhs_mat = try rhs_vi.matReg(isel);
 2030                        const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 2031                        defer if (need_fcvt) isel.freeReg(lhs_ra);
 2032                        const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 2033                        defer if (need_fcvt) isel.freeReg(rhs_ra);
 2034                        bits: switch (bits) {
 2035                            else => unreachable,
 2036                            16 => if (need_fcvt) continue :bits 32 else {
 2037                                switch (air_tag) {
 2038                                    else => unreachable,
 2039                                    .div_trunc, .div_trunc_optimized => try isel.emit(.frintz(res_ra.h(), res_ra.h())),
 2040                                    .div_floor, .div_floor_optimized => try isel.emit(.frintm(res_ra.h(), res_ra.h())),
 2041                                    .div_exact, .div_exact_optimized => {},
 2042                                }
 2043                                try isel.emit(.fdiv(res_ra.h(), lhs_ra.h(), rhs_ra.h()));
 2044                            },
 2045                            32 => {
 2046                                switch (air_tag) {
 2047                                    else => unreachable,
 2048                                    .div_trunc, .div_trunc_optimized => try isel.emit(.frintz(res_ra.s(), res_ra.s())),
 2049                                    .div_floor, .div_floor_optimized => try isel.emit(.frintm(res_ra.s(), res_ra.s())),
 2050                                    .div_exact, .div_exact_optimized => {},
 2051                                }
 2052                                try isel.emit(.fdiv(res_ra.s(), lhs_ra.s(), rhs_ra.s()));
 2053                            },
 2054                            64 => {
 2055                                switch (air_tag) {
 2056                                    else => unreachable,
 2057                                    .div_trunc, .div_trunc_optimized => try isel.emit(.frintz(res_ra.d(), res_ra.d())),
 2058                                    .div_floor, .div_floor_optimized => try isel.emit(.frintm(res_ra.d(), res_ra.d())),
 2059                                    .div_exact, .div_exact_optimized => {},
 2060                                }
 2061                                try isel.emit(.fdiv(res_ra.d(), lhs_ra.d(), rhs_ra.d()));
 2062                            },
 2063                        }
 2064                        if (need_fcvt) {
 2065                            try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 2066                            try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 2067                        }
 2068                        try rhs_mat.finish(isel);
 2069                        try lhs_mat.finish(isel);
 2070                    },
 2071                    80, 128 => |bits| {
 2072                        try call.prepareReturn(isel);
 2073                        switch (bits) {
 2074                            else => unreachable,
 2075                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 2076                            80 => {
 2077                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 2078                                const res_hi16_vi = try res_hi16_it.only(isel);
 2079                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 2080                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 2081                                const res_lo64_vi = try res_lo64_it.only(isel);
 2082                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 2083                            },
 2084                        }
 2085                        try call.finishReturn(isel);
 2086
 2087                        try call.prepareCallee(isel);
 2088                        switch (air_tag) {
 2089                            else => unreachable,
 2090                            .div_trunc, .div_trunc_optimized => {
 2091                                try isel.global_relocs.append(gpa, .{
 2092                                    .name = switch (bits) {
 2093                                        else => unreachable,
 2094                                        16 => "__trunch",
 2095                                        32 => "truncf",
 2096                                        64 => "trunc",
 2097                                        80 => "__truncx",
 2098                                        128 => "truncq",
 2099                                    },
 2100                                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 2101                                });
 2102                                try isel.emit(.bl(0));
 2103                            },
 2104                            .div_floor, .div_floor_optimized => {
 2105                                try isel.global_relocs.append(gpa, .{
 2106                                    .name = switch (bits) {
 2107                                        else => unreachable,
 2108                                        16 => "__floorh",
 2109                                        32 => "floorf",
 2110                                        64 => "floor",
 2111                                        80 => "__floorx",
 2112                                        128 => "floorq",
 2113                                    },
 2114                                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 2115                                });
 2116                                try isel.emit(.bl(0));
 2117                            },
 2118                            .div_exact, .div_exact_optimized => {},
 2119                        }
 2120                        try isel.global_relocs.append(gpa, .{
 2121                            .name = switch (bits) {
 2122                                else => unreachable,
 2123                                16 => "__divhf3",
 2124                                32 => "__divsf3",
 2125                                64 => "__divdf3",
 2126                                80 => "__divxf3",
 2127                                128 => "__divtf3",
 2128                            },
 2129                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 2130                        });
 2131                        try isel.emit(.bl(0));
 2132                        try call.finishCallee(isel);
 2133
 2134                        try call.prepareParams(isel);
 2135                        const lhs_vi = try isel.use(bin_op.lhs);
 2136                        const rhs_vi = try isel.use(bin_op.rhs);
 2137                        switch (bits) {
 2138                            else => unreachable,
 2139                            16, 32, 64, 128 => {
 2140                                try call.paramLiveOut(isel, rhs_vi, .v1);
 2141                                try call.paramLiveOut(isel, lhs_vi, .v0);
 2142                            },
 2143                            80 => {
 2144                                var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 2145                                const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 2146                                try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 2147                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 2148                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 2149                                try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 2150                                var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 2151                                const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 2152                                try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 2153                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 2154                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 2155                                try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 2156                            },
 2157                        }
 2158                        try call.finishParams(isel);
 2159                    },
 2160                }
 2161            }
 2162            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2163        },
 2164        .rem, .rem_optimized, .mod, .mod_optimized => |air_tag| {
 2165            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 2166                defer res_vi.value.deref(isel);
 2167
 2168                const bin_op = air.data(air.inst_index).bin_op;
 2169                const ty = isel.air.typeOf(bin_op.lhs, ip);
 2170                if (!ty.isRuntimeFloat()) {
 2171                    if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2172                    const int_info = ty.intInfo(zcu);
 2173                    if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2174
 2175                    const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2176                    const lhs_vi = try isel.use(bin_op.lhs);
 2177                    const rhs_vi = try isel.use(bin_op.rhs);
 2178                    const lhs_mat = try lhs_vi.matReg(isel);
 2179                    const rhs_mat = try rhs_vi.matReg(isel);
 2180                    const div_ra = try isel.allocIntReg();
 2181                    defer isel.freeReg(div_ra);
 2182                    const rem_ra = rem_ra: switch (air_tag) {
 2183                        else => unreachable,
 2184                        .rem => res_ra,
 2185                        .mod => switch (int_info.signedness) {
 2186                            .signed => {
 2187                                const rem_ra = try isel.allocIntReg();
 2188                                errdefer isel.freeReg(rem_ra);
 2189                                switch (int_info.bits) {
 2190                                    else => unreachable,
 2191                                    1...32 => {
 2192                                        try isel.emit(.csel(res_ra.w(), rem_ra.w(), div_ra.w(), .pl));
 2193                                        try isel.emit(.add(div_ra.w(), rem_ra.w(), .{ .register = rhs_mat.ra.w() }));
 2194                                        try isel.emit(.ccmp(
 2195                                            div_ra.w(),
 2196                                            .{ .immediate = 0 },
 2197                                            .{ .n = false, .z = false, .c = false, .v = false },
 2198                                            .ne,
 2199                                        ));
 2200                                        try isel.emit(.eor(div_ra.w(), rem_ra.w(), .{ .register = rhs_mat.ra.w() }));
 2201                                        try isel.emit(.subs(.wzr, rem_ra.w(), .{ .immediate = 0 }));
 2202                                    },
 2203                                    33...64 => {
 2204                                        try isel.emit(.csel(res_ra.x(), rem_ra.x(), div_ra.x(), .pl));
 2205                                        try isel.emit(.add(div_ra.x(), rem_ra.x(), .{ .register = rhs_mat.ra.x() }));
 2206                                        try isel.emit(.ccmp(
 2207                                            div_ra.x(),
 2208                                            .{ .immediate = 0 },
 2209                                            .{ .n = false, .z = false, .c = false, .v = false },
 2210                                            .ne,
 2211                                        ));
 2212                                        try isel.emit(.eor(div_ra.x(), rem_ra.x(), .{ .register = rhs_mat.ra.x() }));
 2213                                        try isel.emit(.subs(.xzr, rem_ra.x(), .{ .immediate = 0 }));
 2214                                    },
 2215                                }
 2216                                break :rem_ra rem_ra;
 2217                            },
 2218                            .unsigned => res_ra,
 2219                        },
 2220                    };
 2221                    defer if (rem_ra != res_ra) isel.freeReg(rem_ra);
 2222                    switch (int_info.bits) {
 2223                        else => unreachable,
 2224                        1...32 => {
 2225                            try isel.emit(.msub(rem_ra.w(), div_ra.w(), rhs_mat.ra.w(), lhs_mat.ra.w()));
 2226                            try isel.emit(switch (int_info.signedness) {
 2227                                .signed => .sdiv(div_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 2228                                .unsigned => .udiv(div_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 2229                            });
 2230                        },
 2231                        33...64 => {
 2232                            try isel.emit(.msub(rem_ra.x(), div_ra.x(), rhs_mat.ra.x(), lhs_mat.ra.x()));
 2233                            try isel.emit(switch (int_info.signedness) {
 2234                                .signed => .sdiv(div_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 2235                                .unsigned => .udiv(div_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 2236                            });
 2237                        },
 2238                    }
 2239                    try rhs_mat.finish(isel);
 2240                    try lhs_mat.finish(isel);
 2241                } else {
 2242                    const bits = ty.floatBits(isel.target);
 2243                    switch (air_tag) {
 2244                        else => unreachable,
 2245                        .rem, .rem_optimized => {
 2246                            if (!res_vi.value.isUsed(isel)) break :unused;
 2247                            try call.prepareReturn(isel);
 2248                            switch (bits) {
 2249                                else => unreachable,
 2250                                16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 2251                                80 => {
 2252                                    var res_hi16_it = res_vi.value.field(ty, 8, 8);
 2253                                    const res_hi16_vi = try res_hi16_it.only(isel);
 2254                                    try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 2255                                    var res_lo64_it = res_vi.value.field(ty, 0, 8);
 2256                                    const res_lo64_vi = try res_lo64_it.only(isel);
 2257                                    try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 2258                                },
 2259                            }
 2260                            try call.finishReturn(isel);
 2261                        },
 2262                        .mod, .mod_optimized => switch (bits) {
 2263                            else => unreachable,
 2264                            16, 32, 64 => {
 2265                                const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2266                                try call.prepareReturn(isel);
 2267                                const rem_ra: Register.Alias = .v0;
 2268                                const temp1_ra: Register.Alias = .v1;
 2269                                const temp2_ra: Register.Alias = switch (res_ra) {
 2270                                    rem_ra, temp1_ra => .v2,
 2271                                    else => res_ra,
 2272                                };
 2273                                const need_fcvt = switch (bits) {
 2274                                    else => unreachable,
 2275                                    16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 2276                                    32, 64 => false,
 2277                                };
 2278                                if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 2279                                try isel.emit(switch (res_ra) {
 2280                                    rem_ra => .bif(res_ra.@"8b"(), temp2_ra.@"8b"(), temp1_ra.@"8b"()),
 2281                                    temp1_ra => .bsl(res_ra.@"8b"(), rem_ra.@"8b"(), temp2_ra.@"8b"()),
 2282                                    else => .bit(res_ra.@"8b"(), rem_ra.@"8b"(), temp1_ra.@"8b"()),
 2283                                });
 2284                                const rhs_vi = try isel.use(bin_op.rhs);
 2285                                const rhs_mat = try rhs_vi.matReg(isel);
 2286                                try isel.emit(bits: switch (bits) {
 2287                                    else => unreachable,
 2288                                    16 => if (need_fcvt)
 2289                                        continue :bits 32
 2290                                    else
 2291                                        .fadd(temp2_ra.h(), rem_ra.h(), rhs_mat.ra.h()),
 2292                                    32 => .fadd(temp2_ra.s(), rem_ra.s(), rhs_mat.ra.s()),
 2293                                    64 => .fadd(temp2_ra.d(), rem_ra.d(), rhs_mat.ra.d()),
 2294                                });
 2295                                if (need_fcvt) {
 2296                                    try isel.emit(.fcvt(rhs_mat.ra.s(), rhs_mat.ra.h()));
 2297                                    try isel.emit(.fcvt(rem_ra.s(), rem_ra.h()));
 2298                                }
 2299                                try isel.emit(.orr(temp1_ra.@"8b"(), temp1_ra.@"8b"(), .{
 2300                                    .register = temp2_ra.@"8b"(),
 2301                                }));
 2302                                try isel.emit(switch (bits) {
 2303                                    else => unreachable,
 2304                                    16 => .cmge(temp1_ra.@"4h"(), temp1_ra.@"4h"(), .zero),
 2305                                    32 => .cmge(temp1_ra.@"2s"(), temp1_ra.@"2s"(), .zero),
 2306                                    64 => .cmge(temp1_ra.d(), temp1_ra.d(), .zero),
 2307                                });
 2308                                try isel.emit(switch (bits) {
 2309                                    else => unreachable,
 2310                                    16 => .fcmeq(temp2_ra.h(), rem_ra.h(), .zero),
 2311                                    32 => .fcmeq(temp2_ra.s(), rem_ra.s(), .zero),
 2312                                    64 => .fcmeq(temp2_ra.d(), rem_ra.d(), .zero),
 2313                                });
 2314                                try isel.emit(.eor(temp1_ra.@"8b"(), rem_ra.@"8b"(), .{
 2315                                    .register = rhs_mat.ra.@"8b"(),
 2316                                }));
 2317                                try rhs_mat.finish(isel);
 2318                                try call.finishReturn(isel);
 2319                            },
 2320                            80, 128 => {
 2321                                if (!res_vi.value.isUsed(isel)) break :unused;
 2322                                try call.prepareReturn(isel);
 2323                                switch (bits) {
 2324                                    else => unreachable,
 2325                                    16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 2326                                    80 => {
 2327                                        var res_hi16_it = res_vi.value.field(ty, 8, 8);
 2328                                        const res_hi16_vi = try res_hi16_it.only(isel);
 2329                                        try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 2330                                        var res_lo64_it = res_vi.value.field(ty, 0, 8);
 2331                                        const res_lo64_vi = try res_lo64_it.only(isel);
 2332                                        try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 2333                                    },
 2334                                }
 2335                                const skip_label = isel.instructions.items.len;
 2336                                try isel.global_relocs.append(gpa, .{
 2337                                    .name = switch (bits) {
 2338                                        else => unreachable,
 2339                                        16 => "__addhf3",
 2340                                        32 => "__addsf3",
 2341                                        64 => "__adddf3",
 2342                                        80 => "__addxf3",
 2343                                        128 => "__addtf3",
 2344                                    },
 2345                                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 2346                                });
 2347                                try isel.emit(.bl(0));
 2348                                const rhs_vi = try isel.use(bin_op.rhs);
 2349                                switch (bits) {
 2350                                    else => unreachable,
 2351                                    80 => {
 2352                                        const lhs_lo64_ra: Register.Alias = .r0;
 2353                                        const lhs_hi16_ra: Register.Alias = .r1;
 2354                                        const rhs_lo64_ra: Register.Alias = .r2;
 2355                                        const rhs_hi16_ra: Register.Alias = .r3;
 2356                                        const temp_ra: Register.Alias = .r4;
 2357                                        var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 2358                                        const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 2359                                        try call.paramLiveOut(isel, rhs_hi16_vi.?, rhs_hi16_ra);
 2360                                        var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 2361                                        const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 2362                                        try call.paramLiveOut(isel, rhs_lo64_vi.?, rhs_lo64_ra);
 2363                                        try isel.emit(.cbz(
 2364                                            temp_ra.x(),
 2365                                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 2366                                        ));
 2367                                        try isel.emit(.orr(temp_ra.x(), lhs_lo64_ra.x(), .{ .shifted_register = .{
 2368                                            .register = lhs_hi16_ra.x(),
 2369                                            .shift = .{ .lsl = 64 - 15 },
 2370                                        } }));
 2371                                        try isel.emit(.tbz(
 2372                                            temp_ra.w(),
 2373                                            15,
 2374                                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 2375                                        ));
 2376                                        try isel.emit(.eor(temp_ra.w(), lhs_hi16_ra.w(), .{
 2377                                            .register = rhs_hi16_ra.w(),
 2378                                        }));
 2379                                    },
 2380                                    128 => {
 2381                                        const lhs_ra: Register.Alias = .v0;
 2382                                        const rhs_ra: Register.Alias = .v1;
 2383                                        const temp1_ra: Register.Alias = .r0;
 2384                                        const temp2_ra: Register.Alias = .r1;
 2385                                        try call.paramLiveOut(isel, rhs_vi, rhs_ra);
 2386                                        try isel.emit(.@"b."(
 2387                                            .pl,
 2388                                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 2389                                        ));
 2390                                        try isel.emit(.cbz(
 2391                                            temp1_ra.x(),
 2392                                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 2393                                        ));
 2394                                        try isel.emit(.orr(temp1_ra.x(), temp1_ra.x(), .{ .shifted_register = .{
 2395                                            .register = temp2_ra.x(),
 2396                                            .shift = .{ .lsl = 1 },
 2397                                        } }));
 2398                                        try isel.emit(.fmov(temp1_ra.x(), .{
 2399                                            .register = rhs_ra.d(),
 2400                                        }));
 2401                                        try isel.emit(.tbz(
 2402                                            temp1_ra.x(),
 2403                                            63,
 2404                                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 2405                                        ));
 2406                                        try isel.emit(.eor(temp1_ra.x(), temp1_ra.x(), .{
 2407                                            .register = temp2_ra.x(),
 2408                                        }));
 2409                                        try isel.emit(.fmov(temp2_ra.x(), .{
 2410                                            .register = rhs_ra.@"d[]"(1),
 2411                                        }));
 2412                                        try isel.emit(.fmov(temp1_ra.x(), .{
 2413                                            .register = lhs_ra.@"d[]"(1),
 2414                                        }));
 2415                                    },
 2416                                }
 2417                                try call.finishReturn(isel);
 2418                            },
 2419                        },
 2420                    }
 2421
 2422                    try call.prepareCallee(isel);
 2423                    try isel.global_relocs.append(gpa, .{
 2424                        .name = switch (bits) {
 2425                            else => unreachable,
 2426                            16 => "__fmodh",
 2427                            32 => "fmodf",
 2428                            64 => "fmod",
 2429                            80 => "__fmodx",
 2430                            128 => "fmodq",
 2431                        },
 2432                        .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 2433                    });
 2434                    try isel.emit(.bl(0));
 2435                    try call.finishCallee(isel);
 2436
 2437                    try call.prepareParams(isel);
 2438                    const lhs_vi = try isel.use(bin_op.lhs);
 2439                    const rhs_vi = try isel.use(bin_op.rhs);
 2440                    switch (bits) {
 2441                        else => unreachable,
 2442                        16, 32, 64, 128 => {
 2443                            try call.paramLiveOut(isel, rhs_vi, .v1);
 2444                            try call.paramLiveOut(isel, lhs_vi, .v0);
 2445                        },
 2446                        80 => {
 2447                            var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 2448                            const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 2449                            try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 2450                            var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 2451                            const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 2452                            try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 2453                            var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 2454                            const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 2455                            try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 2456                            var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 2457                            const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 2458                            try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 2459                        },
 2460                    }
 2461                    try call.finishParams(isel);
 2462                }
 2463            }
 2464            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2465        },
 2466        .ptr_add, .ptr_sub => |air_tag| {
 2467            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 2468                defer res_vi.value.deref(isel);
 2469                const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2470
 2471                const ty_pl = air.data(air.inst_index).ty_pl;
 2472                const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
 2473                const elem_size = ty_pl.ty.toType().elemType2(zcu).abiSize(zcu);
 2474
 2475                const base_vi = try isel.use(bin_op.lhs);
 2476                var base_part_it = base_vi.field(ty_pl.ty.toType(), 0, 8);
 2477                const base_part_vi = try base_part_it.only(isel);
 2478                const base_part_mat = try base_part_vi.?.matReg(isel);
 2479                const index_vi = try isel.use(bin_op.rhs);
 2480                try isel.elemPtr(res_ra, base_part_mat.ra, switch (air_tag) {
 2481                    else => unreachable,
 2482                    .ptr_add => .add,
 2483                    .ptr_sub => .sub,
 2484                }, elem_size, index_vi);
 2485                try base_part_mat.finish(isel);
 2486            }
 2487            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2488        },
 2489        .max, .min => |air_tag| {
 2490            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 2491                defer res_vi.value.deref(isel);
 2492
 2493                const bin_op = air.data(air.inst_index).bin_op;
 2494                const ty = isel.air.typeOf(bin_op.lhs, ip);
 2495                if (!ty.isRuntimeFloat()) {
 2496                    if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2497                    const int_info = ty.intInfo(zcu);
 2498                    if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2499
 2500                    const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2501                    const lhs_vi = try isel.use(bin_op.lhs);
 2502                    const rhs_vi = try isel.use(bin_op.rhs);
 2503                    const lhs_mat = try lhs_vi.matReg(isel);
 2504                    const rhs_mat = try rhs_vi.matReg(isel);
 2505                    const cond: codegen.aarch64.encoding.ConditionCode = switch (air_tag) {
 2506                        else => unreachable,
 2507                        .max => switch (int_info.signedness) {
 2508                            .signed => .ge,
 2509                            .unsigned => .hs,
 2510                        },
 2511                        .min => switch (int_info.signedness) {
 2512                            .signed => .lt,
 2513                            .unsigned => .lo,
 2514                        },
 2515                    };
 2516                    switch (int_info.bits) {
 2517                        else => unreachable,
 2518                        1...32 => {
 2519                            try isel.emit(.csel(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w(), cond));
 2520                            try isel.emit(.subs(.wzr, lhs_mat.ra.w(), .{ .register = rhs_mat.ra.w() }));
 2521                        },
 2522                        33...64 => {
 2523                            try isel.emit(.csel(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x(), cond));
 2524                            try isel.emit(.subs(.xzr, lhs_mat.ra.x(), .{ .register = rhs_mat.ra.x() }));
 2525                        },
 2526                    }
 2527                    try rhs_mat.finish(isel);
 2528                    try lhs_mat.finish(isel);
 2529                } else switch (ty.floatBits(isel.target)) {
 2530                    else => unreachable,
 2531                    16, 32, 64 => |bits| {
 2532                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2533                        const need_fcvt = switch (bits) {
 2534                            else => unreachable,
 2535                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 2536                            32, 64 => false,
 2537                        };
 2538                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 2539                        const lhs_vi = try isel.use(bin_op.lhs);
 2540                        const rhs_vi = try isel.use(bin_op.rhs);
 2541                        const lhs_mat = try lhs_vi.matReg(isel);
 2542                        const rhs_mat = try rhs_vi.matReg(isel);
 2543                        const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 2544                        defer if (need_fcvt) isel.freeReg(lhs_ra);
 2545                        const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 2546                        defer if (need_fcvt) isel.freeReg(rhs_ra);
 2547                        try isel.emit(bits: switch (bits) {
 2548                            else => unreachable,
 2549                            16 => if (need_fcvt) continue :bits 32 else switch (air_tag) {
 2550                                else => unreachable,
 2551                                .max => .fmaxnm(res_ra.h(), lhs_ra.h(), rhs_ra.h()),
 2552                                .min => .fminnm(res_ra.h(), lhs_ra.h(), rhs_ra.h()),
 2553                            },
 2554                            32 => switch (air_tag) {
 2555                                else => unreachable,
 2556                                .max => .fmaxnm(res_ra.s(), lhs_ra.s(), rhs_ra.s()),
 2557                                .min => .fminnm(res_ra.s(), lhs_ra.s(), rhs_ra.s()),
 2558                            },
 2559                            64 => switch (air_tag) {
 2560                                else => unreachable,
 2561                                .max => .fmaxnm(res_ra.d(), lhs_ra.d(), rhs_ra.d()),
 2562                                .min => .fminnm(res_ra.d(), lhs_ra.d(), rhs_ra.d()),
 2563                            },
 2564                        });
 2565                        if (need_fcvt) {
 2566                            try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 2567                            try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 2568                        }
 2569                        try rhs_mat.finish(isel);
 2570                        try lhs_mat.finish(isel);
 2571                    },
 2572                    80, 128 => |bits| {
 2573                        try call.prepareReturn(isel);
 2574                        switch (bits) {
 2575                            else => unreachable,
 2576                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 2577                            80 => {
 2578                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 2579                                const res_hi16_vi = try res_hi16_it.only(isel);
 2580                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 2581                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 2582                                const res_lo64_vi = try res_lo64_it.only(isel);
 2583                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 2584                            },
 2585                        }
 2586                        try call.finishReturn(isel);
 2587
 2588                        try call.prepareCallee(isel);
 2589                        try isel.global_relocs.append(gpa, .{
 2590                            .name = switch (air_tag) {
 2591                                else => unreachable,
 2592                                .max => switch (bits) {
 2593                                    else => unreachable,
 2594                                    16 => "__fmaxh",
 2595                                    32 => "fmaxf",
 2596                                    64 => "fmax",
 2597                                    80 => "__fmaxx",
 2598                                    128 => "fmaxq",
 2599                                },
 2600                                .min => switch (bits) {
 2601                                    else => unreachable,
 2602                                    16 => "__fminh",
 2603                                    32 => "fminf",
 2604                                    64 => "fmin",
 2605                                    80 => "__fminx",
 2606                                    128 => "fminq",
 2607                                },
 2608                            },
 2609                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 2610                        });
 2611                        try isel.emit(.bl(0));
 2612                        try call.finishCallee(isel);
 2613
 2614                        try call.prepareParams(isel);
 2615                        const lhs_vi = try isel.use(bin_op.lhs);
 2616                        const rhs_vi = try isel.use(bin_op.rhs);
 2617                        switch (bits) {
 2618                            else => unreachable,
 2619                            16, 32, 64, 128 => {
 2620                                try call.paramLiveOut(isel, rhs_vi, .v1);
 2621                                try call.paramLiveOut(isel, lhs_vi, .v0);
 2622                            },
 2623                            80 => {
 2624                                var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 2625                                const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 2626                                try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 2627                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 2628                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 2629                                try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 2630                                var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 2631                                const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 2632                                try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 2633                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 2634                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 2635                                try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 2636                            },
 2637                        }
 2638                        try call.finishParams(isel);
 2639                    },
 2640                }
 2641            }
 2642            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2643        },
 2644        .add_with_overflow, .sub_with_overflow => |air_tag| {
 2645            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| {
 2646                defer res_vi.value.deref(isel);
 2647
 2648                const ty_pl = air.data(air.inst_index).ty_pl;
 2649                const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
 2650                const ty = isel.air.typeOf(bin_op.lhs, ip);
 2651                const lhs_vi = try isel.use(bin_op.lhs);
 2652                const rhs_vi = try isel.use(bin_op.rhs);
 2653                const ty_size = lhs_vi.size(isel);
 2654                var overflow_it = res_vi.value.field(ty_pl.ty.toType(), ty_size, 1);
 2655                const overflow_vi = try overflow_it.only(isel);
 2656                var wrapped_it = res_vi.value.field(ty_pl.ty.toType(), 0, ty_size);
 2657                const wrapped_vi = try wrapped_it.only(isel);
 2658                try wrapped_vi.?.addOrSubtract(isel, ty, lhs_vi, switch (air_tag) {
 2659                    else => unreachable,
 2660                    .add_with_overflow => .add,
 2661                    .sub_with_overflow => .sub,
 2662                }, rhs_vi, .{
 2663                    .overflow = if (try overflow_vi.?.defReg(isel)) |overflow_ra| .{ .ra = overflow_ra } else .wrap,
 2664                });
 2665            }
 2666            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2667        },
 2668        .alloc, .ret_ptr => |air_tag| {
 2669            if (isel.live_values.fetchRemove(air.inst_index)) |ptr_vi| unused: {
 2670                defer ptr_vi.value.deref(isel);
 2671                switch (air_tag) {
 2672                    else => unreachable,
 2673                    .alloc => {},
 2674                    .ret_ptr => if (isel.live_values.get(Block.main)) |ret_vi| switch (ret_vi.parent(isel)) {
 2675                        .unallocated, .stack_slot => {},
 2676                        .value, .constant => unreachable,
 2677                        .address => break :unused,
 2678                    },
 2679                }
 2680                const ptr_ra = try ptr_vi.value.defReg(isel) orelse break :unused;
 2681
 2682                const ty = air.data(air.inst_index).ty;
 2683                const slot_size = ty.childType(zcu).abiSize(zcu);
 2684                const slot_align = ty.ptrAlignment(zcu);
 2685                const slot_offset = slot_align.forward(isel.stack_size);
 2686                isel.stack_size = @intCast(slot_offset + slot_size);
 2687                const lo12: u12 = @truncate(slot_offset >> 0);
 2688                const hi12: u12 = @intCast(slot_offset >> 12);
 2689                if (hi12 > 0) try isel.emit(.add(
 2690                    ptr_ra.x(),
 2691                    if (lo12 > 0) ptr_ra.x() else .sp,
 2692                    .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 2693                ));
 2694                if (lo12 > 0 or hi12 == 0) try isel.emit(.add(ptr_ra.x(), .sp, .{ .immediate = lo12 }));
 2695            }
 2696            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2697        },
 2698        .inferred_alloc, .inferred_alloc_comptime => unreachable,
 2699        .assembly => {
 2700            const ty_pl = air.data(air.inst_index).ty_pl;
 2701            const extra = isel.air.extraData(Air.Asm, ty_pl.payload);
 2702            var extra_index = extra.end;
 2703            const outputs: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra_index..][0..extra.data.flags.outputs_len]);
 2704            extra_index += outputs.len;
 2705            const inputs: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra_index..][0..extra.data.inputs_len]);
 2706            extra_index += inputs.len;
 2707
 2708            var as: codegen.aarch64.Assemble = .{
 2709                .source = undefined,
 2710                .operands = .empty,
 2711            };
 2712            defer as.operands.deinit(gpa);
 2713
 2714            for (outputs) |output| {
 2715                const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]);
 2716                const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]), 0);
 2717                const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
 2718                // This equation accounts for the fact that even if we have exactly 4 bytes
 2719                // for the string, we still use the next u32 for the null terminator.
 2720                extra_index += (constraint.len + name.len + (2 + 3)) / 4;
 2721
 2722                switch (output) {
 2723                    else => return isel.fail("invalid constraint: '{s}'", .{constraint}),
 2724                    .none => if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) {
 2725                        const output_reg = Register.parse(constraint["={".len .. constraint.len - "}".len]) orelse
 2726                            return isel.fail("invalid constraint: '{s}'", .{constraint});
 2727                        const output_ra = output_reg.alias;
 2728                        if (isel.live_values.fetchRemove(air.inst_index)) |output_vi| {
 2729                            defer output_vi.value.deref(isel);
 2730                            try output_vi.value.defLiveIn(isel, output_reg.alias, comptime &.initFill(.free));
 2731                            isel.freeReg(output_ra);
 2732                        }
 2733                        if (!std.mem.eql(u8, name, "_")) {
 2734                            const operand_gop = try as.operands.getOrPut(gpa, name);
 2735                            if (operand_gop.found_existing) return isel.fail("duplicate output name: '{s}'", .{name});
 2736                            operand_gop.value_ptr.* = .{ .register = switch (ty_pl.ty.toType().abiSize(zcu)) {
 2737                                0 => unreachable,
 2738                                1...4 => output_ra.w(),
 2739                                5...8 => output_ra.x(),
 2740                                else => return isel.fail("too big output type: '{f}'", .{isel.fmtType(ty_pl.ty.toType())}),
 2741                            } };
 2742                        }
 2743                    } else if (std.mem.eql(u8, constraint, "=r")) {
 2744                        const output_ra = if (isel.live_values.fetchRemove(air.inst_index)) |output_vi| output_ra: {
 2745                            defer output_vi.value.deref(isel);
 2746                            break :output_ra try output_vi.value.defReg(isel) orelse try isel.allocIntReg();
 2747                        } else try isel.allocIntReg();
 2748                        if (!std.mem.eql(u8, name, "_")) {
 2749                            const operand_gop = try as.operands.getOrPut(gpa, name);
 2750                            if (operand_gop.found_existing) return isel.fail("duplicate output name: '{s}'", .{name});
 2751                            operand_gop.value_ptr.* = .{ .register = switch (ty_pl.ty.toType().abiSize(zcu)) {
 2752                                0 => unreachable,
 2753                                1...4 => output_ra.w(),
 2754                                5...8 => output_ra.x(),
 2755                                else => return isel.fail("too big output type: '{f}'", .{isel.fmtType(ty_pl.ty.toType())}),
 2756                            } };
 2757                        }
 2758                    } else return isel.fail("invalid constraint: '{s}'", .{constraint}),
 2759                }
 2760            }
 2761
 2762            const input_mats = try gpa.alloc(Value.Materialize, inputs.len);
 2763            defer gpa.free(input_mats);
 2764            const inputs_extra_index = extra_index;
 2765            for (inputs, input_mats) |input, *input_mat| {
 2766                const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]);
 2767                const constraint = std.mem.sliceTo(extra_bytes, 0);
 2768                const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
 2769                // This equation accounts for the fact that even if we have exactly 4 bytes
 2770                // for the string, we still use the next u32 for the null terminator.
 2771                extra_index += (constraint.len + name.len + (2 + 3)) / 4;
 2772
 2773                if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) {
 2774                    const input_reg = Register.parse(constraint["{".len .. constraint.len - "}".len]) orelse
 2775                        return isel.fail("invalid constraint: '{s}'", .{constraint});
 2776                    input_mat.* = .{ .vi = try isel.use(input), .ra = input_reg.alias };
 2777                    if (!std.mem.eql(u8, name, "_")) {
 2778                        const operand_gop = try as.operands.getOrPut(gpa, name);
 2779                        if (operand_gop.found_existing) return isel.fail("duplicate input name: '{s}'", .{name});
 2780                        const input_ty = isel.air.typeOf(input, ip);
 2781                        operand_gop.value_ptr.* = .{ .register = switch (input_ty.abiSize(zcu)) {
 2782                            0 => unreachable,
 2783                            1...4 => input_reg.alias.w(),
 2784                            5...8 => input_reg.alias.x(),
 2785                            else => return isel.fail("too big input type: '{f}'", .{
 2786                                isel.fmtType(isel.air.typeOf(input, ip)),
 2787                            }),
 2788                        } };
 2789                    }
 2790                } else if (std.mem.eql(u8, constraint, "r")) {
 2791                    const input_vi = try isel.use(input);
 2792                    input_mat.* = try input_vi.matReg(isel);
 2793                    if (!std.mem.eql(u8, name, "_")) {
 2794                        const operand_gop = try as.operands.getOrPut(gpa, name);
 2795                        if (operand_gop.found_existing) return isel.fail("duplicate input name: '{s}'", .{name});
 2796                        operand_gop.value_ptr.* = .{ .register = switch (input_vi.size(isel)) {
 2797                            0 => unreachable,
 2798                            1...4 => input_mat.ra.w(),
 2799                            5...8 => input_mat.ra.x(),
 2800                            else => return isel.fail("too big input type: '{f}'", .{
 2801                                isel.fmtType(isel.air.typeOf(input, ip)),
 2802                            }),
 2803                        } };
 2804                    }
 2805                } else if (std.mem.eql(u8, name, "_")) {
 2806                    input_mat.vi = try isel.use(input);
 2807                } else return isel.fail("invalid constraint: '{s}'", .{constraint});
 2808            }
 2809
 2810            const clobbers = ip.indexToKey(extra.data.clobbers).aggregate;
 2811            const clobbers_ty: ZigType = .fromInterned(clobbers.ty);
 2812            for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
 2813                switch (switch (clobbers.storage) {
 2814                    .bytes => unreachable,
 2815                    .elems => |elems| elems[field_index],
 2816                    .repeated_elem => |repeated_elem| repeated_elem,
 2817                }) {
 2818                    else => unreachable,
 2819                    .bool_false => continue,
 2820                    .bool_true => {},
 2821                }
 2822                const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
 2823                if (std.mem.eql(u8, clobber_name, "memory")) continue;
 2824                if (std.mem.eql(u8, clobber_name, "nzcv")) continue;
 2825                const clobber_reg = Register.parse(clobber_name) orelse
 2826                    return isel.fail("unable to parse clobber: '{s}'", .{clobber_name});
 2827                const live_vi = isel.live_registers.getPtr(clobber_reg.alias);
 2828                switch (live_vi.*) {
 2829                    _ => {},
 2830                    .allocating => return isel.fail("clobbered twice: '{s}'", .{clobber_name}),
 2831                    .free => live_vi.* = .allocating,
 2832                }
 2833            }
 2834            for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
 2835                switch (switch (clobbers.storage) {
 2836                    .bytes => unreachable,
 2837                    .elems => |elems| elems[field_index],
 2838                    .repeated_elem => |repeated_elem| repeated_elem,
 2839                }) {
 2840                    else => unreachable,
 2841                    .bool_false => continue,
 2842                    .bool_true => {},
 2843                }
 2844                const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
 2845                if (std.mem.eql(u8, clobber_name, "memory")) continue;
 2846                if (std.mem.eql(u8, clobber_name, "nzcv")) continue;
 2847                const clobber_ra = Register.parse(clobber_name).?.alias;
 2848                const live_vi = isel.live_registers.getPtr(clobber_ra);
 2849                switch (live_vi.*) {
 2850                    _ => {
 2851                        if (!try isel.fill(clobber_ra))
 2852                            return isel.fail("unable to clobber: '{s}'", .{clobber_name});
 2853                        assert(live_vi.* == .free);
 2854                        live_vi.* = .allocating;
 2855                    },
 2856                    .allocating => {},
 2857                    .free => unreachable,
 2858                }
 2859            }
 2860
 2861            as.source = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..])[0..extra.data.source_len :0];
 2862            const asm_start = isel.instructions.items.len;
 2863            while (as.nextInstruction() catch |err| switch (err) {
 2864                error.InvalidSyntax => {
 2865                    const remaining_source = std.mem.span(as.source);
 2866                    return isel.fail("unable to assemble: '{s}'", .{std.mem.trim(
 2867                        u8,
 2868                        as.source[0 .. std.mem.indexOfScalar(u8, remaining_source, '\n') orelse remaining_source.len],
 2869                        &std.ascii.whitespace,
 2870                    )});
 2871                },
 2872            }) |instruction| try isel.emit(instruction);
 2873            std.mem.reverse(codegen.aarch64.encoding.Instruction, isel.instructions.items[asm_start..]);
 2874
 2875            extra_index = inputs_extra_index;
 2876            for (input_mats) |input_mat| {
 2877                const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]);
 2878                const constraint = std.mem.sliceTo(extra_bytes, 0);
 2879                const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
 2880                // This equation accounts for the fact that even if we have exactly 4 bytes
 2881                // for the string, we still use the next u32 for the null terminator.
 2882                extra_index += (constraint.len + name.len + (2 + 3)) / 4;
 2883
 2884                if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) {
 2885                    try input_mat.vi.liveOut(isel, input_mat.ra);
 2886                } else if (std.mem.eql(u8, constraint, "r")) {
 2887                    try input_mat.finish(isel);
 2888                } else if (std.mem.eql(u8, name, "_")) {
 2889                    try input_mat.vi.mat(isel);
 2890                } else unreachable;
 2891            }
 2892
 2893            for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
 2894                switch (switch (clobbers.storage) {
 2895                    .bytes => unreachable,
 2896                    .elems => |elems| elems[field_index],
 2897                    .repeated_elem => |repeated_elem| repeated_elem,
 2898                }) {
 2899                    else => unreachable,
 2900                    .bool_false => continue,
 2901                    .bool_true => {},
 2902                }
 2903                const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
 2904                if (std.mem.eql(u8, clobber_name, "memory")) continue;
 2905                if (std.mem.eql(u8, clobber_name, "cc")) continue;
 2906                isel.freeReg(Register.parse(clobber_name).?.alias);
 2907            }
 2908
 2909            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2910        },
 2911        .bit_and, .bit_or, .xor, .bool_and, .bool_or => |air_tag| {
 2912            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| {
 2913                defer res_vi.value.deref(isel);
 2914
 2915                const bin_op = air.data(air.inst_index).bin_op;
 2916                const ty = isel.air.typeOf(bin_op.lhs, ip);
 2917                const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type)
 2918                    .{ .signedness = .unsigned, .bits = 1 }
 2919                else if (ty.isAbiInt(zcu))
 2920                    ty.intInfo(zcu)
 2921                else
 2922                    return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2923                if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2924
 2925                const lhs_vi = try isel.use(bin_op.lhs);
 2926                const rhs_vi = try isel.use(bin_op.rhs);
 2927                var offset = res_vi.value.size(isel);
 2928                while (offset > 0) {
 2929                    const size = @min(offset, 8);
 2930                    offset -= size;
 2931                    var res_part_it = res_vi.value.field(ty, offset, size);
 2932                    const res_part_vi = try res_part_it.only(isel);
 2933                    const res_part_ra = try res_part_vi.?.defReg(isel) orelse continue;
 2934                    var lhs_part_it = lhs_vi.field(ty, offset, size);
 2935                    const lhs_part_vi = try lhs_part_it.only(isel);
 2936                    const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
 2937                    var rhs_part_it = rhs_vi.field(ty, offset, size);
 2938                    const rhs_part_vi = try rhs_part_it.only(isel);
 2939                    const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
 2940                    try isel.emit(switch (air_tag) {
 2941                        else => unreachable,
 2942                        .bit_and, .bool_and => switch (size) {
 2943                            else => unreachable,
 2944                            1, 2, 4 => .@"and"(res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 2945                            8 => .@"and"(res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 2946                        },
 2947                        .bit_or, .bool_or => switch (size) {
 2948                            else => unreachable,
 2949                            1, 2, 4 => .orr(res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 2950                            8 => .orr(res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 2951                        },
 2952                        .xor => switch (size) {
 2953                            else => unreachable,
 2954                            1, 2, 4 => .eor(res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 2955                            8 => .eor(res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 2956                        },
 2957                    });
 2958                    try rhs_part_mat.finish(isel);
 2959                    try lhs_part_mat.finish(isel);
 2960                }
 2961            }
 2962            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 2963        },
 2964        .shr, .shr_exact, .shl, .shl_exact => |air_tag| {
 2965            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 2966                defer res_vi.value.deref(isel);
 2967
 2968                const bin_op = air.data(air.inst_index).bin_op;
 2969                const ty = isel.air.typeOf(bin_op.lhs, ip);
 2970                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 2971                const int_info = ty.intInfo(zcu);
 2972                switch (int_info.bits) {
 2973                    0 => unreachable,
 2974                    1...64 => |bits| {
 2975                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 2976                        switch (air_tag) {
 2977                            else => unreachable,
 2978                            .shr, .shr_exact, .shl_exact => {},
 2979                            .shl => switch (bits) {
 2980                                else => unreachable,
 2981                                1...31 => try isel.emit(switch (int_info.signedness) {
 2982                                    .signed => .sbfm(res_ra.w(), res_ra.w(), .{
 2983                                        .N = .word,
 2984                                        .immr = 0,
 2985                                        .imms = @intCast(bits - 1),
 2986                                    }),
 2987                                    .unsigned => .ubfm(res_ra.w(), res_ra.w(), .{
 2988                                        .N = .word,
 2989                                        .immr = 0,
 2990                                        .imms = @intCast(bits - 1),
 2991                                    }),
 2992                                }),
 2993                                32 => {},
 2994                                33...63 => try isel.emit(switch (int_info.signedness) {
 2995                                    .signed => .sbfm(res_ra.x(), res_ra.x(), .{
 2996                                        .N = .doubleword,
 2997                                        .immr = 0,
 2998                                        .imms = @intCast(bits - 1),
 2999                                    }),
 3000                                    .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{
 3001                                        .N = .doubleword,
 3002                                        .immr = 0,
 3003                                        .imms = @intCast(bits - 1),
 3004                                    }),
 3005                                }),
 3006                                64 => {},
 3007                            },
 3008                        }
 3009
 3010                        const lhs_vi = try isel.use(bin_op.lhs);
 3011                        const rhs_vi = try isel.use(bin_op.rhs);
 3012                        const lhs_mat = try lhs_vi.matReg(isel);
 3013                        const rhs_mat = try rhs_vi.matReg(isel);
 3014                        try isel.emit(switch (air_tag) {
 3015                            else => unreachable,
 3016                            .shr, .shr_exact => switch (bits) {
 3017                                else => unreachable,
 3018                                1...32 => switch (int_info.signedness) {
 3019                                    .signed => .asrv(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 3020                                    .unsigned => .lsrv(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 3021                                },
 3022                                33...64 => switch (int_info.signedness) {
 3023                                    .signed => .asrv(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 3024                                    .unsigned => .lsrv(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 3025                                },
 3026                            },
 3027                            .shl, .shl_exact => switch (bits) {
 3028                                else => unreachable,
 3029                                1...32 => .lslv(res_ra.w(), lhs_mat.ra.w(), rhs_mat.ra.w()),
 3030                                33...64 => .lslv(res_ra.x(), lhs_mat.ra.x(), rhs_mat.ra.x()),
 3031                            },
 3032                        });
 3033                        try rhs_mat.finish(isel);
 3034                        try lhs_mat.finish(isel);
 3035                    },
 3036                    65...128 => |bits| {
 3037                        var res_hi64_it = res_vi.value.field(ty, 8, 8);
 3038                        const res_hi64_vi = try res_hi64_it.only(isel);
 3039                        const res_hi64_ra = try res_hi64_vi.?.defReg(isel);
 3040                        var res_lo64_it = res_vi.value.field(ty, 0, 8);
 3041                        const res_lo64_vi = try res_lo64_it.only(isel);
 3042                        const res_lo64_ra = try res_lo64_vi.?.defReg(isel);
 3043                        if (res_hi64_ra == null and res_lo64_ra == null) break :unused;
 3044                        if (res_hi64_ra) |res_ra| switch (air_tag) {
 3045                            else => unreachable,
 3046                            .shr, .shr_exact, .shl_exact => {},
 3047                            .shl => switch (bits) {
 3048                                else => unreachable,
 3049                                65...127 => try isel.emit(switch (int_info.signedness) {
 3050                                    .signed => .sbfm(res_ra.x(), res_ra.x(), .{
 3051                                        .N = .doubleword,
 3052                                        .immr = 0,
 3053                                        .imms = @intCast(bits - 64 - 1),
 3054                                    }),
 3055                                    .unsigned => .ubfm(res_ra.x(), res_ra.x(), .{
 3056                                        .N = .doubleword,
 3057                                        .immr = 0,
 3058                                        .imms = @intCast(bits - 64 - 1),
 3059                                    }),
 3060                                }),
 3061                                128 => {},
 3062                            },
 3063                        };
 3064
 3065                        const lhs_vi = try isel.use(bin_op.lhs);
 3066                        const lhs_hi64_mat = lhs_hi64_mat: {
 3067                            const res_lock: RegLock = switch (air_tag) {
 3068                                else => unreachable,
 3069                                .shr, .shr_exact => switch (int_info.signedness) {
 3070                                    .signed => if (res_lo64_ra) |res_ra| isel.lockReg(res_ra) else .empty,
 3071                                    .unsigned => .empty,
 3072                                },
 3073                                .shl, .shl_exact => .empty,
 3074                            };
 3075                            defer res_lock.unlock(isel);
 3076                            var lhs_hi64_it = lhs_vi.field(ty, 8, 8);
 3077                            const lhs_hi64_vi = try lhs_hi64_it.only(isel);
 3078                            break :lhs_hi64_mat try lhs_hi64_vi.?.matReg(isel);
 3079                        };
 3080                        var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 3081                        const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 3082                        const lhs_lo64_mat = try lhs_lo64_vi.?.matReg(isel);
 3083                        const rhs_vi = try isel.use(bin_op.rhs);
 3084                        const rhs_mat = try rhs_vi.matReg(isel);
 3085                        const lo64_ra = lo64_ra: {
 3086                            const res_lock: RegLock = switch (air_tag) {
 3087                                else => unreachable,
 3088                                .shr, .shr_exact => switch (int_info.signedness) {
 3089                                    .signed => if (res_lo64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty,
 3090                                    .unsigned => .empty,
 3091                                },
 3092                                .shl, .shl_exact => if (res_hi64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty,
 3093                            };
 3094                            defer res_lock.unlock(isel);
 3095                            break :lo64_ra try isel.allocIntReg();
 3096                        };
 3097                        defer isel.freeReg(lo64_ra);
 3098                        const hi64_ra = hi64_ra: {
 3099                            const res_lock: RegLock = switch (air_tag) {
 3100                                else => unreachable,
 3101                                .shr, .shr_exact => if (res_lo64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty,
 3102                                .shl, .shl_exact => .empty,
 3103                            };
 3104                            defer res_lock.unlock(isel);
 3105                            break :hi64_ra try isel.allocIntReg();
 3106                        };
 3107                        defer isel.freeReg(hi64_ra);
 3108                        switch (air_tag) {
 3109                            else => unreachable,
 3110                            .shr, .shr_exact => {
 3111                                if (res_hi64_ra) |res_ra| switch (int_info.signedness) {
 3112                                    .signed => {
 3113                                        try isel.emit(.csel(res_ra.x(), hi64_ra.x(), lo64_ra.x(), .eq));
 3114                                        try isel.emit(.sbfm(lo64_ra.x(), lhs_hi64_mat.ra.x(), .{
 3115                                            .N = .doubleword,
 3116                                            .immr = @intCast(bits - 64 - 1),
 3117                                            .imms = @intCast(bits - 64 - 1),
 3118                                        }));
 3119                                    },
 3120                                    .unsigned => try isel.emit(.csel(res_ra.x(), hi64_ra.x(), .xzr, .eq)),
 3121                                };
 3122                                if (res_lo64_ra) |res_ra| try isel.emit(.csel(res_ra.x(), lo64_ra.x(), hi64_ra.x(), .eq));
 3123                                switch (int_info.signedness) {
 3124                                    .signed => try isel.emit(.asrv(hi64_ra.x(), lhs_hi64_mat.ra.x(), rhs_mat.ra.x())),
 3125                                    .unsigned => try isel.emit(.lsrv(hi64_ra.x(), lhs_hi64_mat.ra.x(), rhs_mat.ra.x())),
 3126                                }
 3127                            },
 3128                            .shl, .shl_exact => {
 3129                                if (res_lo64_ra) |res_ra| try isel.emit(.csel(res_ra.x(), lo64_ra.x(), .xzr, .eq));
 3130                                if (res_hi64_ra) |res_ra| try isel.emit(.csel(res_ra.x(), hi64_ra.x(), lo64_ra.x(), .eq));
 3131                                try isel.emit(.lslv(lo64_ra.x(), lhs_lo64_mat.ra.x(), rhs_mat.ra.x()));
 3132                            },
 3133                        }
 3134                        try isel.emit(.ands(.wzr, rhs_mat.ra.w(), .{ .immediate = .{ .N = .word, .immr = 32 - 6, .imms = 0 } }));
 3135                        switch (air_tag) {
 3136                            else => unreachable,
 3137                            .shr, .shr_exact => if (res_lo64_ra) |_| {
 3138                                try isel.emit(.orr(
 3139                                    lo64_ra.x(),
 3140                                    lo64_ra.x(),
 3141                                    .{ .shifted_register = .{ .register = hi64_ra.x(), .shift = .{ .lsl = 1 } } },
 3142                                ));
 3143                                try isel.emit(.lslv(hi64_ra.x(), lhs_hi64_mat.ra.x(), hi64_ra.x()));
 3144                                try isel.emit(.lsrv(lo64_ra.x(), lhs_lo64_mat.ra.x(), rhs_mat.ra.x()));
 3145                                try isel.emit(.orn(hi64_ra.w(), .wzr, .{ .register = rhs_mat.ra.w() }));
 3146                            },
 3147                            .shl, .shl_exact => if (res_hi64_ra) |_| {
 3148                                try isel.emit(.orr(
 3149                                    hi64_ra.x(),
 3150                                    hi64_ra.x(),
 3151                                    .{ .shifted_register = .{ .register = lo64_ra.x(), .shift = .{ .lsr = 1 } } },
 3152                                ));
 3153                                try isel.emit(.lsrv(lo64_ra.x(), lhs_lo64_mat.ra.x(), lo64_ra.x()));
 3154                                try isel.emit(.lslv(hi64_ra.x(), lhs_hi64_mat.ra.x(), rhs_mat.ra.x()));
 3155                                try isel.emit(.orn(lo64_ra.w(), .wzr, .{ .register = rhs_mat.ra.w() }));
 3156                            },
 3157                        }
 3158                        try rhs_mat.finish(isel);
 3159                        try lhs_lo64_mat.finish(isel);
 3160                        try lhs_hi64_mat.finish(isel);
 3161                        break :unused;
 3162                    },
 3163                    else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 3164                }
 3165            }
 3166            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3167        },
 3168        .not => |air_tag| {
 3169            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| {
 3170                defer res_vi.value.deref(isel);
 3171
 3172                const ty_op = air.data(air.inst_index).ty_op;
 3173                const ty = ty_op.ty.toType();
 3174                const int_info: std.builtin.Type.Int = int_info: {
 3175                    if (ty_op.ty == .bool_type) break :int_info .{ .signedness = .unsigned, .bits = 1 };
 3176                    if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3177                    break :int_info ty.intInfo(zcu);
 3178                };
 3179                if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3180
 3181                const src_vi = try isel.use(ty_op.operand);
 3182                var offset = res_vi.value.size(isel);
 3183                while (offset > 0) {
 3184                    const size = @min(offset, 8);
 3185                    offset -= size;
 3186                    var res_part_it = res_vi.value.field(ty, offset, size);
 3187                    const res_part_vi = try res_part_it.only(isel);
 3188                    const res_part_ra = try res_part_vi.?.defReg(isel) orelse continue;
 3189                    var src_part_it = src_vi.field(ty, offset, size);
 3190                    const src_part_vi = try src_part_it.only(isel);
 3191                    const src_part_mat = try src_part_vi.?.matReg(isel);
 3192                    try isel.emit(switch (int_info.signedness) {
 3193                        .signed => switch (size) {
 3194                            else => unreachable,
 3195                            1, 2, 4 => .orn(res_part_ra.w(), .wzr, .{ .register = src_part_mat.ra.w() }),
 3196                            8 => .orn(res_part_ra.x(), .xzr, .{ .register = src_part_mat.ra.x() }),
 3197                        },
 3198                        .unsigned => switch (@min(int_info.bits - 8 * offset, 64)) {
 3199                            else => unreachable,
 3200                            1...31 => |bits| .eor(res_part_ra.w(), src_part_mat.ra.w(), .{ .immediate = .{
 3201                                .N = .word,
 3202                                .immr = 0,
 3203                                .imms = @intCast(bits - 1),
 3204                            } }),
 3205                            32 => .orn(res_part_ra.w(), .wzr, .{ .register = src_part_mat.ra.w() }),
 3206                            33...63 => |bits| .eor(res_part_ra.x(), src_part_mat.ra.x(), .{ .immediate = .{
 3207                                .N = .doubleword,
 3208                                .immr = 0,
 3209                                .imms = @intCast(bits - 1),
 3210                            } }),
 3211                            64 => .orn(res_part_ra.x(), .xzr, .{ .register = src_part_mat.ra.x() }),
 3212                        },
 3213                    });
 3214                    try src_part_mat.finish(isel);
 3215                }
 3216            }
 3217            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3218        },
 3219        .bitcast => |air_tag| {
 3220            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 3221                defer dst_vi.value.deref(isel);
 3222                const ty_op = air.data(air.inst_index).ty_op;
 3223                const dst_ty = ty_op.ty.toType();
 3224                const dst_tag = dst_ty.zigTypeTag(zcu);
 3225                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 3226                const src_tag = src_ty.zigTypeTag(zcu);
 3227                if (dst_ty.isAbiInt(zcu) and (src_tag == .bool or src_ty.isAbiInt(zcu))) {
 3228                    const dst_int_info = dst_ty.intInfo(zcu);
 3229                    const src_int_info: std.builtin.Type.Int = if (src_tag == .bool) .{ .signedness = undefined, .bits = 1 } else src_ty.intInfo(zcu);
 3230                    assert(dst_int_info.bits == src_int_info.bits);
 3231                    if (dst_tag != .@"struct" and src_tag != .@"struct" and src_tag != .bool and dst_int_info.signedness == src_int_info.signedness) {
 3232                        try dst_vi.value.move(isel, ty_op.operand);
 3233                    } else switch (dst_int_info.bits) {
 3234                        0 => unreachable,
 3235                        1...31 => |dst_bits| {
 3236                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3237                            const src_vi = try isel.use(ty_op.operand);
 3238                            const src_mat = try src_vi.matReg(isel);
 3239                            try isel.emit(switch (dst_int_info.signedness) {
 3240                                .signed => .sbfm(dst_ra.w(), src_mat.ra.w(), .{
 3241                                    .N = .word,
 3242                                    .immr = 0,
 3243                                    .imms = @intCast(dst_bits - 1),
 3244                                }),
 3245                                .unsigned => .ubfm(dst_ra.w(), src_mat.ra.w(), .{
 3246                                    .N = .word,
 3247                                    .immr = 0,
 3248                                    .imms = @intCast(dst_bits - 1),
 3249                                }),
 3250                            });
 3251                            try src_mat.finish(isel);
 3252                        },
 3253                        32 => try dst_vi.value.move(isel, ty_op.operand),
 3254                        33...63 => |dst_bits| {
 3255                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3256                            const src_vi = try isel.use(ty_op.operand);
 3257                            const src_mat = try src_vi.matReg(isel);
 3258                            try isel.emit(switch (dst_int_info.signedness) {
 3259                                .signed => .sbfm(dst_ra.x(), src_mat.ra.x(), .{
 3260                                    .N = .doubleword,
 3261                                    .immr = 0,
 3262                                    .imms = @intCast(dst_bits - 1),
 3263                                }),
 3264                                .unsigned => .ubfm(dst_ra.x(), src_mat.ra.x(), .{
 3265                                    .N = .doubleword,
 3266                                    .immr = 0,
 3267                                    .imms = @intCast(dst_bits - 1),
 3268                                }),
 3269                            });
 3270                            try src_mat.finish(isel);
 3271                        },
 3272                        64 => try dst_vi.value.move(isel, ty_op.operand),
 3273                        65...127 => |dst_bits| {
 3274                            const src_vi = try isel.use(ty_op.operand);
 3275                            var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8);
 3276                            const dst_hi64_vi = try dst_hi64_it.only(isel);
 3277                            if (try dst_hi64_vi.?.defReg(isel)) |dst_hi64_ra| {
 3278                                var src_hi64_it = src_vi.field(src_ty, 8, 8);
 3279                                const src_hi64_vi = try src_hi64_it.only(isel);
 3280                                const src_hi64_mat = try src_hi64_vi.?.matReg(isel);
 3281                                try isel.emit(switch (dst_int_info.signedness) {
 3282                                    .signed => .sbfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{
 3283                                        .N = .doubleword,
 3284                                        .immr = 0,
 3285                                        .imms = @intCast(dst_bits - 64 - 1),
 3286                                    }),
 3287                                    .unsigned => .ubfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{
 3288                                        .N = .doubleword,
 3289                                        .immr = 0,
 3290                                        .imms = @intCast(dst_bits - 64 - 1),
 3291                                    }),
 3292                                });
 3293                                try src_hi64_mat.finish(isel);
 3294                            }
 3295                            var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 3296                            const dst_lo64_vi = try dst_lo64_it.only(isel);
 3297                            if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| {
 3298                                var src_lo64_it = src_vi.field(src_ty, 0, 8);
 3299                                const src_lo64_vi = try src_lo64_it.only(isel);
 3300                                try src_lo64_vi.?.liveOut(isel, dst_lo64_ra);
 3301                            }
 3302                        },
 3303                        128 => try dst_vi.value.move(isel, ty_op.operand),
 3304                        else => return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
 3305                    }
 3306                } else if ((dst_ty.isPtrAtRuntime(zcu) or dst_ty.isAbiInt(zcu)) and (src_ty.isPtrAtRuntime(zcu) or src_ty.isAbiInt(zcu))) {
 3307                    try dst_vi.value.move(isel, ty_op.operand);
 3308                } else if (dst_ty.isSliceAtRuntime(zcu) and src_ty.isSliceAtRuntime(zcu)) {
 3309                    try dst_vi.value.move(isel, ty_op.operand);
 3310                } else if (dst_tag == .error_union and src_tag == .error_union) {
 3311                    assert(dst_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu) ==
 3312                        src_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu));
 3313                    if (dst_ty.errorUnionPayload(zcu).toIntern() == src_ty.errorUnionPayload(zcu).toIntern()) {
 3314                        try dst_vi.value.move(isel, ty_op.operand);
 3315                    } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 3316                } else if (dst_tag == .float and src_tag == .float) {
 3317                    assert(dst_ty.floatBits(isel.target) == src_ty.floatBits(isel.target));
 3318                    try dst_vi.value.move(isel, ty_op.operand);
 3319                } else if (dst_ty.isAbiInt(zcu) and src_tag == .float) {
 3320                    const dst_int_info = dst_ty.intInfo(zcu);
 3321                    assert(dst_int_info.bits == src_ty.floatBits(isel.target));
 3322                    switch (dst_int_info.bits) {
 3323                        else => unreachable,
 3324                        16 => {
 3325                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3326                            const src_vi = try isel.use(ty_op.operand);
 3327                            const src_mat = try src_vi.matReg(isel);
 3328                            switch (dst_int_info.signedness) {
 3329                                .signed => try isel.emit(.smov(dst_ra.w(), src_mat.ra.@"h[]"(0))),
 3330                                .unsigned => try isel.emit(if (isel.target.cpu.has(.aarch64, .fullfp16))
 3331                                    .fmov(dst_ra.w(), .{ .register = src_mat.ra.h() })
 3332                                else
 3333                                    .umov(dst_ra.w(), src_mat.ra.@"h[]"(0))),
 3334                            }
 3335                            try src_mat.finish(isel);
 3336                        },
 3337                        32 => {
 3338                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3339                            const src_vi = try isel.use(ty_op.operand);
 3340                            const src_mat = try src_vi.matReg(isel);
 3341                            try isel.emit(.fmov(dst_ra.w(), .{ .register = src_mat.ra.s() }));
 3342                            try src_mat.finish(isel);
 3343                        },
 3344                        64 => {
 3345                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3346                            const src_vi = try isel.use(ty_op.operand);
 3347                            const src_mat = try src_vi.matReg(isel);
 3348                            try isel.emit(.fmov(dst_ra.x(), .{ .register = src_mat.ra.d() }));
 3349                            try src_mat.finish(isel);
 3350                        },
 3351                        80 => switch (dst_int_info.signedness) {
 3352                            .signed => {
 3353                                const src_vi = try isel.use(ty_op.operand);
 3354                                var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8);
 3355                                const dst_hi16_vi = try dst_hi16_it.only(isel);
 3356                                if (try dst_hi16_vi.?.defReg(isel)) |dst_hi16_ra| {
 3357                                    var src_hi16_it = src_vi.field(src_ty, 8, 8);
 3358                                    const src_hi16_vi = try src_hi16_it.only(isel);
 3359                                    const src_hi16_mat = try src_hi16_vi.?.matReg(isel);
 3360                                    try isel.emit(.sbfm(dst_hi16_ra.x(), src_hi16_mat.ra.x(), .{
 3361                                        .N = .doubleword,
 3362                                        .immr = 0,
 3363                                        .imms = 16 - 1,
 3364                                    }));
 3365                                    try src_hi16_mat.finish(isel);
 3366                                }
 3367                                var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 3368                                const dst_lo64_vi = try dst_lo64_it.only(isel);
 3369                                if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| {
 3370                                    var src_lo64_it = src_vi.field(src_ty, 0, 8);
 3371                                    const src_lo64_vi = try src_lo64_it.only(isel);
 3372                                    try src_lo64_vi.?.liveOut(isel, dst_lo64_ra);
 3373                                }
 3374                            },
 3375                            else => try dst_vi.value.move(isel, ty_op.operand),
 3376                        },
 3377                        128 => {
 3378                            const src_vi = try isel.use(ty_op.operand);
 3379                            const src_mat = try src_vi.matReg(isel);
 3380                            var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8);
 3381                            const dst_hi64_vi = try dst_hi64_it.only(isel);
 3382                            if (try dst_hi64_vi.?.defReg(isel)) |dst_hi64_ra| try isel.emit(.fmov(dst_hi64_ra.x(), .{ .register = src_mat.ra.@"d[]"(1) }));
 3383                            var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 3384                            const dst_lo64_vi = try dst_lo64_it.only(isel);
 3385                            if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| try isel.emit(.fmov(dst_lo64_ra.x(), .{ .register = src_mat.ra.d() }));
 3386                            try src_mat.finish(isel);
 3387                        },
 3388                    }
 3389                } else if (dst_tag == .float and src_ty.isAbiInt(zcu)) {
 3390                    const src_int_info = src_ty.intInfo(zcu);
 3391                    assert(dst_ty.floatBits(isel.target) == src_int_info.bits);
 3392                    switch (src_int_info.bits) {
 3393                        else => unreachable,
 3394                        16 => {
 3395                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3396                            const src_vi = try isel.use(ty_op.operand);
 3397                            const src_mat = try src_vi.matReg(isel);
 3398                            try isel.emit(.fmov(
 3399                                if (isel.target.cpu.has(.aarch64, .fullfp16)) dst_ra.h() else dst_ra.s(),
 3400                                .{ .register = src_mat.ra.w() },
 3401                            ));
 3402                            try src_mat.finish(isel);
 3403                        },
 3404                        32 => {
 3405                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3406                            const src_vi = try isel.use(ty_op.operand);
 3407                            const src_mat = try src_vi.matReg(isel);
 3408                            try isel.emit(.fmov(dst_ra.s(), .{ .register = src_mat.ra.w() }));
 3409                            try src_mat.finish(isel);
 3410                        },
 3411                        64 => {
 3412                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3413                            const src_vi = try isel.use(ty_op.operand);
 3414                            const src_mat = try src_vi.matReg(isel);
 3415                            try isel.emit(.fmov(dst_ra.d(), .{ .register = src_mat.ra.x() }));
 3416                            try src_mat.finish(isel);
 3417                        },
 3418                        80 => switch (src_int_info.signedness) {
 3419                            .signed => {
 3420                                const src_vi = try isel.use(ty_op.operand);
 3421                                var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8);
 3422                                const dst_hi16_vi = try dst_hi16_it.only(isel);
 3423                                if (try dst_hi16_vi.?.defReg(isel)) |dst_hi16_ra| {
 3424                                    var src_hi16_it = src_vi.field(src_ty, 8, 8);
 3425                                    const src_hi16_vi = try src_hi16_it.only(isel);
 3426                                    const src_hi16_mat = try src_hi16_vi.?.matReg(isel);
 3427                                    try isel.emit(.ubfm(dst_hi16_ra.x(), src_hi16_mat.ra.x(), .{
 3428                                        .N = .doubleword,
 3429                                        .immr = 0,
 3430                                        .imms = 16 - 1,
 3431                                    }));
 3432                                    try src_hi16_mat.finish(isel);
 3433                                }
 3434                                var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 3435                                const dst_lo64_vi = try dst_lo64_it.only(isel);
 3436                                if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| {
 3437                                    var src_lo64_it = src_vi.field(src_ty, 0, 8);
 3438                                    const src_lo64_vi = try src_lo64_it.only(isel);
 3439                                    try src_lo64_vi.?.liveOut(isel, dst_lo64_ra);
 3440                                }
 3441                            },
 3442                            else => try dst_vi.value.move(isel, ty_op.operand),
 3443                        },
 3444                        128 => {
 3445                            const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 3446                            const src_vi = try isel.use(ty_op.operand);
 3447                            var src_hi64_it = src_vi.field(src_ty, 8, 8);
 3448                            const src_hi64_vi = try src_hi64_it.only(isel);
 3449                            const src_hi64_mat = try src_hi64_vi.?.matReg(isel);
 3450                            try isel.emit(.fmov(dst_ra.@"d[]"(1), .{ .register = src_hi64_mat.ra.x() }));
 3451                            try src_hi64_mat.finish(isel);
 3452                            var src_lo64_it = src_vi.field(src_ty, 0, 8);
 3453                            const src_lo64_vi = try src_lo64_it.only(isel);
 3454                            const src_lo64_mat = try src_lo64_vi.?.matReg(isel);
 3455                            try isel.emit(.fmov(dst_ra.d(), .{ .register = src_lo64_mat.ra.x() }));
 3456                            try src_lo64_mat.finish(isel);
 3457                        },
 3458                    }
 3459                } else if (dst_ty.isAbiInt(zcu) and src_tag == .array and src_ty.childType(zcu).isAbiInt(zcu)) {
 3460                    const dst_int_info = dst_ty.intInfo(zcu);
 3461                    const src_child_int_info = src_ty.childType(zcu).intInfo(zcu);
 3462                    const src_len = src_ty.arrayLenIncludingSentinel(zcu);
 3463                    assert(dst_int_info.bits == src_child_int_info.bits * src_len);
 3464                    const src_child_size = src_ty.childType(zcu).abiSize(zcu);
 3465                    if (8 * src_child_size == src_child_int_info.bits) {
 3466                        try dst_vi.value.defAddr(isel, dst_ty, .{ .wrap = dst_int_info }) orelse break :unused;
 3467
 3468                        try call.prepareReturn(isel);
 3469                        try call.finishReturn(isel);
 3470
 3471                        try call.prepareCallee(isel);
 3472                        try isel.global_relocs.append(gpa, .{
 3473                            .name = "memcpy",
 3474                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 3475                        });
 3476                        try isel.emit(.bl(0));
 3477                        try call.finishCallee(isel);
 3478
 3479                        try call.prepareParams(isel);
 3480                        const src_vi = try isel.use(ty_op.operand);
 3481                        try isel.movImmediate(.x2, src_child_size * src_len);
 3482                        try call.paramAddress(isel, src_vi, .r1);
 3483                        try call.paramAddress(isel, dst_vi.value, .r0);
 3484                        try call.finishParams(isel);
 3485                    } else return isel.fail("bad  {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 3486                } else if (dst_tag == .array and dst_ty.childType(zcu).isAbiInt(zcu) and src_ty.isAbiInt(zcu)) {
 3487                    const dst_child_int_info = dst_ty.childType(zcu).intInfo(zcu);
 3488                    const src_int_info = src_ty.intInfo(zcu);
 3489                    const dst_len = dst_ty.arrayLenIncludingSentinel(zcu);
 3490                    assert(dst_child_int_info.bits * dst_len == src_int_info.bits);
 3491                    const dst_child_size = dst_ty.childType(zcu).abiSize(zcu);
 3492                    if (8 * dst_child_size == dst_child_int_info.bits) {
 3493                        try dst_vi.value.defAddr(isel, dst_ty, .{}) orelse break :unused;
 3494
 3495                        try call.prepareReturn(isel);
 3496                        try call.finishReturn(isel);
 3497
 3498                        try call.prepareCallee(isel);
 3499                        try isel.global_relocs.append(gpa, .{
 3500                            .name = "memcpy",
 3501                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 3502                        });
 3503                        try isel.emit(.bl(0));
 3504                        try call.finishCallee(isel);
 3505
 3506                        try call.prepareParams(isel);
 3507                        const src_vi = try isel.use(ty_op.operand);
 3508                        try isel.movImmediate(.x2, dst_child_size * dst_len);
 3509                        try call.paramAddress(isel, src_vi, .r1);
 3510                        try call.paramAddress(isel, dst_vi.value, .r0);
 3511                        try call.finishParams(isel);
 3512                    } else return isel.fail("bad  {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 3513                } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 3514            }
 3515            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3516        },
 3517        .block => {
 3518            const ty_pl = air.data(air.inst_index).ty_pl;
 3519            const extra = isel.air.extraData(Air.Block, ty_pl.payload);
 3520            try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast(
 3521                isel.air.extra.items[extra.end..][0..extra.data.body_len],
 3522            ));
 3523            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3524        },
 3525        .loop => {
 3526            const ty_pl = air.data(air.inst_index).ty_pl;
 3527            const extra = isel.air.extraData(Air.Block, ty_pl.payload);
 3528            const loops = isel.loops.values();
 3529            const loop_index = isel.loops.getIndex(air.inst_index).?;
 3530            const loop = &loops[loop_index];
 3531
 3532            tracking_log.debug("{f}", .{
 3533                isel.fmtDom(air.inst_index, loop.dom, @intCast(isel.blocks.count())),
 3534            });
 3535            tracking_log.debug("{f}", .{isel.fmtLoopLive(air.inst_index)});
 3536            assert(loop.depth == isel.blocks.count());
 3537
 3538            if (false) {
 3539                // loops are dumb...
 3540                for (isel.loop_live.list.items[loop.live..loops[loop_index + 1].live]) |live_inst| {
 3541                    const live_vi = try isel.use(live_inst.toRef());
 3542                    try live_vi.mat(isel);
 3543                }
 3544
 3545                // IT'S DOM TIME!!!
 3546                for (isel.blocks.values(), 0..) |*dom_block, dom_index| {
 3547                    if (@as(u1, @truncate(isel.dom.items[
 3548                        loop.dom + dom_index / @bitSizeOf(DomInt)
 3549                    ] >> @truncate(dom_index))) == 0) continue;
 3550                    var live_reg_it = dom_block.live_registers.iterator();
 3551                    while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) {
 3552                        _ => |live_vi| try live_vi.mat(isel),
 3553                        .allocating => unreachable,
 3554                        .free => {},
 3555                    };
 3556                }
 3557            }
 3558
 3559            loop.live_registers = isel.live_registers;
 3560            loop.repeat_list = Loop.empty_list;
 3561            try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
 3562            try isel.merge(&loop.live_registers, .{ .fill_extra = true });
 3563
 3564            var repeat_label = loop.repeat_list;
 3565            assert(repeat_label != Loop.empty_list);
 3566            while (repeat_label != Loop.empty_list) {
 3567                const instruction = &isel.instructions.items[repeat_label];
 3568                const next_repeat_label = instruction.*;
 3569                instruction.* = .b(-@as(i28, @intCast((isel.instructions.items.len - 1 - repeat_label) << 2)));
 3570                repeat_label = @bitCast(next_repeat_label);
 3571            }
 3572
 3573            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3574        },
 3575        .repeat => {
 3576            const repeat = air.data(air.inst_index).repeat;
 3577            try isel.loops.getPtr(repeat.loop_inst).?.branch(isel);
 3578            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3579        },
 3580        .br => {
 3581            const br = air.data(air.inst_index).br;
 3582            try isel.blocks.getPtr(br.block_inst).?.branch(isel);
 3583            if (isel.live_values.get(br.block_inst)) |dst_vi| try dst_vi.move(isel, br.operand);
 3584            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3585        },
 3586        .trap => {
 3587            try isel.emit(.brk(0x1));
 3588            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3589        },
 3590        .breakpoint => {
 3591            try isel.emit(.brk(0xf000));
 3592            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3593        },
 3594        .ret_addr => {
 3595            if (isel.live_values.fetchRemove(air.inst_index)) |addr_vi| unused: {
 3596                defer addr_vi.value.deref(isel);
 3597                const addr_ra = try addr_vi.value.defReg(isel) orelse break :unused;
 3598                try isel.emit(.ldr(addr_ra.x(), .{ .unsigned_offset = .{ .base = .fp, .offset = 8 } }));
 3599            }
 3600            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3601        },
 3602        .frame_addr => {
 3603            if (isel.live_values.fetchRemove(air.inst_index)) |addr_vi| unused: {
 3604                defer addr_vi.value.deref(isel);
 3605                const addr_ra = try addr_vi.value.defReg(isel) orelse break :unused;
 3606                try isel.emit(.orr(addr_ra.x(), .xzr, .{ .register = .fp }));
 3607            }
 3608            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3609        },
 3610        .call => {
 3611            const pl_op = air.data(air.inst_index).pl_op;
 3612            const extra = isel.air.extraData(Air.Call, pl_op.payload);
 3613            const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]);
 3614            const callee_ty = isel.air.typeOf(pl_op.operand, ip);
 3615            const func_info = switch (ip.indexToKey(callee_ty.toIntern())) {
 3616                else => unreachable,
 3617                .func_type => |func_type| func_type,
 3618                .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type,
 3619            };
 3620
 3621            try call.prepareReturn(isel);
 3622            const maybe_def_ret_vi = isel.live_values.fetchRemove(air.inst_index);
 3623            var maybe_ret_addr_vi: ?Value.Index = null;
 3624            if (maybe_def_ret_vi) |def_ret_vi| {
 3625                defer def_ret_vi.value.deref(isel);
 3626
 3627                var ret_it: CallAbiIterator = .init;
 3628                const ret_vi = try ret_it.ret(isel, isel.air.typeOfIndex(air.inst_index, ip));
 3629                defer ret_vi.?.deref(isel);
 3630                switch (ret_vi.?.parent(isel)) {
 3631                    .unallocated, .stack_slot => if (ret_vi.?.hint(isel)) |ret_ra| {
 3632                        try call.returnLiveIn(isel, def_ret_vi.value, ret_ra);
 3633                    } else {
 3634                        var def_ret_part_it = def_ret_vi.value.parts(isel);
 3635                        var ret_part_it = ret_vi.?.parts(isel);
 3636                        while (def_ret_part_it.next()) |ret_part_vi| {
 3637                            try call.returnLiveIn(isel, ret_part_vi, ret_part_it.next().?.hint(isel).?);
 3638                        }
 3639                    },
 3640                    .value, .constant => unreachable,
 3641                    .address => |address_vi| {
 3642                        maybe_ret_addr_vi = address_vi;
 3643                        _ = try def_ret_vi.value.defAddr(isel, isel.air.typeOfIndex(air.inst_index, ip), .{
 3644                            .expected_live_registers = &call.caller_saved_regs,
 3645                        });
 3646                    },
 3647                }
 3648            }
 3649            try call.finishReturn(isel);
 3650
 3651            try call.prepareCallee(isel);
 3652            if (pl_op.operand.toInterned()) |ct_callee| {
 3653                try isel.nav_relocs.append(gpa, switch (ip.indexToKey(ct_callee)) {
 3654                    else => unreachable,
 3655                    inline .@"extern", .func => |func| .{
 3656                        .nav = func.owner_nav,
 3657                        .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 3658                    },
 3659                    .ptr => |ptr| .{
 3660                        .nav = ptr.base_addr.nav,
 3661                        .reloc = .{
 3662                            .label = @intCast(isel.instructions.items.len),
 3663                            .addend = ptr.byte_offset,
 3664                        },
 3665                    },
 3666                });
 3667                try isel.emit(.bl(0));
 3668            } else {
 3669                const callee_vi = try isel.use(pl_op.operand);
 3670                const callee_mat = try callee_vi.matReg(isel);
 3671                try isel.emit(.blr(callee_mat.ra.x()));
 3672                try callee_mat.finish(isel);
 3673            }
 3674            try call.finishCallee(isel);
 3675
 3676            try call.prepareParams(isel);
 3677            if (maybe_ret_addr_vi) |ret_addr_vi| try call.paramAddress(
 3678                isel,
 3679                maybe_def_ret_vi.?.value,
 3680                ret_addr_vi.hint(isel).?,
 3681            );
 3682            var param_it: CallAbiIterator = .init;
 3683            for (args, 0..) |arg, arg_index| {
 3684                const param_ty = isel.air.typeOf(arg, ip);
 3685                const param_vi = param_vi: {
 3686                    if (arg_index >= func_info.param_types.len) {
 3687                        assert(func_info.is_var_args);
 3688                        switch (isel.va_list) {
 3689                            .other => break :param_vi try param_it.nonSysvVarArg(isel, param_ty),
 3690                            .sysv => {},
 3691                        }
 3692                    }
 3693                    break :param_vi try param_it.param(isel, param_ty);
 3694                } orelse continue;
 3695                defer param_vi.deref(isel);
 3696                const arg_vi = try isel.use(arg);
 3697                switch (param_vi.parent(isel)) {
 3698                    .unallocated => if (param_vi.hint(isel)) |param_ra| {
 3699                        try call.paramLiveOut(isel, arg_vi, param_ra);
 3700                    } else {
 3701                        var param_part_it = param_vi.parts(isel);
 3702                        var arg_part_it = arg_vi.parts(isel);
 3703                        if (arg_part_it.only()) |_| {
 3704                            try isel.values.ensureUnusedCapacity(gpa, param_part_it.remaining);
 3705                            arg_vi.setParts(isel, param_part_it.remaining);
 3706                            while (param_part_it.next()) |param_part_vi| _ = arg_vi.addPart(
 3707                                isel,
 3708                                param_part_vi.get(isel).offset_from_parent,
 3709                                param_part_vi.size(isel),
 3710                            );
 3711                            param_part_it = param_vi.parts(isel);
 3712                            arg_part_it = arg_vi.parts(isel);
 3713                        }
 3714                        while (param_part_it.next()) |param_part_vi| {
 3715                            const arg_part_vi = arg_part_it.next().?;
 3716                            assert(arg_part_vi.get(isel).offset_from_parent ==
 3717                                param_part_vi.get(isel).offset_from_parent);
 3718                            assert(arg_part_vi.size(isel) == param_part_vi.size(isel));
 3719                            try call.paramLiveOut(isel, arg_part_vi, param_part_vi.hint(isel).?);
 3720                        }
 3721                    },
 3722                    .stack_slot => |stack_slot| try arg_vi.store(isel, param_ty, stack_slot.base, .{
 3723                        .offset = @intCast(stack_slot.offset),
 3724                    }),
 3725                    .value, .constant => unreachable,
 3726                    .address => |address_vi| try call.paramAddress(isel, arg_vi, address_vi.hint(isel).?),
 3727                }
 3728            }
 3729            try call.finishParams(isel);
 3730
 3731            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3732        },
 3733        .clz => |air_tag| {
 3734            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 3735                defer res_vi.value.deref(isel);
 3736
 3737                const ty_op = air.data(air.inst_index).ty_op;
 3738                const ty = isel.air.typeOf(ty_op.operand, ip);
 3739                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3740                const int_info = ty.intInfo(zcu);
 3741                switch (int_info.bits) {
 3742                    0 => unreachable,
 3743                    1...64 => {
 3744                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3745                        const src_vi = try isel.use(ty_op.operand);
 3746                        const src_mat = try src_vi.matReg(isel);
 3747                        try isel.clzLimb(res_ra, int_info, src_mat.ra);
 3748                        try src_mat.finish(isel);
 3749                    },
 3750                    65...128 => |bits| {
 3751                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3752                        const src_vi = try isel.use(ty_op.operand);
 3753                        var src_hi64_it = src_vi.field(ty, 8, 8);
 3754                        const src_hi64_vi = try src_hi64_it.only(isel);
 3755                        const src_hi64_mat = try src_hi64_vi.?.matReg(isel);
 3756                        var src_lo64_it = src_vi.field(ty, 0, 8);
 3757                        const src_lo64_vi = try src_lo64_it.only(isel);
 3758                        const src_lo64_mat = try src_lo64_vi.?.matReg(isel);
 3759                        const lo64_ra = try isel.allocIntReg();
 3760                        defer isel.freeReg(lo64_ra);
 3761                        const hi64_ra = try isel.allocIntReg();
 3762                        defer isel.freeReg(hi64_ra);
 3763                        try isel.emit(.csel(res_ra.w(), lo64_ra.w(), hi64_ra.w(), .eq));
 3764                        try isel.emit(.add(lo64_ra.w(), lo64_ra.w(), .{ .immediate = @intCast(bits - 64) }));
 3765                        try isel.emit(.subs(.xzr, src_hi64_mat.ra.x(), .{ .immediate = 0 }));
 3766                        try isel.clzLimb(hi64_ra, .{ .signedness = int_info.signedness, .bits = bits - 64 }, src_hi64_mat.ra);
 3767                        try isel.clzLimb(lo64_ra, .{ .signedness = .unsigned, .bits = 64 }, src_lo64_mat.ra);
 3768                        try src_hi64_mat.finish(isel);
 3769                        try src_lo64_mat.finish(isel);
 3770                    },
 3771                    else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 3772                }
 3773            }
 3774            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3775        },
 3776        .ctz => |air_tag| {
 3777            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 3778                defer res_vi.value.deref(isel);
 3779
 3780                const ty_op = air.data(air.inst_index).ty_op;
 3781                const ty = isel.air.typeOf(ty_op.operand, ip);
 3782                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3783                const int_info = ty.intInfo(zcu);
 3784                switch (int_info.bits) {
 3785                    0 => unreachable,
 3786                    1...64 => {
 3787                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3788                        const src_vi = try isel.use(ty_op.operand);
 3789                        const src_mat = try src_vi.matReg(isel);
 3790                        try isel.ctzLimb(res_ra, int_info, src_mat.ra);
 3791                        try src_mat.finish(isel);
 3792                    },
 3793                    65...128 => |bits| {
 3794                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3795                        const src_vi = try isel.use(ty_op.operand);
 3796                        var src_hi64_it = src_vi.field(ty, 8, 8);
 3797                        const src_hi64_vi = try src_hi64_it.only(isel);
 3798                        const src_hi64_mat = try src_hi64_vi.?.matReg(isel);
 3799                        var src_lo64_it = src_vi.field(ty, 0, 8);
 3800                        const src_lo64_vi = try src_lo64_it.only(isel);
 3801                        const src_lo64_mat = try src_lo64_vi.?.matReg(isel);
 3802                        const lo64_ra = try isel.allocIntReg();
 3803                        defer isel.freeReg(lo64_ra);
 3804                        const hi64_ra = try isel.allocIntReg();
 3805                        defer isel.freeReg(hi64_ra);
 3806                        try isel.emit(.csel(res_ra.w(), lo64_ra.w(), hi64_ra.w(), .ne));
 3807                        try isel.emit(.add(hi64_ra.w(), hi64_ra.w(), .{ .immediate = 64 }));
 3808                        try isel.emit(.subs(.xzr, src_lo64_mat.ra.x(), .{ .immediate = 0 }));
 3809                        try isel.ctzLimb(hi64_ra, .{ .signedness = .unsigned, .bits = 64 }, src_hi64_mat.ra);
 3810                        try isel.ctzLimb(lo64_ra, .{ .signedness = int_info.signedness, .bits = bits - 64 }, src_lo64_mat.ra);
 3811                        try src_hi64_mat.finish(isel);
 3812                        try src_lo64_mat.finish(isel);
 3813                    },
 3814                    else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 3815                }
 3816            }
 3817            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3818        },
 3819        .popcount => |air_tag| {
 3820            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 3821                defer res_vi.value.deref(isel);
 3822
 3823                const ty_op = air.data(air.inst_index).ty_op;
 3824                const ty = isel.air.typeOf(ty_op.operand, ip);
 3825                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3826                const int_info = ty.intInfo(zcu);
 3827                if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3828
 3829                const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3830                const src_vi = try isel.use(ty_op.operand);
 3831                const src_mat = try src_vi.matReg(isel);
 3832                const vec_ra = try isel.allocVecReg();
 3833                defer isel.freeReg(vec_ra);
 3834                try isel.emit(.umov(res_ra.w(), vec_ra.@"b[]"(0)));
 3835                switch (int_info.bits) {
 3836                    else => unreachable,
 3837                    1...8 => {},
 3838                    9...16 => try isel.emit(.addp(vec_ra.@"8b"(), vec_ra.@"8b"(), .{ .vector = vec_ra.@"8b"() })),
 3839                    17...64 => try isel.emit(.addv(vec_ra.b(), vec_ra.@"8b"())),
 3840                }
 3841                try isel.emit(.cnt(vec_ra.@"8b"(), vec_ra.@"8b"()));
 3842                switch (int_info.bits) {
 3843                    else => unreachable,
 3844                    1...31 => |bits| switch (int_info.signedness) {
 3845                        .signed => {
 3846                            try isel.emit(.fmov(vec_ra.s(), .{ .register = res_ra.w() }));
 3847                            try isel.emit(.ubfm(res_ra.w(), src_mat.ra.w(), .{
 3848                                .N = .word,
 3849                                .immr = 0,
 3850                                .imms = @intCast(bits - 1),
 3851                            }));
 3852                        },
 3853                        .unsigned => try isel.emit(.fmov(vec_ra.s(), .{ .register = src_mat.ra.w() })),
 3854                    },
 3855                    32 => try isel.emit(.fmov(vec_ra.s(), .{ .register = src_mat.ra.w() })),
 3856                    33...63 => |bits| switch (int_info.signedness) {
 3857                        .signed => {
 3858                            try isel.emit(.fmov(vec_ra.d(), .{ .register = res_ra.x() }));
 3859                            try isel.emit(.ubfm(res_ra.x(), src_mat.ra.x(), .{
 3860                                .N = .doubleword,
 3861                                .immr = 0,
 3862                                .imms = @intCast(bits - 1),
 3863                            }));
 3864                        },
 3865                        .unsigned => try isel.emit(.fmov(vec_ra.d(), .{ .register = src_mat.ra.x() })),
 3866                    },
 3867                    64 => try isel.emit(.fmov(vec_ra.d(), .{ .register = src_mat.ra.x() })),
 3868                }
 3869                try src_mat.finish(isel);
 3870            }
 3871            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3872        },
 3873        .byte_swap => |air_tag| {
 3874            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 3875                defer res_vi.value.deref(isel);
 3876
 3877                const ty_op = air.data(air.inst_index).ty_op;
 3878                const ty = ty_op.ty.toType();
 3879                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3880                const int_info = ty.intInfo(zcu);
 3881                if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3882
 3883                if (int_info.bits == 8) break :unused try res_vi.value.move(isel, ty_op.operand);
 3884                const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3885                const src_vi = try isel.use(ty_op.operand);
 3886                const src_mat = try src_vi.matReg(isel);
 3887                switch (int_info.bits) {
 3888                    else => unreachable,
 3889                    16 => switch (int_info.signedness) {
 3890                        .signed => {
 3891                            try isel.emit(.sbfm(res_ra.w(), res_ra.w(), .{
 3892                                .N = .word,
 3893                                .immr = 32 - 16,
 3894                                .imms = 32 - 1,
 3895                            }));
 3896                            try isel.emit(.rev(res_ra.w(), src_mat.ra.w()));
 3897                        },
 3898                        .unsigned => try isel.emit(.rev16(res_ra.w(), src_mat.ra.w())),
 3899                    },
 3900                    24 => {
 3901                        switch (int_info.signedness) {
 3902                            .signed => try isel.emit(.sbfm(res_ra.w(), res_ra.w(), .{
 3903                                .N = .word,
 3904                                .immr = 32 - 24,
 3905                                .imms = 32 - 1,
 3906                            })),
 3907                            .unsigned => try isel.emit(.ubfm(res_ra.w(), res_ra.w(), .{
 3908                                .N = .word,
 3909                                .immr = 32 - 24,
 3910                                .imms = 32 - 1,
 3911                            })),
 3912                        }
 3913                        try isel.emit(.rev(res_ra.w(), src_mat.ra.w()));
 3914                    },
 3915                    32 => try isel.emit(.rev(res_ra.w(), src_mat.ra.w())),
 3916                    40, 48, 56 => |bits| {
 3917                        switch (int_info.signedness) {
 3918                            .signed => try isel.emit(.sbfm(res_ra.x(), res_ra.x(), .{
 3919                                .N = .doubleword,
 3920                                .immr = @intCast(64 - bits),
 3921                                .imms = 64 - 1,
 3922                            })),
 3923                            .unsigned => try isel.emit(.ubfm(res_ra.x(), res_ra.x(), .{
 3924                                .N = .doubleword,
 3925                                .immr = @intCast(64 - bits),
 3926                                .imms = 64 - 1,
 3927                            })),
 3928                        }
 3929                        try isel.emit(.rev(res_ra.x(), src_mat.ra.x()));
 3930                    },
 3931                    64 => try isel.emit(.rev(res_ra.x(), src_mat.ra.x())),
 3932                }
 3933                try src_mat.finish(isel);
 3934            }
 3935            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3936        },
 3937        .bit_reverse => |air_tag| {
 3938            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 3939                defer res_vi.value.deref(isel);
 3940
 3941                const ty_op = air.data(air.inst_index).ty_op;
 3942                const ty = ty_op.ty.toType();
 3943                if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3944                const int_info = ty.intInfo(zcu);
 3945                if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
 3946
 3947                const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3948                const src_vi = try isel.use(ty_op.operand);
 3949                const src_mat = try src_vi.matReg(isel);
 3950                switch (int_info.bits) {
 3951                    else => unreachable,
 3952                    1...31 => |bits| {
 3953                        switch (int_info.signedness) {
 3954                            .signed => try isel.emit(.sbfm(res_ra.w(), res_ra.w(), .{
 3955                                .N = .word,
 3956                                .immr = @intCast(32 - bits),
 3957                                .imms = 32 - 1,
 3958                            })),
 3959                            .unsigned => try isel.emit(.ubfm(res_ra.w(), res_ra.w(), .{
 3960                                .N = .word,
 3961                                .immr = @intCast(32 - bits),
 3962                                .imms = 32 - 1,
 3963                            })),
 3964                        }
 3965                        try isel.emit(.rbit(res_ra.w(), src_mat.ra.w()));
 3966                    },
 3967                    32 => try isel.emit(.rbit(res_ra.w(), src_mat.ra.w())),
 3968                    33...63 => |bits| {
 3969                        switch (int_info.signedness) {
 3970                            .signed => try isel.emit(.sbfm(res_ra.x(), res_ra.x(), .{
 3971                                .N = .doubleword,
 3972                                .immr = @intCast(64 - bits),
 3973                                .imms = 64 - 1,
 3974                            })),
 3975                            .unsigned => try isel.emit(.ubfm(res_ra.x(), res_ra.x(), .{
 3976                                .N = .doubleword,
 3977                                .immr = @intCast(64 - bits),
 3978                                .imms = 64 - 1,
 3979                            })),
 3980                        }
 3981                        try isel.emit(.rbit(res_ra.x(), src_mat.ra.x()));
 3982                    },
 3983                    64 => try isel.emit(.rbit(res_ra.x(), src_mat.ra.x())),
 3984                }
 3985                try src_mat.finish(isel);
 3986            }
 3987            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 3988        },
 3989        .sqrt, .floor, .ceil, .round, .trunc_float => |air_tag| {
 3990            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 3991                defer res_vi.value.deref(isel);
 3992
 3993                const un_op = air.data(air.inst_index).un_op;
 3994                const ty = isel.air.typeOf(un_op, ip);
 3995                switch (ty.floatBits(isel.target)) {
 3996                    else => unreachable,
 3997                    16, 32, 64 => |bits| {
 3998                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 3999                        const need_fcvt = switch (bits) {
 4000                            else => unreachable,
 4001                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 4002                            32, 64 => false,
 4003                        };
 4004                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 4005                        const src_vi = try isel.use(un_op);
 4006                        const src_mat = try src_vi.matReg(isel);
 4007                        const src_ra = if (need_fcvt) try isel.allocVecReg() else src_mat.ra;
 4008                        defer if (need_fcvt) isel.freeReg(src_ra);
 4009                        try isel.emit(bits: switch (bits) {
 4010                            else => unreachable,
 4011                            16 => if (need_fcvt) continue :bits 32 else switch (air_tag) {
 4012                                else => unreachable,
 4013                                .sqrt => .fsqrt(res_ra.h(), src_ra.h()),
 4014                                .floor => .frintm(res_ra.h(), src_ra.h()),
 4015                                .ceil => .frintp(res_ra.h(), src_ra.h()),
 4016                                .round => .frinta(res_ra.h(), src_ra.h()),
 4017                                .trunc_float => .frintz(res_ra.h(), src_ra.h()),
 4018                            },
 4019                            32 => switch (air_tag) {
 4020                                else => unreachable,
 4021                                .sqrt => .fsqrt(res_ra.s(), src_ra.s()),
 4022                                .floor => .frintm(res_ra.s(), src_ra.s()),
 4023                                .ceil => .frintp(res_ra.s(), src_ra.s()),
 4024                                .round => .frinta(res_ra.s(), src_ra.s()),
 4025                                .trunc_float => .frintz(res_ra.s(), src_ra.s()),
 4026                            },
 4027                            64 => switch (air_tag) {
 4028                                else => unreachable,
 4029                                .sqrt => .fsqrt(res_ra.d(), src_ra.d()),
 4030                                .floor => .frintm(res_ra.d(), src_ra.d()),
 4031                                .ceil => .frintp(res_ra.d(), src_ra.d()),
 4032                                .round => .frinta(res_ra.d(), src_ra.d()),
 4033                                .trunc_float => .frintz(res_ra.d(), src_ra.d()),
 4034                            },
 4035                        });
 4036                        if (need_fcvt) try isel.emit(.fcvt(src_ra.s(), src_mat.ra.h()));
 4037                        try src_mat.finish(isel);
 4038                    },
 4039                    80, 128 => |bits| {
 4040                        try call.prepareReturn(isel);
 4041                        switch (bits) {
 4042                            else => unreachable,
 4043                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 4044                            80 => {
 4045                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 4046                                const res_hi16_vi = try res_hi16_it.only(isel);
 4047                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 4048                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 4049                                const res_lo64_vi = try res_lo64_it.only(isel);
 4050                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 4051                            },
 4052                        }
 4053                        try call.finishReturn(isel);
 4054
 4055                        try call.prepareCallee(isel);
 4056                        try isel.global_relocs.append(gpa, .{
 4057                            .name = switch (air_tag) {
 4058                                else => unreachable,
 4059                                .sqrt => switch (bits) {
 4060                                    else => unreachable,
 4061                                    16 => "__sqrth",
 4062                                    32 => "sqrtf",
 4063                                    64 => "sqrt",
 4064                                    80 => "__sqrtx",
 4065                                    128 => "sqrtq",
 4066                                },
 4067                                .floor => switch (bits) {
 4068                                    else => unreachable,
 4069                                    16 => "__floorh",
 4070                                    32 => "floorf",
 4071                                    64 => "floor",
 4072                                    80 => "__floorx",
 4073                                    128 => "floorq",
 4074                                },
 4075                                .ceil => switch (bits) {
 4076                                    else => unreachable,
 4077                                    16 => "__ceilh",
 4078                                    32 => "ceilf",
 4079                                    64 => "ceil",
 4080                                    80 => "__ceilx",
 4081                                    128 => "ceilq",
 4082                                },
 4083                                .round => switch (bits) {
 4084                                    else => unreachable,
 4085                                    16 => "__roundh",
 4086                                    32 => "roundf",
 4087                                    64 => "round",
 4088                                    80 => "__roundx",
 4089                                    128 => "roundq",
 4090                                },
 4091                                .trunc_float => switch (bits) {
 4092                                    else => unreachable,
 4093                                    16 => "__trunch",
 4094                                    32 => "truncf",
 4095                                    64 => "trunc",
 4096                                    80 => "__truncx",
 4097                                    128 => "truncq",
 4098                                },
 4099                            },
 4100                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 4101                        });
 4102                        try isel.emit(.bl(0));
 4103                        try call.finishCallee(isel);
 4104
 4105                        try call.prepareParams(isel);
 4106                        const src_vi = try isel.use(un_op);
 4107                        switch (bits) {
 4108                            else => unreachable,
 4109                            16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0),
 4110                            80 => {
 4111                                var src_hi16_it = src_vi.field(ty, 8, 8);
 4112                                const src_hi16_vi = try src_hi16_it.only(isel);
 4113                                try call.paramLiveOut(isel, src_hi16_vi.?, .r1);
 4114                                var src_lo64_it = src_vi.field(ty, 0, 8);
 4115                                const src_lo64_vi = try src_lo64_it.only(isel);
 4116                                try call.paramLiveOut(isel, src_lo64_vi.?, .r0);
 4117                            },
 4118                        }
 4119                        try call.finishParams(isel);
 4120                    },
 4121                }
 4122            }
 4123            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4124        },
 4125        .sin, .cos, .tan, .exp, .exp2, .log, .log2, .log10 => |air_tag| {
 4126            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| {
 4127                defer res_vi.value.deref(isel);
 4128
 4129                const un_op = air.data(air.inst_index).un_op;
 4130                const ty = isel.air.typeOf(un_op, ip);
 4131                const bits = ty.floatBits(isel.target);
 4132                try call.prepareReturn(isel);
 4133                switch (bits) {
 4134                    else => unreachable,
 4135                    16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 4136                    80 => {
 4137                        var res_hi16_it = res_vi.value.field(ty, 8, 8);
 4138                        const res_hi16_vi = try res_hi16_it.only(isel);
 4139                        try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 4140                        var res_lo64_it = res_vi.value.field(ty, 0, 8);
 4141                        const res_lo64_vi = try res_lo64_it.only(isel);
 4142                        try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 4143                    },
 4144                }
 4145                try call.finishReturn(isel);
 4146
 4147                try call.prepareCallee(isel);
 4148                try isel.global_relocs.append(gpa, .{
 4149                    .name = switch (air_tag) {
 4150                        else => unreachable,
 4151                        .sin => switch (bits) {
 4152                            else => unreachable,
 4153                            16 => "__sinh",
 4154                            32 => "sinf",
 4155                            64 => "sin",
 4156                            80 => "__sinx",
 4157                            128 => "sinq",
 4158                        },
 4159                        .cos => switch (bits) {
 4160                            else => unreachable,
 4161                            16 => "__cosh",
 4162                            32 => "cosf",
 4163                            64 => "cos",
 4164                            80 => "__cosx",
 4165                            128 => "cosq",
 4166                        },
 4167                        .tan => switch (bits) {
 4168                            else => unreachable,
 4169                            16 => "__tanh",
 4170                            32 => "tanf",
 4171                            64 => "tan",
 4172                            80 => "__tanx",
 4173                            128 => "tanq",
 4174                        },
 4175                        .exp => switch (bits) {
 4176                            else => unreachable,
 4177                            16 => "__exph",
 4178                            32 => "expf",
 4179                            64 => "exp",
 4180                            80 => "__expx",
 4181                            128 => "expq",
 4182                        },
 4183                        .exp2 => switch (bits) {
 4184                            else => unreachable,
 4185                            16 => "__exp2h",
 4186                            32 => "exp2f",
 4187                            64 => "exp2",
 4188                            80 => "__exp2x",
 4189                            128 => "exp2q",
 4190                        },
 4191                        .log => switch (bits) {
 4192                            else => unreachable,
 4193                            16 => "__logh",
 4194                            32 => "logf",
 4195                            64 => "log",
 4196                            80 => "__logx",
 4197                            128 => "logq",
 4198                        },
 4199                        .log2 => switch (bits) {
 4200                            else => unreachable,
 4201                            16 => "__log2h",
 4202                            32 => "log2f",
 4203                            64 => "log2",
 4204                            80 => "__log2x",
 4205                            128 => "log2q",
 4206                        },
 4207                        .log10 => switch (bits) {
 4208                            else => unreachable,
 4209                            16 => "__log10h",
 4210                            32 => "log10f",
 4211                            64 => "log10",
 4212                            80 => "__log10x",
 4213                            128 => "log10q",
 4214                        },
 4215                    },
 4216                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 4217                });
 4218                try isel.emit(.bl(0));
 4219                try call.finishCallee(isel);
 4220
 4221                try call.prepareParams(isel);
 4222                const src_vi = try isel.use(un_op);
 4223                switch (bits) {
 4224                    else => unreachable,
 4225                    16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0),
 4226                    80 => {
 4227                        var src_hi16_it = src_vi.field(ty, 8, 8);
 4228                        const src_hi16_vi = try src_hi16_it.only(isel);
 4229                        try call.paramLiveOut(isel, src_hi16_vi.?, .r1);
 4230                        var src_lo64_it = src_vi.field(ty, 0, 8);
 4231                        const src_lo64_vi = try src_lo64_it.only(isel);
 4232                        try call.paramLiveOut(isel, src_lo64_vi.?, .r0);
 4233                    },
 4234                }
 4235                try call.finishParams(isel);
 4236            }
 4237            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4238        },
 4239        .abs => |air_tag| {
 4240            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 4241                defer res_vi.value.deref(isel);
 4242
 4243                const ty_op = air.data(air.inst_index).ty_op;
 4244                const ty = ty_op.ty.toType();
 4245                if (!ty.isRuntimeFloat()) {
 4246                    if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
 4247                    switch (ty.intInfo(zcu).bits) {
 4248                        0 => unreachable,
 4249                        1...32 => {
 4250                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4251                            const src_vi = try isel.use(ty_op.operand);
 4252                            const src_mat = try src_vi.matReg(isel);
 4253                            try isel.emit(.csneg(res_ra.w(), src_mat.ra.w(), src_mat.ra.w(), .pl));
 4254                            try isel.emit(.subs(.wzr, src_mat.ra.w(), .{ .immediate = 0 }));
 4255                            try src_mat.finish(isel);
 4256                        },
 4257                        33...64 => {
 4258                            const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4259                            const src_vi = try isel.use(ty_op.operand);
 4260                            const src_mat = try src_vi.matReg(isel);
 4261                            try isel.emit(.csneg(res_ra.x(), src_mat.ra.x(), src_mat.ra.x(), .pl));
 4262                            try isel.emit(.subs(.xzr, src_mat.ra.x(), .{ .immediate = 0 }));
 4263                            try src_mat.finish(isel);
 4264                        },
 4265                        65...128 => {
 4266                            var res_hi64_it = res_vi.value.field(ty, 8, 8);
 4267                            const res_hi64_vi = try res_hi64_it.only(isel);
 4268                            const res_hi64_ra = try res_hi64_vi.?.defReg(isel);
 4269                            var res_lo64_it = res_vi.value.field(ty, 0, 8);
 4270                            const res_lo64_vi = try res_lo64_it.only(isel);
 4271                            const res_lo64_ra = try res_lo64_vi.?.defReg(isel);
 4272                            if (res_hi64_ra == null and res_lo64_ra == null) break :unused;
 4273                            const src_ty = isel.air.typeOf(ty_op.operand, ip);
 4274                            const src_vi = try isel.use(ty_op.operand);
 4275                            var src_hi64_it = src_vi.field(src_ty, 8, 8);
 4276                            const src_hi64_vi = try src_hi64_it.only(isel);
 4277                            const src_hi64_mat = try src_hi64_vi.?.matReg(isel);
 4278                            var src_lo64_it = src_vi.field(src_ty, 0, 8);
 4279                            const src_lo64_vi = try src_lo64_it.only(isel);
 4280                            const src_lo64_mat = try src_lo64_vi.?.matReg(isel);
 4281                            const lo64_ra = try isel.allocIntReg();
 4282                            defer isel.freeReg(lo64_ra);
 4283                            const hi64_ra, const mask_ra = alloc_ras: {
 4284                                const res_lo64_lock: RegLock = if (res_lo64_ra) |res_ra| isel.tryLockReg(res_ra) else .empty;
 4285                                defer res_lo64_lock.unlock(isel);
 4286                                break :alloc_ras .{ try isel.allocIntReg(), try isel.allocIntReg() };
 4287                            };
 4288                            defer {
 4289                                isel.freeReg(hi64_ra);
 4290                                isel.freeReg(mask_ra);
 4291                            }
 4292                            if (res_hi64_ra) |res_ra| try isel.emit(.sbc(res_ra.x(), hi64_ra.x(), mask_ra.x()));
 4293                            try isel.emit(.subs(
 4294                                if (res_lo64_ra) |res_ra| res_ra.x() else .xzr,
 4295                                lo64_ra.x(),
 4296                                .{ .register = mask_ra.x() },
 4297                            ));
 4298                            if (res_hi64_ra) |_| try isel.emit(.eor(hi64_ra.x(), src_hi64_mat.ra.x(), .{ .register = mask_ra.x() }));
 4299                            try isel.emit(.eor(lo64_ra.x(), src_lo64_mat.ra.x(), .{ .register = mask_ra.x() }));
 4300                            try isel.emit(.sbfm(mask_ra.x(), src_hi64_mat.ra.x(), .{
 4301                                .N = .doubleword,
 4302                                .immr = 64 - 1,
 4303                                .imms = 64 - 1,
 4304                            }));
 4305                            try src_lo64_mat.finish(isel);
 4306                            try src_hi64_mat.finish(isel);
 4307                        },
 4308                        else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
 4309                    }
 4310                } else switch (ty.floatBits(isel.target)) {
 4311                    else => unreachable,
 4312                    16 => {
 4313                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4314                        const src_vi = try isel.use(ty_op.operand);
 4315                        const src_mat = try src_vi.matReg(isel);
 4316                        try isel.emit(if (isel.target.cpu.has(.aarch64, .fullfp16))
 4317                            .fabs(res_ra.h(), src_mat.ra.h())
 4318                        else
 4319                            .bic(res_ra.@"4h"(), res_ra.@"4h"(), .{ .shifted_immediate = .{
 4320                                .immediate = 0b10000000,
 4321                                .lsl = 8,
 4322                            } }));
 4323                        try src_mat.finish(isel);
 4324                    },
 4325                    32 => {
 4326                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4327                        const src_vi = try isel.use(ty_op.operand);
 4328                        const src_mat = try src_vi.matReg(isel);
 4329                        try isel.emit(.fabs(res_ra.s(), src_mat.ra.s()));
 4330                        try src_mat.finish(isel);
 4331                    },
 4332                    64 => {
 4333                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4334                        const src_vi = try isel.use(ty_op.operand);
 4335                        const src_mat = try src_vi.matReg(isel);
 4336                        try isel.emit(.fabs(res_ra.d(), src_mat.ra.d()));
 4337                        try src_mat.finish(isel);
 4338                    },
 4339                    80 => {
 4340                        const src_vi = try isel.use(ty_op.operand);
 4341                        var res_hi16_it = res_vi.value.field(ty, 8, 8);
 4342                        const res_hi16_vi = try res_hi16_it.only(isel);
 4343                        if (try res_hi16_vi.?.defReg(isel)) |res_hi16_ra| {
 4344                            var src_hi16_it = src_vi.field(ty, 8, 8);
 4345                            const src_hi16_vi = try src_hi16_it.only(isel);
 4346                            const src_hi16_mat = try src_hi16_vi.?.matReg(isel);
 4347                            try isel.emit(.@"and"(res_hi16_ra.w(), src_hi16_mat.ra.w(), .{ .immediate = .{
 4348                                .N = .word,
 4349                                .immr = 0,
 4350                                .imms = 15 - 1,
 4351                            } }));
 4352                            try src_hi16_mat.finish(isel);
 4353                        }
 4354                        var res_lo64_it = res_vi.value.field(ty, 0, 8);
 4355                        const res_lo64_vi = try res_lo64_it.only(isel);
 4356                        if (try res_lo64_vi.?.defReg(isel)) |res_lo64_ra| {
 4357                            var src_lo64_it = src_vi.field(ty, 0, 8);
 4358                            const src_lo64_vi = try src_lo64_it.only(isel);
 4359                            try src_lo64_vi.?.liveOut(isel, res_lo64_ra);
 4360                        }
 4361                    },
 4362                    128 => {
 4363                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4364                        const src_vi = try isel.use(ty_op.operand);
 4365                        const src_mat = try src_vi.matReg(isel);
 4366                        const neg_zero_ra = try isel.allocVecReg();
 4367                        defer isel.freeReg(neg_zero_ra);
 4368                        try isel.emit(.bic(res_ra.@"16b"(), src_mat.ra.@"16b"(), .{ .register = neg_zero_ra.@"16b"() }));
 4369                        try isel.literals.appendNTimes(gpa, 0, -%isel.literals.items.len % 4);
 4370                        try isel.literal_relocs.append(gpa, .{
 4371                            .label = @intCast(isel.instructions.items.len),
 4372                        });
 4373                        try isel.emit(.ldr(neg_zero_ra.q(), .{
 4374                            .literal = @intCast((isel.instructions.items.len + 1 + isel.literals.items.len) << 2),
 4375                        }));
 4376                        try isel.emitLiteral(&(.{0} ** 15 ++ .{0x80}));
 4377                        try src_mat.finish(isel);
 4378                    },
 4379                }
 4380            }
 4381            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4382        },
 4383        .neg, .neg_optimized => {
 4384            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 4385                defer res_vi.value.deref(isel);
 4386
 4387                const un_op = air.data(air.inst_index).un_op;
 4388                const ty = isel.air.typeOf(un_op, ip);
 4389                switch (ty.floatBits(isel.target)) {
 4390                    else => unreachable,
 4391                    16 => {
 4392                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4393                        const src_vi = try isel.use(un_op);
 4394                        const src_mat = try src_vi.matReg(isel);
 4395                        if (isel.target.cpu.has(.aarch64, .fullfp16)) {
 4396                            try isel.emit(.fneg(res_ra.h(), src_mat.ra.h()));
 4397                        } else {
 4398                            const neg_zero_ra = try isel.allocVecReg();
 4399                            defer isel.freeReg(neg_zero_ra);
 4400                            try isel.emit(.eor(res_ra.@"8b"(), res_ra.@"8b"(), .{ .register = neg_zero_ra.@"8b"() }));
 4401                            try isel.emit(.movi(neg_zero_ra.@"4h"(), 0b10000000, .{ .lsl = 8 }));
 4402                        }
 4403                        try src_mat.finish(isel);
 4404                    },
 4405                    32 => {
 4406                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4407                        const src_vi = try isel.use(un_op);
 4408                        const src_mat = try src_vi.matReg(isel);
 4409                        try isel.emit(.fneg(res_ra.s(), src_mat.ra.s()));
 4410                        try src_mat.finish(isel);
 4411                    },
 4412                    64 => {
 4413                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4414                        const src_vi = try isel.use(un_op);
 4415                        const src_mat = try src_vi.matReg(isel);
 4416                        try isel.emit(.fneg(res_ra.d(), src_mat.ra.d()));
 4417                        try src_mat.finish(isel);
 4418                    },
 4419                    80 => {
 4420                        const src_vi = try isel.use(un_op);
 4421                        var res_hi16_it = res_vi.value.field(ty, 8, 8);
 4422                        const res_hi16_vi = try res_hi16_it.only(isel);
 4423                        if (try res_hi16_vi.?.defReg(isel)) |res_hi16_ra| {
 4424                            var src_hi16_it = src_vi.field(ty, 8, 8);
 4425                            const src_hi16_vi = try src_hi16_it.only(isel);
 4426                            const src_hi16_mat = try src_hi16_vi.?.matReg(isel);
 4427                            try isel.emit(.eor(res_hi16_ra.w(), src_hi16_mat.ra.w(), .{ .immediate = .{
 4428                                .N = .word,
 4429                                .immr = 32 - 15,
 4430                                .imms = 1 - 1,
 4431                            } }));
 4432                            try src_hi16_mat.finish(isel);
 4433                        }
 4434                        var res_lo64_it = res_vi.value.field(ty, 0, 8);
 4435                        const res_lo64_vi = try res_lo64_it.only(isel);
 4436                        if (try res_lo64_vi.?.defReg(isel)) |res_lo64_ra| {
 4437                            var src_lo64_it = src_vi.field(ty, 0, 8);
 4438                            const src_lo64_vi = try src_lo64_it.only(isel);
 4439                            try src_lo64_vi.?.liveOut(isel, res_lo64_ra);
 4440                        }
 4441                    },
 4442                    128 => {
 4443                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 4444                        const src_vi = try isel.use(un_op);
 4445                        const src_mat = try src_vi.matReg(isel);
 4446                        const neg_zero_ra = try isel.allocVecReg();
 4447                        defer isel.freeReg(neg_zero_ra);
 4448                        try isel.emit(.eor(res_ra.@"16b"(), src_mat.ra.@"16b"(), .{ .register = neg_zero_ra.@"16b"() }));
 4449                        try isel.literals.appendNTimes(gpa, 0, -%isel.literals.items.len % 4);
 4450                        try isel.literal_relocs.append(gpa, .{
 4451                            .label = @intCast(isel.instructions.items.len),
 4452                        });
 4453                        try isel.emit(.ldr(neg_zero_ra.q(), .{
 4454                            .literal = @intCast((isel.instructions.items.len + 1 + isel.literals.items.len) << 2),
 4455                        }));
 4456                        try isel.emitLiteral(&(.{0} ** 15 ++ .{0x80}));
 4457                        try src_mat.finish(isel);
 4458                    },
 4459                }
 4460            }
 4461            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4462        },
 4463        .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq => |air_tag| {
 4464            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 4465                defer res_vi.value.deref(isel);
 4466
 4467                const bin_op = air.data(air.inst_index).bin_op;
 4468                const ty = isel.air.typeOf(bin_op.lhs, ip);
 4469                switch (ip.indexToKey(ty.toIntern())) {
 4470                    else => {},
 4471                    .opt_type => |payload_ty| switch (air_tag) {
 4472                        else => unreachable,
 4473                        .cmp_eq, .cmp_neq => if (!ty.optionalReprIsPayload(zcu)) {
 4474                            const lhs_vi = try isel.use(bin_op.lhs);
 4475                            const rhs_vi = try isel.use(bin_op.rhs);
 4476                            const payload_size = ZigType.abiSize(.fromInterned(payload_ty), zcu);
 4477                            var lhs_payload_part_it = lhs_vi.field(ty, 0, payload_size);
 4478                            const lhs_payload_part_vi = try lhs_payload_part_it.only(isel);
 4479                            var rhs_payload_part_it = rhs_vi.field(ty, 0, payload_size);
 4480                            const rhs_payload_part_vi = try rhs_payload_part_it.only(isel);
 4481                            const cmp_info = try isel.cmp(
 4482                                try res_vi.value.defReg(isel) orelse break :unused,
 4483                                .fromInterned(payload_ty),
 4484                                lhs_payload_part_vi.?,
 4485                                air_tag.toCmpOp().?,
 4486                                rhs_payload_part_vi.?,
 4487                            );
 4488                            try isel.emit(.@"b."(
 4489                                .vc,
 4490                                @intCast((isel.instructions.items.len + 1 - cmp_info.cset_label) << 2),
 4491                            ));
 4492                            var lhs_has_value_part_it = lhs_vi.field(ty, payload_size, 1);
 4493                            const lhs_has_value_part_vi = try lhs_has_value_part_it.only(isel);
 4494                            const lhs_has_value_part_mat = try lhs_has_value_part_vi.?.matReg(isel);
 4495                            var rhs_has_value_part_it = rhs_vi.field(ty, payload_size, 1);
 4496                            const rhs_has_value_part_vi = try rhs_has_value_part_it.only(isel);
 4497                            const rhs_has_value_part_mat = try rhs_has_value_part_vi.?.matReg(isel);
 4498                            try isel.emit(.ccmp(
 4499                                lhs_has_value_part_mat.ra.w(),
 4500                                .{ .register = rhs_has_value_part_mat.ra.w() },
 4501                                .{ .n = false, .z = false, .c = false, .v = true },
 4502                                .eq,
 4503                            ));
 4504                            try isel.emit(.ands(
 4505                                .wzr,
 4506                                lhs_has_value_part_mat.ra.w(),
 4507                                .{ .register = rhs_has_value_part_mat.ra.w() },
 4508                            ));
 4509                            try rhs_has_value_part_mat.finish(isel);
 4510                            try lhs_has_value_part_mat.finish(isel);
 4511                            break :unused;
 4512                        },
 4513                    },
 4514                }
 4515                _ = try isel.cmp(
 4516                    try res_vi.value.defReg(isel) orelse break :unused,
 4517                    ty,
 4518                    try isel.use(bin_op.lhs),
 4519                    air_tag.toCmpOp().?,
 4520                    try isel.use(bin_op.rhs),
 4521                );
 4522            }
 4523            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4524        },
 4525        .cond_br => {
 4526            const pl_op = air.data(air.inst_index).pl_op;
 4527            const extra = isel.air.extraData(Air.CondBr, pl_op.payload);
 4528
 4529            try isel.body(@ptrCast(isel.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]));
 4530            const else_label = isel.instructions.items.len;
 4531            const else_live_registers = isel.live_registers;
 4532            try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.then_body_len]));
 4533            try isel.merge(&else_live_registers, .{});
 4534
 4535            const cond_vi = try isel.use(pl_op.operand);
 4536            const cond_mat = try cond_vi.matReg(isel);
 4537            try isel.emit(.tbz(
 4538                cond_mat.ra.x(),
 4539                0,
 4540                @intCast((isel.instructions.items.len + 1 - else_label) << 2),
 4541            ));
 4542            try cond_mat.finish(isel);
 4543
 4544            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4545        },
 4546        .switch_br => {
 4547            const switch_br = isel.air.unwrapSwitch(air.inst_index);
 4548            const cond_ty = isel.air.typeOf(switch_br.operand, ip);
 4549            const cond_int_info: std.builtin.Type.Int = if (cond_ty.toIntern() == .bool_type)
 4550                .{ .signedness = .unsigned, .bits = 1 }
 4551            else if (cond_ty.isAbiInt(zcu))
 4552                cond_ty.intInfo(zcu)
 4553            else
 4554                return isel.fail("bad switch cond {f}", .{isel.fmtType(cond_ty)});
 4555
 4556            var final_case = true;
 4557            if (switch_br.else_body_len > 0) {
 4558                var cases_it = switch_br.iterateCases();
 4559                while (cases_it.next()) |_| {}
 4560                try isel.body(cases_it.elseBody());
 4561                assert(final_case);
 4562                final_case = false;
 4563            }
 4564            const zero_reg: Register = switch (cond_int_info.bits) {
 4565                else => unreachable,
 4566                1...32 => .wzr,
 4567                33...64 => .xzr,
 4568            };
 4569            var cond_mat: ?Value.Materialize = null;
 4570            var cond_reg: Register = undefined;
 4571            var cases_it = switch_br.iterateCases();
 4572            while (cases_it.next()) |case| {
 4573                const next_label = isel.instructions.items.len;
 4574                const next_live_registers = isel.live_registers;
 4575                try isel.body(case.body);
 4576                if (final_case) {
 4577                    final_case = false;
 4578                    continue;
 4579                }
 4580                try isel.merge(&next_live_registers, .{});
 4581                if (cond_mat == null) {
 4582                    var cond_vi = try isel.use(switch_br.operand);
 4583                    cond_mat = try cond_vi.matReg(isel);
 4584                    cond_reg = switch (cond_int_info.bits) {
 4585                        else => unreachable,
 4586                        1...32 => cond_mat.?.ra.w(),
 4587                        33...64 => cond_mat.?.ra.x(),
 4588                    };
 4589                }
 4590                if (case.ranges.len == 0 and case.items.len == 1 and Constant.fromInterned(
 4591                    case.items[0].toInterned().?,
 4592                ).orderAgainstZero(zcu).compare(.eq)) {
 4593                    try isel.emit(.cbnz(
 4594                        cond_reg,
 4595                        @intCast((isel.instructions.items.len + 1 - next_label) << 2),
 4596                    ));
 4597                    continue;
 4598                }
 4599                try isel.emit(.@"b."(
 4600                    .invert(switch (case.ranges.len) {
 4601                        0 => .eq,
 4602                        else => .ls,
 4603                    }),
 4604                    @intCast((isel.instructions.items.len + 1 - next_label) << 2),
 4605                ));
 4606                var case_range_index = case.ranges.len;
 4607                while (case_range_index > 0) {
 4608                    case_range_index -= 1;
 4609
 4610                    const low_val: Constant = .fromInterned(case.ranges[case_range_index][0].toInterned().?);
 4611                    var low_bigint_space: Constant.BigIntSpace = undefined;
 4612                    const low_bigint = low_val.toBigInt(&low_bigint_space, zcu);
 4613                    const low_int: i64 = if (low_bigint.positive) @bitCast(
 4614                        low_bigint.toInt(u64) catch
 4615                            return isel.fail("too big case range start: {f}", .{isel.fmtConstant(low_val)}),
 4616                    ) else low_bigint.toInt(i64) catch
 4617                        return isel.fail("too big case range start: {f}", .{isel.fmtConstant(low_val)});
 4618
 4619                    const high_val: Constant = .fromInterned(case.ranges[case_range_index][1].toInterned().?);
 4620                    var high_bigint_space: Constant.BigIntSpace = undefined;
 4621                    const high_bigint = high_val.toBigInt(&high_bigint_space, zcu);
 4622                    const high_int: i64 = if (high_bigint.positive) @bitCast(
 4623                        high_bigint.toInt(u64) catch
 4624                            return isel.fail("too big case range end: {f}", .{isel.fmtConstant(high_val)}),
 4625                    ) else high_bigint.toInt(i64) catch
 4626                        return isel.fail("too big case range end: {f}", .{isel.fmtConstant(high_val)});
 4627
 4628                    const adjusted_ra = switch (low_int) {
 4629                        0 => cond_mat.?.ra,
 4630                        else => try isel.allocIntReg(),
 4631                    };
 4632                    defer if (adjusted_ra != cond_mat.?.ra) isel.freeReg(adjusted_ra);
 4633                    const adjusted_reg = switch (cond_int_info.bits) {
 4634                        else => unreachable,
 4635                        1...32 => adjusted_ra.w(),
 4636                        33...64 => adjusted_ra.x(),
 4637                    };
 4638                    const delta_int = high_int -% low_int;
 4639                    if (case_range_index | case.items.len > 0) {
 4640                        if (std.math.cast(u5, delta_int)) |pos_imm| try isel.emit(.ccmp(
 4641                            adjusted_reg,
 4642                            .{ .immediate = pos_imm },
 4643                            .{ .n = false, .z = true, .c = false, .v = false },
 4644                            if (case_range_index > 0) .hi else .ne,
 4645                        )) else if (std.math.cast(u5, -delta_int)) |neg_imm| try isel.emit(.ccmn(
 4646                            adjusted_reg,
 4647                            .{ .immediate = neg_imm },
 4648                            .{ .n = false, .z = true, .c = false, .v = false },
 4649                            if (case_range_index > 0) .hi else .ne,
 4650                        )) else {
 4651                            const imm_ra = try isel.allocIntReg();
 4652                            defer isel.freeReg(imm_ra);
 4653                            const imm_reg = switch (cond_int_info.bits) {
 4654                                else => unreachable,
 4655                                1...32 => imm_ra.w(),
 4656                                33...64 => imm_ra.x(),
 4657                            };
 4658                            try isel.emit(.ccmp(
 4659                                cond_reg,
 4660                                .{ .register = imm_reg },
 4661                                .{ .n = false, .z = true, .c = false, .v = false },
 4662                                if (case_range_index > 0) .hi else .ne,
 4663                            ));
 4664                            try isel.movImmediate(imm_reg, @bitCast(delta_int));
 4665                        }
 4666                    } else {
 4667                        if (std.math.cast(u12, delta_int)) |pos_imm| try isel.emit(.subs(
 4668                            zero_reg,
 4669                            adjusted_reg,
 4670                            .{ .immediate = pos_imm },
 4671                        )) else if (std.math.cast(u12, -delta_int)) |neg_imm| try isel.emit(.adds(
 4672                            zero_reg,
 4673                            adjusted_reg,
 4674                            .{ .immediate = neg_imm },
 4675                        )) else if (if (@as(i12, @truncate(delta_int)) == 0)
 4676                            std.math.cast(u12, delta_int >> 12)
 4677                        else
 4678                            null) |pos_imm_lsr_12| try isel.emit(.subs(
 4679                            zero_reg,
 4680                            adjusted_reg,
 4681                            .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
 4682                        )) else if (if (@as(i12, @truncate(-delta_int)) == 0)
 4683                            std.math.cast(u12, -delta_int >> 12)
 4684                        else
 4685                            null) |neg_imm_lsr_12| try isel.emit(.adds(
 4686                            zero_reg,
 4687                            adjusted_reg,
 4688                            .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
 4689                        )) else {
 4690                            const imm_ra = try isel.allocIntReg();
 4691                            defer isel.freeReg(imm_ra);
 4692                            const imm_reg = switch (cond_int_info.bits) {
 4693                                else => unreachable,
 4694                                1...32 => imm_ra.w(),
 4695                                33...64 => imm_ra.x(),
 4696                            };
 4697                            try isel.emit(.subs(zero_reg, adjusted_reg, .{ .register = imm_reg }));
 4698                            try isel.movImmediate(imm_reg, @bitCast(delta_int));
 4699                        }
 4700                    }
 4701
 4702                    switch (low_int) {
 4703                        0 => {},
 4704                        else => {
 4705                            if (std.math.cast(u12, low_int)) |pos_imm| try isel.emit(.sub(
 4706                                adjusted_reg,
 4707                                cond_reg,
 4708                                .{ .immediate = pos_imm },
 4709                            )) else if (std.math.cast(u12, -low_int)) |neg_imm| try isel.emit(.add(
 4710                                adjusted_reg,
 4711                                cond_reg,
 4712                                .{ .immediate = neg_imm },
 4713                            )) else if (if (@as(i12, @truncate(low_int)) == 0)
 4714                                std.math.cast(u12, low_int >> 12)
 4715                            else
 4716                                null) |pos_imm_lsr_12| try isel.emit(.sub(
 4717                                adjusted_reg,
 4718                                cond_reg,
 4719                                .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
 4720                            )) else if (if (@as(i12, @truncate(-low_int)) == 0)
 4721                                std.math.cast(u12, -low_int >> 12)
 4722                            else
 4723                                null) |neg_imm_lsr_12| try isel.emit(.add(
 4724                                adjusted_reg,
 4725                                cond_reg,
 4726                                .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
 4727                            )) else {
 4728                                const imm_ra = try isel.allocIntReg();
 4729                                defer isel.freeReg(imm_ra);
 4730                                const imm_reg = switch (cond_int_info.bits) {
 4731                                    else => unreachable,
 4732                                    1...32 => imm_ra.w(),
 4733                                    33...64 => imm_ra.x(),
 4734                                };
 4735                                try isel.emit(.sub(adjusted_reg, cond_reg, .{ .register = imm_reg }));
 4736                                try isel.movImmediate(imm_reg, @bitCast(low_int));
 4737                            }
 4738                        },
 4739                    }
 4740                }
 4741                var case_item_index = case.items.len;
 4742                while (case_item_index > 0) {
 4743                    case_item_index -= 1;
 4744
 4745                    const item_val: Constant = .fromInterned(case.items[case_item_index].toInterned().?);
 4746                    var item_bigint_space: Constant.BigIntSpace = undefined;
 4747                    const item_bigint = item_val.toBigInt(&item_bigint_space, zcu);
 4748                    const item_int: i64 = if (item_bigint.positive) @bitCast(
 4749                        item_bigint.toInt(u64) catch
 4750                            return isel.fail("too big case item: {f}", .{isel.fmtConstant(item_val)}),
 4751                    ) else item_bigint.toInt(i64) catch
 4752                        return isel.fail("too big case item: {f}", .{isel.fmtConstant(item_val)});
 4753
 4754                    if (case_item_index > 0) {
 4755                        if (std.math.cast(u5, item_int)) |pos_imm| try isel.emit(.ccmp(
 4756                            cond_reg,
 4757                            .{ .immediate = pos_imm },
 4758                            .{ .n = false, .z = true, .c = false, .v = false },
 4759                            .ne,
 4760                        )) else if (std.math.cast(u5, -item_int)) |neg_imm| try isel.emit(.ccmn(
 4761                            cond_reg,
 4762                            .{ .immediate = neg_imm },
 4763                            .{ .n = false, .z = true, .c = false, .v = false },
 4764                            .ne,
 4765                        )) else {
 4766                            const imm_ra = try isel.allocIntReg();
 4767                            defer isel.freeReg(imm_ra);
 4768                            const imm_reg = switch (cond_int_info.bits) {
 4769                                else => unreachable,
 4770                                1...32 => imm_ra.w(),
 4771                                33...64 => imm_ra.x(),
 4772                            };
 4773                            try isel.emit(.ccmp(
 4774                                cond_reg,
 4775                                .{ .register = imm_reg },
 4776                                .{ .n = false, .z = true, .c = false, .v = false },
 4777                                .ne,
 4778                            ));
 4779                            try isel.movImmediate(imm_reg, @bitCast(item_int));
 4780                        }
 4781                    } else {
 4782                        if (std.math.cast(u12, item_int)) |pos_imm| try isel.emit(.subs(
 4783                            zero_reg,
 4784                            cond_reg,
 4785                            .{ .immediate = pos_imm },
 4786                        )) else if (std.math.cast(u12, -item_int)) |neg_imm| try isel.emit(.adds(
 4787                            zero_reg,
 4788                            cond_reg,
 4789                            .{ .immediate = neg_imm },
 4790                        )) else if (if (@as(i12, @truncate(item_int)) == 0)
 4791                            std.math.cast(u12, item_int >> 12)
 4792                        else
 4793                            null) |pos_imm_lsr_12| try isel.emit(.subs(
 4794                            zero_reg,
 4795                            cond_reg,
 4796                            .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
 4797                        )) else if (if (@as(i12, @truncate(-item_int)) == 0)
 4798                            std.math.cast(u12, -item_int >> 12)
 4799                        else
 4800                            null) |neg_imm_lsr_12| try isel.emit(.adds(
 4801                            zero_reg,
 4802                            cond_reg,
 4803                            .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
 4804                        )) else {
 4805                            const imm_ra = try isel.allocIntReg();
 4806                            defer isel.freeReg(imm_ra);
 4807                            const imm_reg = switch (cond_int_info.bits) {
 4808                                else => unreachable,
 4809                                1...32 => imm_ra.w(),
 4810                                33...64 => imm_ra.x(),
 4811                            };
 4812                            try isel.emit(.subs(zero_reg, cond_reg, .{ .register = imm_reg }));
 4813                            try isel.movImmediate(imm_reg, @bitCast(item_int));
 4814                        }
 4815                    }
 4816                }
 4817            }
 4818            if (cond_mat) |mat| try mat.finish(isel);
 4819            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4820        },
 4821        .@"try", .try_cold => {
 4822            const pl_op = air.data(air.inst_index).pl_op;
 4823            const extra = isel.air.extraData(Air.Try, pl_op.payload);
 4824            const error_union_ty = isel.air.typeOf(pl_op.operand, ip);
 4825            const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
 4826            const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
 4827
 4828            const error_union_vi = try isel.use(pl_op.operand);
 4829            if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| {
 4830                defer payload_vi.value.deref(isel);
 4831
 4832                var payload_part_it = error_union_vi.field(
 4833                    error_union_ty,
 4834                    codegen.errUnionPayloadOffset(payload_ty, zcu),
 4835                    payload_vi.value.size(isel),
 4836                );
 4837                const payload_part_vi = try payload_part_it.only(isel);
 4838                try payload_vi.value.copy(isel, payload_ty, payload_part_vi.?);
 4839            }
 4840
 4841            const cont_label = isel.instructions.items.len;
 4842            const cont_live_registers = isel.live_registers;
 4843            try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
 4844            try isel.merge(&cont_live_registers, .{});
 4845
 4846            var error_set_part_it = error_union_vi.field(
 4847                error_union_ty,
 4848                codegen.errUnionErrorOffset(payload_ty, zcu),
 4849                ZigType.fromInterned(error_union_info.error_set_type).abiSize(zcu),
 4850            );
 4851            const error_set_part_vi = try error_set_part_it.only(isel);
 4852            const error_set_part_mat = try error_set_part_vi.?.matReg(isel);
 4853            try isel.emit(.cbz(
 4854                error_set_part_mat.ra.w(),
 4855                @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
 4856            ));
 4857            try error_set_part_mat.finish(isel);
 4858
 4859            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4860        },
 4861        .try_ptr, .try_ptr_cold => {
 4862            const ty_pl = air.data(air.inst_index).ty_pl;
 4863            const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
 4864            const error_union_ty = isel.air.typeOf(extra.data.ptr, ip).childType(zcu);
 4865            const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
 4866            const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
 4867
 4868            const error_union_ptr_vi = try isel.use(extra.data.ptr);
 4869            const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
 4870            if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
 4871                defer payload_ptr_vi.value.deref(isel);
 4872                switch (codegen.errUnionPayloadOffset(ty_pl.ty.toType().childType(zcu), zcu)) {
 4873                    0 => try payload_ptr_vi.value.move(isel, extra.data.ptr),
 4874                    else => |payload_offset| {
 4875                        const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
 4876                        const lo12: u12 = @truncate(payload_offset >> 0);
 4877                        const hi12: u12 = @intCast(payload_offset >> 12);
 4878                        if (hi12 > 0) try isel.emit(.add(
 4879                            payload_ptr_ra.x(),
 4880                            if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
 4881                            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 4882                        ));
 4883                        if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
 4884                    },
 4885                }
 4886            }
 4887
 4888            const cont_label = isel.instructions.items.len;
 4889            const cont_live_registers = isel.live_registers;
 4890            try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
 4891            try isel.merge(&cont_live_registers, .{});
 4892
 4893            const error_set_ra = try isel.allocIntReg();
 4894            defer isel.freeReg(error_set_ra);
 4895            try isel.loadReg(
 4896                error_set_ra,
 4897                ZigType.fromInterned(error_union_info.error_set_type).abiSize(zcu),
 4898                .unsigned,
 4899                error_union_ptr_mat.ra,
 4900                codegen.errUnionErrorOffset(payload_ty, zcu),
 4901            );
 4902            try error_union_ptr_mat.finish(isel);
 4903            try isel.emit(.cbz(
 4904                error_set_ra.w(),
 4905                @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
 4906            ));
 4907
 4908            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4909        },
 4910        .dbg_stmt => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
 4911        .dbg_empty_stmt => {
 4912            try isel.emit(.nop());
 4913            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4914        },
 4915        .dbg_inline_block => {
 4916            const ty_pl = air.data(air.inst_index).ty_pl;
 4917            const extra = isel.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
 4918            try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast(
 4919                isel.air.extra.items[extra.end..][0..extra.data.body_len],
 4920            ));
 4921            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4922        },
 4923        .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => {
 4924            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4925        },
 4926        .is_null, .is_non_null => |air_tag| {
 4927            if (isel.live_values.fetchRemove(air.inst_index)) |is_vi| unused: {
 4928                defer is_vi.value.deref(isel);
 4929                const is_ra = try is_vi.value.defReg(isel) orelse break :unused;
 4930
 4931                const un_op = air.data(air.inst_index).un_op;
 4932                const opt_ty = isel.air.typeOf(un_op, ip);
 4933                const payload_ty = opt_ty.optionalChild(zcu);
 4934                const payload_size = payload_ty.abiSize(zcu);
 4935                const has_value_offset, const has_value_size = if (!opt_ty.optionalReprIsPayload(zcu))
 4936                    .{ payload_size, 1 }
 4937                else if (payload_ty.isSlice(zcu))
 4938                    .{ 0, 8 }
 4939                else
 4940                    .{ 0, payload_size };
 4941
 4942                try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(switch (air_tag) {
 4943                    else => unreachable,
 4944                    .is_null => .eq,
 4945                    .is_non_null => .ne,
 4946                })));
 4947                const opt_vi = try isel.use(un_op);
 4948                var has_value_part_it = opt_vi.field(opt_ty, has_value_offset, has_value_size);
 4949                const has_value_part_vi = try has_value_part_it.only(isel);
 4950                const has_value_part_mat = try has_value_part_vi.?.matReg(isel);
 4951                try isel.emit(switch (has_value_size) {
 4952                    else => unreachable,
 4953                    1...4 => .subs(.wzr, has_value_part_mat.ra.w(), .{ .immediate = 0 }),
 4954                    5...8 => .subs(.xzr, has_value_part_mat.ra.x(), .{ .immediate = 0 }),
 4955                });
 4956                try has_value_part_mat.finish(isel);
 4957            }
 4958            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4959        },
 4960        .is_err, .is_non_err => |air_tag| {
 4961            if (isel.live_values.fetchRemove(air.inst_index)) |is_vi| unused: {
 4962                defer is_vi.value.deref(isel);
 4963                const is_ra = try is_vi.value.defReg(isel) orelse break :unused;
 4964
 4965                const un_op = air.data(air.inst_index).un_op;
 4966                const error_union_ty = isel.air.typeOf(un_op, ip);
 4967                const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
 4968                const error_set_ty: ZigType = .fromInterned(error_union_info.error_set_type);
 4969                const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
 4970                const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
 4971                const error_set_size = error_set_ty.abiSize(zcu);
 4972
 4973                try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(switch (air_tag) {
 4974                    else => unreachable,
 4975                    .is_err => .ne,
 4976                    .is_non_err => .eq,
 4977                })));
 4978                const error_union_vi = try isel.use(un_op);
 4979                var error_set_part_it = error_union_vi.field(error_union_ty, error_set_offset, error_set_size);
 4980                const error_set_part_vi = try error_set_part_it.only(isel);
 4981                const error_set_part_mat = try error_set_part_vi.?.matReg(isel);
 4982                try isel.emit(.ands(.wzr, error_set_part_mat.ra.w(), .{ .immediate = .{
 4983                    .N = .word,
 4984                    .immr = 0,
 4985                    .imms = @intCast(8 * error_set_size - 1),
 4986                } }));
 4987                try error_set_part_mat.finish(isel);
 4988            }
 4989            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 4990        },
 4991        .load => {
 4992            const ty_op = air.data(air.inst_index).ty_op;
 4993            const ptr_ty = isel.air.typeOf(ty_op.operand, ip);
 4994            const ptr_info = ptr_ty.ptrInfo(zcu);
 4995            if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed load", .{});
 4996
 4997            if (ptr_info.flags.is_volatile) _ = try isel.use(air.inst_index.toRef());
 4998            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 4999                defer dst_vi.value.deref(isel);
 5000                const size = dst_vi.value.size(isel);
 5001                if (size <= Value.max_parts and ip.zigTypeTag(ptr_info.child) != .@"union") {
 5002                    const ptr_vi = try isel.use(ty_op.operand);
 5003                    const ptr_mat = try ptr_vi.matReg(isel);
 5004                    _ = try dst_vi.value.load(isel, ty_op.ty.toType(), ptr_mat.ra, .{
 5005                        .@"volatile" = ptr_info.flags.is_volatile,
 5006                    });
 5007                    try ptr_mat.finish(isel);
 5008                } else {
 5009                    try dst_vi.value.defAddr(isel, .fromInterned(ptr_info.child), .{}) orelse break :unused;
 5010
 5011                    try call.prepareReturn(isel);
 5012                    try call.finishReturn(isel);
 5013
 5014                    try call.prepareCallee(isel);
 5015                    try isel.global_relocs.append(gpa, .{
 5016                        .name = "memcpy",
 5017                        .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 5018                    });
 5019                    try isel.emit(.bl(0));
 5020                    try call.finishCallee(isel);
 5021
 5022                    try call.prepareParams(isel);
 5023                    const ptr_vi = try isel.use(ty_op.operand);
 5024                    try isel.movImmediate(.x2, size);
 5025                    try call.paramLiveOut(isel, ptr_vi, .r1);
 5026                    try call.paramAddress(isel, dst_vi.value, .r0);
 5027                    try call.finishParams(isel);
 5028                }
 5029            }
 5030
 5031            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5032        },
 5033        .ret, .ret_safe => {
 5034            assert(isel.blocks.keys()[0] == Block.main);
 5035            try isel.blocks.values()[0].branch(isel);
 5036            if (isel.live_values.get(Block.main)) |ret_vi| {
 5037                const un_op = air.data(air.inst_index).un_op;
 5038                const src_vi = try isel.use(un_op);
 5039                switch (ret_vi.parent(isel)) {
 5040                    .unallocated, .stack_slot => if (ret_vi.hint(isel)) |ret_ra| {
 5041                        try src_vi.liveOut(isel, ret_ra);
 5042                    } else {
 5043                        var ret_part_it = ret_vi.parts(isel);
 5044                        var src_part_it = src_vi.parts(isel);
 5045                        if (src_part_it.only()) |_| {
 5046                            try isel.values.ensureUnusedCapacity(gpa, ret_part_it.remaining);
 5047                            src_vi.setParts(isel, ret_part_it.remaining);
 5048                            while (ret_part_it.next()) |ret_part_vi| {
 5049                                const src_part_vi = src_vi.addPart(
 5050                                    isel,
 5051                                    ret_part_vi.get(isel).offset_from_parent,
 5052                                    ret_part_vi.size(isel),
 5053                                );
 5054                                switch (ret_part_vi.signedness(isel)) {
 5055                                    .signed => src_part_vi.setSignedness(isel, .signed),
 5056                                    .unsigned => {},
 5057                                }
 5058                                if (ret_part_vi.isVector(isel)) src_part_vi.setIsVector(isel);
 5059                            }
 5060                            ret_part_it = ret_vi.parts(isel);
 5061                            src_part_it = src_vi.parts(isel);
 5062                        }
 5063                        while (ret_part_it.next()) |ret_part_vi| {
 5064                            const src_part_vi = src_part_it.next().?;
 5065                            assert(ret_part_vi.get(isel).offset_from_parent == src_part_vi.get(isel).offset_from_parent);
 5066                            assert(ret_part_vi.size(isel) == src_part_vi.size(isel));
 5067                            try src_part_vi.liveOut(isel, ret_part_vi.hint(isel).?);
 5068                        }
 5069                    },
 5070                    .value, .constant => unreachable,
 5071                    .address => |address_vi| {
 5072                        const ptr_mat = try address_vi.matReg(isel);
 5073                        try src_vi.store(isel, isel.air.typeOf(un_op, ip), ptr_mat.ra, .{});
 5074                        try ptr_mat.finish(isel);
 5075                    },
 5076                }
 5077            }
 5078            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5079        },
 5080        .ret_load => {
 5081            const un_op = air.data(air.inst_index).un_op;
 5082            const ptr_ty = isel.air.typeOf(un_op, ip);
 5083            const ptr_info = ptr_ty.ptrInfo(zcu);
 5084            if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed load", .{});
 5085
 5086            assert(isel.blocks.keys()[0] == Block.main);
 5087            try isel.blocks.values()[0].branch(isel);
 5088            if (isel.live_values.get(Block.main)) |ret_vi| switch (ret_vi.parent(isel)) {
 5089                .unallocated, .stack_slot => {
 5090                    var ret_part_it: Value.PartIterator = if (ret_vi.hint(isel)) |_| .initOne(ret_vi) else ret_vi.parts(isel);
 5091                    while (ret_part_it.next()) |ret_part_vi| try ret_part_vi.liveOut(isel, ret_part_vi.hint(isel).?);
 5092                    const ptr_vi = try isel.use(un_op);
 5093                    const ptr_mat = try ptr_vi.matReg(isel);
 5094                    _ = try ret_vi.load(isel, .fromInterned(ptr_info.child), ptr_mat.ra, .{});
 5095                    try ptr_mat.finish(isel);
 5096                },
 5097                .value, .constant => unreachable,
 5098                .address => {},
 5099            };
 5100            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5101        },
 5102        .store, .store_safe, .atomic_store_unordered => {
 5103            const bin_op = air.data(air.inst_index).bin_op;
 5104            const ptr_ty = isel.air.typeOf(bin_op.lhs, ip);
 5105            const ptr_info = ptr_ty.ptrInfo(zcu);
 5106            if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed store", .{});
 5107            if (bin_op.rhs.toInterned()) |rhs_val| if (ip.isUndef(rhs_val))
 5108                break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5109
 5110            const src_vi = try isel.use(bin_op.rhs);
 5111            const size = src_vi.size(isel);
 5112            if (ZigType.fromInterned(ptr_info.child).zigTypeTag(zcu) != .@"union") switch (size) {
 5113                0 => unreachable,
 5114                1...Value.max_parts => {
 5115                    const ptr_vi = try isel.use(bin_op.lhs);
 5116                    const ptr_mat = try ptr_vi.matReg(isel);
 5117                    try src_vi.store(isel, isel.air.typeOf(bin_op.rhs, ip), ptr_mat.ra, .{
 5118                        .@"volatile" = ptr_info.flags.is_volatile,
 5119                    });
 5120                    try ptr_mat.finish(isel);
 5121
 5122                    break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5123                },
 5124                else => {},
 5125            };
 5126            try call.prepareReturn(isel);
 5127            try call.finishReturn(isel);
 5128
 5129            try call.prepareCallee(isel);
 5130            try isel.global_relocs.append(gpa, .{
 5131                .name = "memcpy",
 5132                .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 5133            });
 5134            try isel.emit(.bl(0));
 5135            try call.finishCallee(isel);
 5136
 5137            try call.prepareParams(isel);
 5138            const ptr_vi = try isel.use(bin_op.lhs);
 5139            try isel.movImmediate(.x2, size);
 5140            try call.paramAddress(isel, src_vi, .r1);
 5141            try call.paramLiveOut(isel, ptr_vi, .r0);
 5142            try call.finishParams(isel);
 5143
 5144            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5145        },
 5146        .unreach => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
 5147        .fptrunc, .fpext => {
 5148            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5149                defer dst_vi.value.deref(isel);
 5150
 5151                const ty_op = air.data(air.inst_index).ty_op;
 5152                const dst_ty = ty_op.ty.toType();
 5153                const dst_bits = dst_ty.floatBits(isel.target);
 5154                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 5155                const src_bits = src_ty.floatBits(isel.target);
 5156                assert(dst_bits != src_bits);
 5157                switch (@max(dst_bits, src_bits)) {
 5158                    else => unreachable,
 5159                    16, 32, 64 => {
 5160                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5161                        const src_vi = try isel.use(ty_op.operand);
 5162                        const src_mat = try src_vi.matReg(isel);
 5163                        try isel.emit(.fcvt(switch (dst_bits) {
 5164                            else => unreachable,
 5165                            16 => dst_ra.h(),
 5166                            32 => dst_ra.s(),
 5167                            64 => dst_ra.d(),
 5168                        }, switch (src_bits) {
 5169                            else => unreachable,
 5170                            16 => src_mat.ra.h(),
 5171                            32 => src_mat.ra.s(),
 5172                            64 => src_mat.ra.d(),
 5173                        }));
 5174                        try src_mat.finish(isel);
 5175                    },
 5176                    80, 128 => {
 5177                        try call.prepareReturn(isel);
 5178                        switch (dst_bits) {
 5179                            else => unreachable,
 5180                            16, 32, 64, 128 => try call.returnLiveIn(isel, dst_vi.value, .v0),
 5181                            80 => {
 5182                                var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8);
 5183                                const dst_hi16_vi = try dst_hi16_it.only(isel);
 5184                                try call.returnLiveIn(isel, dst_hi16_vi.?, .r1);
 5185                                var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 5186                                const dst_lo64_vi = try dst_lo64_it.only(isel);
 5187                                try call.returnLiveIn(isel, dst_lo64_vi.?, .r0);
 5188                            },
 5189                        }
 5190                        try call.finishReturn(isel);
 5191
 5192                        try call.prepareCallee(isel);
 5193                        try isel.global_relocs.append(gpa, .{
 5194                            .name = switch (dst_bits) {
 5195                                else => unreachable,
 5196                                16 => switch (src_bits) {
 5197                                    else => unreachable,
 5198                                    32 => "__truncsfhf2",
 5199                                    64 => "__truncdfhf2",
 5200                                    80 => "__truncxfhf2",
 5201                                    128 => "__trunctfhf2",
 5202                                },
 5203                                32 => switch (src_bits) {
 5204                                    else => unreachable,
 5205                                    16 => "__extendhfsf2",
 5206                                    64 => "__truncdfsf2",
 5207                                    80 => "__truncxfsf2",
 5208                                    128 => "__trunctfsf2",
 5209                                },
 5210                                64 => switch (src_bits) {
 5211                                    else => unreachable,
 5212                                    16 => "__extendhfdf2",
 5213                                    32 => "__extendsfdf2",
 5214                                    80 => "__truncxfdf2",
 5215                                    128 => "__trunctfdf2",
 5216                                },
 5217                                80 => switch (src_bits) {
 5218                                    else => unreachable,
 5219                                    16 => "__extendhfxf2",
 5220                                    32 => "__extendsfxf2",
 5221                                    64 => "__extenddfxf2",
 5222                                    128 => "__trunctfxf2",
 5223                                },
 5224                                128 => switch (src_bits) {
 5225                                    else => unreachable,
 5226                                    16 => "__extendhftf2",
 5227                                    32 => "__extendsftf2",
 5228                                    64 => "__extenddftf2",
 5229                                    80 => "__extendxftf2",
 5230                                },
 5231                            },
 5232                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 5233                        });
 5234                        try isel.emit(.bl(0));
 5235                        try call.finishCallee(isel);
 5236
 5237                        try call.prepareParams(isel);
 5238                        const src_vi = try isel.use(ty_op.operand);
 5239                        switch (src_bits) {
 5240                            else => unreachable,
 5241                            16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0),
 5242                            80 => {
 5243                                var src_hi16_it = src_vi.field(src_ty, 8, 8);
 5244                                const src_hi16_vi = try src_hi16_it.only(isel);
 5245                                try call.paramLiveOut(isel, src_hi16_vi.?, .r1);
 5246                                var src_lo64_it = src_vi.field(src_ty, 0, 8);
 5247                                const src_lo64_vi = try src_lo64_it.only(isel);
 5248                                try call.paramLiveOut(isel, src_lo64_vi.?, .r0);
 5249                            },
 5250                        }
 5251                        try call.finishParams(isel);
 5252                    },
 5253                }
 5254            }
 5255            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5256        },
 5257        .intcast => |air_tag| {
 5258            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5259                defer dst_vi.value.deref(isel);
 5260
 5261                const ty_op = air.data(air.inst_index).ty_op;
 5262                const dst_ty = ty_op.ty.toType();
 5263                const dst_int_info = dst_ty.intInfo(zcu);
 5264                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 5265                const src_int_info = src_ty.intInfo(zcu);
 5266                const can_be_negative = dst_int_info.signedness == .signed and
 5267                    src_int_info.signedness == .signed;
 5268                if ((dst_int_info.bits <= 8 and src_int_info.bits <= 8) or
 5269                    (dst_int_info.bits > 8 and dst_int_info.bits <= 16 and
 5270                        src_int_info.bits > 8 and src_int_info.bits <= 16) or
 5271                    (dst_int_info.bits > 16 and dst_int_info.bits <= 32 and
 5272                        src_int_info.bits > 16 and src_int_info.bits <= 32) or
 5273                    (dst_int_info.bits > 32 and dst_int_info.bits <= 64 and
 5274                        src_int_info.bits > 32 and src_int_info.bits <= 64) or
 5275                    (dst_int_info.bits > 64 and src_int_info.bits > 64 and
 5276                        (dst_int_info.bits - 1) / 128 == (src_int_info.bits - 1) / 128))
 5277                {
 5278                    try dst_vi.value.move(isel, ty_op.operand);
 5279                } else if (dst_int_info.bits <= 32 and src_int_info.bits <= 64) {
 5280                    const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5281                    const src_vi = try isel.use(ty_op.operand);
 5282                    const src_mat = try src_vi.matReg(isel);
 5283                    try isel.emit(.orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }));
 5284                    try src_mat.finish(isel);
 5285                } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 32) {
 5286                    const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5287                    const src_vi = try isel.use(ty_op.operand);
 5288                    const src_mat = try src_vi.matReg(isel);
 5289                    try isel.emit(if (can_be_negative) .sbfm(dst_ra.x(), src_mat.ra.x(), .{
 5290                        .N = .doubleword,
 5291                        .immr = 0,
 5292                        .imms = @intCast(src_int_info.bits - 1),
 5293                    }) else .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }));
 5294                    try src_mat.finish(isel);
 5295                } else if (dst_int_info.bits <= 32 and src_int_info.bits <= 128) {
 5296                    assert(src_int_info.bits > 64);
 5297                    const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5298                    const src_vi = try isel.use(ty_op.operand);
 5299
 5300                    var src_lo64_it = src_vi.field(src_ty, 0, 8);
 5301                    const src_lo64_vi = try src_lo64_it.only(isel);
 5302                    const src_lo64_mat = try src_lo64_vi.?.matReg(isel);
 5303                    try isel.emit(.orr(dst_ra.w(), .wzr, .{ .register = src_lo64_mat.ra.w() }));
 5304                    try src_lo64_mat.finish(isel);
 5305                } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 128) {
 5306                    assert(dst_int_info.bits > 32 and src_int_info.bits > 64);
 5307                    const src_vi = try isel.use(ty_op.operand);
 5308
 5309                    var src_lo64_it = src_vi.field(src_ty, 0, 8);
 5310                    const src_lo64_vi = try src_lo64_it.only(isel);
 5311                    try dst_vi.value.copy(isel, dst_ty, src_lo64_vi.?);
 5312                } else if (dst_int_info.bits <= 128 and src_int_info.bits <= 64) {
 5313                    assert(dst_int_info.bits > 64);
 5314                    const src_vi = try isel.use(ty_op.operand);
 5315
 5316                    var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 5317                    const dst_lo64_vi = try dst_lo64_it.only(isel);
 5318                    if (src_int_info.bits <= 32) unused_lo64: {
 5319                        const dst_lo64_ra = try dst_lo64_vi.?.defReg(isel) orelse break :unused_lo64;
 5320                        const src_mat = try src_vi.matReg(isel);
 5321                        try isel.emit(if (can_be_negative) .sbfm(dst_lo64_ra.x(), src_mat.ra.x(), .{
 5322                            .N = .doubleword,
 5323                            .immr = 0,
 5324                            .imms = @intCast(src_int_info.bits - 1),
 5325                        }) else .orr(dst_lo64_ra.w(), .wzr, .{ .register = src_mat.ra.w() }));
 5326                        try src_mat.finish(isel);
 5327                    } else try dst_lo64_vi.?.copy(isel, src_ty, src_vi);
 5328
 5329                    var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8);
 5330                    const dst_hi64_vi = try dst_hi64_it.only(isel);
 5331                    const dst_hi64_ra = try dst_hi64_vi.?.defReg(isel);
 5332                    if (dst_hi64_ra) |dst_ra| switch (can_be_negative) {
 5333                        false => try isel.emit(.orr(dst_ra.x(), .xzr, .{ .register = .xzr })),
 5334                        true => {
 5335                            const src_mat = try src_vi.matReg(isel);
 5336                            try isel.emit(.sbfm(dst_ra.x(), src_mat.ra.x(), .{
 5337                                .N = .doubleword,
 5338                                .immr = @intCast(src_int_info.bits - 1),
 5339                                .imms = @intCast(src_int_info.bits - 1),
 5340                            }));
 5341                            try src_mat.finish(isel);
 5342                        },
 5343                    };
 5344                } else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 5345            }
 5346            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5347        },
 5348        .intcast_safe => |air_tag| {
 5349            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5350                defer dst_vi.value.deref(isel);
 5351
 5352                const ty_op = air.data(air.inst_index).ty_op;
 5353                const dst_ty = ty_op.ty.toType();
 5354                const dst_int_info = dst_ty.intInfo(zcu);
 5355                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 5356                const src_int_info = src_ty.intInfo(zcu);
 5357                const can_be_negative = dst_int_info.signedness == .signed and
 5358                    src_int_info.signedness == .signed;
 5359                const panic_id: Zcu.SimplePanicId = panic_id: switch (dst_ty.zigTypeTag(zcu)) {
 5360                    else => unreachable,
 5361                    .int => .integer_out_of_bounds,
 5362                    .@"enum" => {
 5363                        if (!dst_ty.isNonexhaustiveEnum(zcu)) {
 5364                            return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 5365                        }
 5366                        break :panic_id .invalid_enum_value;
 5367                    },
 5368                };
 5369                if (dst_ty.toIntern() == src_ty.toIntern()) {
 5370                    try dst_vi.value.move(isel, ty_op.operand);
 5371                } else if (dst_int_info.bits <= 64 and src_int_info.bits <= 64) {
 5372                    const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5373                    const src_vi = try isel.use(ty_op.operand);
 5374                    const dst_active_bits = dst_int_info.bits - @intFromBool(dst_int_info.signedness == .signed);
 5375                    const src_active_bits = src_int_info.bits - @intFromBool(src_int_info.signedness == .signed);
 5376                    if ((dst_int_info.signedness != .unsigned or src_int_info.signedness != .signed) and dst_active_bits >= src_active_bits) {
 5377                        const src_mat = try src_vi.matReg(isel);
 5378                        try isel.emit(if (can_be_negative and dst_active_bits > 32 and src_active_bits <= 32)
 5379                            .sbfm(dst_ra.x(), src_mat.ra.x(), .{
 5380                                .N = .doubleword,
 5381                                .immr = 0,
 5382                                .imms = @intCast(src_int_info.bits - 1),
 5383                            })
 5384                        else switch (src_int_info.bits) {
 5385                            else => unreachable,
 5386                            1...32 => .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }),
 5387                            33...64 => .orr(dst_ra.x(), .xzr, .{ .register = src_mat.ra.x() }),
 5388                        });
 5389                        try src_mat.finish(isel);
 5390                    } else {
 5391                        const skip_label = isel.instructions.items.len;
 5392                        try isel.emitPanic(panic_id);
 5393                        try isel.emit(.@"b."(
 5394                            .eq,
 5395                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 5396                        ));
 5397                        if (can_be_negative) {
 5398                            const src_mat = src_mat: {
 5399                                const dst_lock = isel.lockReg(dst_ra);
 5400                                defer dst_lock.unlock(isel);
 5401                                break :src_mat try src_vi.matReg(isel);
 5402                            };
 5403                            try isel.emit(switch (src_int_info.bits) {
 5404                                else => unreachable,
 5405                                1...32 => .subs(.wzr, dst_ra.w(), .{ .register = src_mat.ra.w() }),
 5406                                33...64 => .subs(.xzr, dst_ra.x(), .{ .register = src_mat.ra.x() }),
 5407                            });
 5408                            try isel.emit(switch (@max(dst_int_info.bits, src_int_info.bits)) {
 5409                                else => unreachable,
 5410                                1...32 => .sbfm(dst_ra.w(), src_mat.ra.w(), .{
 5411                                    .N = .word,
 5412                                    .immr = 0,
 5413                                    .imms = @intCast(dst_int_info.bits - 1),
 5414                                }),
 5415                                33...64 => .sbfm(dst_ra.x(), src_mat.ra.x(), .{
 5416                                    .N = .doubleword,
 5417                                    .immr = 0,
 5418                                    .imms = @intCast(dst_int_info.bits - 1),
 5419                                }),
 5420                            });
 5421                            try src_mat.finish(isel);
 5422                        } else {
 5423                            const src_mat = try src_vi.matReg(isel);
 5424                            try isel.emit(switch (@min(dst_int_info.bits, src_int_info.bits)) {
 5425                                else => unreachable,
 5426                                1...32 => .orr(dst_ra.w(), .wzr, .{ .register = src_mat.ra.w() }),
 5427                                33...64 => .orr(dst_ra.x(), .xzr, .{ .register = src_mat.ra.x() }),
 5428                            });
 5429                            const active_bits = @min(dst_active_bits, src_active_bits);
 5430                            try isel.emit(switch (src_int_info.bits) {
 5431                                else => unreachable,
 5432                                1...32 => .ands(.wzr, src_mat.ra.w(), .{ .immediate = .{
 5433                                    .N = .word,
 5434                                    .immr = @intCast(32 - active_bits),
 5435                                    .imms = @intCast(32 - active_bits - 1),
 5436                                } }),
 5437                                33...64 => .ands(.xzr, src_mat.ra.x(), .{ .immediate = .{
 5438                                    .N = .doubleword,
 5439                                    .immr = @intCast(64 - active_bits),
 5440                                    .imms = @intCast(64 - active_bits - 1),
 5441                                } }),
 5442                            });
 5443                            try src_mat.finish(isel);
 5444                        }
 5445                    }
 5446                } else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 5447            }
 5448            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5449        },
 5450        .trunc => |air_tag| {
 5451            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5452                defer dst_vi.value.deref(isel);
 5453
 5454                const ty_op = air.data(air.inst_index).ty_op;
 5455                const dst_ty = ty_op.ty.toType();
 5456                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 5457                if (!dst_ty.isAbiInt(zcu) or !src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 5458                const dst_int_info = dst_ty.intInfo(zcu);
 5459                switch (dst_int_info.bits) {
 5460                    0 => unreachable,
 5461                    1...64 => |dst_bits| {
 5462                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5463                        const src_vi = try isel.use(ty_op.operand);
 5464                        var src_part_it = src_vi.field(src_ty, 0, @min(src_vi.size(isel), 8));
 5465                        const src_part_vi = try src_part_it.only(isel);
 5466                        const src_part_mat = try src_part_vi.?.matReg(isel);
 5467                        try isel.emit(switch (dst_bits) {
 5468                            else => unreachable,
 5469                            1...31 => |bits| switch (dst_int_info.signedness) {
 5470                                .signed => .sbfm(dst_ra.w(), src_part_mat.ra.w(), .{
 5471                                    .N = .word,
 5472                                    .immr = 0,
 5473                                    .imms = @intCast(bits - 1),
 5474                                }),
 5475                                .unsigned => .ubfm(dst_ra.w(), src_part_mat.ra.w(), .{
 5476                                    .N = .word,
 5477                                    .immr = 0,
 5478                                    .imms = @intCast(bits - 1),
 5479                                }),
 5480                            },
 5481                            32 => .orr(dst_ra.w(), .wzr, .{ .register = src_part_mat.ra.w() }),
 5482                            33...63 => |bits| switch (dst_int_info.signedness) {
 5483                                .signed => .sbfm(dst_ra.x(), src_part_mat.ra.x(), .{
 5484                                    .N = .doubleword,
 5485                                    .immr = 0,
 5486                                    .imms = @intCast(bits - 1),
 5487                                }),
 5488                                .unsigned => .ubfm(dst_ra.x(), src_part_mat.ra.x(), .{
 5489                                    .N = .doubleword,
 5490                                    .immr = 0,
 5491                                    .imms = @intCast(bits - 1),
 5492                                }),
 5493                            },
 5494                            64 => .orr(dst_ra.x(), .xzr, .{ .register = src_part_mat.ra.x() }),
 5495                        });
 5496                        try src_part_mat.finish(isel);
 5497                    },
 5498                    65...128 => |dst_bits| switch (src_ty.intInfo(zcu).bits) {
 5499                        0 => unreachable,
 5500                        65...128 => {
 5501                            const src_vi = try isel.use(ty_op.operand);
 5502                            var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8);
 5503                            const dst_hi64_vi = try dst_hi64_it.only(isel);
 5504                            if (try dst_hi64_vi.?.defReg(isel)) |dst_hi64_ra| {
 5505                                var src_hi64_it = src_vi.field(src_ty, 8, 8);
 5506                                const src_hi64_vi = try src_hi64_it.only(isel);
 5507                                const src_hi64_mat = try src_hi64_vi.?.matReg(isel);
 5508                                try isel.emit(switch (dst_int_info.signedness) {
 5509                                    .signed => .sbfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{
 5510                                        .N = .doubleword,
 5511                                        .immr = 0,
 5512                                        .imms = @intCast(dst_bits - 64 - 1),
 5513                                    }),
 5514                                    .unsigned => .ubfm(dst_hi64_ra.x(), src_hi64_mat.ra.x(), .{
 5515                                        .N = .doubleword,
 5516                                        .immr = 0,
 5517                                        .imms = @intCast(dst_bits - 64 - 1),
 5518                                    }),
 5519                                });
 5520                                try src_hi64_mat.finish(isel);
 5521                            }
 5522                            var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 5523                            const dst_lo64_vi = try dst_lo64_it.only(isel);
 5524                            if (try dst_lo64_vi.?.defReg(isel)) |dst_lo64_ra| {
 5525                                var src_lo64_it = src_vi.field(src_ty, 0, 8);
 5526                                const src_lo64_vi = try src_lo64_it.only(isel);
 5527                                try src_lo64_vi.?.liveOut(isel, dst_lo64_ra);
 5528                            }
 5529                        },
 5530                        else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
 5531                    },
 5532                    else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
 5533                }
 5534            }
 5535            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5536        },
 5537        .optional_payload => {
 5538            if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| unused: {
 5539                defer payload_vi.value.deref(isel);
 5540
 5541                const ty_op = air.data(air.inst_index).ty_op;
 5542                const opt_ty = isel.air.typeOf(ty_op.operand, ip);
 5543                if (opt_ty.optionalReprIsPayload(zcu)) {
 5544                    try payload_vi.value.move(isel, ty_op.operand);
 5545                    break :unused;
 5546                }
 5547
 5548                const opt_vi = try isel.use(ty_op.operand);
 5549                var payload_part_it = opt_vi.field(opt_ty, 0, payload_vi.value.size(isel));
 5550                const payload_part_vi = try payload_part_it.only(isel);
 5551                try payload_vi.value.copy(isel, ty_op.ty.toType(), payload_part_vi.?);
 5552            }
 5553            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5554        },
 5555        .optional_payload_ptr => {
 5556            if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
 5557                defer payload_ptr_vi.value.deref(isel);
 5558                const ty_op = air.data(air.inst_index).ty_op;
 5559                try payload_ptr_vi.value.move(isel, ty_op.operand);
 5560            }
 5561            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5562        },
 5563        .optional_payload_ptr_set => {
 5564            if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
 5565                defer payload_ptr_vi.value.deref(isel);
 5566                const ty_op = air.data(air.inst_index).ty_op;
 5567                const opt_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
 5568                if (!opt_ty.optionalReprIsPayload(zcu)) {
 5569                    const opt_ptr_vi = try isel.use(ty_op.operand);
 5570                    const opt_ptr_mat = try opt_ptr_vi.matReg(isel);
 5571                    const has_value_ra = try isel.allocIntReg();
 5572                    defer isel.freeReg(has_value_ra);
 5573                    try isel.storeReg(
 5574                        has_value_ra,
 5575                        1,
 5576                        opt_ptr_mat.ra,
 5577                        opt_ty.optionalChild(zcu).abiSize(zcu),
 5578                    );
 5579                    try opt_ptr_mat.finish(isel);
 5580                    try isel.emit(.movz(has_value_ra.w(), 1, .{ .lsl = .@"0" }));
 5581                }
 5582                try payload_ptr_vi.value.move(isel, ty_op.operand);
 5583            }
 5584            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5585        },
 5586        .wrap_optional => {
 5587            if (isel.live_values.fetchRemove(air.inst_index)) |opt_vi| unused: {
 5588                defer opt_vi.value.deref(isel);
 5589
 5590                const ty_op = air.data(air.inst_index).ty_op;
 5591                if (ty_op.ty.toType().optionalReprIsPayload(zcu)) {
 5592                    try opt_vi.value.move(isel, ty_op.operand);
 5593                    break :unused;
 5594                }
 5595
 5596                const payload_size = isel.air.typeOf(ty_op.operand, ip).abiSize(zcu);
 5597                var payload_part_it = opt_vi.value.field(ty_op.ty.toType(), 0, payload_size);
 5598                const payload_part_vi = try payload_part_it.only(isel);
 5599                try payload_part_vi.?.move(isel, ty_op.operand);
 5600                var has_value_part_it = opt_vi.value.field(ty_op.ty.toType(), payload_size, 1);
 5601                const has_value_part_vi = try has_value_part_it.only(isel);
 5602                const has_value_part_ra = try has_value_part_vi.?.defReg(isel) orelse break :unused;
 5603                try isel.emit(.movz(has_value_part_ra.w(), 1, .{ .lsl = .@"0" }));
 5604            }
 5605            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5606        },
 5607        .unwrap_errunion_payload => {
 5608            if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| {
 5609                defer payload_vi.value.deref(isel);
 5610
 5611                const ty_op = air.data(air.inst_index).ty_op;
 5612                const error_union_ty = isel.air.typeOf(ty_op.operand, ip);
 5613
 5614                const error_union_vi = try isel.use(ty_op.operand);
 5615                var payload_part_it = error_union_vi.field(
 5616                    error_union_ty,
 5617                    codegen.errUnionPayloadOffset(ty_op.ty.toType(), zcu),
 5618                    payload_vi.value.size(isel),
 5619                );
 5620                const payload_part_vi = try payload_part_it.only(isel);
 5621                try payload_vi.value.copy(isel, ty_op.ty.toType(), payload_part_vi.?);
 5622            }
 5623            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5624        },
 5625        .unwrap_errunion_err => {
 5626            if (isel.live_values.fetchRemove(air.inst_index)) |error_set_vi| {
 5627                defer error_set_vi.value.deref(isel);
 5628
 5629                const ty_op = air.data(air.inst_index).ty_op;
 5630                const error_union_ty = isel.air.typeOf(ty_op.operand, ip);
 5631
 5632                const error_union_vi = try isel.use(ty_op.operand);
 5633                var error_set_part_it = error_union_vi.field(
 5634                    error_union_ty,
 5635                    codegen.errUnionErrorOffset(error_union_ty.errorUnionPayload(zcu), zcu),
 5636                    error_set_vi.value.size(isel),
 5637                );
 5638                const error_set_part_vi = try error_set_part_it.only(isel);
 5639                try error_set_vi.value.copy(isel, ty_op.ty.toType(), error_set_part_vi.?);
 5640            }
 5641            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5642        },
 5643        .unwrap_errunion_payload_ptr => {
 5644            if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
 5645                defer payload_ptr_vi.value.deref(isel);
 5646                const ty_op = air.data(air.inst_index).ty_op;
 5647                switch (codegen.errUnionPayloadOffset(ty_op.ty.toType().childType(zcu), zcu)) {
 5648                    0 => try payload_ptr_vi.value.move(isel, ty_op.operand),
 5649                    else => |payload_offset| {
 5650                        const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
 5651                        const error_union_ptr_vi = try isel.use(ty_op.operand);
 5652                        const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
 5653                        const lo12: u12 = @truncate(payload_offset >> 0);
 5654                        const hi12: u12 = @intCast(payload_offset >> 12);
 5655                        if (hi12 > 0) try isel.emit(.add(
 5656                            payload_ptr_ra.x(),
 5657                            if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
 5658                            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 5659                        ));
 5660                        if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
 5661                        try error_union_ptr_mat.finish(isel);
 5662                    },
 5663                }
 5664            }
 5665            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5666        },
 5667        .unwrap_errunion_err_ptr => {
 5668            if (isel.live_values.fetchRemove(air.inst_index)) |error_vi| {
 5669                defer error_vi.value.deref(isel);
 5670                const ty_op = air.data(air.inst_index).ty_op;
 5671                const error_union_ptr_ty = isel.air.typeOf(ty_op.operand, ip);
 5672                const error_union_ptr_info = error_union_ptr_ty.ptrInfo(zcu);
 5673                const error_union_ptr_vi = try isel.use(ty_op.operand);
 5674                const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
 5675                _ = try error_vi.value.load(isel, ty_op.ty.toType(), error_union_ptr_mat.ra, .{
 5676                    .offset = codegen.errUnionErrorOffset(
 5677                        ZigType.fromInterned(error_union_ptr_info.child).errorUnionPayload(zcu),
 5678                        zcu,
 5679                    ),
 5680                    .@"volatile" = error_union_ptr_info.flags.is_volatile,
 5681                });
 5682                try error_union_ptr_mat.finish(isel);
 5683            }
 5684            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5685        },
 5686        .errunion_payload_ptr_set => {
 5687            if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
 5688                defer payload_ptr_vi.value.deref(isel);
 5689                const ty_op = air.data(air.inst_index).ty_op;
 5690                const payload_ty = ty_op.ty.toType().childType(zcu);
 5691                const error_union_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
 5692                const error_set_size = error_union_ty.errorUnionSet(zcu).abiSize(zcu);
 5693                const error_union_ptr_vi = try isel.use(ty_op.operand);
 5694                const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
 5695                if (error_set_size > 0) try isel.storeReg(
 5696                    .zr,
 5697                    error_set_size,
 5698                    error_union_ptr_mat.ra,
 5699                    codegen.errUnionErrorOffset(payload_ty, zcu),
 5700                );
 5701                switch (codegen.errUnionPayloadOffset(payload_ty, zcu)) {
 5702                    0 => {
 5703                        try error_union_ptr_mat.finish(isel);
 5704                        try payload_ptr_vi.value.move(isel, ty_op.operand);
 5705                    },
 5706                    else => |payload_offset| {
 5707                        const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
 5708                        const lo12: u12 = @truncate(payload_offset >> 0);
 5709                        const hi12: u12 = @intCast(payload_offset >> 12);
 5710                        if (hi12 > 0) try isel.emit(.add(
 5711                            payload_ptr_ra.x(),
 5712                            if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
 5713                            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 5714                        ));
 5715                        if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
 5716                        try error_union_ptr_mat.finish(isel);
 5717                    },
 5718                }
 5719            }
 5720            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5721        },
 5722        .wrap_errunion_payload => {
 5723            if (isel.live_values.fetchRemove(air.inst_index)) |error_union_vi| {
 5724                defer error_union_vi.value.deref(isel);
 5725
 5726                const ty_op = air.data(air.inst_index).ty_op;
 5727                const error_union_ty = ty_op.ty.toType();
 5728                const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
 5729                const error_set_ty: ZigType = .fromInterned(error_union_info.error_set_type);
 5730                const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
 5731                const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
 5732                const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
 5733                const error_set_size = error_set_ty.abiSize(zcu);
 5734                const payload_size = payload_ty.abiSize(zcu);
 5735
 5736                var payload_part_it = error_union_vi.value.field(error_union_ty, payload_offset, payload_size);
 5737                const payload_part_vi = try payload_part_it.only(isel);
 5738                try payload_part_vi.?.move(isel, ty_op.operand);
 5739                var error_set_part_it = error_union_vi.value.field(error_union_ty, error_set_offset, error_set_size);
 5740                const error_set_part_vi = try error_set_part_it.only(isel);
 5741                if (try error_set_part_vi.?.defReg(isel)) |error_set_part_ra| try isel.emit(switch (error_set_size) {
 5742                    else => unreachable,
 5743                    1...4 => .orr(error_set_part_ra.w(), .wzr, .{ .register = .wzr }),
 5744                    5...8 => .orr(error_set_part_ra.x(), .xzr, .{ .register = .xzr }),
 5745                });
 5746            }
 5747            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5748        },
 5749        .wrap_errunion_err => {
 5750            if (isel.live_values.fetchRemove(air.inst_index)) |error_union_vi| {
 5751                defer error_union_vi.value.deref(isel);
 5752
 5753                const ty_op = air.data(air.inst_index).ty_op;
 5754                const error_union_ty = ty_op.ty.toType();
 5755                const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
 5756                const error_set_ty: ZigType = .fromInterned(error_union_info.error_set_type);
 5757                const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
 5758                const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
 5759                const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
 5760                const error_set_size = error_set_ty.abiSize(zcu);
 5761                const payload_size = payload_ty.abiSize(zcu);
 5762
 5763                var error_set_part_it = error_union_vi.value.field(error_union_ty, error_set_offset, error_set_size);
 5764                const error_set_part_vi = try error_set_part_it.only(isel);
 5765                try error_set_part_vi.?.move(isel, ty_op.operand);
 5766                if (payload_size > 0) {
 5767                    var payload_part_it = error_union_vi.value.field(error_union_ty, payload_offset, payload_size);
 5768                    const payload_part_vi = try payload_part_it.only(isel);
 5769                    try payload_part_vi.?.defUndef(isel, payload_ty, .{});
 5770                }
 5771            }
 5772            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5773        },
 5774        .struct_field_ptr => {
 5775            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5776                defer dst_vi.value.deref(isel);
 5777                const ty_pl = air.data(air.inst_index).ty_pl;
 5778                const extra = isel.air.extraData(Air.StructField, ty_pl.payload).data;
 5779                switch (codegen.fieldOffset(
 5780                    isel.air.typeOf(extra.struct_operand, ip),
 5781                    ty_pl.ty.toType(),
 5782                    extra.field_index,
 5783                    zcu,
 5784                )) {
 5785                    0 => try dst_vi.value.move(isel, extra.struct_operand),
 5786                    else => |field_offset| {
 5787                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5788                        const src_vi = try isel.use(extra.struct_operand);
 5789                        const src_mat = try src_vi.matReg(isel);
 5790                        const lo12: u12 = @truncate(field_offset >> 0);
 5791                        const hi12: u12 = @intCast(field_offset >> 12);
 5792                        if (hi12 > 0) try isel.emit(.add(
 5793                            dst_ra.x(),
 5794                            if (lo12 > 0) dst_ra.x() else src_mat.ra.x(),
 5795                            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 5796                        ));
 5797                        if (lo12 > 0) try isel.emit(.add(dst_ra.x(), src_mat.ra.x(), .{ .immediate = lo12 }));
 5798                        try src_mat.finish(isel);
 5799                    },
 5800                }
 5801            }
 5802            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5803        },
 5804        .struct_field_ptr_index_0,
 5805        .struct_field_ptr_index_1,
 5806        .struct_field_ptr_index_2,
 5807        .struct_field_ptr_index_3,
 5808        => |air_tag| {
 5809            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5810                defer dst_vi.value.deref(isel);
 5811                const ty_op = air.data(air.inst_index).ty_op;
 5812                switch (codegen.fieldOffset(
 5813                    isel.air.typeOf(ty_op.operand, ip),
 5814                    ty_op.ty.toType(),
 5815                    switch (air_tag) {
 5816                        else => unreachable,
 5817                        .struct_field_ptr_index_0 => 0,
 5818                        .struct_field_ptr_index_1 => 1,
 5819                        .struct_field_ptr_index_2 => 2,
 5820                        .struct_field_ptr_index_3 => 3,
 5821                    },
 5822                    zcu,
 5823                )) {
 5824                    0 => try dst_vi.value.move(isel, ty_op.operand),
 5825                    else => |field_offset| {
 5826                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5827                        const src_vi = try isel.use(ty_op.operand);
 5828                        const src_mat = try src_vi.matReg(isel);
 5829                        const lo12: u12 = @truncate(field_offset >> 0);
 5830                        const hi12: u12 = @intCast(field_offset >> 12);
 5831                        if (hi12 > 0) try isel.emit(.add(
 5832                            dst_ra.x(),
 5833                            if (lo12 > 0) dst_ra.x() else src_mat.ra.x(),
 5834                            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 5835                        ));
 5836                        if (lo12 > 0) try isel.emit(.add(dst_ra.x(), src_mat.ra.x(), .{ .immediate = lo12 }));
 5837                        try src_mat.finish(isel);
 5838                    },
 5839                }
 5840            }
 5841            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5842        },
 5843        .struct_field_val => {
 5844            if (isel.live_values.fetchRemove(air.inst_index)) |field_vi| unused: {
 5845                defer field_vi.value.deref(isel);
 5846
 5847                const ty_pl = air.data(air.inst_index).ty_pl;
 5848                const extra = isel.air.extraData(Air.StructField, ty_pl.payload).data;
 5849                const agg_ty = isel.air.typeOf(extra.struct_operand, ip);
 5850                const field_ty = ty_pl.ty.toType();
 5851                const field_bit_offset, const field_bit_size, const is_packed = switch (agg_ty.containerLayout(zcu)) {
 5852                    .auto, .@"extern" => .{
 5853                        8 * agg_ty.structFieldOffset(extra.field_index, zcu),
 5854                        8 * field_ty.abiSize(zcu),
 5855                        false,
 5856                    },
 5857                    .@"packed" => .{
 5858                        if (zcu.typeToPackedStruct(agg_ty)) |loaded_struct|
 5859                            zcu.structPackedFieldBitOffset(loaded_struct, extra.field_index)
 5860                        else
 5861                            0,
 5862                        field_ty.bitSize(zcu),
 5863                        true,
 5864                    },
 5865                };
 5866                if (is_packed) return isel.fail("packed field of {f}", .{
 5867                    isel.fmtType(agg_ty),
 5868                });
 5869
 5870                const agg_vi = try isel.use(extra.struct_operand);
 5871                switch (agg_ty.zigTypeTag(zcu)) {
 5872                    else => unreachable,
 5873                    .@"struct" => {
 5874                        var agg_part_it = agg_vi.field(agg_ty, @divExact(field_bit_offset, 8), @divExact(field_bit_size, 8));
 5875                        while (try agg_part_it.next(isel)) |agg_part| {
 5876                            var field_part_it = field_vi.value.field(ty_pl.ty.toType(), agg_part.offset, agg_part.vi.size(isel));
 5877                            const field_part_vi = try field_part_it.only(isel);
 5878                            if (field_part_vi.? == agg_part.vi) continue;
 5879                            var field_subpart_it = field_part_vi.?.parts(isel);
 5880                            const field_part_offset = if (field_subpart_it.only()) |field_subpart_vi|
 5881                                field_subpart_vi.get(isel).offset_from_parent
 5882                            else
 5883                                0;
 5884                            while (field_subpart_it.next()) |field_subpart_vi| {
 5885                                const field_subpart_ra = try field_subpart_vi.defReg(isel) orelse continue;
 5886                                const field_subpart_offset, const field_subpart_size = field_subpart_vi.position(isel);
 5887                                var agg_subpart_it = agg_part.vi.field(
 5888                                    field_ty,
 5889                                    agg_part.offset + field_subpart_offset - field_part_offset,
 5890                                    field_subpart_size,
 5891                                );
 5892                                const agg_subpart_vi = try agg_subpart_it.only(isel);
 5893                                try agg_subpart_vi.?.liveOut(isel, field_subpart_ra);
 5894                            }
 5895                        }
 5896                    },
 5897                    .@"union" => {
 5898                        try field_vi.value.defAddr(isel, field_ty, .{}) orelse break :unused;
 5899
 5900                        try call.prepareReturn(isel);
 5901                        try call.finishReturn(isel);
 5902
 5903                        try call.prepareCallee(isel);
 5904                        try isel.global_relocs.append(gpa, .{
 5905                            .name = "memcpy",
 5906                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 5907                        });
 5908                        try isel.emit(.bl(0));
 5909                        try call.finishCallee(isel);
 5910
 5911                        try call.prepareParams(isel);
 5912                        const union_layout = agg_ty.unionGetLayout(zcu);
 5913                        var payload_it = agg_vi.field(agg_ty, union_layout.payloadOffset(), union_layout.payload_size);
 5914                        const payload_vi = try payload_it.only(isel);
 5915                        try isel.movImmediate(.x2, field_vi.value.size(isel));
 5916                        try call.paramAddress(isel, payload_vi.?, .r1);
 5917                        try call.paramAddress(isel, field_vi.value, .r0);
 5918                        try call.finishParams(isel);
 5919                    },
 5920                }
 5921            }
 5922            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5923        },
 5924        .set_union_tag => {
 5925            const bin_op = air.data(air.inst_index).bin_op;
 5926            const union_ty = isel.air.typeOf(bin_op.lhs, ip).childType(zcu);
 5927            const union_layout = union_ty.unionGetLayout(zcu);
 5928            const tag_vi = try isel.use(bin_op.rhs);
 5929            const union_ptr_vi = try isel.use(bin_op.lhs);
 5930            const union_ptr_mat = try union_ptr_vi.matReg(isel);
 5931            try tag_vi.store(isel, isel.air.typeOf(bin_op.rhs, ip), union_ptr_mat.ra, .{
 5932                .offset = union_layout.tagOffset(),
 5933            });
 5934            try union_ptr_mat.finish(isel);
 5935            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5936        },
 5937        .get_union_tag => {
 5938            if (isel.live_values.fetchRemove(air.inst_index)) |tag_vi| {
 5939                defer tag_vi.value.deref(isel);
 5940                const ty_op = air.data(air.inst_index).ty_op;
 5941                const union_ty = isel.air.typeOf(ty_op.operand, ip);
 5942                const union_layout = union_ty.unionGetLayout(zcu);
 5943                const union_vi = try isel.use(ty_op.operand);
 5944                var tag_part_it = union_vi.field(union_ty, union_layout.tagOffset(), union_layout.tag_size);
 5945                const tag_part_vi = try tag_part_it.only(isel);
 5946                try tag_vi.value.copy(isel, ty_op.ty.toType(), tag_part_vi.?);
 5947            }
 5948            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5949        },
 5950        .slice => {
 5951            if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {
 5952                defer slice_vi.value.deref(isel);
 5953                const ty_pl = air.data(air.inst_index).ty_pl;
 5954                const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
 5955                var ptr_part_it = slice_vi.value.field(ty_pl.ty.toType(), 0, 8);
 5956                const ptr_part_vi = try ptr_part_it.only(isel);
 5957                try ptr_part_vi.?.move(isel, bin_op.lhs);
 5958                var len_part_it = slice_vi.value.field(ty_pl.ty.toType(), 8, 8);
 5959                const len_part_vi = try len_part_it.only(isel);
 5960                try len_part_vi.?.move(isel, bin_op.rhs);
 5961            }
 5962            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5963        },
 5964        .slice_len => {
 5965            if (isel.live_values.fetchRemove(air.inst_index)) |len_vi| {
 5966                defer len_vi.value.deref(isel);
 5967                const ty_op = air.data(air.inst_index).ty_op;
 5968                const slice_vi = try isel.use(ty_op.operand);
 5969                var len_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 8, 8);
 5970                const len_part_vi = try len_part_it.only(isel);
 5971                try len_vi.value.copy(isel, ty_op.ty.toType(), len_part_vi.?);
 5972            }
 5973            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5974        },
 5975        .slice_ptr => {
 5976            if (isel.live_values.fetchRemove(air.inst_index)) |ptr_vi| {
 5977                defer ptr_vi.value.deref(isel);
 5978                const ty_op = air.data(air.inst_index).ty_op;
 5979                const slice_vi = try isel.use(ty_op.operand);
 5980                var ptr_part_it = slice_vi.field(isel.air.typeOf(ty_op.operand, ip), 0, 8);
 5981                const ptr_part_vi = try ptr_part_it.only(isel);
 5982                try ptr_vi.value.copy(isel, ty_op.ty.toType(), ptr_part_vi.?);
 5983            }
 5984            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5985        },
 5986        .ptr_slice_len_ptr => {
 5987            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 5988                defer dst_vi.value.deref(isel);
 5989                const ty_op = air.data(air.inst_index).ty_op;
 5990                const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 5991                const src_vi = try isel.use(ty_op.operand);
 5992                const src_mat = try src_vi.matReg(isel);
 5993                try isel.emit(.add(dst_ra.x(), src_mat.ra.x(), .{ .immediate = 8 }));
 5994                try src_mat.finish(isel);
 5995            }
 5996            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 5997        },
 5998        .ptr_slice_ptr_ptr => {
 5999            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| {
 6000                defer dst_vi.value.deref(isel);
 6001                const ty_op = air.data(air.inst_index).ty_op;
 6002                try dst_vi.value.move(isel, ty_op.operand);
 6003            }
 6004            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6005        },
 6006        .array_elem_val => {
 6007            if (isel.live_values.fetchRemove(air.inst_index)) |elem_vi| unused: {
 6008                defer elem_vi.value.deref(isel);
 6009
 6010                const bin_op = air.data(air.inst_index).bin_op;
 6011                const array_ty = isel.air.typeOf(bin_op.lhs, ip);
 6012                const elem_ty = array_ty.childType(zcu);
 6013                const elem_size = elem_ty.abiSize(zcu);
 6014                if (elem_size <= 16 and array_ty.arrayLenIncludingSentinel(zcu) <= Value.max_parts) if (bin_op.rhs.toInterned()) |index_val| {
 6015                    const elem_offset = elem_size * Constant.fromInterned(index_val).toUnsignedInt(zcu);
 6016                    const array_vi = try isel.use(bin_op.lhs);
 6017                    var elem_part_it = array_vi.field(array_ty, elem_offset, elem_size);
 6018                    const elem_part_vi = try elem_part_it.only(isel);
 6019                    try elem_vi.value.copy(isel, elem_ty, elem_part_vi.?);
 6020                    break :unused;
 6021                };
 6022                switch (elem_size) {
 6023                    0 => unreachable,
 6024                    1, 2, 4, 8 => {
 6025                        const elem_ra = try elem_vi.value.defReg(isel) orelse break :unused;
 6026                        const array_ptr_ra = try isel.allocIntReg();
 6027                        defer isel.freeReg(array_ptr_ra);
 6028                        const index_vi = try isel.use(bin_op.rhs);
 6029                        const index_mat = try index_vi.matReg(isel);
 6030                        try isel.emit(switch (elem_size) {
 6031                            else => unreachable,
 6032                            1 => if (elem_vi.value.isVector(isel)) .ldr(elem_ra.b(), .{ .extended_register = .{
 6033                                .base = array_ptr_ra.x(),
 6034                                .index = index_mat.ra.x(),
 6035                                .extend = .{ .lsl = 0 },
 6036                            } }) else switch (elem_vi.value.signedness(isel)) {
 6037                                .signed => .ldrsb(elem_ra.w(), .{ .extended_register = .{
 6038                                    .base = array_ptr_ra.x(),
 6039                                    .index = index_mat.ra.x(),
 6040                                    .extend = .{ .lsl = 0 },
 6041                                } }),
 6042                                .unsigned => .ldrb(elem_ra.w(), .{ .extended_register = .{
 6043                                    .base = array_ptr_ra.x(),
 6044                                    .index = index_mat.ra.x(),
 6045                                    .extend = .{ .lsl = 0 },
 6046                                } }),
 6047                            },
 6048                            2 => if (elem_vi.value.isVector(isel)) .ldr(elem_ra.h(), .{ .extended_register = .{
 6049                                .base = array_ptr_ra.x(),
 6050                                .index = index_mat.ra.x(),
 6051                                .extend = .{ .lsl = 1 },
 6052                            } }) else switch (elem_vi.value.signedness(isel)) {
 6053                                .signed => .ldrsh(elem_ra.w(), .{ .extended_register = .{
 6054                                    .base = array_ptr_ra.x(),
 6055                                    .index = index_mat.ra.x(),
 6056                                    .extend = .{ .lsl = 1 },
 6057                                } }),
 6058                                .unsigned => .ldrh(elem_ra.w(), .{ .extended_register = .{
 6059                                    .base = array_ptr_ra.x(),
 6060                                    .index = index_mat.ra.x(),
 6061                                    .extend = .{ .lsl = 1 },
 6062                                } }),
 6063                            },
 6064                            4 => .ldr(if (elem_vi.value.isVector(isel)) elem_ra.s() else elem_ra.w(), .{ .extended_register = .{
 6065                                .base = array_ptr_ra.x(),
 6066                                .index = index_mat.ra.x(),
 6067                                .extend = .{ .lsl = 2 },
 6068                            } }),
 6069                            8 => .ldr(if (elem_vi.value.isVector(isel)) elem_ra.d() else elem_ra.x(), .{ .extended_register = .{
 6070                                .base = array_ptr_ra.x(),
 6071                                .index = index_mat.ra.x(),
 6072                                .extend = .{ .lsl = 3 },
 6073                            } }),
 6074                            16 => .ldr(elem_ra.q(), .{ .extended_register = .{
 6075                                .base = array_ptr_ra.x(),
 6076                                .index = index_mat.ra.x(),
 6077                                .extend = .{ .lsl = 4 },
 6078                            } }),
 6079                        });
 6080                        try index_mat.finish(isel);
 6081                        const array_vi = try isel.use(bin_op.lhs);
 6082                        try array_vi.address(isel, 0, array_ptr_ra);
 6083                    },
 6084                    else => {
 6085                        const ptr_ra = try isel.allocIntReg();
 6086                        defer isel.freeReg(ptr_ra);
 6087                        if (!try elem_vi.value.load(isel, elem_ty, ptr_ra, .{})) break :unused;
 6088                        const index_vi = try isel.use(bin_op.rhs);
 6089                        try isel.elemPtr(ptr_ra, ptr_ra, .add, elem_size, index_vi);
 6090                        const array_vi = try isel.use(bin_op.lhs);
 6091                        try array_vi.address(isel, 0, ptr_ra);
 6092                    },
 6093                }
 6094            }
 6095            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6096        },
 6097        .slice_elem_val => {
 6098            if (isel.live_values.fetchRemove(air.inst_index)) |elem_vi| unused: {
 6099                defer elem_vi.value.deref(isel);
 6100
 6101                const bin_op = air.data(air.inst_index).bin_op;
 6102                const slice_ty = isel.air.typeOf(bin_op.lhs, ip);
 6103                const ptr_info = slice_ty.ptrInfo(zcu);
 6104                const elem_size = elem_vi.value.size(isel);
 6105                const elem_is_vector = elem_vi.value.isVector(isel);
 6106                if (switch (elem_size) {
 6107                    0 => unreachable,
 6108                    1, 2, 4, 8 => true,
 6109                    16 => elem_is_vector,
 6110                    else => false,
 6111                }) {
 6112                    const elem_ra = try elem_vi.value.defReg(isel) orelse break :unused;
 6113                    const slice_vi = try isel.use(bin_op.lhs);
 6114                    const index_vi = try isel.use(bin_op.rhs);
 6115                    var ptr_part_it = slice_vi.field(slice_ty, 0, 8);
 6116                    const ptr_part_vi = try ptr_part_it.only(isel);
 6117                    const base_mat = try ptr_part_vi.?.matReg(isel);
 6118                    const index_mat = try index_vi.matReg(isel);
 6119                    try isel.emit(switch (elem_size) {
 6120                        else => unreachable,
 6121                        1 => if (elem_is_vector) .ldr(elem_ra.b(), .{ .extended_register = .{
 6122                            .base = base_mat.ra.x(),
 6123                            .index = index_mat.ra.x(),
 6124                            .extend = .{ .lsl = 0 },
 6125                        } }) else switch (elem_vi.value.signedness(isel)) {
 6126                            .signed => .ldrsb(elem_ra.w(), .{ .extended_register = .{
 6127                                .base = base_mat.ra.x(),
 6128                                .index = index_mat.ra.x(),
 6129                                .extend = .{ .lsl = 0 },
 6130                            } }),
 6131                            .unsigned => .ldrb(elem_ra.w(), .{ .extended_register = .{
 6132                                .base = base_mat.ra.x(),
 6133                                .index = index_mat.ra.x(),
 6134                                .extend = .{ .lsl = 0 },
 6135                            } }),
 6136                        },
 6137                        2 => if (elem_is_vector) .ldr(elem_ra.h(), .{ .extended_register = .{
 6138                            .base = base_mat.ra.x(),
 6139                            .index = index_mat.ra.x(),
 6140                            .extend = .{ .lsl = 1 },
 6141                        } }) else switch (elem_vi.value.signedness(isel)) {
 6142                            .signed => .ldrsh(elem_ra.w(), .{ .extended_register = .{
 6143                                .base = base_mat.ra.x(),
 6144                                .index = index_mat.ra.x(),
 6145                                .extend = .{ .lsl = 1 },
 6146                            } }),
 6147                            .unsigned => .ldrh(elem_ra.w(), .{ .extended_register = .{
 6148                                .base = base_mat.ra.x(),
 6149                                .index = index_mat.ra.x(),
 6150                                .extend = .{ .lsl = 1 },
 6151                            } }),
 6152                        },
 6153                        4 => .ldr(if (elem_is_vector) elem_ra.s() else elem_ra.w(), .{ .extended_register = .{
 6154                            .base = base_mat.ra.x(),
 6155                            .index = index_mat.ra.x(),
 6156                            .extend = .{ .lsl = 2 },
 6157                        } }),
 6158                        8 => .ldr(if (elem_is_vector) elem_ra.d() else elem_ra.x(), .{ .extended_register = .{
 6159                            .base = base_mat.ra.x(),
 6160                            .index = index_mat.ra.x(),
 6161                            .extend = .{ .lsl = 3 },
 6162                        } }),
 6163                        16 => if (elem_is_vector) .ldr(elem_ra.q(), .{ .extended_register = .{
 6164                            .base = base_mat.ra.x(),
 6165                            .index = index_mat.ra.x(),
 6166                            .extend = .{ .lsl = 4 },
 6167                        } }) else unreachable,
 6168                    });
 6169                    try index_mat.finish(isel);
 6170                    try base_mat.finish(isel);
 6171                } else {
 6172                    const elem_ptr_ra = try isel.allocIntReg();
 6173                    defer isel.freeReg(elem_ptr_ra);
 6174                    if (!try elem_vi.value.load(isel, slice_ty.elemType2(zcu), elem_ptr_ra, .{
 6175                        .@"volatile" = ptr_info.flags.is_volatile,
 6176                    })) break :unused;
 6177                    const slice_vi = try isel.use(bin_op.lhs);
 6178                    var ptr_part_it = slice_vi.field(slice_ty, 0, 8);
 6179                    const ptr_part_vi = try ptr_part_it.only(isel);
 6180                    const ptr_part_mat = try ptr_part_vi.?.matReg(isel);
 6181                    const index_vi = try isel.use(bin_op.rhs);
 6182                    try isel.elemPtr(elem_ptr_ra, ptr_part_mat.ra, .add, elem_size, index_vi);
 6183                    try ptr_part_mat.finish(isel);
 6184                }
 6185            }
 6186            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6187        },
 6188        .slice_elem_ptr => {
 6189            if (isel.live_values.fetchRemove(air.inst_index)) |elem_ptr_vi| unused: {
 6190                defer elem_ptr_vi.value.deref(isel);
 6191                const elem_ptr_ra = try elem_ptr_vi.value.defReg(isel) orelse break :unused;
 6192
 6193                const ty_pl = air.data(air.inst_index).ty_pl;
 6194                const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
 6195                const elem_size = ty_pl.ty.toType().childType(zcu).abiSize(zcu);
 6196
 6197                const slice_vi = try isel.use(bin_op.lhs);
 6198                var ptr_part_it = slice_vi.field(isel.air.typeOf(bin_op.lhs, ip), 0, 8);
 6199                const ptr_part_vi = try ptr_part_it.only(isel);
 6200                const ptr_part_mat = try ptr_part_vi.?.matReg(isel);
 6201                const index_vi = try isel.use(bin_op.rhs);
 6202                try isel.elemPtr(elem_ptr_ra, ptr_part_mat.ra, .add, elem_size, index_vi);
 6203                try ptr_part_mat.finish(isel);
 6204            }
 6205            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6206        },
 6207        .ptr_elem_val => {
 6208            if (isel.live_values.fetchRemove(air.inst_index)) |elem_vi| unused: {
 6209                defer elem_vi.value.deref(isel);
 6210
 6211                const bin_op = air.data(air.inst_index).bin_op;
 6212                const ptr_ty = isel.air.typeOf(bin_op.lhs, ip);
 6213                const ptr_info = ptr_ty.ptrInfo(zcu);
 6214                const elem_size = elem_vi.value.size(isel);
 6215                const elem_is_vector = elem_vi.value.isVector(isel);
 6216                if (switch (elem_size) {
 6217                    0 => unreachable,
 6218                    1, 2, 4, 8 => true,
 6219                    16 => elem_is_vector,
 6220                    else => false,
 6221                }) {
 6222                    const elem_ra = try elem_vi.value.defReg(isel) orelse break :unused;
 6223                    const base_vi = try isel.use(bin_op.lhs);
 6224                    const index_vi = try isel.use(bin_op.rhs);
 6225                    const base_mat = try base_vi.matReg(isel);
 6226                    const index_mat = try index_vi.matReg(isel);
 6227                    try isel.emit(switch (elem_size) {
 6228                        else => unreachable,
 6229                        1 => if (elem_is_vector) .ldr(elem_ra.b(), .{ .extended_register = .{
 6230                            .base = base_mat.ra.x(),
 6231                            .index = index_mat.ra.x(),
 6232                            .extend = .{ .lsl = 0 },
 6233                        } }) else switch (elem_vi.value.signedness(isel)) {
 6234                            .signed => .ldrsb(elem_ra.w(), .{ .extended_register = .{
 6235                                .base = base_mat.ra.x(),
 6236                                .index = index_mat.ra.x(),
 6237                                .extend = .{ .lsl = 0 },
 6238                            } }),
 6239                            .unsigned => .ldrb(elem_ra.w(), .{ .extended_register = .{
 6240                                .base = base_mat.ra.x(),
 6241                                .index = index_mat.ra.x(),
 6242                                .extend = .{ .lsl = 0 },
 6243                            } }),
 6244                        },
 6245                        2 => if (elem_is_vector) .ldr(elem_ra.h(), .{ .extended_register = .{
 6246                            .base = base_mat.ra.x(),
 6247                            .index = index_mat.ra.x(),
 6248                            .extend = .{ .lsl = 1 },
 6249                        } }) else switch (elem_vi.value.signedness(isel)) {
 6250                            .signed => .ldrsh(elem_ra.w(), .{ .extended_register = .{
 6251                                .base = base_mat.ra.x(),
 6252                                .index = index_mat.ra.x(),
 6253                                .extend = .{ .lsl = 1 },
 6254                            } }),
 6255                            .unsigned => .ldrh(elem_ra.w(), .{ .extended_register = .{
 6256                                .base = base_mat.ra.x(),
 6257                                .index = index_mat.ra.x(),
 6258                                .extend = .{ .lsl = 1 },
 6259                            } }),
 6260                        },
 6261                        4 => .ldr(if (elem_is_vector) elem_ra.s() else elem_ra.w(), .{ .extended_register = .{
 6262                            .base = base_mat.ra.x(),
 6263                            .index = index_mat.ra.x(),
 6264                            .extend = .{ .lsl = 2 },
 6265                        } }),
 6266                        8 => .ldr(if (elem_is_vector) elem_ra.d() else elem_ra.x(), .{ .extended_register = .{
 6267                            .base = base_mat.ra.x(),
 6268                            .index = index_mat.ra.x(),
 6269                            .extend = .{ .lsl = 3 },
 6270                        } }),
 6271                        16 => if (elem_is_vector) .ldr(elem_ra.q(), .{ .extended_register = .{
 6272                            .base = base_mat.ra.x(),
 6273                            .index = index_mat.ra.x(),
 6274                            .extend = .{ .lsl = 4 },
 6275                        } }) else unreachable,
 6276                    });
 6277                    try index_mat.finish(isel);
 6278                    try base_mat.finish(isel);
 6279                } else {
 6280                    const elem_ptr_ra = try isel.allocIntReg();
 6281                    defer isel.freeReg(elem_ptr_ra);
 6282                    if (!try elem_vi.value.load(isel, ptr_ty.elemType2(zcu), elem_ptr_ra, .{
 6283                        .@"volatile" = ptr_info.flags.is_volatile,
 6284                    })) break :unused;
 6285                    const base_vi = try isel.use(bin_op.lhs);
 6286                    const base_mat = try base_vi.matReg(isel);
 6287                    const index_vi = try isel.use(bin_op.rhs);
 6288                    try isel.elemPtr(elem_ptr_ra, base_mat.ra, .add, elem_size, index_vi);
 6289                    try base_mat.finish(isel);
 6290                }
 6291            }
 6292            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6293        },
 6294        .ptr_elem_ptr => {
 6295            if (isel.live_values.fetchRemove(air.inst_index)) |elem_ptr_vi| unused: {
 6296                defer elem_ptr_vi.value.deref(isel);
 6297                const elem_ptr_ra = try elem_ptr_vi.value.defReg(isel) orelse break :unused;
 6298
 6299                const ty_pl = air.data(air.inst_index).ty_pl;
 6300                const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
 6301                const elem_size = ty_pl.ty.toType().childType(zcu).abiSize(zcu);
 6302
 6303                const base_vi = try isel.use(bin_op.lhs);
 6304                const base_mat = try base_vi.matReg(isel);
 6305                const index_vi = try isel.use(bin_op.rhs);
 6306                try isel.elemPtr(elem_ptr_ra, base_mat.ra, .add, elem_size, index_vi);
 6307                try base_mat.finish(isel);
 6308            }
 6309            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6310        },
 6311        .array_to_slice => {
 6312            if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {
 6313                defer slice_vi.value.deref(isel);
 6314                const ty_op = air.data(air.inst_index).ty_op;
 6315                var ptr_part_it = slice_vi.value.field(ty_op.ty.toType(), 0, 8);
 6316                const ptr_part_vi = try ptr_part_it.only(isel);
 6317                try ptr_part_vi.?.move(isel, ty_op.operand);
 6318                var len_part_it = slice_vi.value.field(ty_op.ty.toType(), 8, 8);
 6319                const len_part_vi = try len_part_it.only(isel);
 6320                if (try len_part_vi.?.defReg(isel)) |len_ra| try isel.movImmediate(
 6321                    len_ra.x(),
 6322                    isel.air.typeOf(ty_op.operand, ip).childType(zcu).arrayLen(zcu),
 6323                );
 6324            }
 6325            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6326        },
 6327        .int_from_float, .int_from_float_optimized => |air_tag| {
 6328            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 6329                defer dst_vi.value.deref(isel);
 6330
 6331                const ty_op = air.data(air.inst_index).ty_op;
 6332                const dst_ty = ty_op.ty.toType();
 6333                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 6334                if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 6335                const dst_int_info = dst_ty.intInfo(zcu);
 6336                const src_bits = src_ty.floatBits(isel.target);
 6337                switch (@max(dst_int_info.bits, src_bits)) {
 6338                    0 => unreachable,
 6339                    1...64 => {
 6340                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 6341                        const need_fcvt = switch (src_bits) {
 6342                            else => unreachable,
 6343                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 6344                            32, 64 => false,
 6345                        };
 6346                        const src_vi = try isel.use(ty_op.operand);
 6347                        const src_mat = try src_vi.matReg(isel);
 6348                        const src_ra = if (need_fcvt) try isel.allocVecReg() else src_mat.ra;
 6349                        defer if (need_fcvt) isel.freeReg(src_ra);
 6350                        const dst_reg = switch (dst_int_info.bits) {
 6351                            else => unreachable,
 6352                            1...32 => dst_ra.w(),
 6353                            33...64 => dst_ra.x(),
 6354                        };
 6355                        const src_reg = switch (src_bits) {
 6356                            else => unreachable,
 6357                            16 => if (need_fcvt) src_ra.s() else src_ra.h(),
 6358                            32 => src_ra.s(),
 6359                            64 => src_ra.d(),
 6360                        };
 6361                        try isel.emit(switch (dst_int_info.signedness) {
 6362                            .signed => .fcvtzs(dst_reg, src_reg),
 6363                            .unsigned => .fcvtzu(dst_reg, src_reg),
 6364                        });
 6365                        if (need_fcvt) try isel.emit(.fcvt(src_reg, src_mat.ra.h()));
 6366                        try src_mat.finish(isel);
 6367                    },
 6368                    65...128 => {
 6369                        try call.prepareReturn(isel);
 6370                        switch (dst_int_info.bits) {
 6371                            else => unreachable,
 6372                            1...64 => try call.returnLiveIn(isel, dst_vi.value, .r0),
 6373                            65...128 => {
 6374                                var dst_hi64_it = dst_vi.value.field(dst_ty, 8, 8);
 6375                                const dst_hi64_vi = try dst_hi64_it.only(isel);
 6376                                try call.returnLiveIn(isel, dst_hi64_vi.?, .r1);
 6377                                var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 6378                                const dst_lo64_vi = try dst_lo64_it.only(isel);
 6379                                try call.returnLiveIn(isel, dst_lo64_vi.?, .r0);
 6380                            },
 6381                        }
 6382                        try call.finishReturn(isel);
 6383
 6384                        try call.prepareCallee(isel);
 6385                        try isel.global_relocs.append(gpa, .{
 6386                            .name = switch (dst_int_info.bits) {
 6387                                else => unreachable,
 6388                                1...32 => switch (dst_int_info.signedness) {
 6389                                    .signed => switch (src_bits) {
 6390                                        else => unreachable,
 6391                                        16 => "__fixhfsi",
 6392                                        32 => "__fixsfsi",
 6393                                        64 => "__fixdfsi",
 6394                                        80 => "__fixxfsi",
 6395                                        128 => "__fixtfsi",
 6396                                    },
 6397                                    .unsigned => switch (src_bits) {
 6398                                        else => unreachable,
 6399                                        16 => "__fixunshfsi",
 6400                                        32 => "__fixunssfsi",
 6401                                        64 => "__fixunsdfsi",
 6402                                        80 => "__fixunsxfsi",
 6403                                        128 => "__fixunstfsi",
 6404                                    },
 6405                                },
 6406                                33...64 => switch (dst_int_info.signedness) {
 6407                                    .signed => switch (src_bits) {
 6408                                        else => unreachable,
 6409                                        16 => "__fixhfdi",
 6410                                        32 => "__fixsfdi",
 6411                                        64 => "__fixdfdi",
 6412                                        80 => "__fixxfdi",
 6413                                        128 => "__fixtfdi",
 6414                                    },
 6415                                    .unsigned => switch (src_bits) {
 6416                                        else => unreachable,
 6417                                        16 => "__fixunshfdi",
 6418                                        32 => "__fixunssfdi",
 6419                                        64 => "__fixunsdfdi",
 6420                                        80 => "__fixunsxfdi",
 6421                                        128 => "__fixunstfdi",
 6422                                    },
 6423                                },
 6424                                65...128 => switch (dst_int_info.signedness) {
 6425                                    .signed => switch (src_bits) {
 6426                                        else => unreachable,
 6427                                        16 => "__fixhfti",
 6428                                        32 => "__fixsfti",
 6429                                        64 => "__fixdfti",
 6430                                        80 => "__fixxfti",
 6431                                        128 => "__fixtfti",
 6432                                    },
 6433                                    .unsigned => switch (src_bits) {
 6434                                        else => unreachable,
 6435                                        16 => "__fixunshfti",
 6436                                        32 => "__fixunssfti",
 6437                                        64 => "__fixunsdfti",
 6438                                        80 => "__fixunsxfti",
 6439                                        128 => "__fixunstfti",
 6440                                    },
 6441                                },
 6442                            },
 6443                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 6444                        });
 6445                        try isel.emit(.bl(0));
 6446                        try call.finishCallee(isel);
 6447
 6448                        try call.prepareParams(isel);
 6449                        const src_vi = try isel.use(ty_op.operand);
 6450                        switch (src_bits) {
 6451                            else => unreachable,
 6452                            16, 32, 64, 128 => try call.paramLiveOut(isel, src_vi, .v0),
 6453                            80 => {
 6454                                var src_hi16_it = src_vi.field(src_ty, 8, 8);
 6455                                const src_hi16_vi = try src_hi16_it.only(isel);
 6456                                try call.paramLiveOut(isel, src_hi16_vi.?, .r1);
 6457                                var src_lo64_it = src_vi.field(src_ty, 0, 8);
 6458                                const src_lo64_vi = try src_lo64_it.only(isel);
 6459                                try call.paramLiveOut(isel, src_lo64_vi.?, .r0);
 6460                            },
 6461                        }
 6462                        try call.finishParams(isel);
 6463                    },
 6464                    else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
 6465                }
 6466            }
 6467            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6468        },
 6469        .float_from_int => |air_tag| {
 6470            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 6471                defer dst_vi.value.deref(isel);
 6472
 6473                const ty_op = air.data(air.inst_index).ty_op;
 6474                const dst_ty = ty_op.ty.toType();
 6475                const src_ty = isel.air.typeOf(ty_op.operand, ip);
 6476                const dst_bits = dst_ty.floatBits(isel.target);
 6477                if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
 6478                const src_int_info = src_ty.intInfo(zcu);
 6479                switch (@max(dst_bits, src_int_info.bits)) {
 6480                    0 => unreachable,
 6481                    1...64 => {
 6482                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 6483                        const need_fcvt = switch (dst_bits) {
 6484                            else => unreachable,
 6485                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 6486                            32, 64 => false,
 6487                        };
 6488                        if (need_fcvt) try isel.emit(.fcvt(dst_ra.h(), dst_ra.s()));
 6489                        const src_vi = try isel.use(ty_op.operand);
 6490                        const src_mat = try src_vi.matReg(isel);
 6491                        const dst_reg = switch (dst_bits) {
 6492                            else => unreachable,
 6493                            16 => if (need_fcvt) dst_ra.s() else dst_ra.h(),
 6494                            32 => dst_ra.s(),
 6495                            64 => dst_ra.d(),
 6496                        };
 6497                        const src_reg = switch (src_int_info.bits) {
 6498                            else => unreachable,
 6499                            1...32 => src_mat.ra.w(),
 6500                            33...64 => src_mat.ra.x(),
 6501                        };
 6502                        try isel.emit(switch (src_int_info.signedness) {
 6503                            .signed => .scvtf(dst_reg, src_reg),
 6504                            .unsigned => .ucvtf(dst_reg, src_reg),
 6505                        });
 6506                        try src_mat.finish(isel);
 6507                    },
 6508                    65...128 => {
 6509                        try call.prepareReturn(isel);
 6510                        switch (dst_bits) {
 6511                            else => unreachable,
 6512                            16, 32, 64, 128 => try call.returnLiveIn(isel, dst_vi.value, .v0),
 6513                            80 => {
 6514                                var dst_hi16_it = dst_vi.value.field(dst_ty, 8, 8);
 6515                                const dst_hi16_vi = try dst_hi16_it.only(isel);
 6516                                try call.returnLiveIn(isel, dst_hi16_vi.?, .r1);
 6517                                var dst_lo64_it = dst_vi.value.field(dst_ty, 0, 8);
 6518                                const dst_lo64_vi = try dst_lo64_it.only(isel);
 6519                                try call.returnLiveIn(isel, dst_lo64_vi.?, .r0);
 6520                            },
 6521                        }
 6522                        try call.finishReturn(isel);
 6523
 6524                        try call.prepareCallee(isel);
 6525                        try isel.global_relocs.append(gpa, .{
 6526                            .name = switch (src_int_info.bits) {
 6527                                else => unreachable,
 6528                                1...32 => switch (src_int_info.signedness) {
 6529                                    .signed => switch (dst_bits) {
 6530                                        else => unreachable,
 6531                                        16 => "__floatsihf",
 6532                                        32 => "__floatsisf",
 6533                                        64 => "__floatsidf",
 6534                                        80 => "__floatsixf",
 6535                                        128 => "__floatsitf",
 6536                                    },
 6537                                    .unsigned => switch (dst_bits) {
 6538                                        else => unreachable,
 6539                                        16 => "__floatunsihf",
 6540                                        32 => "__floatunsisf",
 6541                                        64 => "__floatunsidf",
 6542                                        80 => "__floatunsixf",
 6543                                        128 => "__floatunsitf",
 6544                                    },
 6545                                },
 6546                                33...64 => switch (src_int_info.signedness) {
 6547                                    .signed => switch (dst_bits) {
 6548                                        else => unreachable,
 6549                                        16 => "__floatdihf",
 6550                                        32 => "__floatdisf",
 6551                                        64 => "__floatdidf",
 6552                                        80 => "__floatdixf",
 6553                                        128 => "__floatditf",
 6554                                    },
 6555                                    .unsigned => switch (dst_bits) {
 6556                                        else => unreachable,
 6557                                        16 => "__floatundihf",
 6558                                        32 => "__floatundisf",
 6559                                        64 => "__floatundidf",
 6560                                        80 => "__floatundixf",
 6561                                        128 => "__floatunditf",
 6562                                    },
 6563                                },
 6564                                65...128 => switch (src_int_info.signedness) {
 6565                                    .signed => switch (dst_bits) {
 6566                                        else => unreachable,
 6567                                        16 => "__floattihf",
 6568                                        32 => "__floattisf",
 6569                                        64 => "__floattidf",
 6570                                        80 => "__floattixf",
 6571                                        128 => "__floattitf",
 6572                                    },
 6573                                    .unsigned => switch (dst_bits) {
 6574                                        else => unreachable,
 6575                                        16 => "__floatuntihf",
 6576                                        32 => "__floatuntisf",
 6577                                        64 => "__floatuntidf",
 6578                                        80 => "__floatuntixf",
 6579                                        128 => "__floatuntitf",
 6580                                    },
 6581                                },
 6582                            },
 6583                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 6584                        });
 6585                        try isel.emit(.bl(0));
 6586                        try call.finishCallee(isel);
 6587
 6588                        try call.prepareParams(isel);
 6589                        const src_vi = try isel.use(ty_op.operand);
 6590                        switch (src_int_info.bits) {
 6591                            else => unreachable,
 6592                            1...64 => try call.paramLiveOut(isel, src_vi, .r0),
 6593                            65...128 => {
 6594                                var src_hi64_it = src_vi.field(src_ty, 8, 8);
 6595                                const src_hi64_vi = try src_hi64_it.only(isel);
 6596                                try call.paramLiveOut(isel, src_hi64_vi.?, .r1);
 6597                                var src_lo64_it = src_vi.field(src_ty, 0, 8);
 6598                                const src_lo64_vi = try src_lo64_it.only(isel);
 6599                                try call.paramLiveOut(isel, src_lo64_vi.?, .r0);
 6600                            },
 6601                        }
 6602                        try call.finishParams(isel);
 6603                    },
 6604                    else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
 6605                }
 6606            }
 6607            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6608        },
 6609        .memset, .memset_safe => |air_tag| {
 6610            const bin_op = air.data(air.inst_index).bin_op;
 6611            const dst_ty = isel.air.typeOf(bin_op.lhs, ip);
 6612            const dst_info = dst_ty.ptrInfo(zcu);
 6613            const fill_byte: union(enum) { constant: u8, value: Air.Inst.Ref } = fill_byte: {
 6614                if (bin_op.rhs.toInterned()) |fill_val| {
 6615                    if (ip.isUndef(fill_val)) switch (air_tag) {
 6616                        else => unreachable,
 6617                        .memset => break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
 6618                        .memset_safe => break :fill_byte .{ .constant = 0xaa },
 6619                    };
 6620                    if (try isel.hasRepeatedByteRepr(.fromInterned(fill_val))) |fill_byte|
 6621                        break :fill_byte .{ .constant = fill_byte };
 6622                }
 6623                switch (dst_ty.elemType2(zcu).abiSize(zcu)) {
 6624                    0 => unreachable,
 6625                    1 => break :fill_byte .{ .value = bin_op.rhs },
 6626                    2, 4, 8 => |size| {
 6627                        const dst_vi = try isel.use(bin_op.lhs);
 6628                        const ptr_ra = try isel.allocIntReg();
 6629                        const fill_vi = try isel.use(bin_op.rhs);
 6630                        const fill_mat = try fill_vi.matReg(isel);
 6631                        const len_mat: Value.Materialize = len_mat: switch (dst_info.flags.size) {
 6632                            .one => .{ .vi = undefined, .ra = try isel.allocIntReg() },
 6633                            .many => unreachable,
 6634                            .slice => {
 6635                                var dst_len_it = dst_vi.field(dst_ty, 8, 8);
 6636                                const dst_len_vi = try dst_len_it.only(isel);
 6637                                break :len_mat try dst_len_vi.?.matReg(isel);
 6638                            },
 6639                            .c => unreachable,
 6640                        };
 6641
 6642                        const skip_label = isel.instructions.items.len;
 6643                        _ = try isel.instructions.addOne(gpa);
 6644                        try isel.emit(.sub(len_mat.ra.x(), len_mat.ra.x(), .{ .immediate = 1 }));
 6645                        try isel.emit(switch (size) {
 6646                            else => unreachable,
 6647                            2 => .strh(fill_mat.ra.w(), .{ .post_index = .{ .base = ptr_ra.x(), .index = 2 } }),
 6648                            4 => .str(fill_mat.ra.w(), .{ .post_index = .{ .base = ptr_ra.x(), .index = 4 } }),
 6649                            8 => .str(fill_mat.ra.x(), .{ .post_index = .{ .base = ptr_ra.x(), .index = 8 } }),
 6650                        });
 6651                        isel.instructions.items[skip_label] = .cbnz(
 6652                            len_mat.ra.x(),
 6653                            -@as(i21, @intCast((isel.instructions.items.len - 1 - skip_label) << 2)),
 6654                        );
 6655                        switch (dst_info.flags.size) {
 6656                            .one => {
 6657                                const len_imm = ZigType.fromInterned(dst_info.child).arrayLen(zcu);
 6658                                assert(len_imm > 0);
 6659                                try isel.movImmediate(len_mat.ra.x(), len_imm);
 6660                                isel.freeReg(len_mat.ra);
 6661                                try fill_mat.finish(isel);
 6662                                isel.freeReg(ptr_ra);
 6663                                try dst_vi.liveOut(isel, ptr_ra);
 6664                            },
 6665                            .many => unreachable,
 6666                            .slice => {
 6667                                try isel.emit(.cbz(
 6668                                    len_mat.ra.x(),
 6669                                    @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 6670                                ));
 6671                                try len_mat.finish(isel);
 6672                                try fill_mat.finish(isel);
 6673                                isel.freeReg(ptr_ra);
 6674                                var dst_ptr_it = dst_vi.field(dst_ty, 0, 8);
 6675                                const dst_ptr_vi = try dst_ptr_it.only(isel);
 6676                                try dst_ptr_vi.?.liveOut(isel, ptr_ra);
 6677                            },
 6678                            .c => unreachable,
 6679                        }
 6680
 6681                        break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6682                    },
 6683                    else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(dst_ty) }),
 6684                }
 6685            };
 6686
 6687            try call.prepareReturn(isel);
 6688            try call.finishReturn(isel);
 6689
 6690            try call.prepareCallee(isel);
 6691            try isel.global_relocs.append(gpa, .{
 6692                .name = "memset",
 6693                .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 6694            });
 6695            try isel.emit(.bl(0));
 6696            try call.finishCallee(isel);
 6697
 6698            try call.prepareParams(isel);
 6699            const dst_vi = try isel.use(bin_op.lhs);
 6700            switch (dst_info.flags.size) {
 6701                .one => {
 6702                    try isel.movImmediate(.x2, ZigType.fromInterned(dst_info.child).abiSize(zcu));
 6703                    switch (fill_byte) {
 6704                        .constant => |byte| try isel.movImmediate(.w1, byte),
 6705                        .value => |byte| try call.paramLiveOut(isel, try isel.use(byte), .r1),
 6706                    }
 6707                    try call.paramLiveOut(isel, dst_vi, .r0);
 6708                },
 6709                .many => unreachable,
 6710                .slice => {
 6711                    var dst_ptr_it = dst_vi.field(dst_ty, 0, 8);
 6712                    const dst_ptr_vi = try dst_ptr_it.only(isel);
 6713                    var dst_len_it = dst_vi.field(dst_ty, 8, 8);
 6714                    const dst_len_vi = try dst_len_it.only(isel);
 6715                    try isel.elemPtr(.r2, .zr, .add, ZigType.fromInterned(dst_info.child).abiSize(zcu), dst_len_vi.?);
 6716                    switch (fill_byte) {
 6717                        .constant => |byte| try isel.movImmediate(.w1, byte),
 6718                        .value => |byte| try call.paramLiveOut(isel, try isel.use(byte), .r1),
 6719                    }
 6720                    try call.paramLiveOut(isel, dst_ptr_vi.?, .r0);
 6721                },
 6722                .c => unreachable,
 6723            }
 6724            try call.finishParams(isel);
 6725
 6726            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6727        },
 6728        .memcpy, .memmove => |air_tag| {
 6729            const bin_op = air.data(air.inst_index).bin_op;
 6730            const dst_ty = isel.air.typeOf(bin_op.lhs, ip);
 6731            const dst_info = dst_ty.ptrInfo(zcu);
 6732
 6733            try call.prepareReturn(isel);
 6734            try call.finishReturn(isel);
 6735
 6736            try call.prepareCallee(isel);
 6737            try isel.global_relocs.append(gpa, .{
 6738                .name = @tagName(air_tag),
 6739                .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 6740            });
 6741            try isel.emit(.bl(0));
 6742            try call.finishCallee(isel);
 6743
 6744            try call.prepareParams(isel);
 6745            switch (dst_info.flags.size) {
 6746                .one => {
 6747                    const dst_vi = try isel.use(bin_op.lhs);
 6748                    const src_vi = try isel.use(bin_op.rhs);
 6749                    try isel.movImmediate(.x2, ZigType.fromInterned(dst_info.child).abiSize(zcu));
 6750                    try call.paramLiveOut(isel, src_vi, .r1);
 6751                    try call.paramLiveOut(isel, dst_vi, .r0);
 6752                },
 6753                .many => unreachable,
 6754                .slice => {
 6755                    const dst_vi = try isel.use(bin_op.lhs);
 6756                    var dst_ptr_it = dst_vi.field(dst_ty, 0, 8);
 6757                    const dst_ptr_vi = try dst_ptr_it.only(isel);
 6758                    var dst_len_it = dst_vi.field(dst_ty, 8, 8);
 6759                    const dst_len_vi = try dst_len_it.only(isel);
 6760                    const src_vi = try isel.use(bin_op.rhs);
 6761                    try isel.elemPtr(.r2, .zr, .add, ZigType.fromInterned(dst_info.child).abiSize(zcu), dst_len_vi.?);
 6762                    try call.paramLiveOut(isel, src_vi, .r1);
 6763                    try call.paramLiveOut(isel, dst_ptr_vi.?, .r0);
 6764                },
 6765                .c => unreachable,
 6766            }
 6767            try call.finishParams(isel);
 6768
 6769            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6770        },
 6771        .atomic_load => {
 6772            const atomic_load = air.data(air.inst_index).atomic_load;
 6773            const ptr_ty = isel.air.typeOf(atomic_load.ptr, ip);
 6774            const ptr_info = ptr_ty.ptrInfo(zcu);
 6775            if (atomic_load.order != .unordered) return isel.fail("ordered atomic load", .{});
 6776            if (ptr_info.packed_offset.host_size > 0) return isel.fail("packed atomic load", .{});
 6777
 6778            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| {
 6779                defer dst_vi.value.deref(isel);
 6780                var ptr_mat: ?Value.Materialize = null;
 6781                var dst_part_it = dst_vi.value.parts(isel);
 6782                while (dst_part_it.next()) |dst_part_vi| {
 6783                    const dst_ra = try dst_part_vi.defReg(isel) orelse continue;
 6784                    if (ptr_mat == null) {
 6785                        const ptr_vi = try isel.use(atomic_load.ptr);
 6786                        ptr_mat = try ptr_vi.matReg(isel);
 6787                    }
 6788                    try isel.emit(switch (dst_part_vi.size(isel)) {
 6789                        else => |size| return isel.fail("bad atomic load size of {d} from {f}", .{
 6790                            size, isel.fmtType(ptr_ty),
 6791                        }),
 6792                        1 => switch (dst_part_vi.signedness(isel)) {
 6793                            .signed => .ldrsb(dst_ra.w(), .{ .unsigned_offset = .{
 6794                                .base = ptr_mat.?.ra.x(),
 6795                                .offset = @intCast(dst_part_vi.get(isel).offset_from_parent),
 6796                            } }),
 6797                            .unsigned => .ldrb(dst_ra.w(), .{ .unsigned_offset = .{
 6798                                .base = ptr_mat.?.ra.x(),
 6799                                .offset = @intCast(dst_part_vi.get(isel).offset_from_parent),
 6800                            } }),
 6801                        },
 6802                        2 => switch (dst_part_vi.signedness(isel)) {
 6803                            .signed => .ldrsh(dst_ra.w(), .{ .unsigned_offset = .{
 6804                                .base = ptr_mat.?.ra.x(),
 6805                                .offset = @intCast(dst_part_vi.get(isel).offset_from_parent),
 6806                            } }),
 6807                            .unsigned => .ldrh(dst_ra.w(), .{ .unsigned_offset = .{
 6808                                .base = ptr_mat.?.ra.x(),
 6809                                .offset = @intCast(dst_part_vi.get(isel).offset_from_parent),
 6810                            } }),
 6811                        },
 6812                        4 => .ldr(dst_ra.w(), .{ .unsigned_offset = .{
 6813                            .base = ptr_mat.?.ra.x(),
 6814                            .offset = @intCast(dst_part_vi.get(isel).offset_from_parent),
 6815                        } }),
 6816                        8 => .ldr(dst_ra.x(), .{ .unsigned_offset = .{
 6817                            .base = ptr_mat.?.ra.x(),
 6818                            .offset = @intCast(dst_part_vi.get(isel).offset_from_parent),
 6819                        } }),
 6820                    });
 6821                }
 6822                if (ptr_mat) |mat| try mat.finish(isel);
 6823            } else if (ptr_info.flags.is_volatile) return isel.fail("volatile atomic load", .{});
 6824
 6825            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6826        },
 6827        .error_name => {
 6828            if (isel.live_values.fetchRemove(air.inst_index)) |name_vi| unused: {
 6829                defer name_vi.value.deref(isel);
 6830                var ptr_part_it = name_vi.value.field(.slice_const_u8_sentinel_0, 0, 8);
 6831                const ptr_part_vi = try ptr_part_it.only(isel);
 6832                const ptr_part_ra = try ptr_part_vi.?.defReg(isel);
 6833                var len_part_it = name_vi.value.field(.slice_const_u8_sentinel_0, 8, 8);
 6834                const len_part_vi = try len_part_it.only(isel);
 6835                const len_part_ra = try len_part_vi.?.defReg(isel);
 6836                if (ptr_part_ra == null and len_part_ra == null) break :unused;
 6837
 6838                const un_op = air.data(air.inst_index).un_op;
 6839                const error_vi = try isel.use(un_op);
 6840                const error_mat = try error_vi.matReg(isel);
 6841                const ptr_ra = try isel.allocIntReg();
 6842                defer isel.freeReg(ptr_ra);
 6843                const start_ra, const end_ra = range_ras: {
 6844                    const name_lock: RegLock = if (len_part_ra != null) if (ptr_part_ra) |name_ptr_ra|
 6845                        isel.tryLockReg(name_ptr_ra)
 6846                    else
 6847                        .empty else .empty;
 6848                    defer name_lock.unlock(isel);
 6849                    break :range_ras .{ try isel.allocIntReg(), try isel.allocIntReg() };
 6850                };
 6851                defer {
 6852                    isel.freeReg(start_ra);
 6853                    isel.freeReg(end_ra);
 6854                }
 6855                if (len_part_ra) |name_len_ra| try isel.emit(.sub(
 6856                    name_len_ra.w(),
 6857                    end_ra.w(),
 6858                    .{ .register = start_ra.w() },
 6859                ));
 6860                if (ptr_part_ra) |name_ptr_ra| try isel.emit(.add(
 6861                    name_ptr_ra.x(),
 6862                    ptr_ra.x(),
 6863                    .{ .extended_register = .{
 6864                        .register = start_ra.w(),
 6865                        .extend = .{ .uxtw = 0 },
 6866                    } },
 6867                ));
 6868                if (len_part_ra) |_| try isel.emit(.sub(end_ra.w(), end_ra.w(), .{ .immediate = 1 }));
 6869                try isel.emit(.ldp(start_ra.w(), end_ra.w(), .{ .base = start_ra.x() }));
 6870                try isel.emit(.add(start_ra.x(), ptr_ra.x(), .{ .extended_register = .{
 6871                    .register = error_mat.ra.w(),
 6872                    .extend = switch (zcu.errorSetBits()) {
 6873                        else => unreachable,
 6874                        1...8 => .{ .uxtb = 2 },
 6875                        9...16 => .{ .uxth = 2 },
 6876                        17...32 => .{ .uxtw = 2 },
 6877                    },
 6878                } }));
 6879                try isel.lazy_relocs.append(gpa, .{
 6880                    .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
 6881                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 6882                });
 6883                try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 }));
 6884                try isel.lazy_relocs.append(gpa, .{
 6885                    .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
 6886                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 6887                });
 6888                try isel.emit(.adrp(ptr_ra.x(), 0));
 6889                try error_mat.finish(isel);
 6890            }
 6891            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6892        },
 6893        .aggregate_init => {
 6894            if (isel.live_values.fetchRemove(air.inst_index)) |agg_vi| {
 6895                defer agg_vi.value.deref(isel);
 6896
 6897                const ty_pl = air.data(air.inst_index).ty_pl;
 6898                const agg_ty = ty_pl.ty.toType();
 6899                switch (ip.indexToKey(agg_ty.toIntern())) {
 6900                    .array_type => |array_type| {
 6901                        const elems: []const Air.Inst.Ref =
 6902                            @ptrCast(isel.air.extra.items[ty_pl.payload..][0..@intCast(array_type.len)]);
 6903                        var elem_offset: u64 = 0;
 6904                        const elem_size = ZigType.fromInterned(array_type.child).abiSize(zcu);
 6905                        for (elems) |elem| {
 6906                            var agg_part_it = agg_vi.value.field(agg_ty, elem_offset, elem_size);
 6907                            const agg_part_vi = try agg_part_it.only(isel);
 6908                            try agg_part_vi.?.move(isel, elem);
 6909                            elem_offset += elem_size;
 6910                        }
 6911                        switch (array_type.sentinel) {
 6912                            .none => {},
 6913                            else => |sentinel| {
 6914                                var agg_part_it = agg_vi.value.field(agg_ty, elem_offset, elem_size);
 6915                                const agg_part_vi = try agg_part_it.only(isel);
 6916                                try agg_part_vi.?.move(isel, .fromIntern(sentinel));
 6917                            },
 6918                        }
 6919                    },
 6920                    .struct_type => {
 6921                        const loaded_struct = ip.loadStructType(agg_ty.toIntern());
 6922                        const elems: []const Air.Inst.Ref =
 6923                            @ptrCast(isel.air.extra.items[ty_pl.payload..][0..loaded_struct.field_types.len]);
 6924                        var field_offset: u64 = 0;
 6925                        var field_it = loaded_struct.iterateRuntimeOrder(ip);
 6926                        while (field_it.next()) |field_index| {
 6927                            const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
 6928                            field_offset = field_ty.structFieldAlignment(
 6929                                loaded_struct.fieldAlign(ip, field_index),
 6930                                loaded_struct.layout,
 6931                                zcu,
 6932                            ).forward(field_offset);
 6933                            const field_size = field_ty.abiSize(zcu);
 6934                            if (field_size == 0) continue;
 6935                            var agg_part_it = agg_vi.value.field(agg_ty, field_offset, field_size);
 6936                            const agg_part_vi = try agg_part_it.only(isel);
 6937                            try agg_part_vi.?.move(isel, elems[field_index]);
 6938                            field_offset += field_size;
 6939                        }
 6940                        assert(loaded_struct.flagsUnordered(ip).alignment.forward(field_offset) == agg_vi.value.size(isel));
 6941                    },
 6942                    .tuple_type => |tuple_type| {
 6943                        const elems: []const Air.Inst.Ref =
 6944                            @ptrCast(isel.air.extra.items[ty_pl.payload..][0..tuple_type.types.len]);
 6945                        var tuple_align: InternPool.Alignment = .@"1";
 6946                        var field_offset: u64 = 0;
 6947                        for (
 6948                            tuple_type.types.get(ip),
 6949                            tuple_type.values.get(ip),
 6950                            elems,
 6951                        ) |field_ty_index, field_val, elem| {
 6952                            if (field_val != .none) continue;
 6953                            const field_ty: ZigType = .fromInterned(field_ty_index);
 6954                            const field_align = field_ty.abiAlignment(zcu);
 6955                            tuple_align = tuple_align.maxStrict(field_align);
 6956                            field_offset = field_align.forward(field_offset);
 6957                            const field_size = field_ty.abiSize(zcu);
 6958                            if (field_size == 0) continue;
 6959                            var agg_part_it = agg_vi.value.field(agg_ty, field_offset, field_size);
 6960                            const agg_part_vi = try agg_part_it.only(isel);
 6961                            try agg_part_vi.?.move(isel, elem);
 6962                            field_offset += field_size;
 6963                        }
 6964                        assert(tuple_align.forward(field_offset) == agg_vi.value.size(isel));
 6965                    },
 6966                    else => return isel.fail("aggregate init {f}", .{isel.fmtType(agg_ty)}),
 6967                }
 6968            }
 6969            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 6970        },
 6971        .union_init => |air_tag| {
 6972            if (isel.live_values.fetchRemove(air.inst_index)) |union_vi| unused: {
 6973                defer union_vi.value.deref(isel);
 6974
 6975                const ty_pl = air.data(air.inst_index).ty_pl;
 6976                const extra = isel.air.extraData(Air.UnionInit, ty_pl.payload).data;
 6977                const union_ty = ty_pl.ty.toType();
 6978                const loaded_union = ip.loadUnionType(union_ty.toIntern());
 6979                const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
 6980
 6981                if (union_layout.tag_size > 0) unused_tag: {
 6982                    const loaded_tag = loaded_union.loadTagType(ip);
 6983                    var tag_it = union_vi.value.field(union_ty, union_layout.tagOffset(), union_layout.tag_size);
 6984                    const tag_vi = try tag_it.only(isel);
 6985                    const tag_ra = try tag_vi.?.defReg(isel) orelse break :unused_tag;
 6986                    switch (union_layout.tag_size) {
 6987                        0 => unreachable,
 6988                        1...4 => try isel.movImmediate(tag_ra.w(), @as(u32, switch (loaded_tag.values.len) {
 6989                            0 => extra.field_index,
 6990                            else => switch (ip.indexToKey(loaded_tag.values.get(ip)[extra.field_index]).int.storage) {
 6991                                .u64 => |imm| @intCast(imm),
 6992                                .i64 => |imm| @bitCast(@as(i32, @intCast(imm))),
 6993                                else => unreachable,
 6994                            },
 6995                        })),
 6996                        5...8 => try isel.movImmediate(tag_ra.x(), switch (loaded_tag.values.len) {
 6997                            0 => extra.field_index,
 6998                            else => switch (ip.indexToKey(loaded_tag.values.get(ip)[extra.field_index]).int.storage) {
 6999                                .u64 => |imm| imm,
 7000                                .i64 => |imm| @bitCast(imm),
 7001                                else => unreachable,
 7002                            },
 7003                        }),
 7004                        else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(union_ty) }),
 7005                    }
 7006                }
 7007                var payload_it = union_vi.value.field(union_ty, union_layout.payloadOffset(), union_layout.payload_size);
 7008                const payload_vi = try payload_it.only(isel);
 7009                try payload_vi.?.defAddr(isel, union_ty, .{ .root_vi = union_vi.value }) orelse break :unused;
 7010
 7011                try call.prepareReturn(isel);
 7012                try call.finishReturn(isel);
 7013
 7014                try call.prepareCallee(isel);
 7015                try isel.global_relocs.append(gpa, .{
 7016                    .name = "memcpy",
 7017                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7018                });
 7019                try isel.emit(.bl(0));
 7020                try call.finishCallee(isel);
 7021
 7022                try call.prepareParams(isel);
 7023                const init_vi = try isel.use(extra.init);
 7024                try isel.movImmediate(.x2, init_vi.size(isel));
 7025                try call.paramAddress(isel, init_vi, .r1);
 7026                try call.paramAddress(isel, payload_vi.?, .r0);
 7027                try call.finishParams(isel);
 7028            }
 7029            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7030        },
 7031        .prefetch => {
 7032            const prefetch = air.data(air.inst_index).prefetch;
 7033            if (!(prefetch.rw == .write and prefetch.cache == .instruction)) {
 7034                const maybe_slice_ty = isel.air.typeOf(prefetch.ptr, ip);
 7035                const maybe_slice_vi = try isel.use(prefetch.ptr);
 7036                const ptr_vi = if (maybe_slice_ty.isSlice(zcu)) ptr_vi: {
 7037                    var ptr_part_it = maybe_slice_vi.field(maybe_slice_ty, 0, 8);
 7038                    const ptr_part_vi = try ptr_part_it.only(isel);
 7039                    break :ptr_vi ptr_part_vi.?;
 7040                } else maybe_slice_vi;
 7041                const ptr_mat = try ptr_vi.matReg(isel);
 7042                try isel.emit(.prfm(.{
 7043                    .policy = switch (prefetch.locality) {
 7044                        1, 2, 3 => .keep,
 7045                        0 => .strm,
 7046                    },
 7047                    .target = switch (prefetch.locality) {
 7048                        0, 3 => .l1,
 7049                        2 => .l2,
 7050                        1 => .l3,
 7051                    },
 7052                    .type = switch (prefetch.rw) {
 7053                        .read => switch (prefetch.cache) {
 7054                            .data => .pld,
 7055                            .instruction => .pli,
 7056                        },
 7057                        .write => switch (prefetch.cache) {
 7058                            .data => .pst,
 7059                            .instruction => unreachable,
 7060                        },
 7061                    },
 7062                }, .{ .base = ptr_mat.ra.x() }));
 7063                try ptr_mat.finish(isel);
 7064            }
 7065            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7066        },
 7067        .mul_add => {
 7068            if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
 7069                defer res_vi.value.deref(isel);
 7070
 7071                const pl_op = air.data(air.inst_index).pl_op;
 7072                const bin_op = isel.air.extraData(Air.Bin, pl_op.payload).data;
 7073                const ty = isel.air.typeOf(pl_op.operand, ip);
 7074                switch (ty.floatBits(isel.target)) {
 7075                    else => unreachable,
 7076                    16, 32, 64 => |bits| {
 7077                        const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
 7078                        const need_fcvt = switch (bits) {
 7079                            else => unreachable,
 7080                            16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 7081                            32, 64 => false,
 7082                        };
 7083                        if (need_fcvt) try isel.emit(.fcvt(res_ra.h(), res_ra.s()));
 7084                        const lhs_vi = try isel.use(bin_op.lhs);
 7085                        const rhs_vi = try isel.use(bin_op.rhs);
 7086                        const addend_vi = try isel.use(pl_op.operand);
 7087                        const lhs_mat = try lhs_vi.matReg(isel);
 7088                        const rhs_mat = try rhs_vi.matReg(isel);
 7089                        const addend_mat = try addend_vi.matReg(isel);
 7090                        const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 7091                        defer if (need_fcvt) isel.freeReg(lhs_ra);
 7092                        const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 7093                        defer if (need_fcvt) isel.freeReg(rhs_ra);
 7094                        const addend_ra = if (need_fcvt) try isel.allocVecReg() else addend_mat.ra;
 7095                        defer if (need_fcvt) isel.freeReg(addend_ra);
 7096                        try isel.emit(bits: switch (bits) {
 7097                            else => unreachable,
 7098                            16 => if (need_fcvt)
 7099                                continue :bits 32
 7100                            else
 7101                                .fmadd(res_ra.h(), lhs_ra.h(), rhs_ra.h(), addend_ra.h()),
 7102                            32 => .fmadd(res_ra.s(), lhs_ra.s(), rhs_ra.s(), addend_ra.s()),
 7103                            64 => .fmadd(res_ra.d(), lhs_ra.d(), rhs_ra.d(), addend_ra.d()),
 7104                        });
 7105                        if (need_fcvt) {
 7106                            try isel.emit(.fcvt(addend_ra.s(), addend_mat.ra.h()));
 7107                            try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 7108                            try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 7109                        }
 7110                        try addend_mat.finish(isel);
 7111                        try rhs_mat.finish(isel);
 7112                        try lhs_mat.finish(isel);
 7113                    },
 7114                    80, 128 => |bits| {
 7115                        try call.prepareReturn(isel);
 7116                        switch (bits) {
 7117                            else => unreachable,
 7118                            16, 32, 64, 128 => try call.returnLiveIn(isel, res_vi.value, .v0),
 7119                            80 => {
 7120                                var res_hi16_it = res_vi.value.field(ty, 8, 8);
 7121                                const res_hi16_vi = try res_hi16_it.only(isel);
 7122                                try call.returnLiveIn(isel, res_hi16_vi.?, .r1);
 7123                                var res_lo64_it = res_vi.value.field(ty, 0, 8);
 7124                                const res_lo64_vi = try res_lo64_it.only(isel);
 7125                                try call.returnLiveIn(isel, res_lo64_vi.?, .r0);
 7126                            },
 7127                        }
 7128                        try call.finishReturn(isel);
 7129
 7130                        try call.prepareCallee(isel);
 7131                        try isel.global_relocs.append(gpa, .{
 7132                            .name = switch (bits) {
 7133                                else => unreachable,
 7134                                16 => "__fmah",
 7135                                32 => "fmaf",
 7136                                64 => "fma",
 7137                                80 => "__fmax",
 7138                                128 => "fmaq",
 7139                            },
 7140                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7141                        });
 7142                        try isel.emit(.bl(0));
 7143                        try call.finishCallee(isel);
 7144
 7145                        try call.prepareParams(isel);
 7146                        const lhs_vi = try isel.use(bin_op.lhs);
 7147                        const rhs_vi = try isel.use(bin_op.rhs);
 7148                        const addend_vi = try isel.use(pl_op.operand);
 7149                        switch (bits) {
 7150                            else => unreachable,
 7151                            16, 32, 64, 128 => {
 7152                                try call.paramLiveOut(isel, addend_vi, .v2);
 7153                                try call.paramLiveOut(isel, rhs_vi, .v1);
 7154                                try call.paramLiveOut(isel, lhs_vi, .v0);
 7155                            },
 7156                            80 => {
 7157                                var addend_hi16_it = addend_vi.field(ty, 8, 8);
 7158                                const addend_hi16_vi = try addend_hi16_it.only(isel);
 7159                                try call.paramLiveOut(isel, addend_hi16_vi.?, .r5);
 7160                                var addend_lo64_it = addend_vi.field(ty, 0, 8);
 7161                                const addend_lo64_vi = try addend_lo64_it.only(isel);
 7162                                try call.paramLiveOut(isel, addend_lo64_vi.?, .r4);
 7163                                var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 7164                                const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 7165                                try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 7166                                var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 7167                                const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 7168                                try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 7169                                var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 7170                                const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 7171                                try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 7172                                var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 7173                                const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 7174                                try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 7175                            },
 7176                        }
 7177                        try call.finishParams(isel);
 7178                    },
 7179                }
 7180            }
 7181            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7182        },
 7183        .field_parent_ptr => {
 7184            if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| unused: {
 7185                defer dst_vi.value.deref(isel);
 7186                const ty_pl = air.data(air.inst_index).ty_pl;
 7187                const extra = isel.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
 7188                switch (codegen.fieldOffset(
 7189                    ty_pl.ty.toType(),
 7190                    isel.air.typeOf(extra.field_ptr, ip),
 7191                    extra.field_index,
 7192                    zcu,
 7193                )) {
 7194                    0 => try dst_vi.value.move(isel, extra.field_ptr),
 7195                    else => |field_offset| {
 7196                        const dst_ra = try dst_vi.value.defReg(isel) orelse break :unused;
 7197                        const src_vi = try isel.use(extra.field_ptr);
 7198                        const src_mat = try src_vi.matReg(isel);
 7199                        const lo12: u12 = @truncate(field_offset >> 0);
 7200                        const hi12: u12 = @intCast(field_offset >> 12);
 7201                        if (hi12 > 0) try isel.emit(.sub(
 7202                            dst_ra.x(),
 7203                            if (lo12 > 0) dst_ra.x() else src_mat.ra.x(),
 7204                            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 7205                        ));
 7206                        if (lo12 > 0) try isel.emit(.sub(dst_ra.x(), src_mat.ra.x(), .{ .immediate = lo12 }));
 7207                        try src_mat.finish(isel);
 7208                    },
 7209                }
 7210            }
 7211            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7212        },
 7213        .wasm_memory_size, .wasm_memory_grow => unreachable,
 7214        .cmp_lt_errors_len => {
 7215            if (isel.live_values.fetchRemove(air.inst_index)) |is_vi| unused: {
 7216                defer is_vi.value.deref(isel);
 7217                const is_ra = try is_vi.value.defReg(isel) orelse break :unused;
 7218                try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(.ls)));
 7219
 7220                const un_op = air.data(air.inst_index).un_op;
 7221                const error_vi = try isel.use(un_op);
 7222                const error_mat = try error_vi.matReg(isel);
 7223                const ptr_ra = try isel.allocIntReg();
 7224                defer isel.freeReg(ptr_ra);
 7225                try isel.emit(.subs(.wzr, error_mat.ra.w(), .{ .register = ptr_ra.w() }));
 7226                try isel.lazy_relocs.append(gpa, .{
 7227                    .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
 7228                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7229                });
 7230                try isel.emit(.ldr(ptr_ra.w(), .{ .base = ptr_ra.x() }));
 7231                try isel.lazy_relocs.append(gpa, .{
 7232                    .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
 7233                    .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7234                });
 7235                try isel.emit(.adrp(ptr_ra.x(), 0));
 7236                try error_mat.finish(isel);
 7237            }
 7238            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7239        },
 7240        .runtime_nav_ptr => {
 7241            if (isel.live_values.fetchRemove(air.inst_index)) |ptr_vi| unused: {
 7242                defer ptr_vi.value.deref(isel);
 7243                const ptr_ra = try ptr_vi.value.defReg(isel) orelse break :unused;
 7244
 7245                const ty_nav = air.data(air.inst_index).ty_nav;
 7246                if (ZigType.fromInterned(ip.getNav(ty_nav.nav).typeOf(ip)).isFnOrHasRuntimeBits(zcu)) switch (true) {
 7247                    false => {
 7248                        try isel.nav_relocs.append(gpa, .{
 7249                            .nav = ty_nav.nav,
 7250                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7251                        });
 7252                        try isel.emit(.adr(ptr_ra.x(), 0));
 7253                    },
 7254                    true => {
 7255                        try isel.nav_relocs.append(gpa, .{
 7256                            .nav = ty_nav.nav,
 7257                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7258                        });
 7259                        if (ip.getNav(ty_nav.nav).getExtern(ip)) |_|
 7260                            try isel.emit(.ldr(ptr_ra.x(), .{ .unsigned_offset = .{ .base = ptr_ra.x(), .offset = 0 } }))
 7261                        else
 7262                            try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 }));
 7263                        try isel.nav_relocs.append(gpa, .{
 7264                            .nav = ty_nav.nav,
 7265                            .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 7266                        });
 7267                        try isel.emit(.adrp(ptr_ra.x(), 0));
 7268                    },
 7269                } else try isel.movImmediate(ptr_ra.x(), isel.pt.navAlignment(ty_nav.nav).forward(0xaaaaaaaaaaaaaaaa));
 7270            }
 7271            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7272        },
 7273        .c_va_arg => {
 7274            const maybe_arg_vi = isel.live_values.fetchRemove(air.inst_index);
 7275            defer if (maybe_arg_vi) |arg_vi| arg_vi.value.deref(isel);
 7276            const ty_op = air.data(air.inst_index).ty_op;
 7277            const ty = ty_op.ty.toType();
 7278            var param_it: CallAbiIterator = .init;
 7279            const param_vi = try param_it.param(isel, ty);
 7280            defer param_vi.?.deref(isel);
 7281            const passed_vi = switch (param_vi.?.parent(isel)) {
 7282                .unallocated => param_vi.?,
 7283                .stack_slot, .value, .constant => unreachable,
 7284                .address => |address_vi| address_vi,
 7285            };
 7286            const passed_size: u5 = @intCast(passed_vi.alignment(isel).forward(passed_vi.size(isel)));
 7287            const passed_is_vector = passed_vi.isVector(isel);
 7288
 7289            const va_list_ptr_vi = try isel.use(ty_op.operand);
 7290            const va_list_ptr_mat = try va_list_ptr_vi.matReg(isel);
 7291            const offs_ra = try isel.allocIntReg();
 7292            defer isel.freeReg(offs_ra);
 7293            const stack_ra = try isel.allocIntReg();
 7294            defer isel.freeReg(stack_ra);
 7295
 7296            var part_vis: [2]Value.Index = undefined;
 7297            var arg_part_ras: [2]?Register.Alias = @splat(null);
 7298            const parts_len = parts_len: {
 7299                var parts_len: u2 = 0;
 7300                var part_it = passed_vi.parts(isel);
 7301                while (part_it.next()) |part_vi| : (parts_len += 1) {
 7302                    part_vis[parts_len] = part_vi;
 7303                    const arg_vi = maybe_arg_vi orelse continue;
 7304                    const part_offset, const part_size = part_vi.position(isel);
 7305                    var arg_part_it = arg_vi.value.field(ty, part_offset, part_size);
 7306                    const arg_part_vi = try arg_part_it.only(isel);
 7307                    arg_part_ras[parts_len] = try arg_part_vi.?.defReg(isel);
 7308                }
 7309                break :parts_len parts_len;
 7310            };
 7311
 7312            const done_label = isel.instructions.items.len;
 7313            try isel.emit(.str(stack_ra.x(), .{ .unsigned_offset = .{
 7314                .base = va_list_ptr_mat.ra.x(),
 7315                .offset = 0,
 7316            } }));
 7317            try isel.emit(switch (parts_len) {
 7318                else => unreachable,
 7319                1 => if (arg_part_ras[0]) |arg_part_ra| switch (part_vis[0].size(isel)) {
 7320                    else => unreachable,
 7321                    1 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.b(), .{ .post_index = .{
 7322                        .base = stack_ra.x(),
 7323                        .index = passed_size,
 7324                    } }) else switch (part_vis[0].signedness(isel)) {
 7325                        .signed => .ldrsb(arg_part_ra.w(), .{ .post_index = .{
 7326                            .base = stack_ra.x(),
 7327                            .index = passed_size,
 7328                        } }),
 7329                        .unsigned => .ldrb(arg_part_ra.w(), .{ .post_index = .{
 7330                            .base = stack_ra.x(),
 7331                            .index = passed_size,
 7332                        } }),
 7333                    },
 7334                    2 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.h(), .{ .post_index = .{
 7335                        .base = stack_ra.x(),
 7336                        .index = passed_size,
 7337                    } }) else switch (part_vis[0].signedness(isel)) {
 7338                        .signed => .ldrsh(arg_part_ra.w(), .{ .post_index = .{
 7339                            .base = stack_ra.x(),
 7340                            .index = passed_size,
 7341                        } }),
 7342                        .unsigned => .ldrh(arg_part_ra.w(), .{ .post_index = .{
 7343                            .base = stack_ra.x(),
 7344                            .index = passed_size,
 7345                        } }),
 7346                    },
 7347                    4 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.s() else arg_part_ra.w(), .{ .post_index = .{
 7348                        .base = stack_ra.x(),
 7349                        .index = passed_size,
 7350                    } }),
 7351                    8 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.d() else arg_part_ra.x(), .{ .post_index = .{
 7352                        .base = stack_ra.x(),
 7353                        .index = passed_size,
 7354                    } }),
 7355                    16 => .ldr(arg_part_ra.q(), .{ .post_index = .{
 7356                        .base = stack_ra.x(),
 7357                        .index = passed_size,
 7358                    } }),
 7359                } else .add(stack_ra.x(), stack_ra.x(), .{ .immediate = passed_size }),
 7360                2 => if (arg_part_ras[0] != null or arg_part_ras[1] != null) .ldp(
 7361                    @as(Register.Alias, arg_part_ras[0] orelse .zr).x(),
 7362                    @as(Register.Alias, arg_part_ras[1] orelse .zr).x(),
 7363                    .{ .post_index = .{
 7364                        .base = stack_ra.x(),
 7365                        .index = passed_size,
 7366                    } },
 7367                ) else .add(stack_ra.x(), stack_ra.x(), .{ .immediate = passed_size }),
 7368            });
 7369            try isel.emit(.ldr(stack_ra.x(), .{ .unsigned_offset = .{
 7370                .base = va_list_ptr_mat.ra.x(),
 7371                .offset = 0,
 7372            } }));
 7373            switch (isel.va_list) {
 7374                .other => {},
 7375                .sysv => {
 7376                    const stack_label = isel.instructions.items.len;
 7377                    try isel.emit(.b(
 7378                        @intCast((isel.instructions.items.len + 1 - done_label) << 2),
 7379                    ));
 7380                    switch (parts_len) {
 7381                        else => unreachable,
 7382                        1 => if (arg_part_ras[0]) |arg_part_ra| try isel.emit(switch (part_vis[0].size(isel)) {
 7383                            else => unreachable,
 7384                            1 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.b(), .{ .extended_register = .{
 7385                                .base = stack_ra.x(),
 7386                                .index = offs_ra.w(),
 7387                                .extend = .{ .sxtw = 0 },
 7388                            } }) else switch (part_vis[0].signedness(isel)) {
 7389                                .signed => .ldrsb(arg_part_ra.w(), .{ .extended_register = .{
 7390                                    .base = stack_ra.x(),
 7391                                    .index = offs_ra.w(),
 7392                                    .extend = .{ .sxtw = 0 },
 7393                                } }),
 7394                                .unsigned => .ldrb(arg_part_ra.w(), .{ .extended_register = .{
 7395                                    .base = stack_ra.x(),
 7396                                    .index = offs_ra.w(),
 7397                                    .extend = .{ .sxtw = 0 },
 7398                                } }),
 7399                            },
 7400                            2 => if (arg_part_ra.isVector()) .ldr(arg_part_ra.h(), .{ .extended_register = .{
 7401                                .base = stack_ra.x(),
 7402                                .index = offs_ra.w(),
 7403                                .extend = .{ .sxtw = 0 },
 7404                            } }) else switch (part_vis[0].signedness(isel)) {
 7405                                .signed => .ldrsh(arg_part_ra.w(), .{ .extended_register = .{
 7406                                    .base = stack_ra.x(),
 7407                                    .index = offs_ra.w(),
 7408                                    .extend = .{ .sxtw = 0 },
 7409                                } }),
 7410                                .unsigned => .ldrh(arg_part_ra.w(), .{ .extended_register = .{
 7411                                    .base = stack_ra.x(),
 7412                                    .index = offs_ra.w(),
 7413                                    .extend = .{ .sxtw = 0 },
 7414                                } }),
 7415                            },
 7416                            4 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.s() else arg_part_ra.w(), .{ .extended_register = .{
 7417                                .base = stack_ra.x(),
 7418                                .index = offs_ra.w(),
 7419                                .extend = .{ .sxtw = 0 },
 7420                            } }),
 7421                            8 => .ldr(if (arg_part_ra.isVector()) arg_part_ra.d() else arg_part_ra.x(), .{ .extended_register = .{
 7422                                .base = stack_ra.x(),
 7423                                .index = offs_ra.w(),
 7424                                .extend = .{ .sxtw = 0 },
 7425                            } }),
 7426                            16 => .ldr(arg_part_ra.q(), .{ .extended_register = .{
 7427                                .base = stack_ra.x(),
 7428                                .index = offs_ra.w(),
 7429                                .extend = .{ .sxtw = 0 },
 7430                            } }),
 7431                        }),
 7432                        2 => if (arg_part_ras[0] != null or arg_part_ras[1] != null) {
 7433                            try isel.emit(.ldp(
 7434                                @as(Register.Alias, arg_part_ras[0] orelse .zr).x(),
 7435                                @as(Register.Alias, arg_part_ras[1] orelse .zr).x(),
 7436                                .{ .base = stack_ra.x() },
 7437                            ));
 7438                            try isel.emit(.add(stack_ra.x(), stack_ra.x(), .{ .extended_register = .{
 7439                                .register = offs_ra.w(),
 7440                                .extend = .{ .sxtw = 0 },
 7441                            } }));
 7442                        },
 7443                    }
 7444                    try isel.emit(.ldr(stack_ra.x(), .{ .unsigned_offset = .{
 7445                        .base = va_list_ptr_mat.ra.x(),
 7446                        .offset = if (passed_is_vector) 16 else 8,
 7447                    } }));
 7448                    try isel.emit(.@"b."(
 7449                        .gt,
 7450                        @intCast((isel.instructions.items.len + 1 - stack_label) << 2),
 7451                    ));
 7452                    try isel.emit(.str(stack_ra.w(), .{ .unsigned_offset = .{
 7453                        .base = va_list_ptr_mat.ra.x(),
 7454                        .offset = if (passed_is_vector) 28 else 24,
 7455                    } }));
 7456                    try isel.emit(.adds(stack_ra.w(), offs_ra.w(), .{ .immediate = passed_size }));
 7457                    try isel.emit(.tbz(
 7458                        offs_ra.w(),
 7459                        31,
 7460                        @intCast((isel.instructions.items.len + 1 - stack_label) << 2),
 7461                    ));
 7462                    try isel.emit(.ldr(offs_ra.w(), .{ .unsigned_offset = .{
 7463                        .base = va_list_ptr_mat.ra.x(),
 7464                        .offset = if (passed_is_vector) 28 else 24,
 7465                    } }));
 7466                },
 7467            }
 7468            try va_list_ptr_mat.finish(isel);
 7469            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7470        },
 7471        .c_va_copy => {
 7472            if (isel.live_values.fetchRemove(air.inst_index)) |va_list_vi| {
 7473                defer va_list_vi.value.deref(isel);
 7474                const ty_op = air.data(air.inst_index).ty_op;
 7475                const va_list_ptr_vi = try isel.use(ty_op.operand);
 7476                const va_list_ptr_mat = try va_list_ptr_vi.matReg(isel);
 7477                _ = try va_list_vi.value.load(isel, ty_op.ty.toType(), va_list_ptr_mat.ra, .{});
 7478                try va_list_ptr_mat.finish(isel);
 7479            }
 7480            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7481        },
 7482        .c_va_end => if (air.next()) |next_air_tag| continue :air_tag next_air_tag,
 7483        .c_va_start => {
 7484            if (isel.live_values.fetchRemove(air.inst_index)) |va_list_vi| {
 7485                defer va_list_vi.value.deref(isel);
 7486                const ty = air.data(air.inst_index).ty;
 7487                switch (isel.va_list) {
 7488                    .other => |va_list| if (try va_list_vi.value.defReg(isel)) |va_list_ra| try isel.emit(.add(
 7489                        va_list_ra.x(),
 7490                        va_list.base.x(),
 7491                        .{ .immediate = @intCast(va_list.offset) },
 7492                    )),
 7493                    .sysv => |va_list| {
 7494                        var vr_offs_it = va_list_vi.value.field(ty, 28, 4);
 7495                        const vr_offs_vi = try vr_offs_it.only(isel);
 7496                        if (try vr_offs_vi.?.defReg(isel)) |vr_offs_ra| try isel.movImmediate(
 7497                            vr_offs_ra.w(),
 7498                            @as(u32, @bitCast(va_list.__vr_offs)),
 7499                        );
 7500                        var gr_offs_it = va_list_vi.value.field(ty, 24, 4);
 7501                        const gr_offs_vi = try gr_offs_it.only(isel);
 7502                        if (try gr_offs_vi.?.defReg(isel)) |gr_offs_ra| try isel.movImmediate(
 7503                            gr_offs_ra.w(),
 7504                            @as(u32, @bitCast(va_list.__gr_offs)),
 7505                        );
 7506                        var vr_top_it = va_list_vi.value.field(ty, 16, 8);
 7507                        const vr_top_vi = try vr_top_it.only(isel);
 7508                        if (try vr_top_vi.?.defReg(isel)) |vr_top_ra| try isel.emit(.add(
 7509                            vr_top_ra.x(),
 7510                            va_list.__vr_top.base.x(),
 7511                            .{ .immediate = @intCast(va_list.__vr_top.offset) },
 7512                        ));
 7513                        var gr_top_it = va_list_vi.value.field(ty, 8, 8);
 7514                        const gr_top_vi = try gr_top_it.only(isel);
 7515                        if (try gr_top_vi.?.defReg(isel)) |gr_top_ra| try isel.emit(.add(
 7516                            gr_top_ra.x(),
 7517                            va_list.__gr_top.base.x(),
 7518                            .{ .immediate = @intCast(va_list.__gr_top.offset) },
 7519                        ));
 7520                        var stack_it = va_list_vi.value.field(ty, 0, 8);
 7521                        const stack_vi = try stack_it.only(isel);
 7522                        if (try stack_vi.?.defReg(isel)) |stack_ra| try isel.emit(.add(
 7523                            stack_ra.x(),
 7524                            va_list.__stack.base.x(),
 7525                            .{ .immediate = @intCast(va_list.__stack.offset) },
 7526                        ));
 7527                    },
 7528                }
 7529            }
 7530            if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
 7531        },
 7532        .work_item_id, .work_group_size, .work_group_id => unreachable,
 7533    }
 7534    assert(air.body_index == 0);
 7535}
 7536
 7537pub fn verify(isel: *Select, check_values: bool) void {
 7538    if (!std.debug.runtime_safety) return;
 7539    assert(isel.blocks.count() == 1 and isel.blocks.keys()[0] == Select.Block.main);
 7540    assert(isel.active_loops.items.len == 0);
 7541    assert(isel.dom_start == 0 and isel.dom_len == 0);
 7542    var live_reg_it = isel.live_registers.iterator();
 7543    while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) {
 7544        _ => {
 7545            isel.dumpValues(.all);
 7546            unreachable;
 7547        },
 7548        .allocating, .free => {},
 7549    };
 7550    if (check_values) for (isel.values.items) |value| if (value.refs != 0) {
 7551        isel.dumpValues(.only_referenced);
 7552        unreachable;
 7553    };
 7554}
 7555
 7556///           Stack Frame Layout
 7557/// +-+-----------------------------------+
 7558/// |R| allocated stack                   |
 7559/// +-+-----------------------------------+
 7560/// |S| caller frame record               |   +---------------+
 7561/// +-+-----------------------------------+ <-| entry/exit FP |
 7562/// |R| caller frame                      |   +---------------+
 7563/// +-+-----------------------------------+
 7564/// |R| variable incoming stack arguments |   +---------------+
 7565/// +-+-----------------------------------+ <-| __stack       |
 7566/// |S| named incoming stack arguments    |   +---------------+
 7567/// +-+-----------------------------------+ <-| entry/exit SP |
 7568/// |S| incoming gr arguments             |   | __gr_top      |
 7569/// +-+-----------------------------------+   +---------------+
 7570/// |S| alignment gap                     |
 7571/// +-+-----------------------------------+
 7572/// |S| frame record                      |   +----------+
 7573/// +-+-----------------------------------+ <-| FP       |
 7574/// |S| incoming vr arguments             |   | __vr_top |
 7575/// +-+-----------------------------------+   +----------+
 7576/// |L| alignment gap                     |
 7577/// +-+-----------------------------------+
 7578/// |L| callee saved vr area              |
 7579/// +-+-----------------------------------+
 7580/// |L| callee saved gr area              |   +----------------------+
 7581/// +-+-----------------------------------+ <-| prologue/epilogue SP |
 7582/// |R| realignment gap                   |   +----------------------+
 7583/// +-+-----------------------------------+
 7584/// |L| locals                            |
 7585/// +-+-----------------------------------+
 7586/// |S| outgoing stack arguments          |   +----+
 7587/// +-+-----------------------------------+ <-| SP |
 7588/// |R| unallocated stack                 |   +----+
 7589/// +-+-----------------------------------+
 7590/// [S] Size computed by `analyze`, can be used by the body.
 7591/// [L] Size computed by `layout`, can be used by the prologue/epilogue.
 7592/// [R] Size unknown until runtime, can vary from one call to the next.
 7593///
 7594/// Constraints that led to this layout:
 7595///  * FP to __stack/__gr_top/__vr_top must only pass through [S]
 7596///  * SP to outgoing stack arguments/locals must only pass through [S]
 7597///  * entry/exit SP to prologue/epilogue SP must only pass through [S/L]
 7598///  * all save areas must be at a positive offset from prologue/epilogue SP
 7599///  * the entry/exit SP to prologue/epilogue SP distance must
 7600///   - be a multiple of 16 due to hardware restrictions on the value of SP
 7601///   - conform to the limit from the first matching condition in the
 7602///     following list due to instruction encoding limitations
 7603///    1. callee saved gr count >= 2: multiple of 8 of at most 504 bytes
 7604///    2. callee saved vr count >= 2: multiple of 8 of at most 504 bytes
 7605///    3. callee saved gr count >= 1: at most 255 bytes
 7606///    4. callee saved vr count >= 1: at most 255 bytes
 7607///    5. variable incoming vr argument count >= 2: multiple of 16 of at most 1008 bytes
 7608///    6. variable incoming vr argument count >= 1: at most 255 bytes
 7609///    7. have frame record: multiple of 8 of at most 504 bytes
 7610pub fn layout(
 7611    isel: *Select,
 7612    incoming: CallAbiIterator,
 7613    is_sysv_var_args: bool,
 7614    saved_gra_len: u7,
 7615    saved_vra_len: u7,
 7616    mod: *const Package.Module,
 7617) !usize {
 7618    const zcu = isel.pt.zcu;
 7619    const ip = &zcu.intern_pool;
 7620    const nav = ip.getNav(isel.nav_index);
 7621    wip_mir_log.debug("{f}<body>:\n", .{nav.fqn.fmt(ip)});
 7622
 7623    const stack_size: u24 = @intCast(InternPool.Alignment.@"16".forward(isel.stack_size));
 7624
 7625    var saves_buf: [10 + 8 + 8 + 2 + 8]struct {
 7626        class: enum { integer, vector },
 7627        needs_restore: bool,
 7628        register: Register,
 7629        offset: u10,
 7630        size: u5,
 7631    } = undefined;
 7632    const saves, const saves_size, const frame_record_offset = saves: {
 7633        var saves_len: usize = 0;
 7634        var saves_size: u10 = 0;
 7635        var save_ra: Register.Alias = undefined;
 7636
 7637        // callee saved gr area
 7638        save_ra = .r19;
 7639        while (save_ra != .r29) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
 7640            if (!isel.saved_registers.contains(save_ra)) continue;
 7641            saves_size = std.mem.alignForward(u10, saves_size, 8);
 7642            saves_buf[saves_len] = .{
 7643                .class = .integer,
 7644                .needs_restore = true,
 7645                .register = save_ra.x(),
 7646                .offset = saves_size,
 7647                .size = 8,
 7648            };
 7649            saves_len += 1;
 7650            saves_size += 8;
 7651        }
 7652        var deferred_gr = if (saves_size == 8 or (saves_size % 16 != 0 and saved_gra_len % 2 != 0)) gr: {
 7653            saves_len -= 1;
 7654            saves_size -= 8;
 7655            break :gr saves_buf[saves_len].register;
 7656        } else null;
 7657        defer assert(deferred_gr == null);
 7658
 7659        // callee saved vr area
 7660        save_ra = .v8;
 7661        while (save_ra != .v16) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
 7662            if (!isel.saved_registers.contains(save_ra)) continue;
 7663            saves_size = std.mem.alignForward(u10, saves_size, 8);
 7664            saves_buf[saves_len] = .{
 7665                .class = .vector,
 7666                .needs_restore = true,
 7667                .register = save_ra.d(),
 7668                .offset = saves_size,
 7669                .size = 8,
 7670            };
 7671            saves_len += 1;
 7672            saves_size += 8;
 7673        }
 7674        if (deferred_gr != null and saved_gra_len % 2 == 0) {
 7675            saves_size = std.mem.alignForward(u10, saves_size, 8);
 7676            saves_buf[saves_len] = .{
 7677                .class = .integer,
 7678                .needs_restore = true,
 7679                .register = deferred_gr.?,
 7680                .offset = saves_size,
 7681                .size = 8,
 7682            };
 7683            saves_len += 1;
 7684            saves_size += 8;
 7685            deferred_gr = null;
 7686        }
 7687        if (saves_size % 16 != 0 and saved_vra_len % 2 != 0) {
 7688            const prev_save = &saves_buf[saves_len - 1];
 7689            switch (prev_save.class) {
 7690                .integer => {},
 7691                .vector => {
 7692                    prev_save.register = prev_save.register.alias.q();
 7693                    prev_save.size = 16;
 7694                    saves_size += 8;
 7695                },
 7696            }
 7697        }
 7698
 7699        // incoming vr arguments
 7700        save_ra = if (mod.strip) incoming.nsrn else CallAbiIterator.nsrn_start;
 7701        while (save_ra != if (is_sysv_var_args) CallAbiIterator.nsrn_end else incoming.nsrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
 7702            saves_size = std.mem.alignForward(u10, saves_size, 16);
 7703            saves_buf[saves_len] = .{
 7704                .class = .vector,
 7705                .needs_restore = false,
 7706                .register = save_ra.q(),
 7707                .offset = saves_size,
 7708                .size = 16,
 7709            };
 7710            saves_len += 1;
 7711            saves_size += 16;
 7712        }
 7713
 7714        // frame record
 7715        saves_size = std.mem.alignForward(u10, saves_size, 16);
 7716        const frame_record_offset = saves_size;
 7717        saves_buf[saves_len] = .{
 7718            .class = .integer,
 7719            .needs_restore = true,
 7720            .register = .fp,
 7721            .offset = saves_size,
 7722            .size = 8,
 7723        };
 7724        saves_len += 1;
 7725        saves_size += 8;
 7726
 7727        saves_size = std.mem.alignForward(u10, saves_size, 8);
 7728        saves_buf[saves_len] = .{
 7729            .class = .integer,
 7730            .needs_restore = true,
 7731            .register = .lr,
 7732            .offset = saves_size,
 7733            .size = 8,
 7734        };
 7735        saves_len += 1;
 7736        saves_size += 8;
 7737
 7738        // incoming gr arguments
 7739        if (deferred_gr) |gr| {
 7740            saves_size = std.mem.alignForward(u10, saves_size, 8);
 7741            saves_buf[saves_len] = .{
 7742                .class = .integer,
 7743                .needs_restore = true,
 7744                .register = gr,
 7745                .offset = saves_size,
 7746                .size = 8,
 7747            };
 7748            saves_len += 1;
 7749            saves_size += 8;
 7750            deferred_gr = null;
 7751        } else switch (@as(u1, @truncate(saved_gra_len))) {
 7752            0 => {},
 7753            1 => saves_size += 8,
 7754        }
 7755        save_ra = if (mod.strip) incoming.ngrn else CallAbiIterator.ngrn_start;
 7756        while (save_ra != if (is_sysv_var_args) CallAbiIterator.ngrn_end else incoming.ngrn) : (save_ra = @enumFromInt(@intFromEnum(save_ra) + 1)) {
 7757            saves_size = std.mem.alignForward(u10, saves_size, 8);
 7758            saves_buf[saves_len] = .{
 7759                .class = .integer,
 7760                .needs_restore = false,
 7761                .register = save_ra.x(),
 7762                .offset = saves_size,
 7763                .size = 8,
 7764            };
 7765            saves_len += 1;
 7766            saves_size += 8;
 7767        }
 7768
 7769        assert(InternPool.Alignment.@"16".check(saves_size));
 7770        break :saves .{ saves_buf[0..saves_len], saves_size, frame_record_offset };
 7771    };
 7772
 7773    {
 7774        wip_mir_log.debug("{f}<prologue>:", .{nav.fqn.fmt(ip)});
 7775        var save_index: usize = 0;
 7776        while (save_index < saves.len) if (save_index + 2 <= saves.len and
 7777            saves[save_index + 0].class == saves[save_index + 1].class and
 7778            saves[save_index + 0].size == saves[save_index + 1].size and
 7779            saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset)
 7780        {
 7781            try isel.emit(.stp(
 7782                saves[save_index + 0].register,
 7783                saves[save_index + 1].register,
 7784                switch (saves[save_index + 0].offset) {
 7785                    0 => .{ .pre_index = .{
 7786                        .base = .sp,
 7787                        .index = @intCast(-@as(i11, saves_size)),
 7788                    } },
 7789                    else => |offset| .{ .signed_offset = .{
 7790                        .base = .sp,
 7791                        .offset = @intCast(offset),
 7792                    } },
 7793                },
 7794            ));
 7795            save_index += 2;
 7796        } else {
 7797            try isel.emit(.str(
 7798                saves[save_index].register,
 7799                switch (saves[save_index].offset) {
 7800                    0 => .{ .pre_index = .{
 7801                        .base = .sp,
 7802                        .index = @intCast(-@as(i11, saves_size)),
 7803                    } },
 7804                    else => |offset| .{ .unsigned_offset = .{
 7805                        .base = .sp,
 7806                        .offset = @intCast(offset),
 7807                    } },
 7808                },
 7809            ));
 7810            save_index += 1;
 7811        };
 7812
 7813        try isel.emit(.add(.fp, .sp, .{ .immediate = frame_record_offset }));
 7814        const scratch_reg: Register = if (isel.stack_align == .@"16")
 7815            .sp
 7816        else if (stack_size == 0 and frame_record_offset == 0)
 7817            .fp
 7818        else
 7819            .ip0;
 7820        const stack_size_lo: u12 = @truncate(stack_size >> 0);
 7821        const stack_size_hi: u12 = @truncate(stack_size >> 12);
 7822        if (mod.stack_check) {
 7823            if (stack_size_hi > 2) {
 7824                try isel.movImmediate(.ip1, stack_size_hi);
 7825                const loop_label = isel.instructions.items.len;
 7826                try isel.emit(.sub(.sp, .sp, .{
 7827                    .shifted_immediate = .{ .immediate = 1, .lsl = .@"12" },
 7828                }));
 7829                try isel.emit(.sub(.ip1, .ip1, .{ .immediate = 1 }));
 7830                try isel.emit(.ldr(.xzr, .{ .base = .sp }));
 7831                try isel.emit(.cbnz(.ip1, -@as(i21, @intCast(
 7832                    (isel.instructions.items.len - loop_label) << 2,
 7833                ))));
 7834            } else for (0..stack_size_hi) |_| {
 7835                try isel.emit(.sub(.sp, .sp, .{
 7836                    .shifted_immediate = .{ .immediate = 1, .lsl = .@"12" },
 7837                }));
 7838                try isel.emit(.ldr(.xzr, .{ .base = .sp }));
 7839            }
 7840            if (stack_size_lo > 0) try isel.emit(.sub(
 7841                scratch_reg,
 7842                .sp,
 7843                .{ .immediate = stack_size_lo },
 7844            )) else if (scratch_reg.alias == Register.Alias.ip0)
 7845                try isel.emit(.add(scratch_reg, .sp, .{ .immediate = 0 }));
 7846        } else {
 7847            if (stack_size_hi > 0) try isel.emit(.sub(scratch_reg, .sp, .{
 7848                .shifted_immediate = .{ .immediate = stack_size_hi, .lsl = .@"12" },
 7849            }));
 7850            if (stack_size_lo > 0) try isel.emit(.sub(
 7851                scratch_reg,
 7852                if (stack_size_hi > 0) scratch_reg else .sp,
 7853                .{ .immediate = stack_size_lo },
 7854            )) else if (scratch_reg.alias == Register.Alias.ip0 and stack_size_hi == 0)
 7855                try isel.emit(.add(scratch_reg, .sp, .{ .immediate = 0 }));
 7856        }
 7857        if (isel.stack_align != .@"16") try isel.emit(.@"and"(.sp, scratch_reg, .{ .immediate = .{
 7858            .N = .doubleword,
 7859            .immr = -%isel.stack_align.toLog2Units(),
 7860            .imms = ~isel.stack_align.toLog2Units(),
 7861        } }));
 7862        wip_mir_log.debug("", .{});
 7863    }
 7864
 7865    const epilogue = isel.instructions.items.len;
 7866    if (isel.returns) {
 7867        try isel.emit(.ret(.lr));
 7868        var save_index: usize = 0;
 7869        var first_offset: ?u10 = null;
 7870        while (save_index < saves.len) {
 7871            if (save_index + 2 <= saves.len and saves[save_index + 1].needs_restore and
 7872                saves[save_index + 0].class == saves[save_index + 1].class and
 7873                saves[save_index + 0].size == saves[save_index + 1].size and
 7874                saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset)
 7875            {
 7876                try isel.emit(.ldp(
 7877                    saves[save_index + 0].register,
 7878                    saves[save_index + 1].register,
 7879                    if (first_offset) |offset| .{ .signed_offset = .{
 7880                        .base = .sp,
 7881                        .offset = @intCast(saves[save_index + 0].offset - offset),
 7882                    } } else form: {
 7883                        first_offset = @intCast(saves[save_index + 0].offset);
 7884                        break :form .{ .post_index = .{
 7885                            .base = .sp,
 7886                            .index = @intCast(saves_size - first_offset.?),
 7887                        } };
 7888                    },
 7889                ));
 7890                save_index += 2;
 7891            } else if (saves[save_index].needs_restore) {
 7892                try isel.emit(.ldr(
 7893                    saves[save_index].register,
 7894                    if (first_offset) |offset| .{ .unsigned_offset = .{
 7895                        .base = .sp,
 7896                        .offset = saves[save_index + 0].offset - offset,
 7897                    } } else form: {
 7898                        const offset = saves[save_index + 0].offset;
 7899                        first_offset = offset;
 7900                        break :form .{ .post_index = .{
 7901                            .base = .sp,
 7902                            .index = @intCast(saves_size - offset),
 7903                        } };
 7904                    },
 7905                ));
 7906                save_index += 1;
 7907            } else save_index += 1;
 7908        }
 7909        const offset = stack_size + first_offset.?;
 7910        const offset_lo: u12 = @truncate(offset >> 0);
 7911        const offset_hi: u12 = @truncate(offset >> 12);
 7912        if (isel.stack_align != .@"16" or (offset_lo > 0 and offset_hi > 0)) {
 7913            const fp_offset = @as(i11, first_offset.?) - frame_record_offset;
 7914            try isel.emit(if (fp_offset >= 0)
 7915                .add(.sp, .fp, .{ .immediate = @intCast(fp_offset) })
 7916            else
 7917                .sub(.sp, .fp, .{ .immediate = @intCast(-fp_offset) }));
 7918        } else {
 7919            if (offset_hi > 0) try isel.emit(.add(.sp, .sp, .{
 7920                .shifted_immediate = .{ .immediate = offset_hi, .lsl = .@"12" },
 7921            }));
 7922            if (offset_lo > 0) try isel.emit(.add(.sp, .sp, .{
 7923                .immediate = offset_lo,
 7924            }));
 7925        }
 7926        wip_mir_log.debug("{f}<epilogue>:\n", .{nav.fqn.fmt(ip)});
 7927    }
 7928    return epilogue;
 7929}
 7930
 7931fn fmtDom(isel: *Select, inst: Air.Inst.Index, start: u32, len: u32) struct {
 7932    isel: *Select,
 7933    inst: Air.Inst.Index,
 7934    start: u32,
 7935    len: u32,
 7936    pub fn format(data: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
 7937        try writer.print("%{d} -> {{", .{@intFromEnum(data.inst)});
 7938        var first = true;
 7939        for (data.isel.blocks.keys()[0..data.len], 0..) |block_inst_index, dom_index| {
 7940            if (@as(u1, @truncate(data.isel.dom.items[
 7941                data.start + dom_index / @bitSizeOf(DomInt)
 7942            ] >> @truncate(dom_index))) == 0) continue;
 7943            if (first) {
 7944                first = false;
 7945            } else {
 7946                try writer.writeByte(',');
 7947            }
 7948            switch (block_inst_index) {
 7949                Block.main => try writer.writeAll(" %main"),
 7950                else => try writer.print(" %{d}", .{@intFromEnum(block_inst_index)}),
 7951            }
 7952        }
 7953        if (!first) try writer.writeByte(' ');
 7954        try writer.writeByte('}');
 7955    }
 7956} {
 7957    return .{ .isel = isel, .inst = inst, .start = start, .len = len };
 7958}
 7959
 7960fn fmtLoopLive(isel: *Select, loop_inst: Air.Inst.Index) struct {
 7961    isel: *Select,
 7962    inst: Air.Inst.Index,
 7963    pub fn format(data: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
 7964        const loops = data.isel.loops.values();
 7965        const loop_index = data.isel.loops.getIndex(data.inst).?;
 7966        const live_insts =
 7967            data.isel.loop_live.list.items[loops[loop_index].live..loops[loop_index + 1].live];
 7968
 7969        try writer.print("%{d} <- {{", .{@intFromEnum(data.inst)});
 7970        var first = true;
 7971        for (live_insts) |live_inst| {
 7972            if (first) {
 7973                first = false;
 7974            } else {
 7975                try writer.writeByte(',');
 7976            }
 7977            try writer.print(" %{d}", .{@intFromEnum(live_inst)});
 7978        }
 7979        if (!first) try writer.writeByte(' ');
 7980        try writer.writeByte('}');
 7981    }
 7982} {
 7983    return .{ .isel = isel, .inst = loop_inst };
 7984}
 7985
 7986fn fmtType(isel: *Select, ty: ZigType) ZigType.Formatter {
 7987    return ty.fmt(isel.pt);
 7988}
 7989
 7990fn fmtConstant(isel: *Select, constant: Constant) @typeInfo(@TypeOf(Constant.fmtValue)).@"fn".return_type.? {
 7991    return constant.fmtValue(isel.pt);
 7992}
 7993
 7994fn block(
 7995    isel: *Select,
 7996    air_inst_index: Air.Inst.Index,
 7997    res_ty: ZigType,
 7998    air_body: []const Air.Inst.Index,
 7999) !void {
 8000    if (res_ty.toIntern() != .noreturn_type) {
 8001        isel.blocks.putAssumeCapacityNoClobber(air_inst_index, .{
 8002            .live_registers = isel.live_registers,
 8003            .target_label = @intCast(isel.instructions.items.len),
 8004        });
 8005    }
 8006    try isel.body(air_body);
 8007    if (res_ty.toIntern() != .noreturn_type) {
 8008        const block_entry = isel.blocks.pop().?;
 8009        assert(block_entry.key == air_inst_index);
 8010        if (isel.live_values.fetchRemove(air_inst_index)) |result_vi| result_vi.value.deref(isel);
 8011    }
 8012}
 8013
 8014fn emit(isel: *Select, instruction: codegen.aarch64.encoding.Instruction) !void {
 8015    wip_mir_log.debug("  | {f}", .{instruction});
 8016    try isel.instructions.append(isel.pt.zcu.gpa, instruction);
 8017}
 8018
 8019fn emitPanic(isel: *Select, panic_id: Zcu.SimplePanicId) !void {
 8020    const zcu = isel.pt.zcu;
 8021    try isel.nav_relocs.append(zcu.gpa, .{
 8022        .nav = switch (zcu.intern_pool.indexToKey(zcu.builtin_decl_values.get(panic_id.toBuiltin()))) {
 8023            else => unreachable,
 8024            inline .@"extern", .func => |func| func.owner_nav,
 8025        },
 8026        .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 8027    });
 8028    try isel.emit(.bl(0));
 8029}
 8030
 8031fn emitLiteral(isel: *Select, bytes: []const u8) !void {
 8032    const words: []align(1) const u32 = @ptrCast(bytes);
 8033    const literals = try isel.literals.addManyAsSlice(isel.pt.zcu.gpa, words.len);
 8034    switch (isel.target.cpu.arch.endian()) {
 8035        .little => @memcpy(literals, words),
 8036        .big => for (words, 0..) |word, word_index| {
 8037            literals[literals.len - 1 - word_index] = @byteSwap(word);
 8038        },
 8039    }
 8040}
 8041
 8042fn fail(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
 8043    @branchHint(.cold);
 8044    return isel.pt.zcu.codegenFail(isel.nav_index, format, args);
 8045}
 8046
 8047/// dst = src
 8048fn movImmediate(isel: *Select, dst_reg: Register, src_imm: u64) !void {
 8049    const sf = dst_reg.format.general;
 8050    if (src_imm == 0) {
 8051        const zr: Register = switch (sf) {
 8052            .word => .wzr,
 8053            .doubleword => .xzr,
 8054        };
 8055        return isel.emit(.orr(dst_reg, zr, .{ .register = zr }));
 8056    }
 8057
 8058    const Part = u16;
 8059    const min_part: Part = std.math.minInt(Part);
 8060    const max_part: Part = std.math.maxInt(Part);
 8061
 8062    const parts: [4]Part = @bitCast(switch (sf) {
 8063        .word => @as(u32, @intCast(src_imm)),
 8064        .doubleword => @as(u64, @intCast(src_imm)),
 8065    });
 8066    const width: u7 = switch (sf) {
 8067        .word => 32,
 8068        .doubleword => 64,
 8069    };
 8070    const parts_len: u3 = @intCast(@divExact(width, @bitSizeOf(Part)));
 8071    var equal_min_count: u3 = 0;
 8072    var equal_max_count: u3 = 0;
 8073    for (parts[0..parts_len]) |part| {
 8074        equal_min_count += @intFromBool(part == min_part);
 8075        equal_max_count += @intFromBool(part == max_part);
 8076    }
 8077
 8078    const equal_fill_count, const fill_part: Part = if (equal_min_count >= equal_max_count)
 8079        .{ equal_min_count, min_part }
 8080    else
 8081        .{ equal_max_count, max_part };
 8082    var remaining_parts = @max(parts_len - equal_fill_count, 1);
 8083
 8084    if (remaining_parts > 1) {
 8085        var elem_width: u8 = 2;
 8086        while (elem_width <= width) : (elem_width <<= 1) {
 8087            const emask = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - elem_width);
 8088            const rmask = @divExact(@as(u64, switch (sf) {
 8089                .word => std.math.maxInt(u32),
 8090                .doubleword => std.math.maxInt(u64),
 8091            }), emask);
 8092            const elem = src_imm & emask;
 8093            if (src_imm != elem * rmask) continue;
 8094            const imask: u64 = @bitCast(@as(i64, @bitCast(elem << 63)) >> 63);
 8095            const lsb0 = elem ^ (imask & emask);
 8096            const lsb1 = (lsb0 - 1) | lsb0;
 8097            if ((lsb1 +% 1) & lsb1 == 0) {
 8098                const lo: u6 = @intCast(@ctz(lsb0));
 8099                const hi: u6 = @intCast(@clz(lsb0) - (64 - elem_width));
 8100                const mid: u6 = @intCast(elem_width - lo - hi);
 8101                const smask: u6 = @truncate(imask);
 8102                const mid_masked = mid & ~smask;
 8103                return isel.emit(.orr(
 8104                    dst_reg,
 8105                    switch (sf) {
 8106                        .word => .wzr,
 8107                        .doubleword => .xzr,
 8108                    },
 8109                    .{ .immediate = .{
 8110                        .N = @enumFromInt(elem_width >> 6),
 8111                        .immr = hi + mid_masked,
 8112                        .imms = ((((lo + hi) & smask) | mid_masked) - 1) | -%@as(u6, @truncate(elem_width)) << 1,
 8113                    } },
 8114                ));
 8115            }
 8116        }
 8117    }
 8118
 8119    var part_index = parts_len;
 8120    while (part_index > 0) {
 8121        part_index -= 1;
 8122        if (part_index >= remaining_parts and parts[part_index] == fill_part) continue;
 8123        remaining_parts -= 1;
 8124        try isel.emit(if (remaining_parts > 0) .movk(
 8125            dst_reg,
 8126            parts[part_index],
 8127            .{ .lsl = @enumFromInt(part_index) },
 8128        ) else switch (fill_part) {
 8129            else => unreachable,
 8130            min_part => .movz(
 8131                dst_reg,
 8132                parts[part_index],
 8133                .{ .lsl = @enumFromInt(part_index) },
 8134            ),
 8135            max_part => .movn(
 8136                dst_reg,
 8137                ~parts[part_index],
 8138                .{ .lsl = @enumFromInt(part_index) },
 8139            ),
 8140        });
 8141    }
 8142    assert(remaining_parts == 0);
 8143}
 8144
 8145/// elem_ptr = base +- elem_size * index
 8146/// elem_ptr, base, and index may alias
 8147fn elemPtr(
 8148    isel: *Select,
 8149    elem_ptr_ra: Register.Alias,
 8150    base_ra: Register.Alias,
 8151    op: codegen.aarch64.encoding.Instruction.AddSubtractOp,
 8152    elem_size: u64,
 8153    index_vi: Value.Index,
 8154) !void {
 8155    const index_mat = try index_vi.matReg(isel);
 8156    switch (@popCount(elem_size)) {
 8157        0 => unreachable,
 8158        1 => try isel.emit(switch (op) {
 8159            .add => switch (base_ra) {
 8160                else => .add(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{
 8161                    .register = index_mat.ra.x(),
 8162                    .shift = .{ .lsl = @intCast(@ctz(elem_size)) },
 8163                } }),
 8164                .zr => switch (@ctz(elem_size)) {
 8165                    0 => .orr(elem_ptr_ra.x(), .xzr, .{ .register = index_mat.ra.x() }),
 8166                    else => |shift| .ubfm(elem_ptr_ra.x(), index_mat.ra.x(), .{
 8167                        .N = .doubleword,
 8168                        .immr = @intCast(64 - shift),
 8169                        .imms = @intCast(63 - shift),
 8170                    }),
 8171                },
 8172            },
 8173            .sub => .sub(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{
 8174                .register = index_mat.ra.x(),
 8175                .shift = .{ .lsl = @intCast(@ctz(elem_size)) },
 8176            } }),
 8177        }),
 8178        2 => {
 8179            const shift: u6 = @intCast(@ctz(elem_size));
 8180            const temp_ra, const free_temp_ra = temp_ra: switch (op) {
 8181                .add => switch (base_ra) {
 8182                    else => {
 8183                        const temp_ra = try isel.allocIntReg();
 8184                        errdefer isel.freeReg(temp_ra);
 8185                        try isel.emit(.add(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{
 8186                            .register = temp_ra.x(),
 8187                            .shift = .{ .lsl = shift },
 8188                        } }));
 8189                        break :temp_ra .{ temp_ra, true };
 8190                    },
 8191                    .zr => {
 8192                        if (shift > 0) try isel.emit(.ubfm(elem_ptr_ra.x(), elem_ptr_ra.x(), .{
 8193                            .N = .doubleword,
 8194                            .immr = -%shift,
 8195                            .imms = ~shift,
 8196                        }));
 8197                        break :temp_ra .{ elem_ptr_ra, false };
 8198                    },
 8199                },
 8200                .sub => {
 8201                    const temp_ra = try isel.allocIntReg();
 8202                    errdefer isel.freeReg(temp_ra);
 8203                    try isel.emit(.sub(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{
 8204                        .register = temp_ra.x(),
 8205                        .shift = .{ .lsl = shift },
 8206                    } }));
 8207                    break :temp_ra .{ temp_ra, true };
 8208                },
 8209            };
 8210            defer if (free_temp_ra) isel.freeReg(temp_ra);
 8211            try isel.emit(.add(temp_ra.x(), index_mat.ra.x(), .{ .shifted_register = .{
 8212                .register = index_mat.ra.x(),
 8213                .shift = .{ .lsl = @intCast(63 - @clz(elem_size) - shift) },
 8214            } }));
 8215        },
 8216        else => {
 8217            const elem_size_lsb1 = (elem_size - 1) | elem_size;
 8218            if ((elem_size_lsb1 +% 1) & elem_size_lsb1 == 0) {
 8219                const shift: u6 = @intCast(@ctz(elem_size));
 8220                const temp_ra = temp_ra: switch (op) {
 8221                    .add => {
 8222                        const temp_ra = try isel.allocIntReg();
 8223                        errdefer isel.freeReg(temp_ra);
 8224                        try isel.emit(.sub(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{
 8225                            .register = temp_ra.x(),
 8226                            .shift = .{ .lsl = shift },
 8227                        } }));
 8228                        break :temp_ra temp_ra;
 8229                    },
 8230                    .sub => switch (base_ra) {
 8231                        else => {
 8232                            const temp_ra = try isel.allocIntReg();
 8233                            errdefer isel.freeReg(temp_ra);
 8234                            try isel.emit(.add(elem_ptr_ra.x(), base_ra.x(), .{ .shifted_register = .{
 8235                                .register = temp_ra.x(),
 8236                                .shift = .{ .lsl = shift },
 8237                            } }));
 8238                            break :temp_ra temp_ra;
 8239                        },
 8240                        .zr => {
 8241                            if (shift > 0) try isel.emit(.ubfm(elem_ptr_ra.x(), elem_ptr_ra.x(), .{
 8242                                .N = .doubleword,
 8243                                .immr = -%shift,
 8244                                .imms = ~shift,
 8245                            }));
 8246                            break :temp_ra elem_ptr_ra;
 8247                        },
 8248                    },
 8249                };
 8250                defer if (temp_ra != elem_ptr_ra) isel.freeReg(temp_ra);
 8251                try isel.emit(.sub(temp_ra.x(), index_mat.ra.x(), .{ .shifted_register = .{
 8252                    .register = index_mat.ra.x(),
 8253                    .shift = .{ .lsl = @intCast(64 - @clz(elem_size) - shift) },
 8254                } }));
 8255            } else {
 8256                try isel.emit(switch (op) {
 8257                    .add => .madd(elem_ptr_ra.x(), index_mat.ra.x(), elem_ptr_ra.x(), base_ra.x()),
 8258                    .sub => .msub(elem_ptr_ra.x(), index_mat.ra.x(), elem_ptr_ra.x(), base_ra.x()),
 8259                });
 8260                try isel.movImmediate(elem_ptr_ra.x(), elem_size);
 8261            }
 8262        },
 8263    }
 8264    try index_mat.finish(isel);
 8265}
 8266
 8267fn clzLimb(
 8268    isel: *Select,
 8269    res_ra: Register.Alias,
 8270    src_int_info: std.builtin.Type.Int,
 8271    src_ra: Register.Alias,
 8272) !void {
 8273    switch (src_int_info.bits) {
 8274        else => unreachable,
 8275        1...31 => |bits| {
 8276            try isel.emit(.sub(res_ra.w(), res_ra.w(), .{
 8277                .immediate = @intCast(32 - bits),
 8278            }));
 8279            switch (src_int_info.signedness) {
 8280                .signed => {
 8281                    try isel.emit(.clz(res_ra.w(), res_ra.w()));
 8282                    try isel.emit(.ubfm(res_ra.w(), src_ra.w(), .{
 8283                        .N = .word,
 8284                        .immr = 0,
 8285                        .imms = @intCast(bits - 1),
 8286                    }));
 8287                },
 8288                .unsigned => try isel.emit(.clz(res_ra.w(), src_ra.w())),
 8289            }
 8290        },
 8291        32 => try isel.emit(.clz(res_ra.w(), src_ra.w())),
 8292        33...63 => |bits| {
 8293            try isel.emit(.sub(res_ra.w(), res_ra.w(), .{
 8294                .immediate = @intCast(64 - bits),
 8295            }));
 8296            switch (src_int_info.signedness) {
 8297                .signed => {
 8298                    try isel.emit(.clz(res_ra.x(), res_ra.x()));
 8299                    try isel.emit(.ubfm(res_ra.x(), src_ra.x(), .{
 8300                        .N = .doubleword,
 8301                        .immr = 0,
 8302                        .imms = @intCast(bits - 1),
 8303                    }));
 8304                },
 8305                .unsigned => try isel.emit(.clz(res_ra.x(), src_ra.x())),
 8306            }
 8307        },
 8308        64 => try isel.emit(.clz(res_ra.x(), src_ra.x())),
 8309    }
 8310}
 8311
 8312fn ctzLimb(
 8313    isel: *Select,
 8314    res_ra: Register.Alias,
 8315    src_int_info: std.builtin.Type.Int,
 8316    src_ra: Register.Alias,
 8317) !void {
 8318    switch (src_int_info.bits) {
 8319        else => unreachable,
 8320        1...31 => |bits| {
 8321            try isel.emit(.clz(res_ra.w(), res_ra.w()));
 8322            try isel.emit(.rbit(res_ra.w(), res_ra.w()));
 8323            try isel.emit(.orr(res_ra.w(), src_ra.w(), .{ .immediate = .{
 8324                .N = .word,
 8325                .immr = @intCast(32 - bits),
 8326                .imms = @intCast(32 - bits - 1),
 8327            } }));
 8328        },
 8329        32 => {
 8330            try isel.emit(.clz(res_ra.w(), res_ra.w()));
 8331            try isel.emit(.rbit(res_ra.w(), src_ra.w()));
 8332        },
 8333        33...63 => |bits| {
 8334            try isel.emit(.clz(res_ra.x(), res_ra.x()));
 8335            try isel.emit(.rbit(res_ra.x(), res_ra.x()));
 8336            try isel.emit(.orr(res_ra.x(), src_ra.x(), .{ .immediate = .{
 8337                .N = .doubleword,
 8338                .immr = @intCast(64 - bits),
 8339                .imms = @intCast(64 - bits - 1),
 8340            } }));
 8341        },
 8342        64 => {
 8343            try isel.emit(.clz(res_ra.x(), res_ra.x()));
 8344            try isel.emit(.rbit(res_ra.x(), src_ra.x()));
 8345        },
 8346    }
 8347}
 8348
 8349fn cmp(
 8350    isel: *Select,
 8351    res_ra: Register.Alias,
 8352    ty: ZigType,
 8353    orig_lhs_vi: Value.Index,
 8354    op: std.math.CompareOperator,
 8355    orig_rhs_vi: Value.Index,
 8356) !struct { cset_label: usize } {
 8357    var lhs_vi = orig_lhs_vi;
 8358    var rhs_vi = orig_rhs_vi;
 8359    if (!ty.isRuntimeFloat()) {
 8360        const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type)
 8361            .{ .signedness = .unsigned, .bits = 1 }
 8362        else if (ty.isAbiInt(isel.pt.zcu))
 8363            ty.intInfo(isel.pt.zcu)
 8364        else if (ty.isPtrAtRuntime(isel.pt.zcu))
 8365            .{ .signedness = .unsigned, .bits = 64 }
 8366        else
 8367            return isel.fail("bad cmp_{t} {f}", .{ op, isel.fmtType(ty) });
 8368        if (int_info.bits > 256) return isel.fail("too big cmp_{t} {f}", .{ op, isel.fmtType(ty) });
 8369        try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) {
 8370            .lt => switch (int_info.signedness) {
 8371                .signed => .lt,
 8372                .unsigned => .lo,
 8373            },
 8374            .lte => switch (int_info.bits) {
 8375                else => unreachable,
 8376                1...64 => switch (int_info.signedness) {
 8377                    .signed => .le,
 8378                    .unsigned => .ls,
 8379                },
 8380                65...128 => {
 8381                    std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
 8382                    continue :cond .gte;
 8383                },
 8384            },
 8385            .eq => .eq,
 8386            .gte => switch (int_info.signedness) {
 8387                .signed => .ge,
 8388                .unsigned => .hs,
 8389            },
 8390            .gt => switch (int_info.bits) {
 8391                else => unreachable,
 8392                1...64 => switch (int_info.signedness) {
 8393                    .signed => .gt,
 8394                    .unsigned => .hi,
 8395                },
 8396                65...128 => {
 8397                    std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
 8398                    continue :cond .lt;
 8399                },
 8400            },
 8401            .neq => .ne,
 8402        })));
 8403        const cset_label = isel.instructions.items.len;
 8404
 8405        var part_offset = lhs_vi.size(isel);
 8406        while (part_offset > 0) {
 8407            const part_size = @min(part_offset, 8);
 8408            part_offset -= part_size;
 8409            var lhs_part_it = lhs_vi.field(ty, part_offset, part_size);
 8410            const lhs_part_vi = try lhs_part_it.only(isel);
 8411            const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
 8412            var rhs_part_it = rhs_vi.field(ty, part_offset, part_size);
 8413            const rhs_part_vi = try rhs_part_it.only(isel);
 8414            const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
 8415            try isel.emit(switch (part_size) {
 8416                else => unreachable,
 8417                1...4 => switch (part_offset) {
 8418                    0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 8419                    else => switch (op) {
 8420                        .lt, .lte, .gte, .gt => .sbcs(
 8421                            .wzr,
 8422                            lhs_part_mat.ra.w(),
 8423                            rhs_part_mat.ra.w(),
 8424                        ),
 8425                        .eq, .neq => .ccmp(
 8426                            lhs_part_mat.ra.w(),
 8427                            .{ .register = rhs_part_mat.ra.w() },
 8428                            .{ .n = false, .z = false, .c = false, .v = false },
 8429                            .eq,
 8430                        ),
 8431                    },
 8432                },
 8433                5...8 => switch (part_offset) {
 8434                    0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 8435                    else => switch (op) {
 8436                        .lt, .lte, .gte, .gt => .sbcs(
 8437                            .xzr,
 8438                            lhs_part_mat.ra.x(),
 8439                            rhs_part_mat.ra.x(),
 8440                        ),
 8441                        .eq, .neq => .ccmp(
 8442                            lhs_part_mat.ra.x(),
 8443                            .{ .register = rhs_part_mat.ra.x() },
 8444                            .{ .n = false, .z = false, .c = false, .v = false },
 8445                            .eq,
 8446                        ),
 8447                    },
 8448                },
 8449            });
 8450            try rhs_part_mat.finish(isel);
 8451            try lhs_part_mat.finish(isel);
 8452        }
 8453        return .{ .cset_label = cset_label };
 8454    }
 8455    switch (ty.floatBits(isel.target)) {
 8456        else => unreachable,
 8457        16, 32, 64 => |bits| {
 8458            const need_fcvt = switch (bits) {
 8459                else => unreachable,
 8460                16 => !isel.target.cpu.has(.aarch64, .fullfp16),
 8461                32, 64 => false,
 8462            };
 8463            try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (op) {
 8464                .lt => .lo,
 8465                .lte => .ls,
 8466                .eq => .eq,
 8467                .gte => .ge,
 8468                .gt => .gt,
 8469                .neq => .ne,
 8470            })));
 8471            const cset_label = isel.instructions.items.len;
 8472
 8473            const lhs_mat = try lhs_vi.matReg(isel);
 8474            const rhs_mat = try rhs_vi.matReg(isel);
 8475            const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
 8476            defer if (need_fcvt) isel.freeReg(lhs_ra);
 8477            const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
 8478            defer if (need_fcvt) isel.freeReg(rhs_ra);
 8479            try isel.emit(bits: switch (bits) {
 8480                else => unreachable,
 8481                16 => if (need_fcvt)
 8482                    continue :bits 32
 8483                else
 8484                    .fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }),
 8485                32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }),
 8486                64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }),
 8487            });
 8488            if (need_fcvt) {
 8489                try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
 8490                try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
 8491            }
 8492            try rhs_mat.finish(isel);
 8493            try lhs_mat.finish(isel);
 8494            return .{ .cset_label = cset_label };
 8495        },
 8496        80, 128 => |bits| {
 8497            try call.prepareReturn(isel);
 8498            try call.returnFill(isel, .r0);
 8499            try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) {
 8500                .lt => .lt,
 8501                .lte => .le,
 8502                .eq => .eq,
 8503                .gte => {
 8504                    std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
 8505                    continue :cond .lte;
 8506                },
 8507                .gt => {
 8508                    std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
 8509                    continue :cond .lt;
 8510                },
 8511                .neq => .ne,
 8512            })));
 8513            const cset_label = isel.instructions.items.len;
 8514            try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 }));
 8515            try call.finishReturn(isel);
 8516
 8517            try call.prepareCallee(isel);
 8518            try isel.global_relocs.append(isel.pt.zcu.gpa, .{
 8519                .name = switch (bits) {
 8520                    else => unreachable,
 8521                    16 => "__cmphf2",
 8522                    32 => "__cmpsf2",
 8523                    64 => "__cmpdf2",
 8524                    80 => "__cmpxf2",
 8525                    128 => "__cmptf2",
 8526                },
 8527                .reloc = .{ .label = @intCast(isel.instructions.items.len) },
 8528            });
 8529            try isel.emit(.bl(0));
 8530            try call.finishCallee(isel);
 8531
 8532            try call.prepareParams(isel);
 8533            switch (bits) {
 8534                else => unreachable,
 8535                16, 32, 64, 128 => {
 8536                    try call.paramLiveOut(isel, rhs_vi, .v1);
 8537                    try call.paramLiveOut(isel, lhs_vi, .v0);
 8538                },
 8539                80 => {
 8540                    var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
 8541                    const rhs_hi16_vi = try rhs_hi16_it.only(isel);
 8542                    try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
 8543                    var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
 8544                    const rhs_lo64_vi = try rhs_lo64_it.only(isel);
 8545                    try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
 8546                    var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
 8547                    const lhs_hi16_vi = try lhs_hi16_it.only(isel);
 8548                    try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
 8549                    var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
 8550                    const lhs_lo64_vi = try lhs_lo64_it.only(isel);
 8551                    try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
 8552                },
 8553            }
 8554            try call.finishParams(isel);
 8555            return .{ .cset_label = cset_label };
 8556        },
 8557    }
 8558}
 8559
 8560fn loadReg(
 8561    isel: *Select,
 8562    ra: Register.Alias,
 8563    size: u64,
 8564    signedness: std.builtin.Signedness,
 8565    base_ra: Register.Alias,
 8566    offset: i65,
 8567) !void {
 8568    switch (size) {
 8569        0 => unreachable,
 8570        1 => {
 8571            if (std.math.cast(u12, offset)) |unsigned_offset| return isel.emit(if (ra.isVector()) .ldr(
 8572                ra.b(),
 8573                .{ .unsigned_offset = .{
 8574                    .base = base_ra.x(),
 8575                    .offset = unsigned_offset,
 8576                } },
 8577            ) else switch (signedness) {
 8578                .signed => .ldrsb(ra.w(), .{ .unsigned_offset = .{
 8579                    .base = base_ra.x(),
 8580                    .offset = unsigned_offset,
 8581                } }),
 8582                .unsigned => .ldrb(ra.w(), .{ .unsigned_offset = .{
 8583                    .base = base_ra.x(),
 8584                    .offset = unsigned_offset,
 8585                } }),
 8586            });
 8587            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(if (ra.isVector())
 8588                .ldur(ra.b(), base_ra.x(), signed_offset)
 8589            else switch (signedness) {
 8590                .signed => .ldursb(ra.w(), base_ra.x(), signed_offset),
 8591                .unsigned => .ldurb(ra.w(), base_ra.x(), signed_offset),
 8592            });
 8593        },
 8594        2 => {
 8595            if (std.math.cast(u13, offset)) |unsigned_offset| if (unsigned_offset % 2 == 0)
 8596                return isel.emit(if (ra.isVector()) .ldr(
 8597                    ra.h(),
 8598                    .{ .unsigned_offset = .{
 8599                        .base = base_ra.x(),
 8600                        .offset = unsigned_offset,
 8601                    } },
 8602                ) else switch (signedness) {
 8603                    .signed => .ldrsh(
 8604                        ra.w(),
 8605                        .{ .unsigned_offset = .{
 8606                            .base = base_ra.x(),
 8607                            .offset = unsigned_offset,
 8608                        } },
 8609                    ),
 8610                    .unsigned => .ldrh(
 8611                        ra.w(),
 8612                        .{ .unsigned_offset = .{
 8613                            .base = base_ra.x(),
 8614                            .offset = unsigned_offset,
 8615                        } },
 8616                    ),
 8617                });
 8618            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(if (ra.isVector())
 8619                .ldur(ra.h(), base_ra.x(), signed_offset)
 8620            else switch (signedness) {
 8621                .signed => .ldursh(ra.w(), base_ra.x(), signed_offset),
 8622                .unsigned => .ldurh(ra.w(), base_ra.x(), signed_offset),
 8623            });
 8624        },
 8625        3 => {
 8626            const lo16_ra = try isel.allocIntReg();
 8627            defer isel.freeReg(lo16_ra);
 8628            try isel.emit(.orr(ra.w(), lo16_ra.w(), .{ .shifted_register = .{
 8629                .register = ra.w(),
 8630                .shift = .{ .lsl = 16 },
 8631            } }));
 8632            try isel.loadReg(ra, 1, signedness, base_ra, offset + 2);
 8633            return isel.loadReg(lo16_ra, 2, .unsigned, base_ra, offset);
 8634        },
 8635        4 => {
 8636            if (std.math.cast(u14, offset)) |unsigned_offset| if (unsigned_offset % 4 == 0) return isel.emit(.ldr(
 8637                if (ra.isVector()) ra.s() else ra.w(),
 8638                .{ .unsigned_offset = .{
 8639                    .base = base_ra.x(),
 8640                    .offset = unsigned_offset,
 8641                } },
 8642            ));
 8643            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.ldur(
 8644                if (ra.isVector()) ra.s() else ra.w(),
 8645                base_ra.x(),
 8646                signed_offset,
 8647            ));
 8648        },
 8649        5, 6 => {
 8650            const lo32_ra = try isel.allocIntReg();
 8651            defer isel.freeReg(lo32_ra);
 8652            try isel.emit(.orr(ra.x(), lo32_ra.x(), .{ .shifted_register = .{
 8653                .register = ra.x(),
 8654                .shift = .{ .lsl = 32 },
 8655            } }));
 8656            try isel.loadReg(ra, size - 4, signedness, base_ra, offset + 4);
 8657            return isel.loadReg(lo32_ra, 4, .unsigned, base_ra, offset);
 8658        },
 8659        7 => {
 8660            const lo32_ra = try isel.allocIntReg();
 8661            defer isel.freeReg(lo32_ra);
 8662            const lo48_ra = try isel.allocIntReg();
 8663            defer isel.freeReg(lo48_ra);
 8664            try isel.emit(.orr(ra.x(), lo48_ra.x(), .{ .shifted_register = .{
 8665                .register = ra.x(),
 8666                .shift = .{ .lsl = 32 + 16 },
 8667            } }));
 8668            try isel.loadReg(ra, 1, signedness, base_ra, offset + 4 + 2);
 8669            try isel.emit(.orr(lo48_ra.x(), lo32_ra.x(), .{ .shifted_register = .{
 8670                .register = lo48_ra.x(),
 8671                .shift = .{ .lsl = 32 },
 8672            } }));
 8673            try isel.loadReg(lo48_ra, 2, .unsigned, base_ra, offset + 4);
 8674            return isel.loadReg(lo32_ra, 4, .unsigned, base_ra, offset);
 8675        },
 8676        8 => {
 8677            if (std.math.cast(u15, offset)) |unsigned_offset| if (unsigned_offset % 8 == 0) return isel.emit(.ldr(
 8678                if (ra.isVector()) ra.d() else ra.x(),
 8679                .{ .unsigned_offset = .{
 8680                    .base = base_ra.x(),
 8681                    .offset = unsigned_offset,
 8682                } },
 8683            ));
 8684            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.ldur(
 8685                if (ra.isVector()) ra.d() else ra.x(),
 8686                base_ra.x(),
 8687                signed_offset,
 8688            ));
 8689        },
 8690        16 => {
 8691            if (std.math.cast(u16, offset)) |unsigned_offset| if (unsigned_offset % 16 == 0) return isel.emit(.ldr(
 8692                ra.q(),
 8693                .{ .unsigned_offset = .{
 8694                    .base = base_ra.x(),
 8695                    .offset = unsigned_offset,
 8696                } },
 8697            ));
 8698            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.ldur(ra.q(), base_ra.x(), signed_offset));
 8699        },
 8700        else => return isel.fail("bad load size: {d}", .{size}),
 8701    }
 8702    const ptr_ra = try isel.allocIntReg();
 8703    defer isel.freeReg(ptr_ra);
 8704    try isel.loadReg(ra, size, signedness, ptr_ra, 0);
 8705    if (std.math.cast(u24, offset)) |pos_offset| {
 8706        const lo12: u12 = @truncate(pos_offset >> 0);
 8707        const hi12: u12 = @intCast(pos_offset >> 12);
 8708        if (hi12 > 0) try isel.emit(.add(
 8709            ptr_ra.x(),
 8710            if (lo12 > 0) ptr_ra.x() else base_ra.x(),
 8711            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 8712        ));
 8713        if (lo12 > 0 or hi12 == 0) try isel.emit(.add(ptr_ra.x(), base_ra.x(), .{ .immediate = lo12 }));
 8714    } else if (std.math.cast(u24, -offset)) |neg_offset| {
 8715        const lo12: u12 = @truncate(neg_offset >> 0);
 8716        const hi12: u12 = @intCast(neg_offset >> 12);
 8717        if (hi12 > 0) try isel.emit(.sub(
 8718            ptr_ra.x(),
 8719            if (lo12 > 0) ptr_ra.x() else base_ra.x(),
 8720            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 8721        ));
 8722        if (lo12 > 0 or hi12 == 0) try isel.emit(.sub(ptr_ra.x(), base_ra.x(), .{ .immediate = lo12 }));
 8723    } else {
 8724        try isel.emit(.add(ptr_ra.x(), base_ra.x(), .{ .register = ptr_ra.x() }));
 8725        try isel.movImmediate(ptr_ra.x(), @truncate(@as(u65, @bitCast(offset))));
 8726    }
 8727}
 8728
 8729fn storeReg(
 8730    isel: *Select,
 8731    ra: Register.Alias,
 8732    size: u64,
 8733    base_ra: Register.Alias,
 8734    offset: i65,
 8735) !void {
 8736    switch (size) {
 8737        0 => unreachable,
 8738        1 => {
 8739            if (std.math.cast(u12, offset)) |unsigned_offset| return isel.emit(if (ra.isVector()) .str(
 8740                ra.b(),
 8741                .{ .unsigned_offset = .{
 8742                    .base = base_ra.x(),
 8743                    .offset = unsigned_offset,
 8744                } },
 8745            ) else .strb(
 8746                ra.w(),
 8747                .{ .unsigned_offset = .{
 8748                    .base = base_ra.x(),
 8749                    .offset = unsigned_offset,
 8750                } },
 8751            ));
 8752            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(if (ra.isVector())
 8753                .stur(ra.b(), base_ra.x(), signed_offset)
 8754            else
 8755                .sturb(ra.w(), base_ra.x(), signed_offset));
 8756        },
 8757        2 => {
 8758            if (std.math.cast(u13, offset)) |unsigned_offset| if (unsigned_offset % 2 == 0)
 8759                return isel.emit(if (ra.isVector()) .str(
 8760                    ra.h(),
 8761                    .{ .unsigned_offset = .{
 8762                        .base = base_ra.x(),
 8763                        .offset = unsigned_offset,
 8764                    } },
 8765                ) else .strh(
 8766                    ra.w(),
 8767                    .{ .unsigned_offset = .{
 8768                        .base = base_ra.x(),
 8769                        .offset = unsigned_offset,
 8770                    } },
 8771                ));
 8772            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(if (ra.isVector())
 8773                .stur(ra.h(), base_ra.x(), signed_offset)
 8774            else
 8775                .sturh(ra.w(), base_ra.x(), signed_offset));
 8776        },
 8777        3 => {
 8778            const hi8_ra = try isel.allocIntReg();
 8779            defer isel.freeReg(hi8_ra);
 8780            try isel.storeReg(hi8_ra, 1, base_ra, offset + 2);
 8781            try isel.storeReg(ra, 2, base_ra, offset);
 8782            return isel.emit(.ubfm(hi8_ra.w(), ra.w(), .{
 8783                .N = .word,
 8784                .immr = 16,
 8785                .imms = 16 + 8 - 1,
 8786            }));
 8787        },
 8788        4 => {
 8789            if (std.math.cast(u14, offset)) |unsigned_offset| if (unsigned_offset % 4 == 0) return isel.emit(.str(
 8790                if (ra.isVector()) ra.s() else ra.w(),
 8791                .{ .unsigned_offset = .{
 8792                    .base = base_ra.x(),
 8793                    .offset = unsigned_offset,
 8794                } },
 8795            ));
 8796            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.stur(
 8797                if (ra.isVector()) ra.s() else ra.w(),
 8798                base_ra.x(),
 8799                signed_offset,
 8800            ));
 8801        },
 8802        5 => {
 8803            const hi8_ra = try isel.allocIntReg();
 8804            defer isel.freeReg(hi8_ra);
 8805            try isel.storeReg(hi8_ra, 1, base_ra, offset + 4);
 8806            try isel.storeReg(ra, 4, base_ra, offset);
 8807            return isel.emit(.ubfm(hi8_ra.x(), ra.x(), .{
 8808                .N = .doubleword,
 8809                .immr = 32,
 8810                .imms = 32 + 8 - 1,
 8811            }));
 8812        },
 8813        6 => {
 8814            const hi16_ra = try isel.allocIntReg();
 8815            defer isel.freeReg(hi16_ra);
 8816            try isel.storeReg(hi16_ra, 2, base_ra, offset + 4);
 8817            try isel.storeReg(ra, 4, base_ra, offset);
 8818            return isel.emit(.ubfm(hi16_ra.x(), ra.x(), .{
 8819                .N = .doubleword,
 8820                .immr = 32,
 8821                .imms = 32 + 16 - 1,
 8822            }));
 8823        },
 8824        7 => {
 8825            const hi16_ra = try isel.allocIntReg();
 8826            defer isel.freeReg(hi16_ra);
 8827            const hi8_ra = try isel.allocIntReg();
 8828            defer isel.freeReg(hi8_ra);
 8829            try isel.storeReg(hi8_ra, 1, base_ra, offset + 6);
 8830            try isel.storeReg(hi16_ra, 2, base_ra, offset + 4);
 8831            try isel.storeReg(ra, 4, base_ra, offset);
 8832            try isel.emit(.ubfm(hi8_ra.x(), ra.x(), .{
 8833                .N = .doubleword,
 8834                .immr = 32 + 16,
 8835                .imms = 32 + 16 + 8 - 1,
 8836            }));
 8837            return isel.emit(.ubfm(hi16_ra.x(), ra.x(), .{
 8838                .N = .doubleword,
 8839                .immr = 32,
 8840                .imms = 32 + 16 - 1,
 8841            }));
 8842        },
 8843        8 => {
 8844            if (std.math.cast(u15, offset)) |unsigned_offset| if (unsigned_offset % 8 == 0) return isel.emit(.str(
 8845                if (ra.isVector()) ra.d() else ra.x(),
 8846                .{ .unsigned_offset = .{
 8847                    .base = base_ra.x(),
 8848                    .offset = unsigned_offset,
 8849                } },
 8850            ));
 8851            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.stur(
 8852                if (ra.isVector()) ra.d() else ra.x(),
 8853                base_ra.x(),
 8854                signed_offset,
 8855            ));
 8856        },
 8857        16 => {
 8858            if (std.math.cast(u16, offset)) |unsigned_offset| if (unsigned_offset % 16 == 0) return isel.emit(.str(
 8859                ra.q(),
 8860                .{ .unsigned_offset = .{
 8861                    .base = base_ra.x(),
 8862                    .offset = unsigned_offset,
 8863                } },
 8864            ));
 8865            if (std.math.cast(i9, offset)) |signed_offset| return isel.emit(.stur(ra.q(), base_ra.x(), signed_offset));
 8866        },
 8867        else => return isel.fail("bad store size: {d}", .{size}),
 8868    }
 8869    const ptr_ra = try isel.allocIntReg();
 8870    defer isel.freeReg(ptr_ra);
 8871    try isel.storeReg(ra, size, ptr_ra, 0);
 8872    if (std.math.cast(u24, offset)) |pos_offset| {
 8873        const lo12: u12 = @truncate(pos_offset >> 0);
 8874        const hi12: u12 = @intCast(pos_offset >> 12);
 8875        if (hi12 > 0) try isel.emit(.add(
 8876            ptr_ra.x(),
 8877            if (lo12 > 0) ptr_ra.x() else base_ra.x(),
 8878            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 8879        ));
 8880        if (lo12 > 0 or hi12 == 0) try isel.emit(.add(ptr_ra.x(), base_ra.x(), .{ .immediate = lo12 }));
 8881    } else if (std.math.cast(u24, -offset)) |neg_offset| {
 8882        const lo12: u12 = @truncate(neg_offset >> 0);
 8883        const hi12: u12 = @intCast(neg_offset >> 12);
 8884        if (hi12 > 0) try isel.emit(.sub(
 8885            ptr_ra.x(),
 8886            if (lo12 > 0) ptr_ra.x() else base_ra.x(),
 8887            .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
 8888        ));
 8889        if (lo12 > 0 or hi12 == 0) try isel.emit(.sub(ptr_ra.x(), base_ra.x(), .{ .immediate = lo12 }));
 8890    } else {
 8891        try isel.emit(.add(ptr_ra.x(), base_ra.x(), .{ .register = ptr_ra.x() }));
 8892        try isel.movImmediate(ptr_ra.x(), @truncate(@as(u65, @bitCast(offset))));
 8893    }
 8894}
 8895
 8896const DomInt = u8;
 8897
 8898pub const Value = struct {
 8899    refs: u32,
 8900    flags: Flags,
 8901    offset_from_parent: u64,
 8902    parent_payload: Parent.Payload,
 8903    location_payload: Location.Payload,
 8904    parts: Value.Index,
 8905
 8906    /// Must be at least 16 to compute call abi.
 8907    /// Must be at least 16, the largest hardware alignment.
 8908    pub const max_parts = 16;
 8909    pub const PartsLen = std.math.IntFittingRange(0, Value.max_parts);
 8910
 8911    comptime {
 8912        if (!std.debug.runtime_safety) assert(@sizeOf(Value) == 32);
 8913    }
 8914
 8915    pub const Flags = packed struct(u32) {
 8916        alignment: InternPool.Alignment,
 8917        parent_tag: Parent.Tag,
 8918        location_tag: Location.Tag,
 8919        parts_len_minus_one: std.math.IntFittingRange(0, Value.max_parts - 1),
 8920        unused: u18 = 0,
 8921    };
 8922
 8923    pub const Parent = union(enum(u3)) {
 8924        unallocated: void,
 8925        stack_slot: Indirect,
 8926        address: Value.Index,
 8927        value: Value.Index,
 8928        constant: Constant,
 8929
 8930        pub const Tag = @typeInfo(Parent).@"union".tag_type.?;
 8931        pub const Payload = Payload: {
 8932            const fields = @typeInfo(Parent).@"union".fields;
 8933            var types: [fields.len]type = undefined;
 8934            var names: [fields.len][]const u8 = undefined;
 8935            for (fields, &types, &names) |f, *ty, *name| {
 8936                ty.* = f.type;
 8937                name.* = f.name;
 8938            }
 8939            break :Payload @Union(.auto, null, &names, &types, &@splat(.{}));
 8940        };
 8941    };
 8942
 8943    pub const Location = union(enum(u1)) {
 8944        large: struct {
 8945            size: u64,
 8946        },
 8947        small: struct {
 8948            size: u5,
 8949            signedness: std.builtin.Signedness,
 8950            is_vector: bool,
 8951            hint: Register.Alias,
 8952            register: Register.Alias,
 8953        },
 8954
 8955        pub const Tag = @typeInfo(Location).@"union".tag_type.?;
 8956        pub const Payload = Payload: {
 8957            const fields = @typeInfo(Location).@"union".fields;
 8958            var types: [fields.len]type = undefined;
 8959            var names: [fields.len][]const u8 = undefined;
 8960            for (fields, &types, &names) |f, *ty, *name| {
 8961                ty.* = f.type;
 8962                name.* = f.name;
 8963            }
 8964            break :Payload @Union(.auto, null, &names, &types, &@splat(.{}));
 8965        };
 8966    };
 8967
 8968    pub const Indirect = packed struct(u32) {
 8969        base: Register.Alias,
 8970        offset: i25,
 8971
 8972        pub fn withOffset(ind: Indirect, offset: i25) Indirect {
 8973            return .{
 8974                .base = ind.base,
 8975                .offset = ind.offset + offset,
 8976            };
 8977        }
 8978    };
 8979
 8980    pub const Index = enum(u32) {
 8981        allocating = std.math.maxInt(u32) - 1,
 8982        free = std.math.maxInt(u32) - 0,
 8983        _,
 8984
 8985        fn get(vi: Value.Index, isel: *Select) *Value {
 8986            return &isel.values.items[@intFromEnum(vi)];
 8987        }
 8988
 8989        fn setAlignment(vi: Value.Index, isel: *Select, new_alignment: InternPool.Alignment) void {
 8990            vi.get(isel).flags.alignment = new_alignment;
 8991        }
 8992
 8993        pub fn alignment(vi: Value.Index, isel: *Select) InternPool.Alignment {
 8994            return vi.get(isel).flags.alignment;
 8995        }
 8996
 8997        pub fn setParent(vi: Value.Index, isel: *Select, new_parent: Parent) void {
 8998            const value = vi.get(isel);
 8999            assert(value.flags.parent_tag == .unallocated);
 9000            value.flags.parent_tag = new_parent;
 9001            value.parent_payload = switch (new_parent) {
 9002                .unallocated => unreachable,
 9003                inline else => |payload, tag| @unionInit(Parent.Payload, @tagName(tag), payload),
 9004            };
 9005            if (value.refs > 0) switch (new_parent) {
 9006                .unallocated => unreachable,
 9007                .stack_slot, .constant => {},
 9008                .address, .value => |parent_vi| _ = parent_vi.ref(isel),
 9009            };
 9010        }
 9011
 9012        pub fn changeStackSlot(vi: Value.Index, isel: *Select, new_stack_slot: Indirect) void {
 9013            const value = vi.get(isel);
 9014            assert(value.flags.parent_tag == .stack_slot);
 9015            value.flags.parent_tag = .unallocated;
 9016            vi.setParent(isel, .{ .stack_slot = new_stack_slot });
 9017        }
 9018
 9019        pub fn parent(vi: Value.Index, isel: *Select) Parent {
 9020            const value = vi.get(isel);
 9021            return switch (value.flags.parent_tag) {
 9022                inline else => |tag| @unionInit(
 9023                    Parent,
 9024                    @tagName(tag),
 9025                    @field(value.parent_payload, @tagName(tag)),
 9026                ),
 9027            };
 9028        }
 9029
 9030        pub fn valueParent(initial_vi: Value.Index, isel: *Select) struct { u64, Value.Index } {
 9031            var offset: u64 = 0;
 9032            var vi = initial_vi;
 9033            parent: switch (vi.parent(isel)) {
 9034                else => return .{ offset, vi },
 9035                .value => |parent_vi| {
 9036                    offset += vi.position(isel)[0];
 9037                    vi = parent_vi;
 9038                    continue :parent parent_vi.parent(isel);
 9039                },
 9040            }
 9041        }
 9042
 9043        pub fn location(vi: Value.Index, isel: *Select) Location {
 9044            const value = vi.get(isel);
 9045            return switch (value.flags.location_tag) {
 9046                inline else => |tag| @unionInit(
 9047                    Location,
 9048                    @tagName(tag),
 9049                    @field(value.location_payload, @tagName(tag)),
 9050                ),
 9051            };
 9052        }
 9053
 9054        pub fn position(vi: Value.Index, isel: *Select) struct { u64, u64 } {
 9055            return .{ vi.get(isel).offset_from_parent, vi.size(isel) };
 9056        }
 9057
 9058        pub fn size(vi: Value.Index, isel: *Select) u64 {
 9059            return switch (vi.location(isel)) {
 9060                inline else => |loc| loc.size,
 9061            };
 9062        }
 9063
 9064        fn setHint(vi: Value.Index, isel: *Select, new_hint: Register.Alias) void {
 9065            vi.get(isel).location_payload.small.hint = new_hint;
 9066        }
 9067
 9068        pub fn hint(vi: Value.Index, isel: *Select) ?Register.Alias {
 9069            return switch (vi.location(isel)) {
 9070                .large => null,
 9071                .small => |loc| switch (loc.hint) {
 9072                    .zr => null,
 9073                    else => |hint_reg| hint_reg,
 9074                },
 9075            };
 9076        }
 9077
 9078        fn setSignedness(vi: Value.Index, isel: *Select, new_signedness: std.builtin.Signedness) void {
 9079            const value = vi.get(isel);
 9080            assert(value.location_payload.small.size <= 2);
 9081            value.location_payload.small.signedness = new_signedness;
 9082        }
 9083
 9084        pub fn signedness(vi: Value.Index, isel: *Select) std.builtin.Signedness {
 9085            const value = vi.get(isel);
 9086            return switch (value.flags.location_tag) {
 9087                .large => .unsigned,
 9088                .small => value.location_payload.small.signedness,
 9089            };
 9090        }
 9091
 9092        fn setIsVector(vi: Value.Index, isel: *Select) void {
 9093            const is_vector = &vi.get(isel).location_payload.small.is_vector;
 9094            assert(!is_vector.*);
 9095            is_vector.* = true;
 9096        }
 9097
 9098        pub fn isVector(vi: Value.Index, isel: *Select) bool {
 9099            const value = vi.get(isel);
 9100            return switch (value.flags.location_tag) {
 9101                .large => false,
 9102                .small => value.location_payload.small.is_vector,
 9103            };
 9104        }
 9105
 9106        pub fn register(vi: Value.Index, isel: *Select) ?Register.Alias {
 9107            return switch (vi.location(isel)) {
 9108                .large => null,
 9109                .small => |loc| switch (loc.register) {
 9110                    .zr => null,
 9111                    else => |reg| reg,
 9112                },
 9113            };
 9114        }
 9115
 9116        pub fn isUsed(vi: Value.Index, isel: *Select) bool {
 9117            return vi.valueParent(isel)[1].parent(isel) != .unallocated or vi.hasRegisterRecursive(isel);
 9118        }
 9119
 9120        fn hasRegisterRecursive(vi: Value.Index, isel: *Select) bool {
 9121            if (vi.register(isel)) |_| return true;
 9122            var part_it = vi.parts(isel);
 9123            if (part_it.only() == null) while (part_it.next()) |part_vi| if (part_vi.hasRegisterRecursive(isel)) return true;
 9124            return false;
 9125        }
 9126
 9127        fn setParts(vi: Value.Index, isel: *Select, parts_len: Value.PartsLen) void {
 9128            assert(parts_len > 1);
 9129            const value = vi.get(isel);
 9130            assert(value.flags.parts_len_minus_one == 0);
 9131            value.parts = @enumFromInt(isel.values.items.len);
 9132            value.flags.parts_len_minus_one = @intCast(parts_len - 1);
 9133        }
 9134
 9135        fn addPart(vi: Value.Index, isel: *Select, part_offset: u64, part_size: u64) Value.Index {
 9136            const part_vi = isel.initValueAdvanced(vi.alignment(isel), part_offset, part_size);
 9137            tracking_log.debug("${d} <- ${d}[{d}]", .{
 9138                @intFromEnum(part_vi),
 9139                @intFromEnum(vi),
 9140                part_offset,
 9141            });
 9142            part_vi.setParent(isel, .{ .value = vi });
 9143            return part_vi;
 9144        }
 9145
 9146        pub fn parts(vi: Value.Index, isel: *Select) Value.PartIterator {
 9147            const value = vi.get(isel);
 9148            return switch (value.flags.parts_len_minus_one) {
 9149                0 => .initOne(vi),
 9150                else => |parts_len_minus_one| .{
 9151                    .vi = value.parts,
 9152                    .remaining = @as(Value.PartsLen, parts_len_minus_one) + 1,
 9153                },
 9154            };
 9155        }
 9156
 9157        fn containingParts(vi: Value.Index, isel: *Select, part_offset: u64, part_size: u64) Value.PartIterator {
 9158            const start_vi = vi.partAtOffset(isel, part_offset);
 9159            const start_offset, const start_size = start_vi.position(isel);
 9160            if (part_offset >= start_offset and part_size <= start_size) return .initOne(start_vi);
 9161            const end_vi = vi.partAtOffset(isel, part_size - 1 + part_offset);
 9162            return .{
 9163                .vi = start_vi,
 9164                .remaining = @intCast(@intFromEnum(end_vi) - @intFromEnum(start_vi) + 1),
 9165            };
 9166        }
 9167        comptime {
 9168            _ = containingParts;
 9169        }
 9170
 9171        fn partAtOffset(vi: Value.Index, isel: *Select, offset: u64) Value.Index {
 9172            const SearchPartIndex = std.math.IntFittingRange(0, Value.max_parts * 2 - 1);
 9173            const value = vi.get(isel);
 9174            var last: SearchPartIndex = value.flags.parts_len_minus_one;
 9175            if (last == 0) return vi;
 9176            var first: SearchPartIndex = 0;
 9177            last += 1;
 9178            while (true) {
 9179                const mid = (first + last) / 2;
 9180                const mid_vi: Value.Index = @enumFromInt(@intFromEnum(value.parts) + mid);
 9181                if (mid == first) return mid_vi;
 9182                if (offset < mid_vi.get(isel).offset_from_parent) last = mid else first = mid;
 9183            }
 9184        }
 9185
 9186        fn field(
 9187            vi: Value.Index,
 9188            ty: ZigType,
 9189            field_offset: u64,
 9190            field_size: u64,
 9191        ) Value.FieldPartIterator {
 9192            assert(field_size > 0);
 9193            return .{
 9194                .vi = vi,
 9195                .ty = ty,
 9196                .field_offset = field_offset,
 9197                .field_size = field_size,
 9198                .next_offset = 0,
 9199            };
 9200        }
 9201
 9202        fn ref(initial_vi: Value.Index, isel: *Select) Value.Index {
 9203            var vi = initial_vi;
 9204            while (true) {
 9205                const refs = &vi.get(isel).refs;
 9206                refs.* += 1;
 9207                if (refs.* > 1) return initial_vi;
 9208                switch (vi.parent(isel)) {
 9209                    .unallocated, .stack_slot, .constant => {},
 9210                    .address, .value => |parent_vi| {
 9211                        vi = parent_vi;
 9212                        continue;
 9213                    },
 9214                }
 9215                return initial_vi;
 9216            }
 9217        }
 9218
 9219        pub fn deref(initial_vi: Value.Index, isel: *Select) void {
 9220            var vi = initial_vi;
 9221            while (true) {
 9222                const refs = &vi.get(isel).refs;
 9223                refs.* -= 1;
 9224                if (refs.* > 0) return;
 9225                switch (vi.parent(isel)) {
 9226                    .unallocated, .constant => {},
 9227                    .stack_slot => {
 9228                        // reuse stack slot
 9229                    },
 9230                    .address, .value => |parent_vi| {
 9231                        vi = parent_vi;
 9232                        continue;
 9233                    },
 9234                }
 9235                return;
 9236            }
 9237        }
 9238
 9239        fn move(dst_vi: Value.Index, isel: *Select, src_ref: Air.Inst.Ref) !void {
 9240            try dst_vi.copy(
 9241                isel,
 9242                isel.air.typeOf(src_ref, &isel.pt.zcu.intern_pool),
 9243                try isel.use(src_ref),
 9244            );
 9245        }
 9246
 9247        fn copy(dst_vi: Value.Index, isel: *Select, ty: ZigType, src_vi: Value.Index) !void {
 9248            try dst_vi.copyAdvanced(isel, src_vi, .{
 9249                .ty = ty,
 9250                .dst_vi = dst_vi,
 9251                .dst_offset = 0,
 9252                .src_vi = src_vi,
 9253                .src_offset = 0,
 9254            });
 9255        }
 9256
 9257        fn copyAdvanced(dst_vi: Value.Index, isel: *Select, src_vi: Value.Index, root: struct {
 9258            ty: ZigType,
 9259            dst_vi: Value.Index,
 9260            dst_offset: u64,
 9261            src_vi: Value.Index,
 9262            src_offset: u64,
 9263        }) !void {
 9264            if (dst_vi == src_vi) return;
 9265            var dst_part_it = dst_vi.parts(isel);
 9266            if (dst_part_it.only()) |dst_part_vi| {
 9267                var src_part_it = src_vi.parts(isel);
 9268                if (src_part_it.only()) |src_part_vi| only: {
 9269                    const src_part_size = src_part_vi.size(isel);
 9270                    if (src_part_size > @as(@TypeOf(src_part_size), if (src_part_vi.isVector(isel)) 16 else 8)) {
 9271                        var subpart_it = root.src_vi.field(root.ty, root.src_offset, src_part_size - 1);
 9272                        _ = try subpart_it.next(isel);
 9273                        src_part_it = src_vi.parts(isel);
 9274                        assert(src_part_it.only() == null);
 9275                        break :only;
 9276                    }
 9277                    return src_part_vi.liveOut(isel, try dst_part_vi.defReg(isel) orelse return);
 9278                }
 9279                while (src_part_it.next()) |src_part_vi| {
 9280                    const src_part_offset, const src_part_size = src_part_vi.position(isel);
 9281                    var dst_field_it = root.dst_vi.field(root.ty, root.dst_offset + src_part_offset, src_part_size);
 9282                    const dst_field_vi = try dst_field_it.only(isel);
 9283                    try dst_field_vi.?.copyAdvanced(isel, src_part_vi, .{
 9284                        .ty = root.ty,
 9285                        .dst_vi = root.dst_vi,
 9286                        .dst_offset = root.dst_offset + src_part_offset,
 9287                        .src_vi = root.src_vi,
 9288                        .src_offset = root.src_offset + src_part_offset,
 9289                    });
 9290                }
 9291            } else while (dst_part_it.next()) |dst_part_vi| {
 9292                const dst_part_offset, const dst_part_size = dst_part_vi.position(isel);
 9293                var src_field_it = root.src_vi.field(root.ty, root.src_offset + dst_part_offset, dst_part_size);
 9294                const src_part_vi = try src_field_it.only(isel);
 9295                try dst_part_vi.copyAdvanced(isel, src_part_vi.?, .{
 9296                    .ty = root.ty,
 9297                    .dst_vi = root.dst_vi,
 9298                    .dst_offset = root.dst_offset + dst_part_offset,
 9299                    .src_vi = root.src_vi,
 9300                    .src_offset = root.src_offset + dst_part_offset,
 9301                });
 9302            }
 9303        }
 9304
 9305        const AddOrSubtractOptions = struct {
 9306            overflow: Overflow,
 9307
 9308            const Overflow = union(enum) {
 9309                @"unreachable",
 9310                panic: Zcu.SimplePanicId,
 9311                wrap,
 9312                ra: Register.Alias,
 9313
 9314                fn defCond(overflow: Overflow, isel: *Select, cond: codegen.aarch64.encoding.ConditionCode) !void {
 9315                    switch (overflow) {
 9316                        .@"unreachable" => unreachable,
 9317                        .panic => |panic_id| {
 9318                            const skip_label = isel.instructions.items.len;
 9319                            try isel.emitPanic(panic_id);
 9320                            try isel.emit(.@"b."(
 9321                                cond.invert(),
 9322                                @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 9323                            ));
 9324                        },
 9325                        .wrap => {},
 9326                        .ra => |overflow_ra| try isel.emit(.csinc(overflow_ra.w(), .wzr, .wzr, cond.invert())),
 9327                    }
 9328                }
 9329            };
 9330        };
 9331        fn addOrSubtract(
 9332            res_vi: Value.Index,
 9333            isel: *Select,
 9334            ty: ZigType,
 9335            lhs_vi: Value.Index,
 9336            op: codegen.aarch64.encoding.Instruction.AddSubtractOp,
 9337            rhs_vi: Value.Index,
 9338            opts: AddOrSubtractOptions,
 9339        ) !void {
 9340            const zcu = isel.pt.zcu;
 9341            if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ op, isel.fmtType(ty) });
 9342            const int_info = ty.intInfo(zcu);
 9343            if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ op, isel.fmtType(ty) });
 9344            var part_offset = res_vi.size(isel);
 9345            var need_wrap = switch (opts.overflow) {
 9346                .@"unreachable" => false,
 9347                .panic, .wrap, .ra => true,
 9348            };
 9349            var need_carry = switch (opts.overflow) {
 9350                .@"unreachable", .wrap => false,
 9351                .panic, .ra => true,
 9352            };
 9353            while (part_offset > 0) : (need_wrap = false) {
 9354                const part_size = @min(part_offset, 8);
 9355                part_offset -= part_size;
 9356                var wrapped_res_part_it = res_vi.field(ty, part_offset, part_size);
 9357                const wrapped_res_part_vi = try wrapped_res_part_it.only(isel);
 9358                const wrapped_res_part_ra = wrapped_res_part_ra: {
 9359                    const overflow_ra_lock: RegLock = switch (opts.overflow) {
 9360                        .ra => |ra| isel.lockReg(ra),
 9361                        else => .empty,
 9362                    };
 9363                    defer overflow_ra_lock.unlock(isel);
 9364                    break :wrapped_res_part_ra try wrapped_res_part_vi.?.defReg(isel) orelse if (need_carry) .zr else continue;
 9365                };
 9366                const unwrapped_res_part_ra = unwrapped_res_part_ra: {
 9367                    if (!need_wrap) break :unwrapped_res_part_ra wrapped_res_part_ra;
 9368                    if (int_info.bits % 32 == 0) {
 9369                        try opts.overflow.defCond(isel, switch (int_info.signedness) {
 9370                            .signed => .vs,
 9371                            .unsigned => switch (op) {
 9372                                .add => .cs,
 9373                                .sub => .cc,
 9374                            },
 9375                        });
 9376                        break :unwrapped_res_part_ra wrapped_res_part_ra;
 9377                    }
 9378                    need_carry = false;
 9379                    const wrapped_part_ra, const unwrapped_part_ra = part_ra: switch (opts.overflow) {
 9380                        .@"unreachable" => unreachable,
 9381                        .panic, .ra => switch (int_info.signedness) {
 9382                            .signed => {
 9383                                try opts.overflow.defCond(isel, .ne);
 9384                                const wrapped_part_ra = switch (wrapped_res_part_ra) {
 9385                                    else => |res_part_ra| res_part_ra,
 9386                                    .zr => try isel.allocIntReg(),
 9387                                };
 9388                                errdefer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
 9389                                const unwrapped_part_ra = unwrapped_part_ra: {
 9390                                    const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
 9391                                        else => |res_part_ra| isel.lockReg(res_part_ra),
 9392                                        .zr => .empty,
 9393                                    };
 9394                                    defer wrapped_res_part_lock.unlock(isel);
 9395                                    break :unwrapped_part_ra try isel.allocIntReg();
 9396                                };
 9397                                errdefer isel.freeReg(unwrapped_part_ra);
 9398                                switch (part_size) {
 9399                                    else => unreachable,
 9400                                    1...4 => try isel.emit(.subs(.wzr, wrapped_part_ra.w(), .{ .register = unwrapped_part_ra.w() })),
 9401                                    5...8 => try isel.emit(.subs(.xzr, wrapped_part_ra.x(), .{ .register = unwrapped_part_ra.x() })),
 9402                                }
 9403                                break :part_ra .{ wrapped_part_ra, unwrapped_part_ra };
 9404                            },
 9405                            .unsigned => {
 9406                                const unwrapped_part_ra = unwrapped_part_ra: {
 9407                                    const wrapped_res_part_lock: RegLock = switch (wrapped_res_part_ra) {
 9408                                        else => |res_part_ra| isel.lockReg(res_part_ra),
 9409                                        .zr => .empty,
 9410                                    };
 9411                                    defer wrapped_res_part_lock.unlock(isel);
 9412                                    break :unwrapped_part_ra try isel.allocIntReg();
 9413                                };
 9414                                errdefer isel.freeReg(unwrapped_part_ra);
 9415                                const bit: u6 = @truncate(int_info.bits);
 9416                                switch (opts.overflow) {
 9417                                    .@"unreachable", .wrap => unreachable,
 9418                                    .panic => |panic_id| {
 9419                                        const skip_label = isel.instructions.items.len;
 9420                                        try isel.emitPanic(panic_id);
 9421                                        try isel.emit(.tbz(
 9422                                            switch (bit) {
 9423                                                0, 32 => unreachable,
 9424                                                1...31 => unwrapped_part_ra.w(),
 9425                                                33...63 => unwrapped_part_ra.x(),
 9426                                            },
 9427                                            bit,
 9428                                            @intCast((isel.instructions.items.len + 1 - skip_label) << 2),
 9429                                        ));
 9430                                    },
 9431                                    .ra => |overflow_ra| try isel.emit(switch (bit) {
 9432                                        0, 32 => unreachable,
 9433                                        1...31 => .ubfm(overflow_ra.w(), unwrapped_part_ra.w(), .{
 9434                                            .N = .word,
 9435                                            .immr = bit,
 9436                                            .imms = bit,
 9437                                        }),
 9438                                        33...63 => .ubfm(overflow_ra.x(), unwrapped_part_ra.x(), .{
 9439                                            .N = .doubleword,
 9440                                            .immr = bit,
 9441                                            .imms = bit,
 9442                                        }),
 9443                                    }),
 9444                                }
 9445                                break :part_ra .{ wrapped_res_part_ra, unwrapped_part_ra };
 9446                            },
 9447                        },
 9448                        .wrap => .{ wrapped_res_part_ra, wrapped_res_part_ra },
 9449                    };
 9450                    defer if (wrapped_part_ra != wrapped_res_part_ra) isel.freeReg(wrapped_part_ra);
 9451                    errdefer if (unwrapped_part_ra != wrapped_res_part_ra) isel.freeReg(unwrapped_part_ra);
 9452                    if (wrapped_part_ra != .zr) try isel.emit(switch (part_size) {
 9453                        else => unreachable,
 9454                        1...4 => switch (int_info.signedness) {
 9455                            .signed => .sbfm(wrapped_part_ra.w(), unwrapped_part_ra.w(), .{
 9456                                .N = .word,
 9457                                .immr = 0,
 9458                                .imms = @truncate(int_info.bits - 1),
 9459                            }),
 9460                            .unsigned => .ubfm(wrapped_part_ra.w(), unwrapped_part_ra.w(), .{
 9461                                .N = .word,
 9462                                .immr = 0,
 9463                                .imms = @truncate(int_info.bits - 1),
 9464                            }),
 9465                        },
 9466                        5...8 => switch (int_info.signedness) {
 9467                            .signed => .sbfm(wrapped_part_ra.x(), unwrapped_part_ra.x(), .{
 9468                                .N = .doubleword,
 9469                                .immr = 0,
 9470                                .imms = @truncate(int_info.bits - 1),
 9471                            }),
 9472                            .unsigned => .ubfm(wrapped_part_ra.x(), unwrapped_part_ra.x(), .{
 9473                                .N = .doubleword,
 9474                                .immr = 0,
 9475                                .imms = @truncate(int_info.bits - 1),
 9476                            }),
 9477                        },
 9478                    });
 9479                    break :unwrapped_res_part_ra unwrapped_part_ra;
 9480                };
 9481                defer if (unwrapped_res_part_ra != wrapped_res_part_ra) isel.freeReg(unwrapped_res_part_ra);
 9482                var lhs_part_it = lhs_vi.field(ty, part_offset, part_size);
 9483                const lhs_part_vi = try lhs_part_it.only(isel);
 9484                const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
 9485                var rhs_part_it = rhs_vi.field(ty, part_offset, part_size);
 9486                const rhs_part_vi = try rhs_part_it.only(isel);
 9487                const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
 9488                try isel.emit(switch (part_size) {
 9489                    else => unreachable,
 9490                    1...4 => switch (op) {
 9491                        .add => switch (part_offset) {
 9492                            0 => switch (need_carry) {
 9493                                false => .add(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 9494                                true => .adds(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 9495                            },
 9496                            else => switch (need_carry) {
 9497                                false => .adc(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()),
 9498                                true => .adcs(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()),
 9499                            },
 9500                        },
 9501                        .sub => switch (part_offset) {
 9502                            0 => switch (need_carry) {
 9503                                false => .sub(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 9504                                true => .subs(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
 9505                            },
 9506                            else => switch (need_carry) {
 9507                                false => .sbc(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()),
 9508                                true => .sbcs(unwrapped_res_part_ra.w(), lhs_part_mat.ra.w(), rhs_part_mat.ra.w()),
 9509                            },
 9510                        },
 9511                    },
 9512                    5...8 => switch (op) {
 9513                        .add => switch (part_offset) {
 9514                            0 => switch (need_carry) {
 9515                                false => .add(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 9516                                true => .adds(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 9517                            },
 9518                            else => switch (need_carry) {
 9519                                false => .adc(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()),
 9520                                true => .adcs(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()),
 9521                            },
 9522                        },
 9523                        .sub => switch (part_offset) {
 9524                            0 => switch (need_carry) {
 9525                                false => .sub(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 9526                                true => .subs(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
 9527                            },
 9528                            else => switch (need_carry) {
 9529                                false => .sbc(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()),
 9530                                true => .sbcs(unwrapped_res_part_ra.x(), lhs_part_mat.ra.x(), rhs_part_mat.ra.x()),
 9531                            },
 9532                        },
 9533                    },
 9534                });
 9535                try rhs_part_mat.finish(isel);
 9536                try lhs_part_mat.finish(isel);
 9537                need_carry = true;
 9538            }
 9539        }
 9540
 9541        const MemoryAccessOptions = struct {
 9542            root_vi: Value.Index = .free,
 9543            offset: u64 = 0,
 9544            @"volatile": bool = false,
 9545            split: bool = true,
 9546            wrap: ?std.builtin.Type.Int = null,
 9547            expected_live_registers: *const LiveRegisters = &.initFill(.free),
 9548        };
 9549
 9550        fn load(
 9551            vi: Value.Index,
 9552            isel: *Select,
 9553            root_ty: ZigType,
 9554            base_ra: Register.Alias,
 9555            opts: MemoryAccessOptions,
 9556        ) !bool {
 9557            const root_vi = switch (opts.root_vi) {
 9558                _ => |root_vi| root_vi,
 9559                .allocating => unreachable,
 9560                .free => vi,
 9561            };
 9562            var part_it = vi.parts(isel);
 9563            if (part_it.only()) |part_vi| only: {
 9564                const part_size = part_vi.size(isel);
 9565                const part_is_vector = part_vi.isVector(isel);
 9566                if (part_size > @as(@TypeOf(part_size), if (part_is_vector) 16 else 8)) {
 9567                    if (!opts.split) return false;
 9568                    var subpart_it = root_vi.field(root_ty, opts.offset, part_size - 1);
 9569                    _ = try subpart_it.next(isel);
 9570                    part_it = vi.parts(isel);
 9571                    assert(part_it.only() == null);
 9572                    break :only;
 9573                }
 9574                const part_ra = if (try part_vi.defReg(isel)) |part_ra|
 9575                    part_ra
 9576                else if (opts.@"volatile")
 9577                    .zr
 9578                else
 9579                    return false;
 9580                const part_lock: RegLock = switch (part_ra) {
 9581                    else => isel.lockReg(part_ra),
 9582                    .zr => .empty,
 9583                };
 9584                defer switch (opts.expected_live_registers.get(part_ra)) {
 9585                    _ => {},
 9586                    .allocating => unreachable,
 9587                    .free => part_lock.unlock(isel),
 9588                };
 9589                if (opts.wrap) |int_info| switch (int_info.bits) {
 9590                    else => unreachable,
 9591                    1...7, 9...15, 17...31 => |bits| try isel.emit(switch (int_info.signedness) {
 9592                        .signed => .sbfm(part_ra.w(), part_ra.w(), .{
 9593                            .N = .word,
 9594                            .immr = 0,
 9595                            .imms = @intCast(bits - 1),
 9596                        }),
 9597                        .unsigned => .ubfm(part_ra.w(), part_ra.w(), .{
 9598                            .N = .word,
 9599                            .immr = 0,
 9600                            .imms = @intCast(bits - 1),
 9601                        }),
 9602                    }),
 9603                    8, 16, 32 => {},
 9604                    33...63 => |bits| try isel.emit(switch (int_info.signedness) {
 9605                        .signed => .sbfm(part_ra.x(), part_ra.x(), .{
 9606                            .N = .doubleword,
 9607                            .immr = 0,
 9608                            .imms = @intCast(bits - 1),
 9609                        }),
 9610                        .unsigned => .ubfm(part_ra.x(), part_ra.x(), .{
 9611                            .N = .doubleword,
 9612                            .immr = 0,
 9613                            .imms = @intCast(bits - 1),
 9614                        }),
 9615                    }),
 9616                    64 => {},
 9617                };
 9618                try isel.loadReg(part_ra, part_size, part_vi.signedness(isel), base_ra, opts.offset);
 9619                return true;
 9620            }
 9621            var used = false;
 9622            while (part_it.next()) |part_vi| used |= try part_vi.load(isel, root_ty, base_ra, .{
 9623                .root_vi = root_vi,
 9624                .offset = opts.offset + part_vi.get(isel).offset_from_parent,
 9625                .@"volatile" = opts.@"volatile",
 9626                .split = opts.split,
 9627                .wrap = switch (part_it.remaining) {
 9628                    else => null,
 9629                    0 => if (opts.wrap) |wrap| .{
 9630                        .signedness = wrap.signedness,
 9631                        .bits = @intCast(wrap.bits - 8 * part_vi.position(isel)[0]),
 9632                    } else null,
 9633                },
 9634                .expected_live_registers = opts.expected_live_registers,
 9635            });
 9636            return used;
 9637        }
 9638
 9639        fn store(
 9640            vi: Value.Index,
 9641            isel: *Select,
 9642            root_ty: ZigType,
 9643            base_ra: Register.Alias,
 9644            opts: MemoryAccessOptions,
 9645        ) !void {
 9646            const root_vi = switch (opts.root_vi) {
 9647                _ => |root_vi| root_vi,
 9648                .allocating => unreachable,
 9649                .free => vi,
 9650            };
 9651            var part_it = vi.parts(isel);
 9652            if (part_it.only()) |part_vi| only: {
 9653                const part_size = part_vi.size(isel);
 9654                const part_is_vector = part_vi.isVector(isel);
 9655                if (part_size > @as(@TypeOf(part_size), if (part_is_vector) 16 else 8)) {
 9656                    if (!opts.split) return;
 9657                    var subpart_it = root_vi.field(root_ty, opts.offset, part_size - 1);
 9658                    _ = try subpart_it.next(isel);
 9659                    part_it = vi.parts(isel);
 9660                    assert(part_it.only() == null);
 9661                    break :only;
 9662                }
 9663                const part_mat = try part_vi.matReg(isel);
 9664                try isel.storeReg(part_mat.ra, part_size, base_ra, opts.offset);
 9665                return part_mat.finish(isel);
 9666            }
 9667            while (part_it.next()) |part_vi| try part_vi.store(isel, root_ty, base_ra, .{
 9668                .root_vi = root_vi,
 9669                .offset = opts.offset + part_vi.get(isel).offset_from_parent,
 9670                .@"volatile" = opts.@"volatile",
 9671                .split = opts.split,
 9672                .wrap = switch (part_it.remaining) {
 9673                    else => null,
 9674                    0 => if (opts.wrap) |wrap| .{
 9675                        .signedness = wrap.signedness,
 9676                        .bits = @intCast(wrap.bits - 8 * part_vi.position(isel)[0]),
 9677                    } else null,
 9678                },
 9679                .expected_live_registers = opts.expected_live_registers,
 9680            });
 9681        }
 9682
 9683        fn mat(vi: Value.Index, isel: *Select) !void {
 9684            if (false) {
 9685                var part_it: Value.PartIterator = if (vi.size(isel) > 8) vi.parts(isel) else .initOne(vi);
 9686                if (part_it.only()) |part_vi| only: {
 9687                    const mat_ra = mat_ra: {
 9688                        if (part_vi.register(isel)) |mat_ra| {
 9689                            part_vi.get(isel).location_payload.small.register = .zr;
 9690                            const live_vi = isel.live_registers.getPtr(mat_ra);
 9691                            assert(live_vi.* == part_vi);
 9692                            live_vi.* = .allocating;
 9693                            break :mat_ra mat_ra;
 9694                        }
 9695                        if (part_vi.hint(isel)) |hint_ra| {
 9696                            const live_vi = isel.live_registers.getPtr(hint_ra);
 9697                            if (live_vi.* == .free) {
 9698                                live_vi.* = .allocating;
 9699                                isel.saved_registers.insert(hint_ra);
 9700                                break :mat_ra hint_ra;
 9701                            }
 9702                        }
 9703                        const part_size = part_vi.size(isel);
 9704                        const part_is_vector = part_vi.isVector(isel);
 9705                        if (part_size <= @as(@TypeOf(part_size), if (part_is_vector) 16 else 8))
 9706                            switch (if (part_is_vector) isel.tryAllocVecReg() else isel.tryAllocIntReg()) {
 9707                                .allocated => |ra| break :mat_ra ra,
 9708                                .fill_candidate, .out_of_registers => {},
 9709                            };
 9710                        _, const parent_vi = vi.valueParent(isel);
 9711                        switch (parent_vi.parent(isel)) {
 9712                            .unallocated => parent_vi.setParent(isel, .{ .stack_slot = parent_vi.allocStackSlot(isel) }),
 9713                            else => {},
 9714                        }
 9715                        break :only;
 9716                    };
 9717                    assert(isel.live_registers.get(mat_ra) == .allocating);
 9718                    try Value.Materialize.finish(.{ .vi = part_vi, .ra = mat_ra }, isel);
 9719                } else while (part_it.next()) |part_vi| try part_vi.mat(isel);
 9720            } else {
 9721                _, const parent_vi = vi.valueParent(isel);
 9722                switch (parent_vi.parent(isel)) {
 9723                    .unallocated => parent_vi.setParent(isel, .{ .stack_slot = parent_vi.allocStackSlot(isel) }),
 9724                    else => {},
 9725                }
 9726            }
 9727        }
 9728
 9729        fn matReg(vi: Value.Index, isel: *Select) !Value.Materialize {
 9730            const mat_ra = mat_ra: {
 9731                if (vi.register(isel)) |mat_ra| {
 9732                    vi.get(isel).location_payload.small.register = .zr;
 9733                    const live_vi = isel.live_registers.getPtr(mat_ra);
 9734                    assert(live_vi.* == vi);
 9735                    live_vi.* = .allocating;
 9736                    break :mat_ra mat_ra;
 9737                }
 9738                if (vi.hint(isel)) |hint_ra| {
 9739                    const live_vi = isel.live_registers.getPtr(hint_ra);
 9740                    if (live_vi.* == .free) {
 9741                        live_vi.* = .allocating;
 9742                        isel.saved_registers.insert(hint_ra);
 9743                        break :mat_ra hint_ra;
 9744                    }
 9745                }
 9746                break :mat_ra if (vi.isVector(isel)) try isel.allocVecReg() else try isel.allocIntReg();
 9747            };
 9748            assert(isel.live_registers.get(mat_ra) == .allocating);
 9749            return .{ .vi = vi, .ra = mat_ra };
 9750        }
 9751
 9752        fn defAddr(
 9753            def_vi: Value.Index,
 9754            isel: *Select,
 9755            root_ty: ZigType,
 9756            opts: struct {
 9757                root_vi: Value.Index = .free,
 9758                wrap: ?std.builtin.Type.Int = null,
 9759                expected_live_registers: *const LiveRegisters = &.initFill(.free),
 9760            },
 9761        ) !?void {
 9762            if (!def_vi.isUsed(isel)) return null;
 9763            const offset_from_parent: i65, const parent_vi = def_vi.valueParent(isel);
 9764            const stack_slot, const allocated = switch (parent_vi.parent(isel)) {
 9765                .unallocated => .{ parent_vi.allocStackSlot(isel), true },
 9766                .stack_slot => |stack_slot| .{ stack_slot, false },
 9767                else => unreachable,
 9768            };
 9769            _ = try def_vi.load(isel, root_ty, stack_slot.base, .{
 9770                .root_vi = opts.root_vi,
 9771                .offset = @intCast(stack_slot.offset + offset_from_parent),
 9772                .split = false,
 9773                .wrap = opts.wrap,
 9774                .expected_live_registers = opts.expected_live_registers,
 9775            });
 9776            if (allocated) parent_vi.setParent(isel, .{ .stack_slot = stack_slot });
 9777        }
 9778
 9779        fn defReg(def_vi: Value.Index, isel: *Select) !?Register.Alias {
 9780            var vi = def_vi;
 9781            var offset: i65 = 0;
 9782            var def_ra: ?Register.Alias = null;
 9783            while (true) {
 9784                if (vi.register(isel)) |ra| {
 9785                    vi.get(isel).location_payload.small.register = .zr;
 9786                    const live_vi = isel.live_registers.getPtr(ra);
 9787                    assert(live_vi.* == vi);
 9788                    if (def_ra == null and vi != def_vi) {
 9789                        var part_it = vi.parts(isel);
 9790                        assert(part_it.only() == null);
 9791
 9792                        const first_part_vi = part_it.next().?;
 9793                        const first_part_value = first_part_vi.get(isel);
 9794                        assert(first_part_value.offset_from_parent == 0);
 9795                        first_part_value.location_payload.small.register = ra;
 9796                        live_vi.* = first_part_vi;
 9797
 9798                        const vi_size = vi.size(isel);
 9799                        while (part_it.next()) |part_vi| {
 9800                            const part_offset, const part_size = part_vi.position(isel);
 9801                            const part_mat = try part_vi.matReg(isel);
 9802                            try isel.emit(if (part_vi.isVector(isel)) emit: {
 9803                                assert(part_offset == 0 and part_size == vi_size);
 9804                                break :emit switch (vi_size) {
 9805                                    else => unreachable,
 9806                                    2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
 9807                                        .fmov(ra.h(), .{ .register = part_mat.ra.h() })
 9808                                    else
 9809                                        .dup(ra.h(), part_mat.ra.@"h[]"(0)),
 9810                                    4 => .fmov(ra.s(), .{ .register = part_mat.ra.s() }),
 9811                                    8 => .fmov(ra.d(), .{ .register = part_mat.ra.d() }),
 9812                                    16 => .orr(ra.@"16b"(), part_mat.ra.@"16b"(), .{ .register = part_mat.ra.@"16b"() }),
 9813                                };
 9814                            } else switch (vi_size) {
 9815                                else => unreachable,
 9816                                1...4 => .bfm(ra.w(), part_mat.ra.w(), .{
 9817                                    .N = .word,
 9818                                    .immr = @as(u5, @truncate(32 - 8 * part_offset)),
 9819                                    .imms = @intCast(8 * part_size - 1),
 9820                                }),
 9821                                5...8 => .bfm(ra.x(), part_mat.ra.x(), .{
 9822                                    .N = .doubleword,
 9823                                    .immr = @as(u6, @truncate(64 - 8 * part_offset)),
 9824                                    .imms = @intCast(8 * part_size - 1),
 9825                                }),
 9826                            });
 9827                            try part_mat.finish(isel);
 9828                        }
 9829                        vi = def_vi;
 9830                        offset = 0;
 9831                        continue;
 9832                    }
 9833                    live_vi.* = .free;
 9834                    def_ra = ra;
 9835                }
 9836                offset += vi.get(isel).offset_from_parent;
 9837                switch (vi.parent(isel)) {
 9838                    else => unreachable,
 9839                    .unallocated => return def_ra,
 9840                    .stack_slot => |stack_slot| {
 9841                        offset += stack_slot.offset;
 9842                        const def_is_vector = def_vi.isVector(isel);
 9843                        const ra = def_ra orelse if (def_is_vector) try isel.allocVecReg() else try isel.allocIntReg();
 9844                        defer if (def_ra == null) isel.freeReg(ra);
 9845                        try isel.storeReg(ra, def_vi.size(isel), stack_slot.base, offset);
 9846                        return ra;
 9847                    },
 9848                    .value => |parent_vi| vi = parent_vi,
 9849                }
 9850            }
 9851        }
 9852
 9853        pub fn defUndef(def_vi: Value.Index, isel: *Select, root_ty: ZigType, opts: struct {
 9854            root_vi: Value.Index = .free,
 9855            offset: u64 = 0,
 9856            split: bool = true,
 9857        }) !void {
 9858            const root_vi = switch (opts.root_vi) {
 9859                _ => |root_vi| root_vi,
 9860                .allocating => unreachable,
 9861                .free => def_vi,
 9862            };
 9863            var part_it = def_vi.parts(isel);
 9864            if (part_it.only()) |part_vi| only: {
 9865                const part_size = part_vi.size(isel);
 9866                const part_is_vector = part_vi.isVector(isel);
 9867                if (part_size > @as(@TypeOf(part_size), if (part_is_vector) 16 else 8)) {
 9868                    if (!opts.split) return;
 9869                    var subpart_it = root_vi.field(root_ty, opts.offset, part_size - 1);
 9870                    _ = try subpart_it.next(isel);
 9871                    part_it = def_vi.parts(isel);
 9872                    assert(part_it.only() == null);
 9873                    break :only;
 9874                }
 9875                return if (try part_vi.defReg(isel)) |part_ra| try isel.emit(if (part_is_vector)
 9876                    .movi(switch (part_size) {
 9877                        else => unreachable,
 9878                        1...8 => part_ra.@"8b"(),
 9879                        9...16 => part_ra.@"16b"(),
 9880                    }, 0xaa, .{ .lsl = 0 })
 9881                else switch (part_size) {
 9882                    else => unreachable,
 9883                    1...4 => .orr(part_ra.w(), .wzr, .{ .immediate = .{
 9884                        .N = .word,
 9885                        .immr = 0b000001,
 9886                        .imms = 0b111100,
 9887                    } }),
 9888                    5...8 => .orr(part_ra.x(), .xzr, .{ .immediate = .{
 9889                        .N = .word,
 9890                        .immr = 0b000001,
 9891                        .imms = 0b111100,
 9892                    } }),
 9893                });
 9894            }
 9895            while (part_it.next()) |part_vi| try part_vi.defUndef(isel, root_ty, .{
 9896                .root_vi = root_vi,
 9897            });
 9898        }
 9899
 9900        pub fn liveIn(
 9901            vi: Value.Index,
 9902            isel: *Select,
 9903            src_ra: Register.Alias,
 9904            expected_live_registers: *const LiveRegisters,
 9905        ) !void {
 9906            const src_live_vi = isel.live_registers.getPtr(src_ra);
 9907            if (vi.register(isel)) |dst_ra| {
 9908                const dst_live_vi = isel.live_registers.getPtr(dst_ra);
 9909                assert(dst_live_vi.* == vi);
 9910                if (dst_ra == src_ra) {
 9911                    src_live_vi.* = .allocating;
 9912                    return;
 9913                }
 9914                dst_live_vi.* = .allocating;
 9915                if (try isel.fill(src_ra)) {
 9916                    assert(src_live_vi.* == .free);
 9917                    src_live_vi.* = .allocating;
 9918                }
 9919                assert(src_live_vi.* == .allocating);
 9920                try isel.emit(switch (dst_ra.isVector()) {
 9921                    false => switch (src_ra.isVector()) {
 9922                        false => switch (vi.size(isel)) {
 9923                            else => unreachable,
 9924                            1...4 => .orr(dst_ra.w(), .wzr, .{ .register = src_ra.w() }),
 9925                            5...8 => .orr(dst_ra.x(), .xzr, .{ .register = src_ra.x() }),
 9926                        },
 9927                        true => switch (vi.size(isel)) {
 9928                            else => unreachable,
 9929                            2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
 9930                                .fmov(dst_ra.w(), .{ .register = src_ra.h() })
 9931                            else
 9932                                .umov(dst_ra.w(), src_ra.@"h[]"(0)),
 9933                            4 => .fmov(dst_ra.w(), .{ .register = src_ra.s() }),
 9934                            8 => .fmov(dst_ra.x(), .{ .register = src_ra.d() }),
 9935                        },
 9936                    },
 9937                    true => switch (src_ra.isVector()) {
 9938                        false => size: switch (vi.size(isel)) {
 9939                            else => unreachable,
 9940                            2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
 9941                                .fmov(dst_ra.h(), .{ .register = src_ra.w() })
 9942                            else
 9943                                continue :size 4,
 9944                            4 => .fmov(dst_ra.s(), .{ .register = src_ra.w() }),
 9945                            8 => .fmov(dst_ra.d(), .{ .register = src_ra.x() }),
 9946                        },
 9947                        true => switch (vi.size(isel)) {
 9948                            else => unreachable,
 9949                            2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
 9950                                .fmov(dst_ra.h(), .{ .register = src_ra.h() })
 9951                            else
 9952                                .dup(dst_ra.h(), src_ra.@"h[]"(0)),
 9953                            4 => .fmov(dst_ra.s(), .{ .register = src_ra.s() }),
 9954                            8 => .fmov(dst_ra.d(), .{ .register = src_ra.d() }),
 9955                            16 => .orr(dst_ra.@"16b"(), src_ra.@"16b"(), .{ .register = src_ra.@"16b"() }),
 9956                        },
 9957                    },
 9958                });
 9959                assert(dst_live_vi.* == .allocating);
 9960                dst_live_vi.* = switch (expected_live_registers.get(dst_ra)) {
 9961                    _ => .allocating,
 9962                    .allocating => .allocating,
 9963                    .free => .free,
 9964                };
 9965            } else if (try isel.fill(src_ra)) {
 9966                assert(src_live_vi.* == .free);
 9967                src_live_vi.* = .allocating;
 9968            }
 9969            assert(src_live_vi.* == .allocating);
 9970            vi.get(isel).location_payload.small.register = src_ra;
 9971        }
 9972
 9973        pub fn defLiveIn(
 9974            vi: Value.Index,
 9975            isel: *Select,
 9976            src_ra: Register.Alias,
 9977            expected_live_registers: *const LiveRegisters,
 9978        ) !void {
 9979            try vi.liveIn(isel, src_ra, expected_live_registers);
 9980            const offset_from_parent, const parent_vi = vi.valueParent(isel);
 9981            switch (parent_vi.parent(isel)) {
 9982                .unallocated => {},
 9983                .stack_slot => |stack_slot| if (stack_slot.base != Register.Alias.fp) try isel.storeReg(
 9984                    src_ra,
 9985                    vi.size(isel),
 9986                    stack_slot.base,
 9987                    @as(i65, stack_slot.offset) + offset_from_parent,
 9988                ),
 9989                else => unreachable,
 9990            }
 9991            try vi.spillReg(isel, src_ra, 0, expected_live_registers);
 9992        }
 9993
 9994        fn spillReg(
 9995            vi: Value.Index,
 9996            isel: *Select,
 9997            src_ra: Register.Alias,
 9998            start_offset: u64,
 9999            expected_live_registers: *const LiveRegisters,
10000        ) !void {
10001            assert(isel.live_registers.get(src_ra) == .allocating);
10002            var part_it = vi.parts(isel);
10003            if (part_it.only()) |part_vi| {
10004                const dst_ra = part_vi.register(isel) orelse return;
10005                if (dst_ra == src_ra) return;
10006                const part_size = part_vi.size(isel);
10007                const part_ra = if (part_vi.isVector(isel)) try isel.allocIntReg() else dst_ra;
10008                defer if (part_ra != dst_ra) isel.freeReg(part_ra);
10009                if (part_ra != dst_ra) try isel.emit(part_size: switch (part_size) {
10010                    else => unreachable,
10011                    2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
10012                        .fmov(dst_ra.h(), .{ .register = part_ra.w() })
10013                    else
10014                        continue :part_size 4,
10015                    4 => .fmov(dst_ra.s(), .{ .register = part_ra.w() }),
10016                    8 => .fmov(dst_ra.d(), .{ .register = part_ra.x() }),
10017                });
10018                try isel.emit(switch (start_offset + part_size) {
10019                    else => unreachable,
10020                    1...4 => |end_offset| switch (part_vi.signedness(isel)) {
10021                        .signed => .sbfm(part_ra.w(), src_ra.w(), .{
10022                            .N = .word,
10023                            .immr = @intCast(8 * start_offset),
10024                            .imms = @intCast(8 * end_offset - 1),
10025                        }),
10026                        .unsigned => .ubfm(part_ra.w(), src_ra.w(), .{
10027                            .N = .word,
10028                            .immr = @intCast(8 * start_offset),
10029                            .imms = @intCast(8 * end_offset - 1),
10030                        }),
10031                    },
10032                    5...8 => |end_offset| switch (part_vi.signedness(isel)) {
10033                        .signed => .sbfm(part_ra.x(), src_ra.x(), .{
10034                            .N = .doubleword,
10035                            .immr = @intCast(8 * start_offset),
10036                            .imms = @intCast(8 * end_offset - 1),
10037                        }),
10038                        .unsigned => .ubfm(part_ra.x(), src_ra.x(), .{
10039                            .N = .doubleword,
10040                            .immr = @intCast(8 * start_offset),
10041                            .imms = @intCast(8 * end_offset - 1),
10042                        }),
10043                    },
10044                });
10045                const value_ra = &part_vi.get(isel).location_payload.small.register;
10046                assert(value_ra.* == dst_ra);
10047                value_ra.* = .zr;
10048                const dst_live_vi = isel.live_registers.getPtr(dst_ra);
10049                assert(dst_live_vi.* == part_vi);
10050                dst_live_vi.* = switch (expected_live_registers.get(dst_ra)) {
10051                    _ => .allocating,
10052                    .allocating => unreachable,
10053                    .free => .free,
10054                };
10055            } else while (part_it.next()) |part_vi| try part_vi.spillReg(
10056                isel,
10057                src_ra,
10058                start_offset + part_vi.get(isel).offset_from_parent,
10059                expected_live_registers,
10060            );
10061        }
10062
10063        fn liveOut(vi: Value.Index, isel: *Select, ra: Register.Alias) !void {
10064            assert(try isel.fill(ra));
10065            const live_vi = isel.live_registers.getPtr(ra);
10066            assert(live_vi.* == .free);
10067            live_vi.* = .allocating;
10068            try Value.Materialize.finish(.{ .vi = vi, .ra = ra }, isel);
10069        }
10070
10071        fn allocStackSlot(vi: Value.Index, isel: *Select) Value.Indirect {
10072            const offset = vi.alignment(isel).forward(isel.stack_size);
10073            isel.stack_size = @intCast(offset + vi.size(isel));
10074            tracking_log.debug("${d} -> [sp, #0x{x}]", .{ @intFromEnum(vi), @abs(offset) });
10075            return .{
10076                .base = .sp,
10077                .offset = @intCast(offset),
10078            };
10079        }
10080
10081        fn address(initial_vi: Value.Index, isel: *Select, initial_offset: u64, ptr_ra: Register.Alias) !void {
10082            var vi = initial_vi;
10083            var offset: i65 = vi.get(isel).offset_from_parent + initial_offset;
10084            parent: switch (vi.parent(isel)) {
10085                .unallocated => {
10086                    const stack_slot = vi.allocStackSlot(isel);
10087                    vi.setParent(isel, .{ .stack_slot = stack_slot });
10088                    continue :parent .{ .stack_slot = stack_slot };
10089                },
10090                .stack_slot => |stack_slot| {
10091                    offset += stack_slot.offset;
10092                    const lo12: u12 = @truncate(@abs(offset) >> 0);
10093                    const hi12: u12 = @intCast(@abs(offset) >> 12);
10094                    if (hi12 > 0) try isel.emit(if (offset >= 0) .add(
10095                        ptr_ra.x(),
10096                        if (lo12 > 0) ptr_ra.x() else stack_slot.base.x(),
10097                        .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
10098                    ) else .sub(
10099                        ptr_ra.x(),
10100                        if (lo12 > 0) ptr_ra.x() else stack_slot.base.x(),
10101                        .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
10102                    ));
10103                    if (lo12 > 0 or hi12 == 0) try isel.emit(if (offset >= 0) .add(
10104                        ptr_ra.x(),
10105                        stack_slot.base.x(),
10106                        .{ .immediate = lo12 },
10107                    ) else .sub(
10108                        ptr_ra.x(),
10109                        stack_slot.base.x(),
10110                        .{ .immediate = lo12 },
10111                    ));
10112                },
10113                .address => |address_vi| try address_vi.liveOut(isel, ptr_ra),
10114                .value => |parent_vi| {
10115                    vi = parent_vi;
10116                    offset += vi.get(isel).offset_from_parent;
10117                    continue :parent vi.parent(isel);
10118                },
10119                .constant => |constant| {
10120                    const pt = isel.pt;
10121                    const zcu = pt.zcu;
10122                    switch (true) {
10123                        false => {
10124                            try isel.uav_relocs.append(zcu.gpa, .{
10125                                .uav = .{
10126                                    .val = constant.toIntern(),
10127                                    .orig_ty = (try pt.singleConstPtrType(constant.typeOf(zcu))).toIntern(),
10128                                },
10129                                .reloc = .{
10130                                    .label = @intCast(isel.instructions.items.len),
10131                                    .addend = @intCast(offset),
10132                                },
10133                            });
10134                            try isel.emit(.adr(ptr_ra.x(), 0));
10135                        },
10136                        true => {
10137                            try isel.uav_relocs.append(zcu.gpa, .{
10138                                .uav = .{
10139                                    .val = constant.toIntern(),
10140                                    .orig_ty = (try pt.singleConstPtrType(constant.typeOf(zcu))).toIntern(),
10141                                },
10142                                .reloc = .{
10143                                    .label = @intCast(isel.instructions.items.len),
10144                                    .addend = @intCast(offset),
10145                                },
10146                            });
10147                            try isel.emit(.add(ptr_ra.x(), ptr_ra.x(), .{ .immediate = 0 }));
10148                            try isel.uav_relocs.append(zcu.gpa, .{
10149                                .uav = .{
10150                                    .val = constant.toIntern(),
10151                                    .orig_ty = (try pt.singleConstPtrType(constant.typeOf(zcu))).toIntern(),
10152                                },
10153                                .reloc = .{
10154                                    .label = @intCast(isel.instructions.items.len),
10155                                    .addend = @intCast(offset),
10156                                },
10157                            });
10158                            try isel.emit(.adrp(ptr_ra.x(), 0));
10159                        },
10160                    }
10161                },
10162            }
10163        }
10164    };
10165
10166    pub const PartIterator = struct {
10167        vi: Value.Index,
10168        remaining: Value.PartsLen,
10169
10170        fn initOne(vi: Value.Index) PartIterator {
10171            return .{ .vi = vi, .remaining = 1 };
10172        }
10173
10174        pub fn next(it: *PartIterator) ?Value.Index {
10175            if (it.remaining == 0) return null;
10176            it.remaining -= 1;
10177            defer it.vi = @enumFromInt(@intFromEnum(it.vi) + 1);
10178            return it.vi;
10179        }
10180
10181        pub fn peek(it: PartIterator) ?Value.Index {
10182            var it_mut = it;
10183            return it_mut.next();
10184        }
10185
10186        pub fn only(it: PartIterator) ?Value.Index {
10187            return if (it.remaining == 1) it.vi else null;
10188        }
10189    };
10190
10191    const FieldPartIterator = struct {
10192        vi: Value.Index,
10193        ty: ZigType,
10194        field_offset: u64,
10195        field_size: u64,
10196        next_offset: u64,
10197
10198        fn next(it: *FieldPartIterator, isel: *Select) !?struct { offset: u64, vi: Value.Index } {
10199            const next_offset = it.next_offset;
10200            const next_part_size = it.field_size - next_offset;
10201            if (next_part_size == 0) return null;
10202            var next_part_offset = it.field_offset + next_offset;
10203
10204            const zcu = isel.pt.zcu;
10205            const ip = &zcu.intern_pool;
10206            var vi = it.vi;
10207            var ty = it.ty;
10208            var ty_size = vi.size(isel);
10209            assert(ty_size == ty.abiSize(zcu));
10210            var offset: u64 = 0;
10211            var size = ty_size;
10212            assert(next_part_offset + next_part_size <= size);
10213            while (next_part_offset > 0 or next_part_size < size) {
10214                const part_vi = vi.partAtOffset(isel, next_part_offset);
10215                if (part_vi != vi) {
10216                    vi = part_vi;
10217                    const part_offset, size = part_vi.position(isel);
10218                    assert(part_offset <= next_part_offset and part_offset + size > next_part_offset);
10219                    offset += part_offset;
10220                    next_part_offset -= part_offset;
10221                    continue;
10222                }
10223                try isel.values.ensureUnusedCapacity(zcu.gpa, Value.max_parts);
10224                type_key: switch (ip.indexToKey(ty.toIntern())) {
10225                    else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}),
10226                    .int_type => |int_type| switch (int_type.bits) {
10227                        0 => unreachable,
10228                        1...64 => unreachable,
10229                        65...256 => |bits| if (offset == 0 and size == ty_size) {
10230                            const parts_len = std.math.divCeil(u16, bits, 64) catch unreachable;
10231                            vi.setParts(isel, @intCast(parts_len));
10232                            for (0..parts_len) |part_index| _ = vi.addPart(isel, 8 * part_index, 8);
10233                        },
10234                        else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}),
10235                    },
10236                    .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
10237                        .one, .many, .c => unreachable,
10238                        .slice => if (offset == 0 and size == ty_size) {
10239                            vi.setParts(isel, 2);
10240                            _ = vi.addPart(isel, 0, 8);
10241                            _ = vi.addPart(isel, 8, 8);
10242                        } else unreachable,
10243                    },
10244                    .opt_type => |child_type| if (ty.optionalReprIsPayload(zcu)) continue :type_key ip.indexToKey(child_type) else {
10245                        const child_ty: ZigType = .fromInterned(child_type);
10246                        const child_size = child_ty.abiSize(zcu);
10247                        if (offset == 0 and size == child_size) {
10248                            ty = child_ty;
10249                            ty_size = child_size;
10250                            continue :type_key ip.indexToKey(child_type);
10251                        }
10252                        switch (child_size) {
10253                            0...8, 16 => if (offset == 0 and size == ty_size) {
10254                                vi.setParts(isel, 2);
10255                                _ = vi.addPart(isel, 0, child_size);
10256                                _ = vi.addPart(isel, child_size, 1);
10257                            } else unreachable,
10258                            9...15 => if (offset == 0 and size == ty_size) {
10259                                vi.setParts(isel, 2);
10260                                _ = vi.addPart(isel, 0, 8);
10261                                _ = vi.addPart(isel, 8, ty_size - 8);
10262                            } else if (offset == 8 and size == ty_size - 8) {
10263                                vi.setParts(isel, 2);
10264                                _ = vi.addPart(isel, 0, child_size - 8);
10265                                _ = vi.addPart(isel, child_size - 8, 1);
10266                            } else unreachable,
10267                            else => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}),
10268                        }
10269                    },
10270                    .array_type => |array_type| {
10271                        const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10272                        const array_len = array_type.lenIncludingSentinel();
10273                        if (array_len > Value.max_parts and
10274                            (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10275                            return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10276                        const alignment = vi.alignment(isel);
10277                        const Part = struct { offset: u64, size: u64 };
10278                        var parts: [Value.max_parts]Part = undefined;
10279                        var parts_len: Value.PartsLen = 0;
10280                        const elem_ty: ZigType = .fromInterned(array_type.child);
10281                        const elem_size = elem_ty.abiSize(zcu);
10282                        const elem_signedness = if (ty.isAbiInt(zcu)) elem_signedness: {
10283                            const elem_int_info = elem_ty.intInfo(zcu);
10284                            break :elem_signedness if (elem_int_info.bits <= 16) elem_int_info.signedness else null;
10285                        } else null;
10286                        const elem_is_vector = elem_size <= 16 and
10287                            CallAbiIterator.homogeneousAggregateBaseType(zcu, elem_ty.toIntern()) != null;
10288                        var elem_end: u64 = 0;
10289                        for (0..@intCast(array_len)) |_| {
10290                            const elem_begin = elem_end;
10291                            if (elem_begin >= offset + size) break;
10292                            elem_end = elem_begin + elem_size;
10293                            if (elem_end <= offset) continue;
10294                            if (offset >= elem_begin and offset + size <= elem_begin + elem_size) {
10295                                ty = elem_ty;
10296                                ty_size = elem_size;
10297                                offset -= elem_begin;
10298                                continue :type_key ip.indexToKey(elem_ty.toIntern());
10299                            }
10300                            if (parts_len > 0) combine: {
10301                                const prev_part = &parts[parts_len - 1];
10302                                const combined_size = elem_end - prev_part.offset;
10303                                if (combined_size > @as(u64, 1) << @min(
10304                                    min_part_log2_stride,
10305                                    alignment.toLog2Units(),
10306                                    @ctz(prev_part.offset),
10307                                )) break :combine;
10308                                prev_part.size = combined_size;
10309                                continue;
10310                            }
10311                            parts[parts_len] = .{ .offset = elem_begin, .size = elem_size };
10312                            parts_len += 1;
10313                        }
10314                        vi.setParts(isel, parts_len);
10315                        for (parts[0..parts_len]) |part| {
10316                            const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
10317                            if (elem_signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
10318                            if (elem_is_vector) subpart_vi.setIsVector(isel);
10319                        }
10320                    },
10321                    .anyframe_type => unreachable,
10322                    .error_union_type => |error_union_type| {
10323                        const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10324                        if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10325                            return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10326                        const alignment = vi.alignment(isel);
10327                        const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
10328                        const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
10329                        const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
10330                        const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness, is_vector: bool };
10331                        var parts: [2]Part = undefined;
10332                        var parts_len: Value.PartsLen = 0;
10333                        var field_end: u64 = 0;
10334                        for (0..2) |field_index| {
10335                            const field_ty: ZigType, const field_begin = switch (@as(enum { error_set, payload }, switch (field_index) {
10336                                0 => if (error_set_offset < payload_offset) .error_set else .payload,
10337                                1 => if (error_set_offset < payload_offset) .payload else .error_set,
10338                                else => unreachable,
10339                            })) {
10340                                .error_set => .{ .fromInterned(error_union_type.error_set_type), error_set_offset },
10341                                .payload => .{ payload_ty, payload_offset },
10342                            };
10343                            if (field_begin >= offset + size) break;
10344                            const field_size = field_ty.abiSize(zcu);
10345                            if (field_size == 0) continue;
10346                            field_end = field_begin + field_size;
10347                            if (field_end <= offset) continue;
10348                            if (offset >= field_begin and offset + size <= field_begin + field_size) {
10349                                ty = field_ty;
10350                                ty_size = field_size;
10351                                offset -= field_begin;
10352                                continue :type_key ip.indexToKey(field_ty.toIntern());
10353                            }
10354                            const field_signedness = if (field_ty.isAbiInt(zcu)) field_signedness: {
10355                                const field_int_info = field_ty.intInfo(zcu);
10356                                break :field_signedness if (field_int_info.bits <= 16) field_int_info.signedness else null;
10357                            } else null;
10358                            const field_is_vector = field_size <= 16 and
10359                                CallAbiIterator.homogeneousAggregateBaseType(zcu, field_ty.toIntern()) != null;
10360                            if (parts_len > 0) combine: {
10361                                const prev_part = &parts[parts_len - 1];
10362                                const combined_size = field_end - prev_part.offset;
10363                                if (combined_size > @as(u64, 1) << @min(
10364                                    min_part_log2_stride,
10365                                    alignment.toLog2Units(),
10366                                    @ctz(prev_part.offset),
10367                                )) break :combine;
10368                                prev_part.size = combined_size;
10369                                prev_part.signedness = null;
10370                                prev_part.is_vector &= field_is_vector;
10371                                continue;
10372                            }
10373                            parts[parts_len] = .{
10374                                .offset = field_begin,
10375                                .size = field_size,
10376                                .signedness = field_signedness,
10377                                .is_vector = field_is_vector,
10378                            };
10379                            parts_len += 1;
10380                        }
10381                        vi.setParts(isel, parts_len);
10382                        for (parts[0..parts_len]) |part| {
10383                            const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
10384                            if (part.signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
10385                            if (part.is_vector) subpart_vi.setIsVector(isel);
10386                        }
10387                    },
10388                    .simple_type => |simple_type| switch (simple_type) {
10389                        .f16, .f32, .f64, .f128, .c_longdouble => return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)}),
10390                        .f80 => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 80 } },
10391                        .usize,
10392                        .isize,
10393                        .c_char,
10394                        .c_short,
10395                        .c_ushort,
10396                        .c_int,
10397                        .c_uint,
10398                        .c_long,
10399                        .c_ulong,
10400                        .c_longlong,
10401                        .c_ulonglong,
10402                        => continue :type_key .{ .int_type = ty.intInfo(zcu) },
10403                        .anyopaque,
10404                        .void,
10405                        .type,
10406                        .comptime_int,
10407                        .comptime_float,
10408                        .noreturn,
10409                        .null,
10410                        .undefined,
10411                        .enum_literal,
10412                        .adhoc_inferred_error_set,
10413                        .generic_poison,
10414                        => unreachable,
10415                        .bool => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 1 } },
10416                        .anyerror => continue :type_key .{ .int_type = .{
10417                            .signedness = .unsigned,
10418                            .bits = zcu.errorSetBits(),
10419                        } },
10420                    },
10421                    .struct_type => {
10422                        const loaded_struct = ip.loadStructType(ty.toIntern());
10423                        switch (loaded_struct.layout) {
10424                            .auto, .@"extern" => {},
10425                            .@"packed" => continue :type_key .{
10426                                .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
10427                            },
10428                        }
10429                        const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10430                        if (loaded_struct.field_types.len > Value.max_parts and
10431                            (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10432                            return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10433                        const alignment = vi.alignment(isel);
10434                        const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness, is_vector: bool };
10435                        var parts: [Value.max_parts]Part = undefined;
10436                        var parts_len: Value.PartsLen = 0;
10437                        var field_end: u64 = 0;
10438                        var field_it = loaded_struct.iterateRuntimeOrder(ip);
10439                        while (field_it.next()) |field_index| {
10440                            const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
10441                            const field_begin = switch (loaded_struct.fieldAlign(ip, field_index)) {
10442                                .none => field_ty.abiAlignment(zcu),
10443                                else => |field_align| field_align,
10444                            }.forward(field_end);
10445                            if (field_begin >= offset + size) break;
10446                            const field_size = field_ty.abiSize(zcu);
10447                            field_end = field_begin + field_size;
10448                            if (field_end <= offset) continue;
10449                            if (offset >= field_begin and offset + size <= field_begin + field_size) {
10450                                ty = field_ty;
10451                                ty_size = field_size;
10452                                offset -= field_begin;
10453                                continue :type_key ip.indexToKey(field_ty.toIntern());
10454                            }
10455                            const field_signedness = if (field_ty.isAbiInt(zcu)) field_signedness: {
10456                                const field_int_info = field_ty.intInfo(zcu);
10457                                break :field_signedness if (field_int_info.bits <= 16) field_int_info.signedness else null;
10458                            } else null;
10459                            const field_is_vector = field_size <= 16 and
10460                                CallAbiIterator.homogeneousAggregateBaseType(zcu, field_ty.toIntern()) != null;
10461                            if (parts_len > 0) combine: {
10462                                const prev_part = &parts[parts_len - 1];
10463                                const combined_size = field_end - prev_part.offset;
10464                                if (combined_size > @as(u64, 1) << @min(
10465                                    min_part_log2_stride,
10466                                    alignment.toLog2Units(),
10467                                    @ctz(prev_part.offset),
10468                                )) break :combine;
10469                                prev_part.size = combined_size;
10470                                prev_part.signedness = null;
10471                                prev_part.is_vector &= field_is_vector;
10472                                continue;
10473                            }
10474                            parts[parts_len] = .{
10475                                .offset = field_begin,
10476                                .size = field_size,
10477                                .signedness = field_signedness,
10478                                .is_vector = field_is_vector,
10479                            };
10480                            parts_len += 1;
10481                        }
10482                        vi.setParts(isel, parts_len);
10483                        for (parts[0..parts_len]) |part| {
10484                            const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
10485                            if (part.signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
10486                            if (part.is_vector) subpart_vi.setIsVector(isel);
10487                        }
10488                    },
10489                    .tuple_type => |tuple_type| {
10490                        const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10491                        if (tuple_type.types.len > Value.max_parts and
10492                            (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10493                            return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10494                        const alignment = vi.alignment(isel);
10495                        const Part = struct { offset: u64, size: u64, is_vector: bool };
10496                        var parts: [Value.max_parts]Part = undefined;
10497                        var parts_len: Value.PartsLen = 0;
10498                        var field_end: u64 = 0;
10499                        for (tuple_type.types.get(ip), tuple_type.values.get(ip)) |field_type, field_value| {
10500                            if (field_value != .none) continue;
10501                            const field_ty: ZigType = .fromInterned(field_type);
10502                            const field_begin = field_ty.abiAlignment(zcu).forward(field_end);
10503                            if (field_begin >= offset + size) break;
10504                            const field_size = field_ty.abiSize(zcu);
10505                            if (field_size == 0) continue;
10506                            field_end = field_begin + field_size;
10507                            if (field_end <= offset) continue;
10508                            if (offset >= field_begin and offset + size <= field_begin + field_size) {
10509                                ty = field_ty;
10510                                ty_size = field_size;
10511                                offset -= field_begin;
10512                                continue :type_key ip.indexToKey(field_ty.toIntern());
10513                            }
10514                            const field_is_vector = field_size <= 16 and
10515                                CallAbiIterator.homogeneousAggregateBaseType(zcu, field_ty.toIntern()) != null;
10516                            if (parts_len > 0) combine: {
10517                                const prev_part = &parts[parts_len - 1];
10518                                const combined_size = field_end - prev_part.offset;
10519                                if (combined_size > @as(u64, 1) << @min(
10520                                    min_part_log2_stride,
10521                                    alignment.toLog2Units(),
10522                                    @ctz(prev_part.offset),
10523                                )) break :combine;
10524                                prev_part.size = combined_size;
10525                                prev_part.is_vector &= field_is_vector;
10526                                continue;
10527                            }
10528                            parts[parts_len] = .{ .offset = field_begin, .size = field_size, .is_vector = field_is_vector };
10529                            parts_len += 1;
10530                        }
10531                        vi.setParts(isel, parts_len);
10532                        for (parts[0..parts_len]) |part| {
10533                            const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
10534                            if (part.is_vector) subpart_vi.setIsVector(isel);
10535                        }
10536                    },
10537                    .union_type => {
10538                        const loaded_union = ip.loadUnionType(ty.toIntern());
10539                        switch (loaded_union.flagsUnordered(ip).layout) {
10540                            .auto, .@"extern" => {},
10541                            .@"packed" => continue :type_key .{ .int_type = .{
10542                                .signedness = .unsigned,
10543                                .bits = @intCast(ty.bitSize(zcu)),
10544                            } },
10545                        }
10546                        const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
10547                        if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
10548                            return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
10549                        const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
10550                        const alignment = vi.alignment(isel);
10551                        const tag_offset = union_layout.tagOffset();
10552                        const payload_offset = union_layout.payloadOffset();
10553                        const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness };
10554                        var parts: [2]Part = undefined;
10555                        var parts_len: Value.PartsLen = 0;
10556                        var field_end: u64 = 0;
10557                        for (0..2) |field_index| {
10558                            const field: enum { tag, payload } = switch (field_index) {
10559                                0 => if (tag_offset < payload_offset) .tag else .payload,
10560                                1 => if (tag_offset < payload_offset) .payload else .tag,
10561                                else => unreachable,
10562                            };
10563                            const field_size, const field_begin = switch (field) {
10564                                .tag => .{ union_layout.tag_size, tag_offset },
10565                                .payload => .{ union_layout.payload_size, payload_offset },
10566                            };
10567                            if (field_begin >= offset + size) break;
10568                            if (field_size == 0) continue;
10569                            field_end = field_begin + field_size;
10570                            if (field_end <= offset) continue;
10571                            const field_signedness = field_signedness: switch (field) {
10572                                .tag => {
10573                                    if (offset >= field_begin and offset + size <= field_begin + field_size) {
10574                                        ty = .fromInterned(loaded_union.enum_tag_ty);
10575                                        ty_size = field_size;
10576                                        offset -= field_begin;
10577                                        continue :type_key ip.indexToKey(loaded_union.enum_tag_ty);
10578                                    }
10579                                    break :field_signedness ip.indexToKey(loaded_union.loadTagType(ip).tag_ty).int_type.signedness;
10580                                },
10581                                .payload => null,
10582                            };
10583                            if (parts_len > 0) combine: {
10584                                const prev_part = &parts[parts_len - 1];
10585                                const combined_size = field_end - prev_part.offset;
10586                                if (combined_size > @as(u64, 1) << @min(
10587                                    min_part_log2_stride,
10588                                    alignment.toLog2Units(),
10589                                    @ctz(prev_part.offset),
10590                                )) break :combine;
10591                                prev_part.size = combined_size;
10592                                prev_part.signedness = null;
10593                                continue;
10594                            }
10595                            parts[parts_len] = .{
10596                                .offset = field_begin,
10597                                .size = field_size,
10598                                .signedness = field_signedness,
10599                            };
10600                            parts_len += 1;
10601                        }
10602                        vi.setParts(isel, parts_len);
10603                        for (parts[0..parts_len]) |part| {
10604                            const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
10605                            if (part.signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
10606                        }
10607                    },
10608                    .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
10609                    .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
10610                    .error_set_type,
10611                    .inferred_error_set_type,
10612                    => continue :type_key .{ .simple_type = .anyerror },
10613                    .undef,
10614                    .simple_value,
10615                    .variable,
10616                    .@"extern",
10617                    .func,
10618                    .int,
10619                    .err,
10620                    .error_union,
10621                    .enum_literal,
10622                    .enum_tag,
10623                    .empty_enum_value,
10624                    .float,
10625                    .ptr,
10626                    .slice,
10627                    .opt,
10628                    .aggregate,
10629                    .un,
10630                    .memoized_call,
10631                    => unreachable, // values, not types
10632                }
10633            }
10634            it.next_offset = next_offset + size;
10635            return .{ .offset = next_part_offset - next_offset, .vi = vi };
10636        }
10637
10638        fn only(it: *FieldPartIterator, isel: *Select) !?Value.Index {
10639            const part = try it.next(isel);
10640            assert(part.?.offset == 0);
10641            return if (try it.next(isel)) |_| null else part.?.vi;
10642        }
10643    };
10644
10645    const Materialize = struct {
10646        vi: Value.Index,
10647        ra: Register.Alias,
10648
10649        fn finish(mat: Value.Materialize, isel: *Select) error{ OutOfMemory, CodegenFail }!void {
10650            const live_vi = isel.live_registers.getPtr(mat.ra);
10651            assert(live_vi.* == .allocating);
10652            var vi = mat.vi;
10653            var offset: u64 = 0;
10654            const size = mat.vi.size(isel);
10655            free: while (true) {
10656                if (vi.register(isel)) |ra| {
10657                    if (ra != mat.ra) break :free try isel.emit(if (vi == mat.vi) if (mat.ra.isVector()) switch (size) {
10658                        else => unreachable,
10659                        2 => if (isel.target.cpu.has(.aarch64, .fullfp16))
10660                            .fmov(mat.ra.h(), .{ .register = ra.h() })
10661                        else
10662                            .dup(mat.ra.h(), ra.@"h[]"(0)),
10663                        4 => .fmov(mat.ra.s(), .{ .register = ra.s() }),
10664                        8 => .fmov(mat.ra.d(), .{ .register = ra.d() }),
10665                        16 => .orr(mat.ra.@"16b"(), ra.@"16b"(), .{ .register = ra.@"16b"() }),
10666                    } else switch (size) {
10667                        else => unreachable,
10668                        1...4 => .orr(mat.ra.w(), .wzr, .{ .register = ra.w() }),
10669                        5...8 => .orr(mat.ra.x(), .xzr, .{ .register = ra.x() }),
10670                    } else switch (offset + size) {
10671                        else => unreachable,
10672                        1...4 => |end_offset| switch (mat.vi.signedness(isel)) {
10673                            .signed => .sbfm(mat.ra.w(), ra.w(), .{
10674                                .N = .word,
10675                                .immr = @intCast(8 * offset),
10676                                .imms = @intCast(8 * end_offset - 1),
10677                            }),
10678                            .unsigned => .ubfm(mat.ra.w(), ra.w(), .{
10679                                .N = .word,
10680                                .immr = @intCast(8 * offset),
10681                                .imms = @intCast(8 * end_offset - 1),
10682                            }),
10683                        },
10684                        5...8 => |end_offset| switch (mat.vi.signedness(isel)) {
10685                            .signed => .sbfm(mat.ra.x(), ra.x(), .{
10686                                .N = .doubleword,
10687                                .immr = @intCast(8 * offset),
10688                                .imms = @intCast(8 * end_offset - 1),
10689                            }),
10690                            .unsigned => .ubfm(mat.ra.x(), ra.x(), .{
10691                                .N = .doubleword,
10692                                .immr = @intCast(8 * offset),
10693                                .imms = @intCast(8 * end_offset - 1),
10694                            }),
10695                        },
10696                    });
10697                    mat.vi.get(isel).location_payload.small.register = mat.ra;
10698                    live_vi.* = mat.vi;
10699                    return;
10700                }
10701                offset += vi.get(isel).offset_from_parent;
10702                switch (vi.parent(isel)) {
10703                    .unallocated => {
10704                        mat.vi.get(isel).location_payload.small.register = mat.ra;
10705                        live_vi.* = mat.vi;
10706                        return;
10707                    },
10708                    .stack_slot => |stack_slot| break :free try isel.loadReg(
10709                        mat.ra,
10710                        size,
10711                        mat.vi.signedness(isel),
10712                        stack_slot.base,
10713                        @as(i65, stack_slot.offset) + offset,
10714                    ),
10715                    .address => |base_vi| {
10716                        const base_mat = try base_vi.matReg(isel);
10717                        try isel.loadReg(mat.ra, size, mat.vi.signedness(isel), base_mat.ra, offset);
10718                        break :free try base_mat.finish(isel);
10719                    },
10720                    .value => |parent_vi| vi = parent_vi,
10721                    .constant => |initial_constant| {
10722                        const zcu = isel.pt.zcu;
10723                        const ip = &zcu.intern_pool;
10724                        var constant = initial_constant.toIntern();
10725                        var constant_key = ip.indexToKey(constant);
10726                        while (true) {
10727                            constant_key: switch (constant_key) {
10728                                .int_type,
10729                                .ptr_type,
10730                                .array_type,
10731                                .vector_type,
10732                                .opt_type,
10733                                .anyframe_type,
10734                                .error_union_type,
10735                                .simple_type,
10736                                .struct_type,
10737                                .tuple_type,
10738                                .union_type,
10739                                .opaque_type,
10740                                .enum_type,
10741                                .func_type,
10742                                .error_set_type,
10743                                .inferred_error_set_type,
10744
10745                                .enum_literal,
10746                                .empty_enum_value,
10747                                .memoized_call,
10748                                => unreachable, // not a runtime value
10749                                .undef => break :free try isel.emit(if (mat.ra.isVector()) .movi(switch (size) {
10750                                    else => unreachable,
10751                                    1...8 => mat.ra.@"8b"(),
10752                                    9...16 => mat.ra.@"16b"(),
10753                                }, 0xaa, .{ .lsl = 0 }) else switch (size) {
10754                                    else => unreachable,
10755                                    1...4 => .orr(mat.ra.w(), .wzr, .{ .immediate = .{
10756                                        .N = .word,
10757                                        .immr = 0b000001,
10758                                        .imms = 0b111100,
10759                                    } }),
10760                                    5...8 => .orr(mat.ra.x(), .xzr, .{ .immediate = .{
10761                                        .N = .word,
10762                                        .immr = 0b000001,
10763                                        .imms = 0b111100,
10764                                    } }),
10765                                }),
10766                                .simple_value => |simple_value| switch (simple_value) {
10767                                    .undefined, .void, .null, .empty_tuple, .@"unreachable" => unreachable,
10768                                    .true => continue :constant_key .{ .int = .{
10769                                        .ty = .bool_type,
10770                                        .storage = .{ .u64 = 1 },
10771                                    } },
10772                                    .false => continue :constant_key .{ .int = .{
10773                                        .ty = .bool_type,
10774                                        .storage = .{ .u64 = 0 },
10775                                    } },
10776                                },
10777                                .int => |int| break :free storage: switch (int.storage) {
10778                                    .u64 => |imm| try isel.movImmediate(switch (size) {
10779                                        else => unreachable,
10780                                        1...4 => mat.ra.w(),
10781                                        5...8 => mat.ra.x(),
10782                                    }, @bitCast(std.math.shr(u64, imm, 8 * offset))),
10783                                    .i64 => |imm| switch (size) {
10784                                        else => unreachable,
10785                                        1...4 => try isel.movImmediate(mat.ra.w(), @as(u32, @bitCast(@as(i32, @truncate(std.math.shr(i64, imm, 8 * offset)))))),
10786                                        5...8 => try isel.movImmediate(mat.ra.x(), @bitCast(std.math.shr(i64, imm, 8 * offset))),
10787                                    },
10788                                    .big_int => |big_int| {
10789                                        assert(size == 8);
10790                                        var imm: u64 = 0;
10791                                        const limb_bits = @bitSizeOf(std.math.big.Limb);
10792                                        const limbs = @divExact(64, limb_bits);
10793                                        var limb_index: usize = @intCast(@divExact(offset, @divExact(limb_bits, 8)) + limbs);
10794                                        for (0..limbs) |_| {
10795                                            limb_index -= 1;
10796                                            if (limb_index >= big_int.limbs.len) continue;
10797                                            if (limb_bits < 64) imm <<= limb_bits;
10798                                            imm |= big_int.limbs[limb_index];
10799                                        }
10800                                        if (!big_int.positive) {
10801                                            limb_index = @min(limb_index, big_int.limbs.len);
10802                                            imm = while (limb_index > 0) {
10803                                                limb_index -= 1;
10804                                                if (big_int.limbs[limb_index] != 0) break ~imm;
10805                                            } else -%imm;
10806                                        }
10807                                        try isel.movImmediate(mat.ra.x(), imm);
10808                                    },
10809                                    .lazy_align => |ty| continue :storage .{
10810                                        .u64 = ZigType.fromInterned(ty).abiAlignment(zcu).toByteUnits().?,
10811                                    },
10812                                    .lazy_size => |ty| continue :storage .{
10813                                        .u64 = ZigType.fromInterned(ty).abiSize(zcu),
10814                                    },
10815                                },
10816                                .err => |err| continue :constant_key .{ .int = .{
10817                                    .ty = err.ty,
10818                                    .storage = .{ .u64 = ip.getErrorValueIfExists(err.name).? },
10819                                } },
10820                                .error_union => |error_union| {
10821                                    const error_union_type = ip.indexToKey(error_union.ty).error_union_type;
10822                                    const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
10823                                    const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
10824                                    const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
10825                                    const error_set_size = error_set_ty.abiSize(zcu);
10826                                    if (offset >= error_set_offset and offset + size <= error_set_offset + error_set_size) {
10827                                        offset -= error_set_offset;
10828                                        continue :constant_key switch (error_union.val) {
10829                                            .err_name => |err_name| .{ .err = .{
10830                                                .ty = error_union_type.error_set_type,
10831                                                .name = err_name,
10832                                            } },
10833                                            .payload => .{ .int = .{
10834                                                .ty = error_union_type.error_set_type,
10835                                                .storage = .{ .u64 = 0 },
10836                                            } },
10837                                        };
10838                                    }
10839                                    const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
10840                                    const payload_size = payload_ty.abiSize(zcu);
10841                                    if (offset >= payload_offset and offset + size <= payload_offset + payload_size) {
10842                                        offset -= payload_offset;
10843                                        switch (error_union.val) {
10844                                            .err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
10845                                            .payload => |payload| {
10846                                                constant = payload;
10847                                                constant_key = ip.indexToKey(constant);
10848                                                continue :constant_key constant_key;
10849                                            },
10850                                        }
10851                                    }
10852                                },
10853                                .enum_tag => |enum_tag| continue :constant_key .{ .int = ip.indexToKey(enum_tag.int).int },
10854                                .float => |float| storage: switch (float.storage) {
10855                                    .f16 => |imm| {
10856                                        if (!mat.ra.isVector()) continue :constant_key .{ .int = .{
10857                                            .ty = .u16_type,
10858                                            .storage = .{ .u64 = @as(u16, @bitCast(imm)) },
10859                                        } };
10860                                        const feat_fp16 = isel.target.cpu.has(.aarch64, .fullfp16);
10861                                        if (feat_fp16) {
10862                                            const Repr = std.math.FloatRepr(f16);
10863                                            const repr: Repr = @bitCast(imm);
10864                                            if (repr.mantissa & std.math.maxInt(Repr.Mantissa) >> 5 == 0 and switch (repr.exponent) {
10865                                                .denormal, .infinite => false,
10866                                                else => std.math.cast(i3, repr.exponent.unbias() - 1) != null,
10867                                            }) break :free try isel.emit(.fmov(mat.ra.h(), .{ .immediate = imm }));
10868                                        }
10869                                        const bits: u16 = @bitCast(imm);
10870                                        if (bits == 0) break :free try isel.emit(.movi(mat.ra.d(), 0b00000000, .replicate));
10871                                        if (bits & std.math.maxInt(u8) == 0) break :free try isel.emit(.movi(
10872                                            mat.ra.@"4h"(),
10873                                            @intCast(@shrExact(bits, 8)),
10874                                            .{ .lsl = 8 },
10875                                        ));
10876                                        const temp_ra = try isel.allocIntReg();
10877                                        defer isel.freeReg(temp_ra);
10878                                        try isel.emit(.fmov(if (feat_fp16) mat.ra.h() else mat.ra.s(), .{ .register = temp_ra.w() }));
10879                                        break :free try isel.movImmediate(temp_ra.w(), bits);
10880                                    },
10881                                    .f32 => |imm| {
10882                                        if (!mat.ra.isVector()) continue :constant_key .{ .int = .{
10883                                            .ty = .u32_type,
10884                                            .storage = .{ .u64 = @as(u32, @bitCast(imm)) },
10885                                        } };
10886                                        const Repr = std.math.FloatRepr(f32);
10887                                        const repr: Repr = @bitCast(imm);
10888                                        if (repr.mantissa & std.math.maxInt(Repr.Mantissa) >> 5 == 0 and switch (repr.exponent) {
10889                                            .denormal, .infinite => false,
10890                                            else => std.math.cast(i3, repr.exponent.unbias() - 1) != null,
10891                                        }) break :free try isel.emit(.fmov(mat.ra.s(), .{ .immediate = @floatCast(imm) }));
10892                                        const bits: u32 = @bitCast(imm);
10893                                        if (bits == 0) break :free try isel.emit(.movi(mat.ra.d(), 0b00000000, .replicate));
10894                                        if (bits & std.math.maxInt(u24) == 0) break :free try isel.emit(.movi(
10895                                            mat.ra.@"2s"(),
10896                                            @intCast(@shrExact(bits, 24)),
10897                                            .{ .lsl = 24 },
10898                                        ));
10899                                        const temp_ra = try isel.allocIntReg();
10900                                        defer isel.freeReg(temp_ra);
10901                                        try isel.emit(.fmov(mat.ra.s(), .{ .register = temp_ra.w() }));
10902                                        break :free try isel.movImmediate(temp_ra.w(), bits);
10903                                    },
10904                                    .f64 => |imm| {
10905                                        if (!mat.ra.isVector()) continue :constant_key .{ .int = .{
10906                                            .ty = .u64_type,
10907                                            .storage = .{ .u64 = @as(u64, @bitCast(imm)) },
10908                                        } };
10909                                        const Repr = std.math.FloatRepr(f64);
10910                                        const repr: Repr = @bitCast(imm);
10911                                        if (repr.mantissa & std.math.maxInt(Repr.Mantissa) >> 5 == 0 and switch (repr.exponent) {
10912                                            .denormal, .infinite => false,
10913                                            else => std.math.cast(i3, repr.exponent.unbias() - 1) != null,
10914                                        }) break :free try isel.emit(.fmov(mat.ra.d(), .{ .immediate = @floatCast(imm) }));
10915                                        const bits: u64 = @bitCast(imm);
10916                                        if (bits == 0) break :free try isel.emit(.movi(mat.ra.d(), 0b00000000, .replicate));
10917                                        const temp_ra = try isel.allocIntReg();
10918                                        defer isel.freeReg(temp_ra);
10919                                        try isel.emit(.fmov(mat.ra.d(), .{ .register = temp_ra.x() }));
10920                                        break :free try isel.movImmediate(temp_ra.x(), bits);
10921                                    },
10922                                    .f80 => |imm| break :free try isel.movImmediate(
10923                                        mat.ra.x(),
10924                                        @truncate(std.math.shr(u80, @bitCast(imm), 8 * offset)),
10925                                    ),
10926                                    .f128 => |imm| switch (ZigType.fromInterned(float.ty).floatBits(isel.target)) {
10927                                        else => unreachable,
10928                                        16 => continue :storage .{ .f16 = @floatCast(imm) },
10929                                        32 => continue :storage .{ .f32 = @floatCast(imm) },
10930                                        64 => continue :storage .{ .f64 = @floatCast(imm) },
10931                                        128 => {
10932                                            const bits: u128 = @bitCast(imm);
10933                                            const hi64: u64 = @intCast(bits >> 64);
10934                                            const lo64: u64 = @truncate(bits >> 0);
10935                                            const temp_ra = try isel.allocIntReg();
10936                                            defer isel.freeReg(temp_ra);
10937                                            switch (hi64) {
10938                                                0 => {},
10939                                                else => {
10940                                                    try isel.emit(.fmov(mat.ra.@"d[]"(1), .{ .register = temp_ra.x() }));
10941                                                    try isel.movImmediate(temp_ra.x(), hi64);
10942                                                },
10943                                            }
10944                                            break :free switch (lo64) {
10945                                                0 => try isel.emit(.movi(switch (hi64) {
10946                                                    else => mat.ra.d(),
10947                                                    0 => mat.ra.@"2d"(),
10948                                                }, 0b00000000, .replicate)),
10949                                                else => {
10950                                                    try isel.emit(.fmov(mat.ra.d(), .{ .register = temp_ra.x() }));
10951                                                    try isel.movImmediate(temp_ra.x(), lo64);
10952                                                },
10953                                            };
10954                                        },
10955                                    },
10956                                },
10957                                .ptr => |ptr| {
10958                                    assert(offset == 0 and size == 8);
10959                                    break :free switch (ptr.base_addr) {
10960                                        .nav => |nav| if (ZigType.fromInterned(ip.getNav(nav).typeOf(ip)).isFnOrHasRuntimeBits(zcu)) switch (true) {
10961                                            false => {
10962                                                try isel.nav_relocs.append(zcu.gpa, .{
10963                                                    .nav = nav,
10964                                                    .reloc = .{
10965                                                        .label = @intCast(isel.instructions.items.len),
10966                                                        .addend = ptr.byte_offset,
10967                                                    },
10968                                                });
10969                                                try isel.emit(.adr(mat.ra.x(), 0));
10970                                            },
10971                                            true => {
10972                                                try isel.nav_relocs.append(zcu.gpa, .{
10973                                                    .nav = nav,
10974                                                    .reloc = .{
10975                                                        .label = @intCast(isel.instructions.items.len),
10976                                                        .addend = ptr.byte_offset,
10977                                                    },
10978                                                });
10979                                                if (ip.getNav(nav).getExtern(ip)) |_|
10980                                                    try isel.emit(.ldr(mat.ra.x(), .{ .unsigned_offset = .{ .base = mat.ra.x(), .offset = 0 } }))
10981                                                else
10982                                                    try isel.emit(.add(mat.ra.x(), mat.ra.x(), .{ .immediate = 0 }));
10983                                                try isel.nav_relocs.append(zcu.gpa, .{
10984                                                    .nav = nav,
10985                                                    .reloc = .{
10986                                                        .label = @intCast(isel.instructions.items.len),
10987                                                        .addend = ptr.byte_offset,
10988                                                    },
10989                                                });
10990                                                try isel.emit(.adrp(mat.ra.x(), 0));
10991                                            },
10992                                        } else continue :constant_key .{ .int = .{
10993                                            .ty = .usize_type,
10994                                            .storage = .{ .u64 = isel.pt.navAlignment(nav).forward(0xaaaaaaaaaaaaaaaa) },
10995                                        } },
10996                                        .uav => |uav| if (ZigType.fromInterned(ip.typeOf(uav.val)).isFnOrHasRuntimeBits(zcu)) switch (true) {
10997                                            false => {
10998                                                try isel.uav_relocs.append(zcu.gpa, .{
10999                                                    .uav = uav,
11000                                                    .reloc = .{
11001                                                        .label = @intCast(isel.instructions.items.len),
11002                                                        .addend = ptr.byte_offset,
11003                                                    },
11004                                                });
11005                                                try isel.emit(.adr(mat.ra.x(), 0));
11006                                            },
11007                                            true => {
11008                                                try isel.uav_relocs.append(zcu.gpa, .{
11009                                                    .uav = uav,
11010                                                    .reloc = .{
11011                                                        .label = @intCast(isel.instructions.items.len),
11012                                                        .addend = ptr.byte_offset,
11013                                                    },
11014                                                });
11015                                                try isel.emit(.add(mat.ra.x(), mat.ra.x(), .{ .immediate = 0 }));
11016                                                try isel.uav_relocs.append(zcu.gpa, .{
11017                                                    .uav = uav,
11018                                                    .reloc = .{
11019                                                        .label = @intCast(isel.instructions.items.len),
11020                                                        .addend = ptr.byte_offset,
11021                                                    },
11022                                                });
11023                                                try isel.emit(.adrp(mat.ra.x(), 0));
11024                                            },
11025                                        } else continue :constant_key .{ .int = .{
11026                                            .ty = .usize_type,
11027                                            .storage = .{ .u64 = ZigType.fromInterned(uav.orig_ty).ptrAlignment(zcu).forward(0xaaaaaaaaaaaaaaaa) },
11028                                        } },
11029                                        .int => continue :constant_key .{ .int = .{
11030                                            .ty = .usize_type,
11031                                            .storage = .{ .u64 = ptr.byte_offset },
11032                                        } },
11033                                        .eu_payload => |base| {
11034                                            var base_ptr = ip.indexToKey(base).ptr;
11035                                            const eu_ty = ip.indexToKey(base_ptr.ty).ptr_type.child;
11036                                            const payload_ty = ip.indexToKey(eu_ty).error_union_type.payload_type;
11037                                            base_ptr.byte_offset += codegen.errUnionPayloadOffset(.fromInterned(payload_ty), zcu) + ptr.byte_offset;
11038                                            continue :constant_key .{ .ptr = base_ptr };
11039                                        },
11040                                        .opt_payload => |base| {
11041                                            var base_ptr = ip.indexToKey(base).ptr;
11042                                            base_ptr.byte_offset += ptr.byte_offset;
11043                                            continue :constant_key .{ .ptr = base_ptr };
11044                                        },
11045                                        .field => |field| {
11046                                            var base_ptr = ip.indexToKey(field.base).ptr;
11047                                            const agg_ty: ZigType = .fromInterned(ip.indexToKey(base_ptr.ty).ptr_type.child);
11048                                            base_ptr.byte_offset += agg_ty.structFieldOffset(@intCast(field.index), zcu) + ptr.byte_offset;
11049                                            continue :constant_key .{ .ptr = base_ptr };
11050                                        },
11051                                        .comptime_alloc, .comptime_field, .arr_elem => unreachable,
11052                                    };
11053                                },
11054                                .slice => |slice| switch (offset) {
11055                                    0 => continue :constant_key switch (ip.indexToKey(slice.ptr)) {
11056                                        else => unreachable,
11057                                        .undef => |undef| .{ .undef = undef },
11058                                        .ptr => |ptr| .{ .ptr = ptr },
11059                                    },
11060                                    else => {
11061                                        assert(offset == @divExact(isel.target.ptrBitWidth(), 8));
11062                                        offset = 0;
11063                                        continue :constant_key .{ .int = ip.indexToKey(slice.len).int };
11064                                    },
11065                                },
11066                                .opt => |opt| {
11067                                    const child_ty = ip.indexToKey(opt.ty).opt_type;
11068                                    const child_size = ZigType.fromInterned(child_ty).abiSize(zcu);
11069                                    if (offset == child_size and size == 1) {
11070                                        offset = 0;
11071                                        continue :constant_key .{ .simple_value = switch (opt.val) {
11072                                            .none => .false,
11073                                            else => .true,
11074                                        } };
11075                                    }
11076                                    const opt_ty: ZigType = .fromInterned(opt.ty);
11077                                    if (offset + size <= child_size) continue :constant_key switch (opt.val) {
11078                                        .none => if (opt_ty.optionalReprIsPayload(zcu)) .{ .int = .{
11079                                            .ty = opt.ty,
11080                                            .storage = .{ .u64 = 0 },
11081                                        } } else .{ .undef = child_ty },
11082                                        else => |child| {
11083                                            constant = child;
11084                                            constant_key = ip.indexToKey(constant);
11085                                            continue :constant_key constant_key;
11086                                        },
11087                                    };
11088                                },
11089                                .aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) {
11090                                    else => unreachable,
11091                                    .array_type => |array_type| {
11092                                        const elem_size = ZigType.fromInterned(array_type.child).abiSize(zcu);
11093                                        const elem_offset = @mod(offset, elem_size);
11094                                        if (size <= elem_size - elem_offset) {
11095                                            defer offset = elem_offset;
11096                                            continue :constant_key switch (aggregate.storage) {
11097                                                .bytes => |bytes| .{ .int = .{ .ty = .u8_type, .storage = .{
11098                                                    .u64 = bytes.toSlice(array_type.lenIncludingSentinel(), ip)[@intCast(@divFloor(offset, elem_size))],
11099                                                } } },
11100                                                .elems => |elems| {
11101                                                    constant = elems[@intCast(@divFloor(offset, elem_size))];
11102                                                    constant_key = ip.indexToKey(constant);
11103                                                    continue :constant_key constant_key;
11104                                                },
11105                                                .repeated_elem => |repeated_elem| {
11106                                                    constant = repeated_elem;
11107                                                    constant_key = ip.indexToKey(constant);
11108                                                    continue :constant_key constant_key;
11109                                                },
11110                                            };
11111                                        }
11112                                    },
11113                                    .vector_type => {},
11114                                    .struct_type => {
11115                                        const loaded_struct = ip.loadStructType(aggregate.ty);
11116                                        switch (loaded_struct.layout) {
11117                                            .auto => {
11118                                                var field_offset: u64 = 0;
11119                                                var field_it = loaded_struct.iterateRuntimeOrder(ip);
11120                                                while (field_it.next()) |field_index| {
11121                                                    if (loaded_struct.fieldIsComptime(ip, field_index)) continue;
11122                                                    const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
11123                                                    field_offset = field_ty.structFieldAlignment(
11124                                                        loaded_struct.fieldAlign(ip, field_index),
11125                                                        loaded_struct.layout,
11126                                                        zcu,
11127                                                    ).forward(field_offset);
11128                                                    const field_size = field_ty.abiSize(zcu);
11129                                                    if (offset >= field_offset and offset + size <= field_offset + field_size) {
11130                                                        offset -= field_offset;
11131                                                        constant = switch (aggregate.storage) {
11132                                                            .bytes => unreachable,
11133                                                            .elems => |elems| elems[field_index],
11134                                                            .repeated_elem => |repeated_elem| repeated_elem,
11135                                                        };
11136                                                        constant_key = ip.indexToKey(constant);
11137                                                        continue :constant_key constant_key;
11138                                                    }
11139                                                    field_offset += field_size;
11140                                                }
11141                                            },
11142                                            .@"extern", .@"packed" => {},
11143                                        }
11144                                    },
11145                                    .tuple_type => |tuple_type| {
11146                                        var field_offset: u64 = 0;
11147                                        for (tuple_type.types.get(ip), tuple_type.values.get(ip), 0..) |field_type, field_value, field_index| {
11148                                            if (field_value != .none) continue;
11149                                            const field_ty: ZigType = .fromInterned(field_type);
11150                                            field_offset = field_ty.abiAlignment(zcu).forward(field_offset);
11151                                            const field_size = field_ty.abiSize(zcu);
11152                                            if (offset >= field_offset and offset + size <= field_offset + field_size) {
11153                                                offset -= field_offset;
11154                                                constant = switch (aggregate.storage) {
11155                                                    .bytes => unreachable,
11156                                                    .elems => |elems| elems[field_index],
11157                                                    .repeated_elem => |repeated_elem| repeated_elem,
11158                                                };
11159                                                constant_key = ip.indexToKey(constant);
11160                                                continue :constant_key constant_key;
11161                                            }
11162                                            field_offset += field_size;
11163                                        }
11164                                    },
11165                                },
11166                                .un => |un| {
11167                                    const loaded_union = ip.loadUnionType(un.ty);
11168                                    const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
11169                                    if (loaded_union.hasTag(ip)) {
11170                                        const tag_offset = union_layout.tagOffset();
11171                                        if (offset >= tag_offset and offset + size <= tag_offset + union_layout.tag_size) {
11172                                            offset -= tag_offset;
11173                                            continue :constant_key switch (ip.indexToKey(un.tag)) {
11174                                                else => unreachable,
11175                                                .int => |int| .{ .int = int },
11176                                                .enum_tag => |enum_tag| .{ .enum_tag = enum_tag },
11177                                            };
11178                                        }
11179                                    }
11180                                    const payload_offset = union_layout.payloadOffset();
11181                                    if (offset >= payload_offset and offset + size <= payload_offset + union_layout.payload_size) {
11182                                        offset -= payload_offset;
11183                                        constant = un.val;
11184                                        constant_key = ip.indexToKey(constant);
11185                                        continue :constant_key constant_key;
11186                                    }
11187                                },
11188                                else => {},
11189                            }
11190                            var buffer: [16]u8 = @splat(0);
11191                            if (ZigType.fromInterned(constant_key.typeOf()).abiSize(zcu) <= buffer.len and
11192                                try isel.writeToMemory(.fromInterned(constant), &buffer))
11193                            {
11194                                constant_key = if (mat.ra.isVector()) .{ .float = switch (size) {
11195                                    else => unreachable,
11196                                    2 => .{ .ty = .f16_type, .storage = .{ .f16 = @bitCast(std.mem.readInt(
11197                                        u16,
11198                                        buffer[@intCast(offset)..][0..2],
11199                                        isel.target.cpu.arch.endian(),
11200                                    )) } },
11201                                    4 => .{ .ty = .f32_type, .storage = .{ .f32 = @bitCast(std.mem.readInt(
11202                                        u32,
11203                                        buffer[@intCast(offset)..][0..4],
11204                                        isel.target.cpu.arch.endian(),
11205                                    )) } },
11206                                    8 => .{ .ty = .f64_type, .storage = .{ .f64 = @bitCast(std.mem.readInt(
11207                                        u64,
11208                                        buffer[@intCast(offset)..][0..8],
11209                                        isel.target.cpu.arch.endian(),
11210                                    )) } },
11211                                    16 => .{ .ty = .f128_type, .storage = .{ .f128 = @bitCast(std.mem.readInt(
11212                                        u128,
11213                                        buffer[@intCast(offset)..][0..16],
11214                                        isel.target.cpu.arch.endian(),
11215                                    )) } },
11216                                } } else .{ .int = .{
11217                                    .ty = .u64_type,
11218                                    .storage = .{ .u64 = switch (size) {
11219                                        else => unreachable,
11220                                        inline 1...8 => |ct_size| std.mem.readInt(
11221                                            @Int(.unsigned, 8 * ct_size),
11222                                            buffer[@intCast(offset)..][0..ct_size],
11223                                            isel.target.cpu.arch.endian(),
11224                                        ),
11225                                    } },
11226                                } };
11227                                offset = 0;
11228                                continue;
11229                            }
11230                            return isel.fail("unsupported value <{f}, {f}>", .{
11231                                isel.fmtType(.fromInterned(constant_key.typeOf())),
11232                                isel.fmtConstant(.fromInterned(constant)),
11233                            });
11234                        }
11235                    },
11236                }
11237            }
11238            live_vi.* = .free;
11239        }
11240    };
11241};
11242fn initValue(isel: *Select, ty: ZigType) Value.Index {
11243    const zcu = isel.pt.zcu;
11244    return isel.initValueAdvanced(ty.abiAlignment(zcu), 0, ty.abiSize(zcu));
11245}
11246fn initValueAdvanced(
11247    isel: *Select,
11248    parent_alignment: InternPool.Alignment,
11249    offset_from_parent: u64,
11250    size: u64,
11251) Value.Index {
11252    defer isel.values.addOneAssumeCapacity().* = .{
11253        .refs = 0,
11254        .flags = .{
11255            .alignment = .fromLog2Units(@min(parent_alignment.toLog2Units(), @ctz(offset_from_parent))),
11256            .parent_tag = .unallocated,
11257            .location_tag = if (size > 16) .large else .small,
11258            .parts_len_minus_one = 0,
11259        },
11260        .offset_from_parent = offset_from_parent,
11261        .parent_payload = .{ .unallocated = {} },
11262        .location_payload = if (size > 16) .{ .large = .{
11263            .size = size,
11264        } } else .{ .small = .{
11265            .size = @intCast(size),
11266            .signedness = .unsigned,
11267            .is_vector = false,
11268            .hint = .zr,
11269            .register = .zr,
11270        } },
11271        .parts = undefined,
11272    };
11273    return @enumFromInt(isel.values.items.len);
11274}
11275pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
11276    errdefer |err| @panic(@errorName(err));
11277    const stderr, _ = std.debug.lockStderrWriter(&.{});
11278    defer std.debug.unlockStderrWriter();
11279
11280    const zcu = isel.pt.zcu;
11281    const gpa = zcu.gpa;
11282    const ip = &zcu.intern_pool;
11283    const nav = ip.getNav(isel.nav_index);
11284
11285    var reverse_live_values: std.AutoArrayHashMapUnmanaged(Value.Index, std.ArrayList(Air.Inst.Index)) = .empty;
11286    defer {
11287        for (reverse_live_values.values()) |*list| list.deinit(gpa);
11288        reverse_live_values.deinit(gpa);
11289    }
11290    {
11291        try reverse_live_values.ensureTotalCapacity(gpa, isel.live_values.count());
11292        var live_val_it = isel.live_values.iterator();
11293        while (live_val_it.next()) |live_val_entry| switch (live_val_entry.value_ptr.*) {
11294            _ => {
11295                const gop = reverse_live_values.getOrPutAssumeCapacity(live_val_entry.value_ptr.*);
11296                if (!gop.found_existing) gop.value_ptr.* = .empty;
11297                try gop.value_ptr.append(gpa, live_val_entry.key_ptr.*);
11298            },
11299            .allocating, .free => unreachable,
11300        };
11301    }
11302
11303    var reverse_live_registers: std.AutoHashMapUnmanaged(Value.Index, Register.Alias) = .empty;
11304    defer reverse_live_registers.deinit(gpa);
11305    {
11306        try reverse_live_registers.ensureTotalCapacity(gpa, @typeInfo(Register.Alias).@"enum".fields.len);
11307        var live_reg_it = isel.live_registers.iterator();
11308        while (live_reg_it.next()) |live_reg_entry| switch (live_reg_entry.value.*) {
11309            _ => reverse_live_registers.putAssumeCapacityNoClobber(live_reg_entry.value.*, live_reg_entry.key),
11310            .allocating, .free => {},
11311        };
11312    }
11313
11314    var roots: std.AutoArrayHashMapUnmanaged(Value.Index, u32) = .empty;
11315    defer roots.deinit(gpa);
11316    {
11317        try roots.ensureTotalCapacity(gpa, isel.values.items.len);
11318        var vi: Value.Index = @enumFromInt(isel.values.items.len);
11319        while (@intFromEnum(vi) > 0) {
11320            vi = @enumFromInt(@intFromEnum(vi) - 1);
11321            if (which == .only_referenced and vi.get(isel).refs == 0) continue;
11322            while (true) switch (vi.parent(isel)) {
11323                .unallocated, .stack_slot, .constant => break,
11324                .value => |parent_vi| vi = parent_vi,
11325                .address => |address_vi| break roots.putAssumeCapacity(address_vi, 0),
11326            };
11327            roots.putAssumeCapacity(vi, 0);
11328        }
11329    }
11330
11331    try stderr.print("# Begin {s} Value Dump: {f}:\n", .{ @typeName(Select), nav.fqn.fmt(ip) });
11332    while (roots.pop()) |root_entry| {
11333        const vi = root_entry.key;
11334        const value = vi.get(isel);
11335        try stderr.splatByteAll(' ', 2 * (@as(usize, 1) + root_entry.value));
11336        try stderr.print("${d}", .{@intFromEnum(vi)});
11337        {
11338            var first = true;
11339            if (reverse_live_values.get(vi)) |aiis| for (aiis.items) |aii| {
11340                if (aii == Block.main) {
11341                    try stderr.print("{s}%main", .{if (first) " <- " else ", "});
11342                } else {
11343                    try stderr.print("{s}%{d}", .{ if (first) " <- " else ", ", @intFromEnum(aii) });
11344                }
11345                first = false;
11346            };
11347            if (reverse_live_registers.get(vi)) |ra| {
11348                try stderr.print("{s}{t}", .{ if (first) " <- " else ", ", ra });
11349                first = false;
11350            }
11351        }
11352        try stderr.writeByte(':');
11353        switch (value.flags.parent_tag) {
11354            .unallocated => if (value.offset_from_parent != 0) try stderr.print(" +0x{x}", .{value.offset_from_parent}),
11355            .stack_slot => {
11356                try stderr.print(" [{t}, #{s}0x{x}", .{
11357                    value.parent_payload.stack_slot.base,
11358                    if (value.parent_payload.stack_slot.offset < 0) "-" else "",
11359                    @abs(value.parent_payload.stack_slot.offset),
11360                });
11361                if (value.offset_from_parent != 0) try stderr.print("+0x{x}", .{value.offset_from_parent});
11362                try stderr.writeByte(']');
11363            },
11364            .value => try stderr.print(" ${d}+0x{x}", .{ @intFromEnum(value.parent_payload.value), value.offset_from_parent }),
11365            .address => try stderr.print(" ${d}[0x{x}]", .{ @intFromEnum(value.parent_payload.address), value.offset_from_parent }),
11366            .constant => try stderr.print(" <{f}, {f}>", .{
11367                isel.fmtType(value.parent_payload.constant.typeOf(zcu)),
11368                isel.fmtConstant(value.parent_payload.constant),
11369            }),
11370        }
11371        try stderr.print(" align({t})", .{value.flags.alignment});
11372        switch (value.flags.location_tag) {
11373            .large => try stderr.print(" size=0x{x} large", .{value.location_payload.large.size}),
11374            .small => {
11375                const loc = value.location_payload.small;
11376                try stderr.print(" size=0x{x}", .{loc.size});
11377                switch (loc.signedness) {
11378                    .unsigned => {},
11379                    .signed => try stderr.writeAll(" signed"),
11380                }
11381                if (loc.hint != .zr) try stderr.print(" hint={t}", .{loc.hint});
11382                if (loc.register != .zr) try stderr.print(" loc={t}", .{loc.register});
11383            },
11384        }
11385        try stderr.print(" refs={d}\n", .{value.refs});
11386
11387        var part_index = value.flags.parts_len_minus_one;
11388        if (part_index > 0) while (true) : (part_index -= 1) {
11389            roots.putAssumeCapacityNoClobber(
11390                @enumFromInt(@intFromEnum(value.parts) + part_index),
11391                root_entry.value + 1,
11392            );
11393            if (part_index == 0) break;
11394        };
11395    }
11396    try stderr.print("# End {s} Value Dump: {f}\n\n", .{ @typeName(Select), nav.fqn.fmt(ip) });
11397}
11398
11399fn hasRepeatedByteRepr(isel: *Select, constant: Constant) error{OutOfMemory}!?u8 {
11400    const zcu = isel.pt.zcu;
11401    const ty = constant.typeOf(zcu);
11402    const abi_size = std.math.cast(usize, ty.abiSize(zcu)) orelse return null;
11403    const byte_buffer = try zcu.gpa.alloc(u8, abi_size);
11404    defer zcu.gpa.free(byte_buffer);
11405    return if (try isel.writeToMemory(constant, byte_buffer) and
11406        std.mem.allEqual(u8, byte_buffer[1..], byte_buffer[0])) byte_buffer[0] else null;
11407}
11408
11409fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMemory}!bool {
11410    const zcu = isel.pt.zcu;
11411    const ip = &zcu.intern_pool;
11412    if (try isel.writeKeyToMemory(ip.indexToKey(constant.toIntern()), buffer)) return true;
11413    constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) {
11414        error.OutOfMemory => return error.OutOfMemory,
11415        error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false,
11416    };
11417    return true;
11418}
11419fn writeKeyToMemory(isel: *Select, constant_key: InternPool.Key, buffer: []u8) error{OutOfMemory}!bool {
11420    const zcu = isel.pt.zcu;
11421    const ip = &zcu.intern_pool;
11422    switch (constant_key) {
11423        .int_type,
11424        .ptr_type,
11425        .array_type,
11426        .vector_type,
11427        .opt_type,
11428        .anyframe_type,
11429        .error_union_type,
11430        .simple_type,
11431        .struct_type,
11432        .tuple_type,
11433        .union_type,
11434        .opaque_type,
11435        .enum_type,
11436        .func_type,
11437        .error_set_type,
11438        .inferred_error_set_type,
11439
11440        .enum_literal,
11441        .empty_enum_value,
11442        .memoized_call,
11443        => unreachable, // not a runtime value
11444        .err => |err| {
11445            const error_int = ip.getErrorValueIfExists(err.name).?;
11446            switch (buffer.len) {
11447                else => unreachable,
11448                inline 1...4 => |size| std.mem.writeInt(
11449                    @Int(.unsigned, 8 * size),
11450                    buffer[0..size],
11451                    @intCast(error_int),
11452                    isel.target.cpu.arch.endian(),
11453                ),
11454            }
11455        },
11456        .error_union => |error_union| {
11457            const error_union_type = ip.indexToKey(error_union.ty).error_union_type;
11458            const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
11459            const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
11460            const error_set = buffer[@intCast(codegen.errUnionErrorOffset(payload_ty, zcu))..][0..@intCast(error_set_ty.abiSize(zcu))];
11461            switch (error_union.val) {
11462                .err_name => |err_name| if (!try isel.writeKeyToMemory(.{ .err = .{
11463                    .ty = error_set_ty.toIntern(),
11464                    .name = err_name,
11465                } }, error_set)) return false,
11466                .payload => |payload| {
11467                    if (!try isel.writeToMemory(
11468                        .fromInterned(payload),
11469                        buffer[@intCast(codegen.errUnionPayloadOffset(payload_ty, zcu))..][0..@intCast(payload_ty.abiSize(zcu))],
11470                    )) return false;
11471                    @memset(error_set, 0);
11472                },
11473            }
11474        },
11475        .opt => |opt| {
11476            const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu));
11477            switch (opt.val) {
11478                .none => if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) {
11479                    buffer[child_size] = @intFromBool(false);
11480                } else @memset(buffer[0..child_size], 0x00),
11481                else => |child_constant| {
11482                    if (!try isel.writeToMemory(.fromInterned(child_constant), buffer[0..child_size])) return false;
11483                    if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) buffer[child_size] = @intFromBool(true);
11484                },
11485            }
11486        },
11487        .aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) {
11488            else => unreachable,
11489            .array_type => |array_type| {
11490                var elem_offset: usize = 0;
11491                const elem_size: usize = @intCast(ZigType.fromInterned(array_type.child).abiSize(zcu));
11492                const len_including_sentinel: usize = @intCast(array_type.lenIncludingSentinel());
11493                switch (aggregate.storage) {
11494                    .bytes => |bytes| @memcpy(buffer[0..len_including_sentinel], bytes.toSlice(len_including_sentinel, ip)),
11495                    .elems => |elems| for (elems) |elem| {
11496                        if (!try isel.writeToMemory(.fromInterned(elem), buffer[elem_offset..][0..elem_size])) return false;
11497                        elem_offset += elem_size;
11498                    },
11499                    .repeated_elem => |repeated_elem| for (0..len_including_sentinel) |_| {
11500                        if (!try isel.writeToMemory(.fromInterned(repeated_elem), buffer[elem_offset..][0..elem_size])) return false;
11501                        elem_offset += elem_size;
11502                    },
11503                }
11504            },
11505            .vector_type => return false,
11506            .struct_type => {
11507                const loaded_struct = ip.loadStructType(aggregate.ty);
11508                switch (loaded_struct.layout) {
11509                    .auto => {
11510                        var field_offset: u64 = 0;
11511                        var field_it = loaded_struct.iterateRuntimeOrder(ip);
11512                        while (field_it.next()) |field_index| {
11513                            if (loaded_struct.fieldIsComptime(ip, field_index)) continue;
11514                            const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
11515                            field_offset = field_ty.structFieldAlignment(
11516                                loaded_struct.fieldAlign(ip, field_index),
11517                                loaded_struct.layout,
11518                                zcu,
11519                            ).forward(field_offset);
11520                            const field_size = field_ty.abiSize(zcu);
11521                            if (!try isel.writeToMemory(.fromInterned(switch (aggregate.storage) {
11522                                .bytes => unreachable,
11523                                .elems => |elems| elems[field_index],
11524                                .repeated_elem => |repeated_elem| repeated_elem,
11525                            }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false;
11526                            field_offset += field_size;
11527                        }
11528                    },
11529                    .@"extern", .@"packed" => return false,
11530                }
11531            },
11532            .tuple_type => |tuple_type| {
11533                var field_offset: u64 = 0;
11534                for (tuple_type.types.get(ip), tuple_type.values.get(ip), 0..) |field_type, field_value, field_index| {
11535                    if (field_value != .none) continue;
11536                    const field_ty: ZigType = .fromInterned(field_type);
11537                    field_offset = field_ty.abiAlignment(zcu).forward(field_offset);
11538                    const field_size = field_ty.abiSize(zcu);
11539                    if (!try isel.writeToMemory(.fromInterned(switch (aggregate.storage) {
11540                        .bytes => unreachable,
11541                        .elems => |elems| elems[field_index],
11542                        .repeated_elem => |repeated_elem| repeated_elem,
11543                    }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false;
11544                    field_offset += field_size;
11545                }
11546            },
11547        },
11548        else => return false,
11549    }
11550    return true;
11551}
11552
11553const TryAllocRegResult = union(enum) {
11554    allocated: Register.Alias,
11555    fill_candidate: Register.Alias,
11556    out_of_registers,
11557};
11558
11559fn tryAllocIntReg(isel: *Select) TryAllocRegResult {
11560    var failed_result: TryAllocRegResult = .out_of_registers;
11561    var ra: Register.Alias = .r0;
11562    while (true) : (ra = @enumFromInt(@intFromEnum(ra) + 1)) {
11563        if (ra == .r18) continue; // The Platform Register
11564        if (ra == Register.Alias.fp) continue;
11565        const live_vi = isel.live_registers.getPtr(ra);
11566        switch (live_vi.*) {
11567            _ => switch (failed_result) {
11568                .allocated => unreachable,
11569                .fill_candidate => {},
11570                .out_of_registers => failed_result = .{ .fill_candidate = ra },
11571            },
11572            .allocating => {},
11573            .free => {
11574                live_vi.* = .allocating;
11575                isel.saved_registers.insert(ra);
11576                return .{ .allocated = ra };
11577            },
11578        }
11579        if (ra == Register.Alias.lr) return failed_result;
11580    }
11581}
11582
11583fn allocIntReg(isel: *Select) !Register.Alias {
11584    switch (isel.tryAllocIntReg()) {
11585        .allocated => |ra| return ra,
11586        .fill_candidate => |ra| {
11587            assert(try isel.fillMemory(ra));
11588            const live_vi = isel.live_registers.getPtr(ra);
11589            assert(live_vi.* == .free);
11590            live_vi.* = .allocating;
11591            return ra;
11592        },
11593        .out_of_registers => return isel.fail("ran out of registers", .{}),
11594    }
11595}
11596
11597fn tryAllocVecReg(isel: *Select) TryAllocRegResult {
11598    var failed_result: TryAllocRegResult = .out_of_registers;
11599    var ra: Register.Alias = .v0;
11600    while (true) : (ra = @enumFromInt(@intFromEnum(ra) + 1)) {
11601        const live_vi = isel.live_registers.getPtr(ra);
11602        switch (live_vi.*) {
11603            _ => switch (failed_result) {
11604                .allocated => unreachable,
11605                .fill_candidate => {},
11606                .out_of_registers => failed_result = .{ .fill_candidate = ra },
11607            },
11608            .allocating => {},
11609            .free => {
11610                live_vi.* = .allocating;
11611                isel.saved_registers.insert(ra);
11612                return .{ .allocated = ra };
11613            },
11614        }
11615        if (ra == Register.Alias.v31) return failed_result;
11616    }
11617}
11618
11619fn allocVecReg(isel: *Select) !Register.Alias {
11620    switch (isel.tryAllocVecReg()) {
11621        .allocated => |ra| return ra,
11622        .fill_candidate => |ra| {
11623            assert(try isel.fillMemory(ra));
11624            return ra;
11625        },
11626        .out_of_registers => return isel.fail("ran out of registers", .{}),
11627    }
11628}
11629
11630const RegLock = struct {
11631    ra: Register.Alias,
11632    const empty: RegLock = .{ .ra = .zr };
11633    fn unlock(lock: RegLock, isel: *Select) void {
11634        switch (lock.ra) {
11635            else => |ra| isel.freeReg(ra),
11636            .zr => {},
11637        }
11638    }
11639};
11640fn lockReg(isel: *Select, ra: Register.Alias) RegLock {
11641    assert(ra != .zr);
11642    const live_vi = isel.live_registers.getPtr(ra);
11643    assert(live_vi.* == .free);
11644    live_vi.* = .allocating;
11645    return .{ .ra = ra };
11646}
11647fn tryLockReg(isel: *Select, ra: Register.Alias) RegLock {
11648    assert(ra != .zr);
11649    const live_vi = isel.live_registers.getPtr(ra);
11650    switch (live_vi.*) {
11651        _ => unreachable,
11652        .allocating => return .{ .ra = .zr },
11653        .free => {
11654            live_vi.* = .allocating;
11655            return .{ .ra = ra };
11656        },
11657    }
11658}
11659
11660fn freeReg(isel: *Select, ra: Register.Alias) void {
11661    assert(ra != .zr);
11662    const live_vi = isel.live_registers.getPtr(ra);
11663    assert(live_vi.* == .allocating);
11664    live_vi.* = .free;
11665}
11666
11667fn use(isel: *Select, air_ref: Air.Inst.Ref) !Value.Index {
11668    const zcu = isel.pt.zcu;
11669    const ip = &zcu.intern_pool;
11670    try isel.values.ensureUnusedCapacity(zcu.gpa, 1);
11671    const vi, const ty = if (air_ref.toIndex()) |air_inst_index| vi_ty: {
11672        const live_gop = try isel.live_values.getOrPut(zcu.gpa, air_inst_index);
11673        if (live_gop.found_existing) return live_gop.value_ptr.*;
11674        const ty = isel.air.typeOf(air_ref, ip);
11675        const vi = isel.initValue(ty);
11676        tracking_log.debug("${d} <- %{d}", .{
11677            @intFromEnum(vi),
11678            @intFromEnum(air_inst_index),
11679        });
11680        live_gop.value_ptr.* = vi.ref(isel);
11681        break :vi_ty .{ vi, ty };
11682    } else vi_ty: {
11683        const constant: Constant = .fromInterned(air_ref.toInterned().?);
11684        const ty = constant.typeOf(zcu);
11685        const vi = isel.initValue(ty);
11686        tracking_log.debug("${d} <- <{f}, {f}>", .{
11687            @intFromEnum(vi),
11688            isel.fmtType(ty),
11689            isel.fmtConstant(constant),
11690        });
11691        vi.setParent(isel, .{ .constant = constant });
11692        break :vi_ty .{ vi, ty };
11693    };
11694    if (ty.isAbiInt(zcu)) {
11695        const int_info = ty.intInfo(zcu);
11696        if (int_info.bits <= 16) vi.setSignedness(isel, int_info.signedness);
11697    } else if (vi.size(isel) <= 16 and
11698        CallAbiIterator.homogeneousAggregateBaseType(zcu, ty.toIntern()) != null) vi.setIsVector(isel);
11699    return vi;
11700}
11701
11702fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail }!bool {
11703    switch (dst_ra) {
11704        else => {},
11705        Register.Alias.fp, .zr, .sp, .pc, .fpcr, .fpsr, .ffr => return false,
11706    }
11707    const dst_live_vi = isel.live_registers.getPtr(dst_ra);
11708    const dst_vi = switch (dst_live_vi.*) {
11709        _ => |dst_vi| dst_vi,
11710        .allocating => return false,
11711        .free => return true,
11712    };
11713    const src_ra = src_ra: {
11714        if (dst_vi.hint(isel)) |hint_ra| {
11715            assert(dst_live_vi.* == dst_vi);
11716            dst_live_vi.* = .allocating;
11717            defer dst_live_vi.* = dst_vi;
11718            if (try isel.fill(hint_ra)) {
11719                isel.saved_registers.insert(hint_ra);
11720                break :src_ra hint_ra;
11721            }
11722        }
11723        switch (if (dst_vi.isVector(isel)) isel.tryAllocVecReg() else isel.tryAllocIntReg()) {
11724            .allocated => |ra| break :src_ra ra,
11725            .fill_candidate, .out_of_registers => return isel.fillMemory(dst_ra),
11726        }
11727    };
11728    try dst_vi.liveIn(isel, src_ra, comptime &.initFill(.free));
11729    const src_live_vi = isel.live_registers.getPtr(src_ra);
11730    assert(src_live_vi.* == .allocating);
11731    src_live_vi.* = dst_vi;
11732    return true;
11733}
11734
11735fn fillMemory(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail }!bool {
11736    const dst_live_vi = isel.live_registers.getPtr(dst_ra);
11737    const dst_vi = switch (dst_live_vi.*) {
11738        _ => |dst_vi| dst_vi,
11739        .allocating => return false,
11740        .free => return true,
11741    };
11742    const dst_vi_ra = &dst_vi.get(isel).location_payload.small.register;
11743    assert(dst_vi_ra.* == dst_ra);
11744    const base_ra = if (dst_ra.isVector()) try isel.allocIntReg() else dst_ra;
11745    defer if (base_ra != dst_ra) isel.freeReg(base_ra);
11746    try isel.emit(switch (dst_vi.size(isel)) {
11747        else => unreachable,
11748        1 => if (dst_ra.isVector())
11749            .ldr(dst_ra.b(), .{ .base = base_ra.x() })
11750        else switch (dst_vi.signedness(isel)) {
11751            .signed => .ldrsb(dst_ra.w(), .{ .base = base_ra.x() }),
11752            .unsigned => .ldrb(dst_ra.w(), .{ .base = base_ra.x() }),
11753        },
11754        2 => if (dst_ra.isVector())
11755            .ldr(dst_ra.h(), .{ .base = base_ra.x() })
11756        else switch (dst_vi.signedness(isel)) {
11757            .signed => .ldrsh(dst_ra.w(), .{ .base = base_ra.x() }),
11758            .unsigned => .ldrh(dst_ra.w(), .{ .base = base_ra.x() }),
11759        },
11760        4 => .ldr(if (dst_ra.isVector()) dst_ra.s() else dst_ra.w(), .{ .base = base_ra.x() }),
11761        8 => .ldr(if (dst_ra.isVector()) dst_ra.d() else dst_ra.x(), .{ .base = base_ra.x() }),
11762        16 => .ldr(dst_ra.q(), .{ .base = base_ra.x() }),
11763    });
11764    dst_vi_ra.* = .zr;
11765    try dst_vi.address(isel, 0, base_ra);
11766    dst_live_vi.* = .free;
11767    return true;
11768}
11769
11770/// Merges possibly differing value tracking into a consistent state.
11771///
11772/// At a conditional branch, if a value is expected in the same register on both
11773/// paths, or only expected in a register on only one path, tracking is updated:
11774///
11775///     $0 -> r0 // final state is now consistent with both paths
11776///      b.cond else
11777///     then:
11778///     $0 -> r0 // updated if not already consistent with else
11779///      ...
11780///      b end
11781///     else:
11782///     $0 -> r0
11783///      ...
11784///     end:
11785///
11786/// At a conditional branch, if a value is expected in different registers on
11787/// each path, mov instructions are emitted:
11788///
11789///     $0 -> r0 // final state is now consistent with both paths
11790///      b.cond else
11791///     then:
11792///     $0 -> r0 // updated to be consistent with else
11793///      mov x1, x0 // emitted to merge the inconsistent states
11794///     $0 -> r1
11795///      ...
11796///      b end
11797///     else:
11798///     $0 -> r0
11799///      ...
11800///     end:
11801///
11802/// At a loop, a value that is expected in a register at the repeats is updated:
11803///
11804///     $0 -> r0 // final state is now consistent with all paths
11805///     loop:
11806///     $0 -> r0 // updated to be consistent with the repeats
11807///      ...
11808///     $0 -> r0
11809///      b.cond loop
11810///      ...
11811///     $0 -> r0
11812///      b loop
11813///
11814/// At a loop, a value that is expected in a register at the top is filled:
11815///
11816///     $0 -> [sp, #A] // final state is now consistent with all paths
11817///     loop:
11818///     $0 -> [sp, #A] // updated to be consistent with the repeats
11819///      ldr x0, [sp, #A] // emitted to merge the inconsistent states
11820///     $0 -> r0
11821///      ...
11822///     $0 -> [sp, #A]
11823///      b.cond loop
11824///      ...
11825///     $0 -> [sp, #A]
11826///      b loop
11827///
11828/// At a loop, if a value that is expected in different registers on each path,
11829/// mov instructions are emitted:
11830///
11831///     $0 -> r0 // final state is now consistent with all paths
11832///     loop:
11833///     $0 -> r0 // updated to be consistent with the repeats
11834///      mov x1, x0 // emitted to merge the inconsistent states
11835///     $0 -> r1
11836///      ...
11837///     $0 -> r0
11838///      b.cond loop
11839///      ...
11840///     $0 -> r0
11841///      b loop
11842fn merge(
11843    isel: *Select,
11844    expected_live_registers: *const LiveRegisters,
11845    comptime opts: struct { fill_extra: bool = false },
11846) !void {
11847    var live_reg_it = isel.live_registers.iterator();
11848    while (live_reg_it.next()) |live_reg_entry| {
11849        const ra = live_reg_entry.key;
11850        const actual_vi = live_reg_entry.value;
11851        const expected_vi = expected_live_registers.get(ra);
11852        switch (expected_vi) {
11853            else => switch (actual_vi.*) {
11854                _ => {},
11855                .allocating => unreachable,
11856                .free => actual_vi.* = .allocating,
11857            },
11858            .free => {},
11859        }
11860    }
11861    live_reg_it = isel.live_registers.iterator();
11862    while (live_reg_it.next()) |live_reg_entry| {
11863        const ra = live_reg_entry.key;
11864        const actual_vi = live_reg_entry.value;
11865        const expected_vi = expected_live_registers.get(ra);
11866        switch (expected_vi) {
11867            _ => {
11868                switch (actual_vi.*) {
11869                    _ => _ = if (opts.fill_extra) {
11870                        assert(try isel.fillMemory(ra));
11871                        assert(actual_vi.* == .free);
11872                    },
11873                    .allocating => actual_vi.* = .free,
11874                    .free => unreachable,
11875                }
11876                try expected_vi.liveIn(isel, ra, expected_live_registers);
11877            },
11878            .allocating => if (if (opts.fill_extra) try isel.fillMemory(ra) else try isel.fill(ra)) {
11879                assert(actual_vi.* == .free);
11880                actual_vi.* = .allocating;
11881            },
11882            .free => if (opts.fill_extra) assert(try isel.fillMemory(ra) and actual_vi.* == .free),
11883        }
11884    }
11885    live_reg_it = isel.live_registers.iterator();
11886    while (live_reg_it.next()) |live_reg_entry| {
11887        const ra = live_reg_entry.key;
11888        const actual_vi = live_reg_entry.value;
11889        const expected_vi = expected_live_registers.get(ra);
11890        switch (expected_vi) {
11891            _ => {
11892                assert(actual_vi.* == .allocating and expected_vi.register(isel) == ra);
11893                actual_vi.* = expected_vi;
11894            },
11895            .allocating => assert(actual_vi.* == .allocating),
11896            .free => if (opts.fill_extra) assert(actual_vi.* == .free),
11897        }
11898    }
11899}
11900
11901const call = struct {
11902    const param_reg: Value.Index = @enumFromInt(@intFromEnum(Value.Index.allocating) - 2);
11903    const callee_clobbered_reg: Value.Index = @enumFromInt(@intFromEnum(Value.Index.allocating) - 1);
11904    const caller_saved_regs: LiveRegisters = .init(.{
11905        .r0 = param_reg,
11906        .r1 = param_reg,
11907        .r2 = param_reg,
11908        .r3 = param_reg,
11909        .r4 = param_reg,
11910        .r5 = param_reg,
11911        .r6 = param_reg,
11912        .r7 = param_reg,
11913        .r8 = param_reg,
11914        .r9 = callee_clobbered_reg,
11915        .r10 = callee_clobbered_reg,
11916        .r11 = callee_clobbered_reg,
11917        .r12 = callee_clobbered_reg,
11918        .r13 = callee_clobbered_reg,
11919        .r14 = callee_clobbered_reg,
11920        .r15 = callee_clobbered_reg,
11921        .r16 = callee_clobbered_reg,
11922        .r17 = callee_clobbered_reg,
11923        .r18 = callee_clobbered_reg,
11924        .r19 = .free,
11925        .r20 = .free,
11926        .r21 = .free,
11927        .r22 = .free,
11928        .r23 = .free,
11929        .r24 = .free,
11930        .r25 = .free,
11931        .r26 = .free,
11932        .r27 = .free,
11933        .r28 = .free,
11934        .r29 = .free,
11935        .r30 = callee_clobbered_reg,
11936        .zr = .free,
11937        .sp = .free,
11938
11939        .pc = .free,
11940
11941        .v0 = param_reg,
11942        .v1 = param_reg,
11943        .v2 = param_reg,
11944        .v3 = param_reg,
11945        .v4 = param_reg,
11946        .v5 = param_reg,
11947        .v6 = param_reg,
11948        .v7 = param_reg,
11949        .v8 = .free,
11950        .v9 = .free,
11951        .v10 = .free,
11952        .v11 = .free,
11953        .v12 = .free,
11954        .v13 = .free,
11955        .v14 = .free,
11956        .v15 = .free,
11957        .v16 = callee_clobbered_reg,
11958        .v17 = callee_clobbered_reg,
11959        .v18 = callee_clobbered_reg,
11960        .v19 = callee_clobbered_reg,
11961        .v20 = callee_clobbered_reg,
11962        .v21 = callee_clobbered_reg,
11963        .v22 = callee_clobbered_reg,
11964        .v23 = callee_clobbered_reg,
11965        .v24 = callee_clobbered_reg,
11966        .v25 = callee_clobbered_reg,
11967        .v26 = callee_clobbered_reg,
11968        .v27 = callee_clobbered_reg,
11969        .v28 = callee_clobbered_reg,
11970        .v29 = callee_clobbered_reg,
11971        .v30 = callee_clobbered_reg,
11972        .v31 = callee_clobbered_reg,
11973
11974        .fpcr = .free,
11975        .fpsr = .free,
11976
11977        .p0 = callee_clobbered_reg,
11978        .p1 = callee_clobbered_reg,
11979        .p2 = callee_clobbered_reg,
11980        .p3 = callee_clobbered_reg,
11981        .p4 = callee_clobbered_reg,
11982        .p5 = callee_clobbered_reg,
11983        .p6 = callee_clobbered_reg,
11984        .p7 = callee_clobbered_reg,
11985        .p8 = callee_clobbered_reg,
11986        .p9 = callee_clobbered_reg,
11987        .p10 = callee_clobbered_reg,
11988        .p11 = callee_clobbered_reg,
11989        .p12 = callee_clobbered_reg,
11990        .p13 = callee_clobbered_reg,
11991        .p14 = callee_clobbered_reg,
11992        .p15 = callee_clobbered_reg,
11993
11994        .ffr = .free,
11995    });
11996    fn prepareReturn(isel: *Select) !void {
11997        var live_reg_it = isel.live_registers.iterator();
11998        while (live_reg_it.next()) |live_reg_entry| switch (caller_saved_regs.get(live_reg_entry.key)) {
11999            else => unreachable,
12000            param_reg, callee_clobbered_reg => switch (live_reg_entry.value.*) {
12001                _ => {},
12002                .allocating => unreachable,
12003                .free => live_reg_entry.value.* = .allocating,
12004            },
12005            .free => {},
12006        };
12007    }
12008    fn returnFill(isel: *Select, ra: Register.Alias) !void {
12009        const live_vi = isel.live_registers.getPtr(ra);
12010        if (try isel.fill(ra)) {
12011            assert(live_vi.* == .free);
12012            live_vi.* = .allocating;
12013        }
12014        assert(live_vi.* == .allocating);
12015    }
12016    fn returnLiveIn(isel: *Select, vi: Value.Index, ra: Register.Alias) !void {
12017        try vi.defLiveIn(isel, ra, &caller_saved_regs);
12018    }
12019    fn finishReturn(isel: *Select) !void {
12020        var live_reg_it = isel.live_registers.iterator();
12021        while (live_reg_it.next()) |live_reg_entry| {
12022            switch (live_reg_entry.value.*) {
12023                _ => |live_vi| switch (live_vi.size(isel)) {
12024                    else => unreachable,
12025                    1, 2, 4, 8 => {},
12026                    16 => {
12027                        assert(try isel.fillMemory(live_reg_entry.key));
12028                        assert(live_reg_entry.value.* == .free);
12029                        switch (caller_saved_regs.get(live_reg_entry.key)) {
12030                            else => unreachable,
12031                            param_reg, callee_clobbered_reg => live_reg_entry.value.* = .allocating,
12032                            .free => {},
12033                        }
12034                        continue;
12035                    },
12036                },
12037                .allocating, .free => {},
12038            }
12039            switch (caller_saved_regs.get(live_reg_entry.key)) {
12040                else => unreachable,
12041                param_reg, callee_clobbered_reg => switch (live_reg_entry.value.*) {
12042                    _ => {
12043                        assert(try isel.fill(live_reg_entry.key));
12044                        assert(live_reg_entry.value.* == .free);
12045                        live_reg_entry.value.* = .allocating;
12046                    },
12047                    .allocating => {},
12048                    .free => unreachable,
12049                },
12050                .free => {},
12051            }
12052        }
12053    }
12054    fn prepareCallee(isel: *Select) !void {
12055        var live_reg_it = isel.live_registers.iterator();
12056        while (live_reg_it.next()) |live_reg_entry| switch (caller_saved_regs.get(live_reg_entry.key)) {
12057            else => unreachable,
12058            param_reg => assert(live_reg_entry.value.* == .allocating),
12059            callee_clobbered_reg => isel.freeReg(live_reg_entry.key),
12060            .free => {},
12061        };
12062    }
12063    fn finishCallee(_: *Select) !void {}
12064    fn prepareParams(_: *Select) !void {}
12065    fn paramLiveOut(isel: *Select, vi: Value.Index, ra: Register.Alias) !void {
12066        isel.freeReg(ra);
12067        try vi.liveOut(isel, ra);
12068        const live_vi = isel.live_registers.getPtr(ra);
12069        if (live_vi.* == .free) live_vi.* = .allocating;
12070    }
12071    fn paramAddress(isel: *Select, vi: Value.Index, ra: Register.Alias) !void {
12072        isel.freeReg(ra);
12073        try vi.address(isel, 0, ra);
12074        const live_vi = isel.live_registers.getPtr(ra);
12075        if (live_vi.* == .free) live_vi.* = .allocating;
12076    }
12077    fn finishParams(isel: *Select) !void {
12078        var live_reg_it = isel.live_registers.iterator();
12079        while (live_reg_it.next()) |live_reg_entry| switch (caller_saved_regs.get(live_reg_entry.key)) {
12080            else => unreachable,
12081            param_reg => switch (live_reg_entry.value.*) {
12082                _ => {},
12083                .allocating => live_reg_entry.value.* = .free,
12084                .free => unreachable,
12085            },
12086            callee_clobbered_reg, .free => {},
12087        };
12088    }
12089};
12090
12091pub const CallAbiIterator = struct {
12092    /// Next General-purpose Register Number
12093    ngrn: Register.Alias,
12094    /// Next SIMD and Floating-point Register Number
12095    nsrn: Register.Alias,
12096    /// next stacked argument address
12097    nsaa: u24,
12098
12099    pub const ngrn_start: Register.Alias = .r0;
12100    pub const ngrn_end: Register.Alias = .r8;
12101    pub const nsrn_start: Register.Alias = .v0;
12102    pub const nsrn_end: Register.Alias = .v8;
12103    pub const nsaa_start: u42 = 0;
12104
12105    pub const init: CallAbiIterator = .{
12106        // A.1
12107        .ngrn = ngrn_start,
12108        // A.2
12109        .nsrn = nsrn_start,
12110        // A.3
12111        .nsaa = nsaa_start,
12112    };
12113
12114    pub fn param(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
12115        const zcu = isel.pt.zcu;
12116        const ip = &zcu.intern_pool;
12117
12118        if (ty.isNoReturn(zcu) or !ty.hasRuntimeBitsIgnoreComptime(zcu)) return null;
12119        try isel.values.ensureUnusedCapacity(zcu.gpa, Value.max_parts);
12120        const wip_vi = isel.initValue(ty);
12121        type_key: switch (ip.indexToKey(ty.toIntern())) {
12122            else => return isel.fail("CallAbiIterator.param({f})", .{isel.fmtType(ty)}),
12123            .int_type => |int_type| switch (int_type.bits) {
12124                0 => unreachable,
12125                1...16 => {
12126                    wip_vi.setSignedness(isel, int_type.signedness);
12127                    // C.7
12128                    it.integer(isel, wip_vi);
12129                },
12130                // C.7
12131                17...64 => it.integer(isel, wip_vi),
12132                // C.9
12133                65...128 => it.integers(isel, wip_vi, @splat(@divExact(wip_vi.size(isel), 2))),
12134                else => it.indirect(isel, wip_vi),
12135            },
12136            .array_type => switch (wip_vi.size(isel)) {
12137                0 => unreachable,
12138                1...8 => it.integer(isel, wip_vi),
12139                9...16 => |size| it.integers(isel, wip_vi, .{ 8, size - 8 }),
12140                else => it.indirect(isel, wip_vi),
12141            },
12142            .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
12143                .one, .many, .c => continue :type_key .{ .int_type = .{
12144                    .signedness = .unsigned,
12145                    .bits = 64,
12146                } },
12147                .slice => it.integers(isel, wip_vi, @splat(8)),
12148            },
12149            .opt_type => |child_type| if (ty.optionalReprIsPayload(zcu))
12150                continue :type_key ip.indexToKey(child_type)
12151            else switch (ZigType.fromInterned(child_type).abiSize(zcu)) {
12152                0 => continue :type_key .{ .simple_type = .bool },
12153                1...7 => it.integer(isel, wip_vi),
12154                8...15 => |child_size| it.integers(isel, wip_vi, .{ 8, child_size - 7 }),
12155                else => return isel.fail("CallAbiIterator.param({f})", .{isel.fmtType(ty)}),
12156            },
12157            .anyframe_type => unreachable,
12158            .error_union_type => |error_union_type| switch (wip_vi.size(isel)) {
12159                0 => unreachable,
12160                1...8 => it.integer(isel, wip_vi),
12161                9...16 => {
12162                    var sizes: [2]u64 = @splat(0);
12163                    const payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
12164                    {
12165                        const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
12166                        const offset = codegen.errUnionErrorOffset(payload_ty, zcu);
12167                        const end = offset % 8 + error_set_ty.abiSize(zcu);
12168                        const part_index: usize = @intCast(offset / 8);
12169                        sizes[part_index] = @max(sizes[part_index], @min(end, 8));
12170                        if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
12171                    }
12172                    {
12173                        const offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
12174                        const end = offset % 8 + payload_ty.abiSize(zcu);
12175                        const part_index: usize = @intCast(offset / 8);
12176                        sizes[part_index] = @max(sizes[part_index], @min(end, 8));
12177                        if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
12178                    }
12179                    it.integers(isel, wip_vi, sizes);
12180                },
12181                else => it.indirect(isel, wip_vi),
12182            },
12183            .simple_type => |simple_type| switch (simple_type) {
12184                .f16, .f32, .f64, .f128, .c_longdouble => it.vector(isel, wip_vi),
12185                .f80 => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 80 } },
12186                .usize,
12187                .isize,
12188                .c_char,
12189                .c_short,
12190                .c_ushort,
12191                .c_int,
12192                .c_uint,
12193                .c_long,
12194                .c_ulong,
12195                .c_longlong,
12196                .c_ulonglong,
12197                => continue :type_key .{ .int_type = ty.intInfo(zcu) },
12198                // B.1
12199                .anyopaque => it.indirect(isel, wip_vi),
12200                .bool => continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = 1 } },
12201                .anyerror => continue :type_key .{ .int_type = .{
12202                    .signedness = .unsigned,
12203                    .bits = zcu.errorSetBits(),
12204                } },
12205                .void,
12206                .type,
12207                .comptime_int,
12208                .comptime_float,
12209                .noreturn,
12210                .null,
12211                .undefined,
12212                .enum_literal,
12213                .adhoc_inferred_error_set,
12214                .generic_poison,
12215                => unreachable,
12216            },
12217            .struct_type => {
12218                const loaded_struct = ip.loadStructType(ty.toIntern());
12219                switch (loaded_struct.layout) {
12220                    .auto, .@"extern" => {},
12221                    .@"packed" => continue :type_key .{
12222                        .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
12223                    },
12224                }
12225                const size = wip_vi.size(isel);
12226                if (size <= 16 * 4) homogeneous_aggregate: {
12227                    const fdt = homogeneousStructBaseType(zcu, &loaded_struct) orelse break :homogeneous_aggregate;
12228                    const parts_len = @shrExact(size, fdt.log2Size());
12229                    if (parts_len > 4) break :homogeneous_aggregate;
12230                    it.vectors(isel, wip_vi, fdt, @intCast(parts_len));
12231                    break :type_key;
12232                }
12233                switch (size) {
12234                    0 => unreachable,
12235                    1...8 => it.integer(isel, wip_vi),
12236                    9...16 => {
12237                        var part_offset: u64 = 0;
12238                        var part_sizes: [2]u64 = undefined;
12239                        var parts_len: Value.PartsLen = 0;
12240                        var next_field_end: u64 = 0;
12241                        var field_it = loaded_struct.iterateRuntimeOrder(ip);
12242                        while (part_offset < size) {
12243                            const field_end = next_field_end;
12244                            const next_field_begin = if (field_it.next()) |field_index| next_field_begin: {
12245                                const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
12246                                const next_field_begin = switch (loaded_struct.fieldAlign(ip, field_index)) {
12247                                    .none => field_ty.abiAlignment(zcu),
12248                                    else => |field_align| field_align,
12249                                }.forward(field_end);
12250                                next_field_end = next_field_begin + field_ty.abiSize(zcu);
12251                                break :next_field_begin next_field_begin;
12252                            } else std.mem.alignForward(u64, size, 8);
12253                            while (next_field_begin - part_offset >= 8) {
12254                                const part_size = field_end - part_offset;
12255                                part_sizes[parts_len] = part_size;
12256                                assert(part_offset + part_size <= size);
12257                                parts_len += 1;
12258                                part_offset = next_field_begin;
12259                            }
12260                        }
12261                        assert(parts_len == part_sizes.len);
12262                        it.integers(isel, wip_vi, part_sizes);
12263                    },
12264                    else => it.indirect(isel, wip_vi),
12265                }
12266            },
12267            .tuple_type => |tuple_type| {
12268                const size = wip_vi.size(isel);
12269                if (size <= 16 * 4) homogeneous_aggregate: {
12270                    const fdt = homogeneousTupleBaseType(zcu, tuple_type) orelse break :homogeneous_aggregate;
12271                    const parts_len = @shrExact(size, fdt.log2Size());
12272                    if (parts_len > 4) break :homogeneous_aggregate;
12273                    it.vectors(isel, wip_vi, fdt, @intCast(parts_len));
12274                    break :type_key;
12275                }
12276                switch (size) {
12277                    0 => unreachable,
12278                    1...8 => it.integer(isel, wip_vi),
12279                    9...16 => {
12280                        var part_offset: u64 = 0;
12281                        var part_sizes: [2]u64 = undefined;
12282                        var parts_len: Value.PartsLen = 0;
12283                        var next_field_end: u64 = 0;
12284                        var field_index: usize = 0;
12285                        while (part_offset < size) {
12286                            const field_end = next_field_end;
12287                            const next_field_begin = while (field_index < tuple_type.types.len) {
12288                                defer field_index += 1;
12289                                if (tuple_type.values.get(ip)[field_index] != .none) continue;
12290                                const field_ty: ZigType = .fromInterned(tuple_type.types.get(ip)[field_index]);
12291                                const next_field_begin = field_ty.abiAlignment(zcu).forward(field_end);
12292                                next_field_end = next_field_begin + field_ty.abiSize(zcu);
12293                                break next_field_begin;
12294                            } else std.mem.alignForward(u64, size, 8);
12295                            while (next_field_begin - part_offset >= 8) {
12296                                const part_size = @min(field_end - part_offset, 8);
12297                                part_sizes[parts_len] = part_size;
12298                                assert(part_offset + part_size <= size);
12299                                parts_len += 1;
12300                                part_offset += part_size;
12301                                if (part_offset >= field_end) part_offset = next_field_begin;
12302                            }
12303                        }
12304                        assert(parts_len == part_sizes.len);
12305                        it.integers(isel, wip_vi, part_sizes);
12306                    },
12307                    else => it.indirect(isel, wip_vi),
12308                }
12309            },
12310            .union_type => {
12311                const loaded_union = ip.loadUnionType(ty.toIntern());
12312                switch (loaded_union.flagsUnordered(ip).layout) {
12313                    .auto, .@"extern" => {},
12314                    .@"packed" => continue :type_key .{ .int_type = .{
12315                        .signedness = .unsigned,
12316                        .bits = @intCast(ty.bitSize(zcu)),
12317                    } },
12318                }
12319                switch (wip_vi.size(isel)) {
12320                    0 => unreachable,
12321                    1...8 => it.integer(isel, wip_vi),
12322                    9...16 => {
12323                        const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
12324                        var sizes: [2]u64 = @splat(0);
12325                        {
12326                            const offset = union_layout.tagOffset();
12327                            const end = offset % 8 + union_layout.tag_size;
12328                            const part_index: usize = @intCast(offset / 8);
12329                            sizes[part_index] = @max(sizes[part_index], @min(end, 8));
12330                            if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
12331                        }
12332                        {
12333                            const offset = union_layout.payloadOffset();
12334                            const end = offset % 8 + union_layout.payload_size;
12335                            const part_index: usize = @intCast(offset / 8);
12336                            sizes[part_index] = @max(sizes[part_index], @min(end, 8));
12337                            if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
12338                        }
12339                        it.integers(isel, wip_vi, sizes);
12340                    },
12341                    else => it.indirect(isel, wip_vi),
12342                }
12343            },
12344            .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
12345            .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
12346            .error_set_type,
12347            .inferred_error_set_type,
12348            => continue :type_key .{ .simple_type = .anyerror },
12349            .undef,
12350            .simple_value,
12351            .variable,
12352            .@"extern",
12353            .func,
12354            .int,
12355            .err,
12356            .error_union,
12357            .enum_literal,
12358            .enum_tag,
12359            .empty_enum_value,
12360            .float,
12361            .ptr,
12362            .slice,
12363            .opt,
12364            .aggregate,
12365            .un,
12366            .memoized_call,
12367            => unreachable, // values, not types
12368        }
12369        return wip_vi.ref(isel);
12370    }
12371
12372    pub fn nonSysvVarArg(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
12373        const ngrn = it.ngrn;
12374        defer it.ngrn = ngrn;
12375        it.ngrn = ngrn_end;
12376        const nsrn = it.nsrn;
12377        defer it.nsrn = nsrn;
12378        it.nsrn = nsrn_end;
12379        return it.param(isel, ty);
12380    }
12381
12382    pub fn ret(it: *CallAbiIterator, isel: *Select, ty: ZigType) !?Value.Index {
12383        const wip_vi = try it.param(isel, ty) orelse return null;
12384        switch (wip_vi.parent(isel)) {
12385            .unallocated, .stack_slot => {},
12386            .value, .constant => unreachable,
12387            .address => |address_vi| {
12388                assert(address_vi.hint(isel) == ngrn_start);
12389                address_vi.setHint(isel, ngrn_end);
12390            },
12391        }
12392        return wip_vi;
12393    }
12394
12395    pub const FundamentalDataType = enum {
12396        half,
12397        single,
12398        double,
12399        quad,
12400        vector64,
12401        vector128,
12402        fn log2Size(fdt: FundamentalDataType) u3 {
12403            return switch (fdt) {
12404                .half => 1,
12405                .single => 2,
12406                .double, .vector64 => 3,
12407                .quad, .vector128 => 4,
12408            };
12409        }
12410        fn size(fdt: FundamentalDataType) u64 {
12411            return @as(u64, 1) << fdt.log2Size();
12412        }
12413    };
12414    fn homogeneousAggregateBaseType(zcu: *Zcu, initial_ty: InternPool.Index) ?FundamentalDataType {
12415        const ip = &zcu.intern_pool;
12416        var ty = initial_ty;
12417        return type_key: switch (ip.indexToKey(ty)) {
12418            else => null,
12419            .array_type => |array_type| {
12420                ty = array_type.child;
12421                continue :type_key ip.indexToKey(ty);
12422            },
12423            .vector_type => switch (ZigType.fromInterned(ty).abiSize(zcu)) {
12424                else => null,
12425                8 => .vector64,
12426                16 => .vector128,
12427            },
12428            .simple_type => |simple_type| switch (simple_type) {
12429                .f16 => .half,
12430                .f32 => .single,
12431                .f64 => .double,
12432                .f128 => .quad,
12433                .c_longdouble => switch (zcu.getTarget().cTypeBitSize(.longdouble)) {
12434                    else => unreachable,
12435                    16 => .half,
12436                    32 => .single,
12437                    64 => .double,
12438                    80 => null,
12439                    128 => .quad,
12440                },
12441                else => null,
12442            },
12443            .struct_type => homogeneousStructBaseType(zcu, &ip.loadStructType(ty)),
12444            .tuple_type => |tuple_type| homogeneousTupleBaseType(zcu, tuple_type),
12445        };
12446    }
12447    fn homogeneousStructBaseType(zcu: *Zcu, loaded_struct: *const InternPool.LoadedStructType) ?FundamentalDataType {
12448        const ip = &zcu.intern_pool;
12449        var common_fdt: ?FundamentalDataType = null;
12450        for (0.., loaded_struct.field_types.get(ip)) |field_index, field_ty| {
12451            if (loaded_struct.fieldIsComptime(ip, field_index)) continue;
12452            if (loaded_struct.fieldAlign(ip, field_index) != .none) return null;
12453            if (!ZigType.fromInterned(field_ty).hasRuntimeBits(zcu)) continue;
12454            const fdt = homogeneousAggregateBaseType(zcu, field_ty);
12455            if (common_fdt == null) common_fdt = fdt else if (fdt != common_fdt) return null;
12456        }
12457        return common_fdt;
12458    }
12459    fn homogeneousTupleBaseType(zcu: *Zcu, tuple_type: InternPool.Key.TupleType) ?FundamentalDataType {
12460        const ip = &zcu.intern_pool;
12461        var common_fdt: ?FundamentalDataType = null;
12462        for (tuple_type.values.get(ip), tuple_type.types.get(ip)) |field_val, field_ty| {
12463            if (field_val != .none) continue;
12464            const fdt = homogeneousAggregateBaseType(zcu, field_ty);
12465            if (common_fdt == null) common_fdt = fdt else if (fdt != common_fdt) return null;
12466        }
12467        return common_fdt;
12468    }
12469
12470    const Spec = struct {
12471        offset: u64,
12472        size: u64,
12473    };
12474
12475    fn stack(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void {
12476        // C.12
12477        it.nsaa = @intCast(wip_vi.alignment(isel).forward(it.nsaa));
12478        const parent_vi = switch (wip_vi.parent(isel)) {
12479            .unallocated, .stack_slot => wip_vi,
12480            .address, .constant => unreachable,
12481            .value => |parent_vi| parent_vi,
12482        };
12483        switch (parent_vi.parent(isel)) {
12484            .unallocated => parent_vi.setParent(isel, .{ .stack_slot = .{
12485                .base = .sp,
12486                .offset = it.nsaa,
12487            } }),
12488            .stack_slot => {},
12489            .address, .value, .constant => unreachable,
12490        }
12491        it.nsaa += @intCast(wip_vi.size(isel));
12492    }
12493
12494    fn integer(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void {
12495        assert(wip_vi.size(isel) <= 8);
12496        const natural_alignment = wip_vi.alignment(isel);
12497        assert(natural_alignment.order(.@"16").compare(.lte));
12498        wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8"));
12499        if (it.ngrn == ngrn_end) return it.stack(isel, wip_vi);
12500        wip_vi.setHint(isel, it.ngrn);
12501        it.ngrn = @enumFromInt(@intFromEnum(it.ngrn) + 1);
12502    }
12503
12504    fn integers(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index, part_sizes: [2]u64) void {
12505        assert(wip_vi.size(isel) <= 16);
12506        const natural_alignment = wip_vi.alignment(isel);
12507        assert(natural_alignment.order(.@"16").compare(.lte));
12508        wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8"));
12509        // C.8
12510        if (natural_alignment == .@"16") it.ngrn = @enumFromInt(std.mem.alignForward(
12511            @typeInfo(Register.Alias).@"enum".tag_type,
12512            @intFromEnum(it.ngrn),
12513            2,
12514        ));
12515        if (it.ngrn == ngrn_end) return it.stack(isel, wip_vi);
12516        wip_vi.setParts(isel, part_sizes.len);
12517        for (0.., part_sizes) |part_index, part_size|
12518            it.integer(isel, wip_vi.addPart(isel, 8 * part_index, part_size));
12519    }
12520
12521    fn vector(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void {
12522        assert(wip_vi.size(isel) <= 16);
12523        const natural_alignment = wip_vi.alignment(isel);
12524        assert(natural_alignment.order(.@"16").compare(.lte));
12525        wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8"));
12526        wip_vi.setIsVector(isel);
12527        if (it.nsrn == nsrn_end) return it.stack(isel, wip_vi);
12528        wip_vi.setHint(isel, it.nsrn);
12529        it.nsrn = @enumFromInt(@intFromEnum(it.nsrn) + 1);
12530    }
12531
12532    fn vectors(
12533        it: *CallAbiIterator,
12534        isel: *Select,
12535        wip_vi: Value.Index,
12536        fdt: FundamentalDataType,
12537        parts_len: Value.PartsLen,
12538    ) void {
12539        const fdt_log2_size = fdt.log2Size();
12540        assert(wip_vi.size(isel) == @shlExact(@as(u9, parts_len), fdt_log2_size));
12541        const natural_alignment = wip_vi.alignment(isel);
12542        assert(natural_alignment.order(.@"16").compare(.lte));
12543        wip_vi.setAlignment(isel, natural_alignment.maxStrict(.@"8"));
12544        if (@intFromEnum(it.nsrn) > @intFromEnum(nsrn_end) - parts_len) return it.stack(isel, wip_vi);
12545        if (parts_len == 1) return it.vector(isel, wip_vi);
12546        wip_vi.setParts(isel, parts_len);
12547        const fdt_size = @as(u64, 1) << fdt_log2_size;
12548        for (0..parts_len) |part_index|
12549            it.vector(isel, wip_vi.addPart(isel, part_index << fdt_log2_size, fdt_size));
12550    }
12551
12552    fn indirect(it: *CallAbiIterator, isel: *Select, wip_vi: Value.Index) void {
12553        const wip_address_vi = isel.initValue(.usize);
12554        wip_vi.setParent(isel, .{ .address = wip_address_vi });
12555        it.integer(isel, wip_address_vi);
12556    }
12557};
12558
12559const Air = @import("../../Air.zig");
12560const assert = std.debug.assert;
12561const codegen = @import("../../codegen.zig");
12562const Constant = @import("../../Value.zig");
12563const InternPool = @import("../../InternPool.zig");
12564const Package = @import("../../Package.zig");
12565const Register = codegen.aarch64.encoding.Register;
12566const Select = @This();
12567const std = @import("std");
12568const tracking_log = std.log.scoped(.tracking);
12569const wip_mir_log = std.log.scoped(.@"wip-mir");
12570const Zcu = @import("../../Zcu.zig");
12571const ZigType = @import("../../Type.zig");