zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 90bd3606 (249737B) - Raw


      1 const std = @import("std");
      2 const builtin = @import("builtin");
      3 const mem = std.mem;
      4 const math = std.math;
      5 const assert = std.debug.assert;
      6 const codegen = @import("../../codegen.zig");
      7 const Air = @import("../../Air.zig");
      8 const Mir = @import("Mir.zig");
      9 const Emit = @import("Emit.zig");
     10 const Liveness = @import("../../Liveness.zig");
     11 const Type = @import("../../Type.zig");
     12 const Value = @import("../../Value.zig");
     13 const link = @import("../../link.zig");
     14 const Zcu = @import("../../Zcu.zig");
     15 const InternPool = @import("../../InternPool.zig");
     16 const Compilation = @import("../../Compilation.zig");
     17 const ErrorMsg = Zcu.ErrorMsg;
     18 const Target = std.Target;
     19 const Allocator = mem.Allocator;
     20 const trace = @import("../../tracy.zig").trace;
     21 const leb128 = std.leb;
     22 const log = std.log.scoped(.codegen);
     23 const build_options = @import("build_options");
     24 const Alignment = InternPool.Alignment;
     25 
     26 const Result = codegen.Result;
     27 const CodeGenError = codegen.CodeGenError;
     28 const DebugInfoOutput = codegen.DebugInfoOutput;
     29 
     30 const bits = @import("bits.zig");
     31 const abi = @import("abi.zig");
     32 const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
     33 const errUnionErrorOffset = codegen.errUnionErrorOffset;
     34 const RegisterManager = abi.RegisterManager;
     35 const RegisterLock = RegisterManager.RegisterLock;
     36 const Register = bits.Register;
     37 const Instruction = bits.Instruction;
     38 const Condition = bits.Condition;
     39 const callee_preserved_regs = abi.callee_preserved_regs;
     40 const caller_preserved_regs = abi.caller_preserved_regs;
     41 const c_abi_int_param_regs = abi.c_abi_int_param_regs;
     42 const c_abi_int_return_regs = abi.c_abi_int_return_regs;
     43 const gp = abi.RegisterClass.gp;
     44 
     45 const InnerError = CodeGenError || error{OutOfRegisters};
     46 
     47 gpa: Allocator,
     48 pt: Zcu.PerThread,
     49 air: Air,
     50 liveness: Liveness,
     51 bin_file: *link.File,
     52 debug_output: DebugInfoOutput,
     53 target: *const std.Target,
     54 func_index: InternPool.Index,
     55 err_msg: ?*ErrorMsg,
     56 args: []MCValue,
     57 ret_mcv: MCValue,
     58 fn_type: Type,
     59 arg_index: u32,
     60 src_loc: Zcu.LazySrcLoc,
     61 stack_align: u32,
     62 
     63 /// MIR Instructions
     64 mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
     65 /// MIR extra data
     66 mir_extra: std.ArrayListUnmanaged(u32) = .{},
     67 
     68 /// Byte offset within the source file of the ending curly.
     69 end_di_line: u32,
     70 end_di_column: u32,
     71 
     72 /// The value is an offset into the `Function` `code` from the beginning.
     73 /// To perform the reloc, write 32-bit signed little-endian integer
     74 /// which is a relative jump, based on the address following the reloc.
     75 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
     76 
     77 /// We postpone the creation of debug info for function args and locals
     78 /// until after all Mir instructions have been generated. Only then we
     79 /// will know saved_regs_stack_space which is necessary in order to
     80 /// calculate the right stack offsest with respect to the `.fp` register.
     81 dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .{},
     82 
     83 /// Whenever there is a runtime branch, we push a Branch onto this stack,
     84 /// and pop it off when the runtime branch joins. This provides an "overlay"
     85 /// of the table of mappings from instructions to `MCValue` from within the branch.
     86 /// This way we can modify the `MCValue` for an instruction in different ways
     87 /// within different branches. Special consideration is needed when a branch
     88 /// joins with its parent, to make sure all instructions have the same MCValue
     89 /// across each runtime branch upon joining.
     90 branch_stack: *std.ArrayList(Branch),
     91 
     92 // Key is the block instruction
     93 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
     94 
     95 register_manager: RegisterManager = .{},
     96 /// Maps offset to what is stored there.
     97 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
     98 /// Tracks the current instruction allocated to the compare flags
     99 cpsr_flags_inst: ?Air.Inst.Index = null,
    100 
    101 /// Offset from the stack base, representing the end of the stack frame.
    102 max_end_stack: u32 = 0,
    103 /// Represents the current end stack offset. If there is no existing slot
    104 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
    105 next_stack_offset: u32 = 0,
    106 
    107 saved_regs_stack_space: u32 = 0,
    108 
    109 /// Debug field, used to find bugs in the compiler.
    110 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
    111 
    112 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
    113 
    114 const MCValue = union(enum) {
    115     /// No runtime bits. `void` types, empty structs, u0, enums with 1
    116     /// tag, etc.
    117     ///
    118     /// TODO Look into deleting this tag and using `dead` instead,
    119     /// since every use of MCValue.none should be instead looking at
    120     /// the type and noticing it is 0 bits.
    121     none,
    122     /// Control flow will not allow this value to be observed.
    123     unreach,
    124     /// No more references to this value remain.
    125     dead,
    126     /// The value is undefined.
    127     undef,
    128     /// A pointer-sized integer that fits in a register.
    129     ///
    130     /// If the type is a pointer, this is the pointer address in
    131     /// virtual address space.
    132     immediate: u32,
    133     /// The value is in a target-specific register.
    134     register: Register,
    135     /// The value is a tuple { wrapped: u32, overflow: u1 } where
    136     /// wrapped is stored in the register and the overflow bit is
    137     /// stored in the C flag of the CPSR.
    138     ///
    139     /// This MCValue is only generated by a add_with_overflow or
    140     /// sub_with_overflow instruction operating on u32.
    141     register_c_flag: Register,
    142     /// The value is a tuple { wrapped: i32, overflow: u1 } where
    143     /// wrapped is stored in the register and the overflow bit is
    144     /// stored in the V flag of the CPSR.
    145     ///
    146     /// This MCValue is only generated by a add_with_overflow or
    147     /// sub_with_overflow instruction operating on i32.
    148     register_v_flag: Register,
    149     /// The value is in memory at a hard-coded address.
    150     ///
    151     /// If the type is a pointer, it means the pointer address is at
    152     /// this memory location.
    153     memory: u64,
    154     /// The value is one of the stack variables.
    155     ///
    156     /// If the type is a pointer, it means the pointer address is in
    157     /// the stack at this offset.
    158     stack_offset: u32,
    159     /// The value is a pointer to one of the stack variables (payload
    160     /// is stack offset).
    161     ptr_stack_offset: u32,
    162     /// The value resides in the N, Z, C, V flags of the Current
    163     /// Program Status Register (CPSR). The value is 1 (if the type is
    164     /// u1) or true (if the type in bool) iff the specified condition
    165     /// is true.
    166     cpsr_flags: Condition,
    167     /// The value is a function argument passed via the stack.
    168     stack_argument_offset: u32,
    169 };
    170 
    171 const Branch = struct {
    172     inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
    173 
    174     fn deinit(self: *Branch, gpa: Allocator) void {
    175         self.inst_table.deinit(gpa);
    176         self.* = undefined;
    177     }
    178 };
    179 
    180 const StackAllocation = struct {
    181     inst: Air.Inst.Index,
    182     /// TODO do we need size? should be determined by inst.ty.abiSize()
    183     size: u32,
    184 };
    185 
    186 const BlockData = struct {
    187     relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
    188     /// The first break instruction encounters `null` here and chooses a
    189     /// machine code value for the block result, populating this field.
    190     /// Following break instructions encounter that value and use it for
    191     /// the location to store their block results.
    192     mcv: MCValue,
    193 };
    194 
    195 const BigTomb = struct {
    196     function: *Self,
    197     inst: Air.Inst.Index,
    198     lbt: Liveness.BigTomb,
    199 
    200     fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
    201         const dies = bt.lbt.feed();
    202         const op_index = op_ref.toIndex() orelse return;
    203         if (!dies) return;
    204         bt.function.processDeath(op_index);
    205     }
    206 
    207     fn finishAir(bt: *BigTomb, result: MCValue) void {
    208         const is_used = !bt.function.liveness.isUnused(bt.inst);
    209         if (is_used) {
    210             log.debug("%{d} => {}", .{ bt.inst, result });
    211             const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
    212             branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
    213 
    214             switch (result) {
    215                 .register => |reg| {
    216                     // In some cases (such as bitcast), an operand
    217                     // may be the same MCValue as the result. If
    218                     // that operand died and was a register, it
    219                     // was freed by processDeath. We have to
    220                     // "re-allocate" the register.
    221                     if (bt.function.register_manager.isRegFree(reg)) {
    222                         bt.function.register_manager.getRegAssumeFree(reg, bt.inst);
    223                     }
    224                 },
    225                 .register_c_flag,
    226                 .register_v_flag,
    227                 => |reg| {
    228                     if (bt.function.register_manager.isRegFree(reg)) {
    229                         bt.function.register_manager.getRegAssumeFree(reg, bt.inst);
    230                     }
    231                     bt.function.cpsr_flags_inst = bt.inst;
    232                 },
    233                 .cpsr_flags => {
    234                     bt.function.cpsr_flags_inst = bt.inst;
    235                 },
    236                 else => {},
    237             }
    238         }
    239         bt.function.finishAirBookkeeping();
    240     }
    241 };
    242 
    243 const DbgInfoReloc = struct {
    244     tag: Air.Inst.Tag,
    245     ty: Type,
    246     name: [:0]const u8,
    247     mcv: MCValue,
    248 
    249     fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
    250         switch (reloc.tag) {
    251             .arg,
    252             .dbg_arg_inline,
    253             => try reloc.genArgDbgInfo(function),
    254 
    255             .dbg_var_ptr,
    256             .dbg_var_val,
    257             => try reloc.genVarDbgInfo(function),
    258 
    259             else => unreachable,
    260         }
    261     }
    262 
    263     fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
    264         switch (function.debug_output) {
    265             .dwarf => |dw| {
    266                 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
    267                     .register => |reg| .{ .reg = reg.dwarfNum() },
    268                     .stack_offset,
    269                     .stack_argument_offset,
    270                     => blk: {
    271                         const adjusted_stack_offset = switch (reloc.mcv) {
    272                             .stack_offset => |offset| -@as(i32, @intCast(offset)),
    273                             .stack_argument_offset => |offset| @as(i32, @intCast(function.saved_regs_stack_space + offset)),
    274                             else => unreachable,
    275                         };
    276                         break :blk .{ .plus = .{
    277                             &.{ .reg = 11 },
    278                             &.{ .consts = adjusted_stack_offset },
    279                         } };
    280                     },
    281                     else => unreachable, // not a possible argument
    282                 };
    283 
    284                 try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
    285             },
    286             .plan9 => {},
    287             .none => {},
    288         }
    289     }
    290 
    291     fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
    292         switch (function.debug_output) {
    293             .dwarf => |dw| {
    294                 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
    295                     .register => |reg| .{ .reg = reg.dwarfNum() },
    296                     .ptr_stack_offset,
    297                     .stack_offset,
    298                     .stack_argument_offset,
    299                     => |offset| blk: {
    300                         const adjusted_offset = switch (reloc.mcv) {
    301                             .ptr_stack_offset,
    302                             .stack_offset,
    303                             => -@as(i32, @intCast(offset)),
    304                             .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)),
    305                             else => unreachable,
    306                         };
    307                         break :blk .{ .plus = .{
    308                             &.{ .reg = 11 },
    309                             &.{ .consts = adjusted_offset },
    310                         } };
    311                     },
    312                     .memory => |address| .{ .constu = address },
    313                     .immediate => |x| .{ .constu = x },
    314                     .none => .empty,
    315                     else => blk: {
    316                         log.debug("TODO generate debug info for {}", .{reloc.mcv});
    317                         break :blk .empty;
    318                     },
    319                 };
    320                 try dw.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc);
    321             },
    322             .plan9 => {},
    323             .none => {},
    324         }
    325     }
    326 };
    327 
    328 const Self = @This();
    329 
    330 pub fn generate(
    331     lf: *link.File,
    332     pt: Zcu.PerThread,
    333     src_loc: Zcu.LazySrcLoc,
    334     func_index: InternPool.Index,
    335     air: Air,
    336     liveness: Liveness,
    337     code: *std.ArrayList(u8),
    338     debug_output: DebugInfoOutput,
    339 ) CodeGenError!Result {
    340     const zcu = pt.zcu;
    341     const gpa = zcu.gpa;
    342     const func = zcu.funcInfo(func_index);
    343     const func_ty = Type.fromInterned(func.ty);
    344     const file_scope = zcu.navFileScope(func.owner_nav);
    345     const target = &file_scope.mod.resolved_target.result;
    346 
    347     var branch_stack = std.ArrayList(Branch).init(gpa);
    348     defer {
    349         assert(branch_stack.items.len == 1);
    350         branch_stack.items[0].deinit(gpa);
    351         branch_stack.deinit();
    352     }
    353     try branch_stack.append(.{});
    354 
    355     var function: Self = .{
    356         .gpa = gpa,
    357         .pt = pt,
    358         .air = air,
    359         .liveness = liveness,
    360         .target = target,
    361         .bin_file = lf,
    362         .debug_output = debug_output,
    363         .func_index = func_index,
    364         .err_msg = null,
    365         .args = undefined, // populated after `resolveCallingConventionValues`
    366         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
    367         .fn_type = func_ty,
    368         .arg_index = 0,
    369         .branch_stack = &branch_stack,
    370         .src_loc = src_loc,
    371         .stack_align = undefined,
    372         .end_di_line = func.rbrace_line,
    373         .end_di_column = func.rbrace_column,
    374     };
    375     defer function.stack.deinit(gpa);
    376     defer function.blocks.deinit(gpa);
    377     defer function.exitlude_jump_relocs.deinit(gpa);
    378     defer function.dbg_info_relocs.deinit(gpa);
    379 
    380     var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) {
    381         error.CodegenFail => return Result{ .fail = function.err_msg.? },
    382         error.OutOfRegisters => return Result{
    383             .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    384         },
    385         else => |e| return e,
    386     };
    387     defer call_info.deinit(&function);
    388 
    389     function.args = call_info.args;
    390     function.ret_mcv = call_info.return_value;
    391     function.stack_align = call_info.stack_align;
    392     function.max_end_stack = call_info.stack_byte_count;
    393 
    394     function.gen() catch |err| switch (err) {
    395         error.CodegenFail => return Result{ .fail = function.err_msg.? },
    396         error.OutOfRegisters => return Result{
    397             .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    398         },
    399         else => |e| return e,
    400     };
    401 
    402     for (function.dbg_info_relocs.items) |reloc| {
    403         try reloc.genDbgInfo(function);
    404     }
    405 
    406     var mir = Mir{
    407         .instructions = function.mir_instructions.toOwnedSlice(),
    408         .extra = try function.mir_extra.toOwnedSlice(gpa),
    409     };
    410     defer mir.deinit(gpa);
    411 
    412     var emit = Emit{
    413         .mir = mir,
    414         .bin_file = lf,
    415         .debug_output = debug_output,
    416         .target = target,
    417         .src_loc = src_loc,
    418         .code = code,
    419         .prev_di_pc = 0,
    420         .prev_di_line = func.lbrace_line,
    421         .prev_di_column = func.lbrace_column,
    422         .stack_size = function.max_end_stack,
    423         .saved_regs_stack_space = function.saved_regs_stack_space,
    424     };
    425     defer emit.deinit();
    426 
    427     emit.emitMir() catch |err| switch (err) {
    428         error.EmitFail => return Result{ .fail = emit.err_msg.? },
    429         else => |e| return e,
    430     };
    431 
    432     if (function.err_msg) |em| {
    433         return Result{ .fail = em };
    434     } else {
    435         return Result.ok;
    436     }
    437 }
    438 
    439 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
    440     const gpa = self.gpa;
    441 
    442     try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
    443 
    444     const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
    445     self.mir_instructions.appendAssumeCapacity(inst);
    446     return result_index;
    447 }
    448 
    449 fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
    450     return try self.addInst(.{
    451         .tag = .nop,
    452         .data = .{ .nop = {} },
    453     });
    454 }
    455 
    456 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
    457     const fields = std.meta.fields(@TypeOf(extra));
    458     try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
    459     return self.addExtraAssumeCapacity(extra);
    460 }
    461 
    462 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
    463     const fields = std.meta.fields(@TypeOf(extra));
    464     const result: u32 = @intCast(self.mir_extra.items.len);
    465     inline for (fields) |field| {
    466         self.mir_extra.appendAssumeCapacity(switch (field.type) {
    467             u32 => @field(extra, field.name),
    468             i32 => @bitCast(@field(extra, field.name)),
    469             else => @compileError("bad field type"),
    470         });
    471     }
    472     return result;
    473 }
    474 
    475 fn gen(self: *Self) !void {
    476     const pt = self.pt;
    477     const zcu = pt.zcu;
    478     const cc = self.fn_type.fnCallingConvention(zcu);
    479     if (cc != .Naked) {
    480         // push {fp, lr}
    481         const push_reloc = try self.addNop();
    482 
    483         // mov fp, sp
    484         _ = try self.addInst(.{
    485             .tag = .mov,
    486             .data = .{ .r_op_mov = .{
    487                 .rd = .fp,
    488                 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),
    489             } },
    490         });
    491 
    492         // sub sp, sp, #reloc
    493         const sub_reloc = try self.addNop();
    494 
    495         // The sub_sp_scratch_r4 instruction may use r4, so we mark r4
    496         // as allocated by this function.
    497         const index = RegisterManager.indexOfRegIntoTracked(.r4).?;
    498         self.register_manager.allocated_registers.set(index);
    499 
    500         if (self.ret_mcv == .stack_offset) {
    501             // The address of where to store the return value is in
    502             // r0. As this register might get overwritten along the
    503             // way, save the address to the stack.
    504             const stack_offset = try self.allocMem(4, .@"4", null);
    505 
    506             try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });
    507             self.ret_mcv = MCValue{ .stack_offset = stack_offset };
    508         }
    509 
    510         for (self.args, 0..) |*arg, arg_index| {
    511             // Copy register arguments to the stack
    512             switch (arg.*) {
    513                 .register => |reg| {
    514                     // The first AIR instructions of the main body are guaranteed
    515                     // to be the functions arguments
    516                     const inst = self.air.getMainBody()[arg_index];
    517                     assert(self.air.instructions.items(.tag)[@intFromEnum(inst)] == .arg);
    518 
    519                     const ty = self.typeOfIndex(inst);
    520 
    521                     const abi_size: u32 = @intCast(ty.abiSize(zcu));
    522                     const abi_align = ty.abiAlignment(zcu);
    523                     const stack_offset = try self.allocMem(abi_size, abi_align, inst);
    524                     try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
    525 
    526                     arg.* = MCValue{ .stack_offset = stack_offset };
    527                 },
    528                 else => {},
    529             }
    530         }
    531 
    532         _ = try self.addInst(.{
    533             .tag = .dbg_prologue_end,
    534             .cond = undefined,
    535             .data = .{ .nop = {} },
    536         });
    537 
    538         try self.genBody(self.air.getMainBody());
    539 
    540         // Backpatch push callee saved regs
    541         var saved_regs = Instruction.RegisterList{
    542             .r11 = true, // fp
    543             .r14 = true, // lr
    544         };
    545         self.saved_regs_stack_space = 8;
    546         inline for (callee_preserved_regs) |reg| {
    547             if (self.register_manager.isRegAllocated(reg)) {
    548                 @field(saved_regs, @tagName(reg)) = true;
    549                 self.saved_regs_stack_space += 4;
    550             }
    551         }
    552         self.mir_instructions.set(push_reloc, .{
    553             .tag = .push,
    554             .data = .{ .register_list = saved_regs },
    555         });
    556 
    557         // Backpatch stack offset
    558         const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
    559         const aligned_total_stack_end = mem.alignForward(u32, total_stack_size, self.stack_align);
    560         const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
    561         self.max_end_stack = stack_size;
    562         self.mir_instructions.set(sub_reloc, .{
    563             .tag = .sub_sp_scratch_r4,
    564             .data = .{ .imm32 = stack_size },
    565         });
    566 
    567         _ = try self.addInst(.{
    568             .tag = .dbg_epilogue_begin,
    569             .cond = undefined,
    570             .data = .{ .nop = {} },
    571         });
    572 
    573         // exitlude jumps
    574         if (self.exitlude_jump_relocs.items.len > 0 and
    575             self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
    576         {
    577             // If the last Mir instruction (apart from the
    578             // dbg_epilogue_begin) is the last exitlude jump
    579             // relocation (which would just jump one instruction
    580             // further), it can be safely removed
    581             self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop());
    582         }
    583 
    584         for (self.exitlude_jump_relocs.items) |jmp_reloc| {
    585             self.mir_instructions.set(jmp_reloc, .{
    586                 .tag = .b,
    587                 .data = .{ .inst = @intCast(self.mir_instructions.len) },
    588             });
    589         }
    590 
    591         // Epilogue: pop callee saved registers (swap lr with pc in saved_regs)
    592         saved_regs.r14 = false; // lr
    593         saved_regs.r15 = true; // pc
    594 
    595         // mov sp, fp
    596         _ = try self.addInst(.{
    597             .tag = .mov,
    598             .data = .{ .r_op_mov = .{
    599                 .rd = .sp,
    600                 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),
    601             } },
    602         });
    603 
    604         // pop {fp, pc}
    605         _ = try self.addInst(.{
    606             .tag = .pop,
    607             .data = .{ .register_list = saved_regs },
    608         });
    609     } else {
    610         _ = try self.addInst(.{
    611             .tag = .dbg_prologue_end,
    612             .cond = undefined,
    613             .data = .{ .nop = {} },
    614         });
    615 
    616         try self.genBody(self.air.getMainBody());
    617 
    618         _ = try self.addInst(.{
    619             .tag = .dbg_epilogue_begin,
    620             .cond = undefined,
    621             .data = .{ .nop = {} },
    622         });
    623     }
    624 
    625     // Drop them off at the rbrace.
    626     _ = try self.addInst(.{
    627         .tag = .dbg_line,
    628         .cond = undefined,
    629         .data = .{ .dbg_line_column = .{
    630             .line = self.end_di_line,
    631             .column = self.end_di_column,
    632         } },
    633     });
    634 }
    635 
    636 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
    637     const pt = self.pt;
    638     const zcu = pt.zcu;
    639     const ip = &zcu.intern_pool;
    640     const air_tags = self.air.instructions.items(.tag);
    641 
    642     for (body) |inst| {
    643         // TODO: remove now-redundant isUnused calls from AIR handler functions
    644         if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip))
    645             continue;
    646 
    647         const old_air_bookkeeping = self.air_bookkeeping;
    648         try self.ensureProcessDeathCapacity(Liveness.bpi);
    649 
    650         switch (air_tags[@intFromEnum(inst)]) {
    651             // zig fmt: off
    652             .add,            => try self.airBinOp(inst, .add),
    653             .add_wrap        => try self.airBinOp(inst, .add_wrap),
    654             .sub,            => try self.airBinOp(inst, .sub),
    655             .sub_wrap        => try self.airBinOp(inst, .sub_wrap),
    656             .mul             => try self.airBinOp(inst, .mul),
    657             .mul_wrap        => try self.airBinOp(inst, .mul_wrap),
    658             .shl             => try self.airBinOp(inst, .shl),
    659             .shl_exact       => try self.airBinOp(inst, .shl_exact),
    660             .bool_and        => try self.airBinOp(inst, .bool_and),
    661             .bool_or         => try self.airBinOp(inst, .bool_or),
    662             .bit_and         => try self.airBinOp(inst, .bit_and),
    663             .bit_or          => try self.airBinOp(inst, .bit_or),
    664             .xor             => try self.airBinOp(inst, .xor),
    665             .shr             => try self.airBinOp(inst, .shr),
    666             .shr_exact       => try self.airBinOp(inst, .shr_exact),
    667             .div_float       => try self.airBinOp(inst, .div_float),
    668             .div_trunc       => try self.airBinOp(inst, .div_trunc),
    669             .div_floor       => try self.airBinOp(inst, .div_floor),
    670             .div_exact       => try self.airBinOp(inst, .div_exact),
    671             .rem             => try self.airBinOp(inst, .rem),
    672             .mod             => try self.airBinOp(inst, .mod),
    673 
    674             .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
    675             .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
    676 
    677             .min => try self.airMinMax(inst),
    678             .max => try self.airMinMax(inst),
    679 
    680             .add_sat         => try self.airAddSat(inst),
    681             .sub_sat         => try self.airSubSat(inst),
    682             .mul_sat         => try self.airMulSat(inst),
    683             .shl_sat         => try self.airShlSat(inst),
    684             .slice           => try self.airSlice(inst),
    685 
    686             .sqrt,
    687             .sin,
    688             .cos,
    689             .tan,
    690             .exp,
    691             .exp2,
    692             .log,
    693             .log2,
    694             .log10,
    695             .floor,
    696             .ceil,
    697             .round,
    698             .trunc_float,
    699             .neg,
    700             => try self.airUnaryMath(inst),
    701 
    702             .add_with_overflow => try self.airOverflow(inst),
    703             .sub_with_overflow => try self.airOverflow(inst),
    704             .mul_with_overflow => try self.airMulWithOverflow(inst),
    705             .shl_with_overflow => try self.airShlWithOverflow(inst),
    706 
    707             .cmp_lt  => try self.airCmp(inst, .lt),
    708             .cmp_lte => try self.airCmp(inst, .lte),
    709             .cmp_eq  => try self.airCmp(inst, .eq),
    710             .cmp_gte => try self.airCmp(inst, .gte),
    711             .cmp_gt  => try self.airCmp(inst, .gt),
    712             .cmp_neq => try self.airCmp(inst, .neq),
    713 
    714             .cmp_vector => try self.airCmpVector(inst),
    715             .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
    716 
    717             .alloc           => try self.airAlloc(inst),
    718             .ret_ptr         => try self.airRetPtr(inst),
    719             .arg             => try self.airArg(inst),
    720             .assembly        => try self.airAsm(inst),
    721             .bitcast         => try self.airBitCast(inst),
    722             .block           => try self.airBlock(inst),
    723             .br              => try self.airBr(inst),
    724             .trap            => try self.airTrap(),
    725             .breakpoint      => try self.airBreakpoint(),
    726             .ret_addr        => try self.airRetAddr(inst),
    727             .frame_addr      => try self.airFrameAddress(inst),
    728             .fence           => try self.airFence(),
    729             .cond_br         => try self.airCondBr(inst),
    730             .fptrunc         => try self.airFptrunc(inst),
    731             .fpext           => try self.airFpext(inst),
    732             .intcast         => try self.airIntCast(inst),
    733             .trunc           => try self.airTrunc(inst),
    734             .int_from_bool     => try self.airIntFromBool(inst),
    735             .is_non_null     => try self.airIsNonNull(inst),
    736             .is_non_null_ptr => try self.airIsNonNullPtr(inst),
    737             .is_null         => try self.airIsNull(inst),
    738             .is_null_ptr     => try self.airIsNullPtr(inst),
    739             .is_non_err      => try self.airIsNonErr(inst),
    740             .is_non_err_ptr  => try self.airIsNonErrPtr(inst),
    741             .is_err          => try self.airIsErr(inst),
    742             .is_err_ptr      => try self.airIsErrPtr(inst),
    743             .load            => try self.airLoad(inst),
    744             .loop            => try self.airLoop(inst),
    745             .not             => try self.airNot(inst),
    746             .int_from_ptr        => try self.airIntFromPtr(inst),
    747             .ret             => try self.airRet(inst),
    748             .ret_safe        => try self.airRet(inst), // TODO
    749             .ret_load        => try self.airRetLoad(inst),
    750             .store           => try self.airStore(inst, false),
    751             .store_safe      => try self.airStore(inst, true),
    752             .struct_field_ptr=> try self.airStructFieldPtr(inst),
    753             .struct_field_val=> try self.airStructFieldVal(inst),
    754             .array_to_slice  => try self.airArrayToSlice(inst),
    755             .float_from_int    => try self.airFloatFromInt(inst),
    756             .int_from_float    => try self.airIntFromFloat(inst),
    757             .cmpxchg_strong  => try self.airCmpxchg(inst),
    758             .cmpxchg_weak    => try self.airCmpxchg(inst),
    759             .atomic_rmw      => try self.airAtomicRmw(inst),
    760             .atomic_load     => try self.airAtomicLoad(inst),
    761             .memcpy          => try self.airMemcpy(inst),
    762             .memset          => try self.airMemset(inst, false),
    763             .memset_safe     => try self.airMemset(inst, true),
    764             .set_union_tag   => try self.airSetUnionTag(inst),
    765             .get_union_tag   => try self.airGetUnionTag(inst),
    766             .clz             => try self.airClz(inst),
    767             .ctz             => try self.airCtz(inst),
    768             .popcount        => try self.airPopcount(inst),
    769             .abs             => try self.airAbs(inst),
    770             .byte_swap       => try self.airByteSwap(inst),
    771             .bit_reverse     => try self.airBitReverse(inst),
    772             .tag_name        => try self.airTagName(inst),
    773             .error_name      => try self.airErrorName(inst),
    774             .splat           => try self.airSplat(inst),
    775             .select          => try self.airSelect(inst),
    776             .shuffle         => try self.airShuffle(inst),
    777             .reduce          => try self.airReduce(inst),
    778             .aggregate_init  => try self.airAggregateInit(inst),
    779             .union_init      => try self.airUnionInit(inst),
    780             .prefetch        => try self.airPrefetch(inst),
    781             .mul_add         => try self.airMulAdd(inst),
    782             .addrspace_cast  => return self.fail("TODO implement addrspace_cast", .{}),
    783 
    784             .@"try"          => try self.airTry(inst),
    785             .try_cold        => try self.airTry(inst),
    786             .try_ptr         => try self.airTryPtr(inst),
    787             .try_ptr_cold    => try self.airTryPtr(inst),
    788 
    789             .dbg_stmt         => try self.airDbgStmt(inst),
    790             .dbg_inline_block => try self.airDbgInlineBlock(inst),
    791             .dbg_var_ptr,
    792             .dbg_var_val,
    793             .dbg_arg_inline,
    794             => try self.airDbgVar(inst),
    795 
    796             .call              => try self.airCall(inst, .auto),
    797             .call_always_tail  => try self.airCall(inst, .always_tail),
    798             .call_never_tail   => try self.airCall(inst, .never_tail),
    799             .call_never_inline => try self.airCall(inst, .never_inline),
    800 
    801             .atomic_store_unordered => try self.airAtomicStore(inst, .unordered),
    802             .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic),
    803             .atomic_store_release   => try self.airAtomicStore(inst, .release),
    804             .atomic_store_seq_cst   => try self.airAtomicStore(inst, .seq_cst),
    805 
    806             .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
    807             .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
    808             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
    809             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
    810 
    811             .field_parent_ptr => try self.airFieldParentPtr(inst),
    812 
    813             .switch_br       => try self.airSwitch(inst),
    814             .slice_ptr       => try self.airSlicePtr(inst),
    815             .slice_len       => try self.airSliceLen(inst),
    816 
    817             .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
    818             .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
    819 
    820             .array_elem_val      => try self.airArrayElemVal(inst),
    821             .slice_elem_val      => try self.airSliceElemVal(inst),
    822             .slice_elem_ptr      => try self.airSliceElemPtr(inst),
    823             .ptr_elem_val        => try self.airPtrElemVal(inst),
    824             .ptr_elem_ptr        => try self.airPtrElemPtr(inst),
    825 
    826             .inferred_alloc, .inferred_alloc_comptime => unreachable,
    827             .unreach  => self.finishAirBookkeeping(),
    828 
    829             .optional_payload           => try self.airOptionalPayload(inst),
    830             .optional_payload_ptr       => try self.airOptionalPayloadPtr(inst),
    831             .optional_payload_ptr_set   => try self.airOptionalPayloadPtrSet(inst),
    832             .unwrap_errunion_err        => try self.airUnwrapErrErr(inst),
    833             .unwrap_errunion_payload    => try self.airUnwrapErrPayload(inst),
    834             .unwrap_errunion_err_ptr    => try self.airUnwrapErrErrPtr(inst),
    835             .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
    836             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
    837             .err_return_trace           => try self.airErrReturnTrace(inst),
    838             .set_err_return_trace       => try self.airSetErrReturnTrace(inst),
    839             .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
    840 
    841             .wrap_optional         => try self.airWrapOptional(inst),
    842             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
    843             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
    844 
    845             .add_optimized,
    846             .sub_optimized,
    847             .mul_optimized,
    848             .div_float_optimized,
    849             .div_trunc_optimized,
    850             .div_floor_optimized,
    851             .div_exact_optimized,
    852             .rem_optimized,
    853             .mod_optimized,
    854             .neg_optimized,
    855             .cmp_lt_optimized,
    856             .cmp_lte_optimized,
    857             .cmp_eq_optimized,
    858             .cmp_gte_optimized,
    859             .cmp_gt_optimized,
    860             .cmp_neq_optimized,
    861             .cmp_vector_optimized,
    862             .reduce_optimized,
    863             .int_from_float_optimized,
    864             => return self.fail("TODO implement optimized float mode", .{}),
    865 
    866             .add_safe,
    867             .sub_safe,
    868             .mul_safe,
    869             => return self.fail("TODO implement safety_checked_instructions", .{}),
    870 
    871             .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
    872             .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
    873             .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
    874 
    875             .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
    876             .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
    877             .c_va_end => return self.fail("TODO implement c_va_end", .{}),
    878             .c_va_start => return self.fail("TODO implement c_va_start", .{}),
    879 
    880             .wasm_memory_size => unreachable,
    881             .wasm_memory_grow => unreachable,
    882 
    883             .work_item_id => unreachable,
    884             .work_group_size => unreachable,
    885             .work_group_id => unreachable,
    886             // zig fmt: on
    887         }
    888 
    889         assert(!self.register_manager.lockedRegsExist());
    890 
    891         if (std.debug.runtime_safety) {
    892             if (self.air_bookkeeping < old_air_bookkeeping + 1) {
    893                 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
    894             }
    895         }
    896     }
    897 }
    898 
    899 /// Asserts there is already capacity to insert into top branch inst_table.
    900 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
    901     // When editing this function, note that the logic must synchronize with `reuseOperand`.
    902     const prev_value = self.getResolvedInstValue(inst);
    903     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    904     branch.inst_table.putAssumeCapacity(inst, .dead);
    905     switch (prev_value) {
    906         .register => |reg| {
    907             self.register_manager.freeReg(reg);
    908         },
    909         .register_c_flag,
    910         .register_v_flag,
    911         => |reg| {
    912             self.register_manager.freeReg(reg);
    913             self.cpsr_flags_inst = null;
    914         },
    915         .cpsr_flags => {
    916             self.cpsr_flags_inst = null;
    917         },
    918         else => {}, // TODO process stack allocation death
    919     }
    920 }
    921 
    922 /// Called when there are no operands, and the instruction is always unreferenced.
    923 fn finishAirBookkeeping(self: *Self) void {
    924     if (std.debug.runtime_safety) {
    925         self.air_bookkeeping += 1;
    926     }
    927 }
    928 
    929 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
    930     var tomb_bits = self.liveness.getTombBits(inst);
    931     for (operands) |op| {
    932         const dies = @as(u1, @truncate(tomb_bits)) != 0;
    933         tomb_bits >>= 1;
    934         if (!dies) continue;
    935         const op_index = op.toIndex() orelse continue;
    936         self.processDeath(op_index);
    937     }
    938     const is_used = @as(u1, @truncate(tomb_bits)) == 0;
    939     if (is_used) {
    940         log.debug("%{d} => {}", .{ inst, result });
    941         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    942         branch.inst_table.putAssumeCapacityNoClobber(inst, result);
    943 
    944         switch (result) {
    945             .register => |reg| {
    946                 // In some cases (such as bitcast), an operand
    947                 // may be the same MCValue as the result. If
    948                 // that operand died and was a register, it
    949                 // was freed by processDeath. We have to
    950                 // "re-allocate" the register.
    951                 if (self.register_manager.isRegFree(reg)) {
    952                     self.register_manager.getRegAssumeFree(reg, inst);
    953                 }
    954             },
    955             .register_c_flag,
    956             .register_v_flag,
    957             => |reg| {
    958                 if (self.register_manager.isRegFree(reg)) {
    959                     self.register_manager.getRegAssumeFree(reg, inst);
    960                 }
    961                 self.cpsr_flags_inst = inst;
    962             },
    963             .cpsr_flags => {
    964                 self.cpsr_flags_inst = inst;
    965             },
    966             else => {},
    967         }
    968     }
    969     self.finishAirBookkeeping();
    970 }
    971 
    972 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
    973     const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
    974     try table.ensureUnusedCapacity(self.gpa, additional_count);
    975 }
    976 
    977 fn allocMem(
    978     self: *Self,
    979     abi_size: u32,
    980     abi_align: Alignment,
    981     maybe_inst: ?Air.Inst.Index,
    982 ) !u32 {
    983     assert(abi_size > 0);
    984     assert(abi_align != .none);
    985 
    986     // TODO find a free slot instead of always appending
    987     const offset: u32 = @intCast(abi_align.forward(self.next_stack_offset) + abi_size);
    988     self.next_stack_offset = offset;
    989     self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
    990 
    991     if (maybe_inst) |inst| {
    992         try self.stack.putNoClobber(self.gpa, offset, .{
    993             .inst = inst,
    994             .size = abi_size,
    995         });
    996     }
    997 
    998     return offset;
    999 }
   1000 
   1001 /// Use a pointer instruction as the basis for allocating stack memory.
   1002 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
   1003     const pt = self.pt;
   1004     const zcu = pt.zcu;
   1005     const elem_ty = self.typeOfIndex(inst).childType(zcu);
   1006 
   1007     if (!elem_ty.hasRuntimeBits(zcu)) {
   1008         // As this stack item will never be dereferenced at runtime,
   1009         // return the stack offset 0. Stack offset 0 will be where all
   1010         // zero-sized stack allocations live as non-zero-sized
   1011         // allocations will always have an offset > 0.
   1012         return 0;
   1013     }
   1014 
   1015     const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse {
   1016         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
   1017     };
   1018     // TODO swap this for inst.ty.ptrAlign
   1019     const abi_align = elem_ty.abiAlignment(zcu);
   1020 
   1021     return self.allocMem(abi_size, abi_align, inst);
   1022 }
   1023 
   1024 fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
   1025     const pt = self.pt;
   1026     const abi_size = math.cast(u32, elem_ty.abiSize(pt.zcu)) orelse {
   1027         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
   1028     };
   1029     const abi_align = elem_ty.abiAlignment(pt.zcu);
   1030 
   1031     if (reg_ok) {
   1032         // Make sure the type can fit in a register before we try to allocate one.
   1033         const ptr_bits = self.target.ptrBitWidth();
   1034         const ptr_bytes: u64 = @divExact(ptr_bits, 8);
   1035         if (abi_size <= ptr_bytes) {
   1036             if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
   1037                 return MCValue{ .register = reg };
   1038             }
   1039         }
   1040     }
   1041 
   1042     const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst);
   1043     return MCValue{ .stack_offset = stack_offset };
   1044 }
   1045 
   1046 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
   1047     const stack_mcv = try self.allocRegOrMem(self.typeOfIndex(inst), false, inst);
   1048     log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
   1049 
   1050     const reg_mcv = self.getResolvedInstValue(inst);
   1051     switch (reg_mcv) {
   1052         .register,
   1053         .register_c_flag,
   1054         .register_v_flag,
   1055         => |r| assert(r == reg),
   1056         else => unreachable, // not a register
   1057     }
   1058 
   1059     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   1060     try branch.inst_table.put(self.gpa, inst, stack_mcv);
   1061     try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
   1062 }
   1063 
   1064 /// Save the current instruction stored in the compare flags if
   1065 /// occupied
   1066 fn spillCompareFlagsIfOccupied(self: *Self) !void {
   1067     if (self.cpsr_flags_inst) |inst_to_save| {
   1068         const ty = self.typeOfIndex(inst_to_save);
   1069         const mcv = self.getResolvedInstValue(inst_to_save);
   1070         const new_mcv = switch (mcv) {
   1071             .cpsr_flags => try self.allocRegOrMem(ty, true, inst_to_save),
   1072             .register_c_flag,
   1073             .register_v_flag,
   1074             => try self.allocRegOrMem(ty, false, inst_to_save),
   1075             else => unreachable, // mcv doesn't occupy the compare flags
   1076         };
   1077 
   1078         try self.setRegOrMem(self.typeOfIndex(inst_to_save), new_mcv, mcv);
   1079         log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
   1080 
   1081         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   1082         try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
   1083 
   1084         self.cpsr_flags_inst = null;
   1085 
   1086         // TODO consolidate with register manager and spillInstruction
   1087         // this call should really belong in the register manager!
   1088         switch (mcv) {
   1089             .register_c_flag,
   1090             .register_v_flag,
   1091             => |reg| self.register_manager.freeReg(reg),
   1092             else => {},
   1093         }
   1094     }
   1095 }
   1096 
   1097 /// Copies a value to a register without tracking the register. The register is not considered
   1098 /// allocated. A second call to `copyToTmpRegister` may return the same register.
   1099 /// This can have a side effect of spilling instructions to the stack to free up a register.
   1100 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
   1101     const reg = try self.register_manager.allocReg(null, gp);
   1102     try self.genSetReg(ty, reg, mcv);
   1103     return reg;
   1104 }
   1105 
   1106 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
   1107     const stack_offset = try self.allocMemPtr(inst);
   1108     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
   1109 }
   1110 
   1111 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
   1112     const pt = self.pt;
   1113     const zcu = pt.zcu;
   1114     const result: MCValue = switch (self.ret_mcv) {
   1115         .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) },
   1116         .stack_offset => blk: {
   1117             // self.ret_mcv is an address to where this function
   1118             // should store its result into
   1119             const ret_ty = self.fn_type.fnReturnType(zcu);
   1120             const ptr_ty = try pt.singleMutPtrType(ret_ty);
   1121 
   1122             // addr_reg will contain the address of where to store the
   1123             // result into
   1124             const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv);
   1125             break :blk .{ .register = addr_reg };
   1126         },
   1127         else => unreachable, // invalid return result
   1128     };
   1129 
   1130     return self.finishAir(inst, result, .{ .none, .none, .none });
   1131 }
   1132 
   1133 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
   1134     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1135     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
   1136     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1137 }
   1138 
   1139 fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
   1140     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1141     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
   1142     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1143 }
   1144 
   1145 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
   1146     const pt = self.pt;
   1147     const zcu = pt.zcu;
   1148     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1149     if (self.liveness.isUnused(inst))
   1150         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
   1151 
   1152     const operand = try self.resolveInst(ty_op.operand);
   1153     const operand_ty = self.typeOf(ty_op.operand);
   1154     const dest_ty = self.typeOfIndex(inst);
   1155 
   1156     const operand_abi_size = operand_ty.abiSize(zcu);
   1157     const dest_abi_size = dest_ty.abiSize(zcu);
   1158     const info_a = operand_ty.intInfo(zcu);
   1159     const info_b = dest_ty.intInfo(zcu);
   1160 
   1161     const dst_mcv: MCValue = blk: {
   1162         if (info_a.bits == info_b.bits) {
   1163             break :blk operand;
   1164         }
   1165         if (operand_abi_size > 4 or dest_abi_size > 4) {
   1166             return self.fail("TODO implement intCast for abi sizes larger than 4", .{});
   1167         }
   1168 
   1169         const operand_lock: ?RegisterLock = switch (operand) {
   1170             .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
   1171             else => null,
   1172         };
   1173         defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
   1174 
   1175         const reg = try self.register_manager.allocReg(inst, gp);
   1176         try self.genSetReg(dest_ty, reg, operand);
   1177         break :blk MCValue{ .register = reg };
   1178     };
   1179 
   1180     return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
   1181 }
   1182 
   1183 fn truncRegister(
   1184     self: *Self,
   1185     operand_reg: Register,
   1186     dest_reg: Register,
   1187     int_signedness: std.builtin.Signedness,
   1188     int_bits: u16,
   1189 ) !void {
   1190     // TODO check if sxtb/uxtb/sxth/uxth are more efficient
   1191     _ = try self.addInst(.{
   1192         .tag = switch (int_signedness) {
   1193             .signed => .sbfx,
   1194             .unsigned => .ubfx,
   1195         },
   1196         .data = .{ .rr_lsb_width = .{
   1197             .rd = dest_reg,
   1198             .rn = operand_reg,
   1199             .lsb = 0,
   1200             .width = @intCast(int_bits),
   1201         } },
   1202     });
   1203 }
   1204 
   1205 /// Asserts that both operand_ty and dest_ty are integer types
   1206 fn trunc(
   1207     self: *Self,
   1208     maybe_inst: ?Air.Inst.Index,
   1209     operand_bind: ReadArg.Bind,
   1210     operand_ty: Type,
   1211     dest_ty: Type,
   1212 ) !MCValue {
   1213     const pt = self.pt;
   1214     const zcu = pt.zcu;
   1215     const info_a = operand_ty.intInfo(zcu);
   1216     const info_b = dest_ty.intInfo(zcu);
   1217 
   1218     if (info_b.bits <= 32) {
   1219         if (info_a.bits > 32) {
   1220             return self.fail("TODO load least significant word into register", .{});
   1221         }
   1222 
   1223         var operand_reg: Register = undefined;
   1224         var dest_reg: Register = undefined;
   1225 
   1226         const read_args = [_]ReadArg{
   1227             .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg },
   1228         };
   1229         const write_args = [_]WriteArg{
   1230             .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   1231         };
   1232         try self.allocRegs(
   1233             &read_args,
   1234             &write_args,
   1235             if (maybe_inst) |inst| .{
   1236                 .corresponding_inst = inst,
   1237                 .operand_mapping = &.{0},
   1238             } else null,
   1239         );
   1240 
   1241         switch (info_b.bits) {
   1242             32 => {
   1243                 try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
   1244             },
   1245             else => {
   1246                 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
   1247             },
   1248         }
   1249 
   1250         return MCValue{ .register = dest_reg };
   1251     } else {
   1252         return self.fail("TODO: truncate to ints > 32 bits", .{});
   1253     }
   1254 }
   1255 
   1256 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
   1257     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1258     const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
   1259     const operand_ty = self.typeOf(ty_op.operand);
   1260     const dest_ty = self.typeOfIndex(inst);
   1261 
   1262     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
   1263         break :blk try self.trunc(inst, operand_bind, operand_ty, dest_ty);
   1264     };
   1265 
   1266     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1267 }
   1268 
   1269 fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void {
   1270     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   1271     const operand = try self.resolveInst(un_op);
   1272     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
   1273     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1274 }
   1275 
   1276 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
   1277     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1278     const pt = self.pt;
   1279     const zcu = pt.zcu;
   1280     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1281         const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
   1282         const operand_ty = self.typeOf(ty_op.operand);
   1283         switch (try operand_bind.resolveToMcv(self)) {
   1284             .dead => unreachable,
   1285             .unreach => unreachable,
   1286             .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() },
   1287             else => {
   1288                 switch (operand_ty.zigTypeTag(zcu)) {
   1289                     .Bool => {
   1290                         var op_reg: Register = undefined;
   1291                         var dest_reg: Register = undefined;
   1292 
   1293                         const read_args = [_]ReadArg{
   1294                             .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
   1295                         };
   1296                         const write_args = [_]WriteArg{
   1297                             .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   1298                         };
   1299                         try self.allocRegs(
   1300                             &read_args,
   1301                             &write_args,
   1302                             ReuseMetadata{
   1303                                 .corresponding_inst = inst,
   1304                                 .operand_mapping = &.{0},
   1305                             },
   1306                         );
   1307 
   1308                         _ = try self.addInst(.{
   1309                             .tag = .eor,
   1310                             .data = .{ .rr_op = .{
   1311                                 .rd = dest_reg,
   1312                                 .rn = op_reg,
   1313                                 .op = Instruction.Operand.fromU32(1).?,
   1314                             } },
   1315                         });
   1316 
   1317                         break :result MCValue{ .register = dest_reg };
   1318                     },
   1319                     .Vector => return self.fail("TODO bitwise not for vectors", .{}),
   1320                     .Int => {
   1321                         const int_info = operand_ty.intInfo(zcu);
   1322                         if (int_info.bits <= 32) {
   1323                             var op_reg: Register = undefined;
   1324                             var dest_reg: Register = undefined;
   1325 
   1326                             const read_args = [_]ReadArg{
   1327                                 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
   1328                             };
   1329                             const write_args = [_]WriteArg{
   1330                                 .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   1331                             };
   1332                             try self.allocRegs(
   1333                                 &read_args,
   1334                                 &write_args,
   1335                                 ReuseMetadata{
   1336                                     .corresponding_inst = inst,
   1337                                     .operand_mapping = &.{0},
   1338                                 },
   1339                             );
   1340 
   1341                             _ = try self.addInst(.{
   1342                                 .tag = .mvn,
   1343                                 .data = .{ .r_op_mov = .{
   1344                                     .rd = dest_reg,
   1345                                     .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),
   1346                                 } },
   1347                             });
   1348 
   1349                             if (int_info.bits < 32) {
   1350                                 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
   1351                             }
   1352 
   1353                             break :result MCValue{ .register = dest_reg };
   1354                         } else {
   1355                             return self.fail("TODO ARM not on integers > u32/i32", .{});
   1356                         }
   1357                     },
   1358                     else => unreachable,
   1359                 }
   1360             },
   1361         }
   1362     };
   1363     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1364 }
   1365 
   1366 fn minMax(
   1367     self: *Self,
   1368     tag: Air.Inst.Tag,
   1369     lhs_bind: ReadArg.Bind,
   1370     rhs_bind: ReadArg.Bind,
   1371     lhs_ty: Type,
   1372     rhs_ty: Type,
   1373     maybe_inst: ?Air.Inst.Index,
   1374 ) !MCValue {
   1375     const pt = self.pt;
   1376     const zcu = pt.zcu;
   1377     switch (lhs_ty.zigTypeTag(zcu)) {
   1378         .Float => return self.fail("TODO ARM min/max on floats", .{}),
   1379         .Vector => return self.fail("TODO ARM min/max on vectors", .{}),
   1380         .Int => {
   1381             assert(lhs_ty.eql(rhs_ty, zcu));
   1382             const int_info = lhs_ty.intInfo(zcu);
   1383             if (int_info.bits <= 32) {
   1384                 var lhs_reg: Register = undefined;
   1385                 var rhs_reg: Register = undefined;
   1386                 var dest_reg: Register = undefined;
   1387 
   1388                 const read_args = [_]ReadArg{
   1389                     .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   1390                     .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
   1391                 };
   1392                 const write_args = [_]WriteArg{
   1393                     .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   1394                 };
   1395                 try self.allocRegs(
   1396                     &read_args,
   1397                     &write_args,
   1398                     if (maybe_inst) |inst| .{
   1399                         .corresponding_inst = inst,
   1400                         .operand_mapping = &.{ 0, 1 },
   1401                     } else null,
   1402                 );
   1403 
   1404                 // lhs == reg should have been checked by airMinMax
   1405                 //
   1406                 // By guaranteeing lhs != rhs, we guarantee (dst !=
   1407                 // lhs) or (dst != rhs), which is a property we use to
   1408                 // omit generating one instruction when we reuse a
   1409                 // register.
   1410                 assert(lhs_reg != rhs_reg); // see note above
   1411 
   1412                 _ = try self.addInst(.{
   1413                     .tag = .cmp,
   1414                     .data = .{ .r_op_cmp = .{
   1415                         .rn = lhs_reg,
   1416                         .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
   1417                     } },
   1418                 });
   1419 
   1420                 const cond_choose_lhs: Condition = switch (tag) {
   1421                     .max => switch (int_info.signedness) {
   1422                         .signed => Condition.gt,
   1423                         .unsigned => Condition.hi,
   1424                     },
   1425                     .min => switch (int_info.signedness) {
   1426                         .signed => Condition.lt,
   1427                         .unsigned => Condition.cc,
   1428                     },
   1429                     else => unreachable,
   1430                 };
   1431                 const cond_choose_rhs = cond_choose_lhs.negate();
   1432 
   1433                 if (dest_reg != lhs_reg) {
   1434                     _ = try self.addInst(.{
   1435                         .tag = .mov,
   1436                         .cond = cond_choose_lhs,
   1437                         .data = .{ .r_op_mov = .{
   1438                             .rd = dest_reg,
   1439                             .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none),
   1440                         } },
   1441                     });
   1442                 }
   1443                 if (dest_reg != rhs_reg) {
   1444                     _ = try self.addInst(.{
   1445                         .tag = .mov,
   1446                         .cond = cond_choose_rhs,
   1447                         .data = .{ .r_op_mov = .{
   1448                             .rd = dest_reg,
   1449                             .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
   1450                         } },
   1451                     });
   1452                 }
   1453 
   1454                 return MCValue{ .register = dest_reg };
   1455             } else {
   1456                 return self.fail("TODO ARM min/max on integers > u32/i32", .{});
   1457             }
   1458         },
   1459         else => unreachable,
   1460     }
   1461 }
   1462 
   1463 fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
   1464     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
   1465     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1466     const lhs_ty = self.typeOf(bin_op.lhs);
   1467     const rhs_ty = self.typeOf(bin_op.rhs);
   1468 
   1469     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1470         const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
   1471         const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
   1472 
   1473         const lhs = try self.resolveInst(bin_op.lhs);
   1474         if (bin_op.lhs == bin_op.rhs) break :result lhs;
   1475 
   1476         break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
   1477     };
   1478     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1479 }
   1480 
   1481 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
   1482     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1483     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1484     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1485         const ptr = try self.resolveInst(bin_op.lhs);
   1486         const ptr_ty = self.typeOf(bin_op.lhs);
   1487         const len = try self.resolveInst(bin_op.rhs);
   1488         const len_ty = self.typeOf(bin_op.rhs);
   1489 
   1490         const stack_offset = try self.allocMem(8, .@"4", inst);
   1491         try self.genSetStack(ptr_ty, stack_offset, ptr);
   1492         try self.genSetStack(len_ty, stack_offset - 4, len);
   1493         break :result MCValue{ .stack_offset = stack_offset };
   1494     };
   1495     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1496 }
   1497 
   1498 fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
   1499     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1500     const lhs_ty = self.typeOf(bin_op.lhs);
   1501     const rhs_ty = self.typeOf(bin_op.rhs);
   1502 
   1503     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1504         const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
   1505         const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
   1506 
   1507         break :result switch (tag) {
   1508             .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1509             .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1510 
   1511             .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1512 
   1513             .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1514 
   1515             .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1516 
   1517             .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1518 
   1519             .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1520 
   1521             .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1522 
   1523             .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1524 
   1525             .add_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1526             .sub_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1527             .mul_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1528 
   1529             .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1530             .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1531             .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1532 
   1533             .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1534             .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1535 
   1536             .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1537             .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1538 
   1539             .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1540             .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
   1541 
   1542             else => unreachable,
   1543         };
   1544     };
   1545     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1546 }
   1547 
   1548 fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
   1549     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1550     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1551     const lhs_ty = self.typeOf(bin_op.lhs);
   1552     const rhs_ty = self.typeOf(bin_op.rhs);
   1553 
   1554     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1555         const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
   1556         const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
   1557 
   1558         break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
   1559     };
   1560     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1561 }
   1562 
   1563 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
   1564     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1565     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
   1566     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1567 }
   1568 
   1569 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
   1570     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1571     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
   1572     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1573 }
   1574 
   1575 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
   1576     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1577     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
   1578     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1579 }
   1580 
   1581 fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1582     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
   1583     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1584     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1585     const pt = self.pt;
   1586     const zcu = pt.zcu;
   1587     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1588         const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
   1589         const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
   1590         const lhs_ty = self.typeOf(extra.lhs);
   1591         const rhs_ty = self.typeOf(extra.rhs);
   1592 
   1593         const tuple_ty = self.typeOfIndex(inst);
   1594         const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
   1595         const tuple_align = tuple_ty.abiAlignment(zcu);
   1596         const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
   1597 
   1598         switch (lhs_ty.zigTypeTag(zcu)) {
   1599             .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
   1600             .Int => {
   1601                 assert(lhs_ty.eql(rhs_ty, zcu));
   1602                 const int_info = lhs_ty.intInfo(zcu);
   1603                 if (int_info.bits < 32) {
   1604                     const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
   1605 
   1606                     try self.spillCompareFlagsIfOccupied();
   1607 
   1608                     const base_tag: Air.Inst.Tag = switch (tag) {
   1609                         .add_with_overflow => .add,
   1610                         .sub_with_overflow => .sub,
   1611                         else => unreachable,
   1612                     };
   1613                     const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
   1614                     const dest_reg = dest.register;
   1615                     const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
   1616                     defer self.register_manager.unlockReg(dest_reg_lock);
   1617 
   1618                     const truncated_reg = try self.register_manager.allocReg(null, gp);
   1619                     const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
   1620                     defer self.register_manager.unlockReg(truncated_reg_lock);
   1621 
   1622                     // sbfx/ubfx truncated, dest, #0, #bits
   1623                     try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
   1624 
   1625                     // cmp dest, truncated
   1626                     _ = try self.addInst(.{
   1627                         .tag = .cmp,
   1628                         .data = .{ .r_op_cmp = .{
   1629                             .rn = dest_reg,
   1630                             .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none),
   1631                         } },
   1632                     });
   1633 
   1634                     try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
   1635                     try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
   1636 
   1637                     break :result MCValue{ .stack_offset = stack_offset };
   1638                 } else if (int_info.bits == 32) {
   1639                     const lhs_immediate = try lhs_bind.resolveToImmediate(self);
   1640                     const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   1641 
   1642                     // Only say yes if the operation is
   1643                     // commutative, i.e. we can swap both of the
   1644                     // operands
   1645                     const lhs_immediate_ok = switch (tag) {
   1646                         .add_with_overflow => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
   1647                         .sub_with_overflow => false,
   1648                         else => unreachable,
   1649                     };
   1650                     const rhs_immediate_ok = switch (tag) {
   1651                         .add_with_overflow,
   1652                         .sub_with_overflow,
   1653                         => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
   1654                         else => unreachable,
   1655                     };
   1656 
   1657                     const mir_tag: Mir.Inst.Tag = switch (tag) {
   1658                         .add_with_overflow => .adds,
   1659                         .sub_with_overflow => .subs,
   1660                         else => unreachable,
   1661                     };
   1662 
   1663                     try self.spillCompareFlagsIfOccupied();
   1664                     self.cpsr_flags_inst = inst;
   1665 
   1666                     const dest = blk: {
   1667                         if (rhs_immediate_ok) {
   1668                             break :blk try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, null);
   1669                         } else if (lhs_immediate_ok) {
   1670                             // swap lhs and rhs
   1671                             break :blk try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, null);
   1672                         } else {
   1673                             break :blk try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
   1674                         }
   1675                     };
   1676 
   1677                     if (tag == .sub_with_overflow) {
   1678                         break :result MCValue{ .register_v_flag = dest.register };
   1679                     }
   1680 
   1681                     switch (int_info.signedness) {
   1682                         .unsigned => break :result MCValue{ .register_c_flag = dest.register },
   1683                         .signed => break :result MCValue{ .register_v_flag = dest.register },
   1684                     }
   1685                 } else {
   1686                     return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
   1687                 }
   1688             },
   1689             else => unreachable,
   1690         }
   1691     };
   1692     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1693 }
   1694 
   1695 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1696     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1697     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1698     if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
   1699     const pt = self.pt;
   1700     const zcu = pt.zcu;
   1701     const result: MCValue = result: {
   1702         const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
   1703         const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
   1704         const lhs_ty = self.typeOf(extra.lhs);
   1705         const rhs_ty = self.typeOf(extra.rhs);
   1706 
   1707         const tuple_ty = self.typeOfIndex(inst);
   1708         const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
   1709         const tuple_align = tuple_ty.abiAlignment(zcu);
   1710         const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
   1711 
   1712         switch (lhs_ty.zigTypeTag(zcu)) {
   1713             .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
   1714             .Int => {
   1715                 assert(lhs_ty.eql(rhs_ty, zcu));
   1716                 const int_info = lhs_ty.intInfo(zcu);
   1717                 if (int_info.bits <= 16) {
   1718                     const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
   1719 
   1720                     try self.spillCompareFlagsIfOccupied();
   1721 
   1722                     const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
   1723                         .signed => .smulbb,
   1724                         .unsigned => .mul,
   1725                     };
   1726 
   1727                     const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
   1728                     const dest_reg = dest.register;
   1729                     const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
   1730                     defer self.register_manager.unlockReg(dest_reg_lock);
   1731 
   1732                     const truncated_reg = try self.register_manager.allocReg(null, gp);
   1733                     const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
   1734                     defer self.register_manager.unlockReg(truncated_reg_lock);
   1735 
   1736                     // sbfx/ubfx truncated, dest, #0, #bits
   1737                     try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
   1738 
   1739                     // cmp dest, truncated
   1740                     _ = try self.addInst(.{
   1741                         .tag = .cmp,
   1742                         .data = .{ .r_op_cmp = .{
   1743                             .rn = dest_reg,
   1744                             .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none),
   1745                         } },
   1746                     });
   1747 
   1748                     try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
   1749                     try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
   1750 
   1751                     break :result MCValue{ .stack_offset = stack_offset };
   1752                 } else if (int_info.bits <= 32) {
   1753                     const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
   1754 
   1755                     try self.spillCompareFlagsIfOccupied();
   1756 
   1757                     const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
   1758                         .signed => .smull,
   1759                         .unsigned => .umull,
   1760                     };
   1761 
   1762                     var lhs_reg: Register = undefined;
   1763                     var rhs_reg: Register = undefined;
   1764                     var rdhi: Register = undefined;
   1765                     var rdlo: Register = undefined;
   1766                     var truncated_reg: Register = undefined;
   1767 
   1768                     const read_args = [_]ReadArg{
   1769                         .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   1770                         .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
   1771                     };
   1772                     const write_args = [_]WriteArg{
   1773                         .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdhi },
   1774                         .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdlo },
   1775                         .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg },
   1776                     };
   1777                     try self.allocRegs(
   1778                         &read_args,
   1779                         &write_args,
   1780                         null,
   1781                     );
   1782 
   1783                     _ = try self.addInst(.{
   1784                         .tag = base_tag,
   1785                         .data = .{ .rrrr = .{
   1786                             .rdlo = rdlo,
   1787                             .rdhi = rdhi,
   1788                             .rn = lhs_reg,
   1789                             .rm = rhs_reg,
   1790                         } },
   1791                     });
   1792 
   1793                     // sbfx/ubfx truncated, rdlo, #0, #bits
   1794                     try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits);
   1795 
   1796                     // str truncated, [...]
   1797                     try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
   1798 
   1799                     // cmp truncated, rdlo
   1800                     _ = try self.addInst(.{
   1801                         .tag = .cmp,
   1802                         .data = .{ .r_op_cmp = .{
   1803                             .rn = truncated_reg,
   1804                             .op = Instruction.Operand.reg(rdlo, Instruction.Operand.Shift.none),
   1805                         } },
   1806                     });
   1807 
   1808                     // mov rdlo, #0
   1809                     _ = try self.addInst(.{
   1810                         .tag = .mov,
   1811                         .data = .{ .r_op_mov = .{
   1812                             .rd = rdlo,
   1813                             .op = Instruction.Operand.fromU32(0).?,
   1814                         } },
   1815                     });
   1816 
   1817                     // movne rdlo, #1
   1818                     _ = try self.addInst(.{
   1819                         .tag = .mov,
   1820                         .cond = .ne,
   1821                         .data = .{ .r_op_mov = .{
   1822                             .rd = rdlo,
   1823                             .op = Instruction.Operand.fromU32(1).?,
   1824                         } },
   1825                     });
   1826 
   1827                     // cmp rdhi, #0
   1828                     _ = try self.addInst(.{
   1829                         .tag = .cmp,
   1830                         .data = .{ .r_op_cmp = .{
   1831                             .rn = rdhi,
   1832                             .op = Instruction.Operand.fromU32(0).?,
   1833                         } },
   1834                     });
   1835 
   1836                     // movne rdlo, #1
   1837                     _ = try self.addInst(.{
   1838                         .tag = .mov,
   1839                         .cond = .ne,
   1840                         .data = .{ .r_op_mov = .{
   1841                             .rd = rdlo,
   1842                             .op = Instruction.Operand.fromU32(1).?,
   1843                         } },
   1844                     });
   1845 
   1846                     // strb rdlo, [...]
   1847                     try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .register = rdlo });
   1848 
   1849                     break :result MCValue{ .stack_offset = stack_offset };
   1850                 } else {
   1851                     return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
   1852                 }
   1853             },
   1854             else => unreachable,
   1855         }
   1856     };
   1857     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1858 }
   1859 
   1860 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1861     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1862     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1863     if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
   1864     const pt = self.pt;
   1865     const zcu = pt.zcu;
   1866     const result: MCValue = result: {
   1867         const lhs_ty = self.typeOf(extra.lhs);
   1868         const rhs_ty = self.typeOf(extra.rhs);
   1869 
   1870         const tuple_ty = self.typeOfIndex(inst);
   1871         const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
   1872         const tuple_align = tuple_ty.abiAlignment(zcu);
   1873         const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
   1874 
   1875         switch (lhs_ty.zigTypeTag(zcu)) {
   1876             .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}),
   1877             .Int => {
   1878                 const int_info = lhs_ty.intInfo(zcu);
   1879                 if (int_info.bits <= 32) {
   1880                     const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
   1881 
   1882                     try self.spillCompareFlagsIfOccupied();
   1883 
   1884                     const shr_mir_tag: Mir.Inst.Tag = switch (int_info.signedness) {
   1885                         .signed => Mir.Inst.Tag.asr,
   1886                         .unsigned => Mir.Inst.Tag.lsr,
   1887                     };
   1888 
   1889                     var lhs_reg: Register = undefined;
   1890                     var rhs_reg: Register = undefined;
   1891                     var dest_reg: Register = undefined;
   1892                     var reconstructed_reg: Register = undefined;
   1893 
   1894                     const rhs_mcv = try self.resolveInst(extra.rhs);
   1895                     const rhs_immediate_ok = rhs_mcv == .immediate and Instruction.Operand.fromU32(rhs_mcv.immediate) != null;
   1896 
   1897                     const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
   1898                     const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
   1899 
   1900                     if (rhs_immediate_ok) {
   1901                         const read_args = [_]ReadArg{
   1902                             .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   1903                         };
   1904                         const write_args = [_]WriteArg{
   1905                             .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   1906                             .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
   1907                         };
   1908                         try self.allocRegs(
   1909                             &read_args,
   1910                             &write_args,
   1911                             null,
   1912                         );
   1913 
   1914                         // lsl dest, lhs, rhs
   1915                         _ = try self.addInst(.{
   1916                             .tag = .lsl,
   1917                             .data = .{ .rr_shift = .{
   1918                                 .rd = dest_reg,
   1919                                 .rm = lhs_reg,
   1920                                 .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_mcv.immediate)),
   1921                             } },
   1922                         });
   1923 
   1924                         try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
   1925 
   1926                         // asr/lsr reconstructed, dest, rhs
   1927                         _ = try self.addInst(.{
   1928                             .tag = shr_mir_tag,
   1929                             .data = .{ .rr_shift = .{
   1930                                 .rd = reconstructed_reg,
   1931                                 .rm = dest_reg,
   1932                                 .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_mcv.immediate)),
   1933                             } },
   1934                         });
   1935                     } else {
   1936                         const read_args = [_]ReadArg{
   1937                             .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   1938                             .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
   1939                         };
   1940                         const write_args = [_]WriteArg{
   1941                             .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   1942                             .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
   1943                         };
   1944                         try self.allocRegs(
   1945                             &read_args,
   1946                             &write_args,
   1947                             null,
   1948                         );
   1949 
   1950                         // lsl dest, lhs, rhs
   1951                         _ = try self.addInst(.{
   1952                             .tag = .lsl,
   1953                             .data = .{ .rr_shift = .{
   1954                                 .rd = dest_reg,
   1955                                 .rm = lhs_reg,
   1956                                 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
   1957                             } },
   1958                         });
   1959 
   1960                         try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
   1961 
   1962                         // asr/lsr reconstructed, dest, rhs
   1963                         _ = try self.addInst(.{
   1964                             .tag = shr_mir_tag,
   1965                             .data = .{ .rr_shift = .{
   1966                                 .rd = reconstructed_reg,
   1967                                 .rm = dest_reg,
   1968                                 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
   1969                             } },
   1970                         });
   1971                     }
   1972 
   1973                     // cmp lhs, reconstructed
   1974                     _ = try self.addInst(.{
   1975                         .tag = .cmp,
   1976                         .data = .{ .r_op_cmp = .{
   1977                             .rn = lhs_reg,
   1978                             .op = Instruction.Operand.reg(reconstructed_reg, Instruction.Operand.Shift.none),
   1979                         } },
   1980                     });
   1981 
   1982                     try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });
   1983                     try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
   1984 
   1985                     break :result MCValue{ .stack_offset = stack_offset };
   1986                 } else {
   1987                     return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
   1988                 }
   1989             },
   1990             else => unreachable,
   1991         }
   1992     };
   1993     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1994 }
   1995 
   1996 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
   1997     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1998     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
   1999     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2000 }
   2001 
   2002 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
   2003     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2004     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
   2005     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2006 }
   2007 
   2008 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   2009     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2010     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
   2011     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2012 }
   2013 
   2014 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   2015     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2016     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
   2017     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2018 }
   2019 
   2020 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
   2021     const pt = self.pt;
   2022     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2023     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2024         const optional_ty = self.typeOfIndex(inst);
   2025         const abi_size: u32 = @intCast(optional_ty.abiSize(pt.zcu));
   2026 
   2027         // Optional with a zero-bit payload type is just a boolean true
   2028         if (abi_size == 1) {
   2029             break :result MCValue{ .immediate = 1 };
   2030         } else {
   2031             return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
   2032         }
   2033     };
   2034     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2035 }
   2036 
   2037 /// Given an error union, returns the error
   2038 fn errUnionErr(
   2039     self: *Self,
   2040     error_union_bind: ReadArg.Bind,
   2041     error_union_ty: Type,
   2042     maybe_inst: ?Air.Inst.Index,
   2043 ) !MCValue {
   2044     const pt = self.pt;
   2045     const zcu = pt.zcu;
   2046     const err_ty = error_union_ty.errorUnionSet(zcu);
   2047     const payload_ty = error_union_ty.errorUnionPayload(zcu);
   2048     if (err_ty.errorSetIsEmpty(zcu)) {
   2049         return MCValue{ .immediate = 0 };
   2050     }
   2051     if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   2052         return try error_union_bind.resolveToMcv(self);
   2053     }
   2054 
   2055     const err_offset: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu));
   2056     switch (try error_union_bind.resolveToMcv(self)) {
   2057         .register => {
   2058             var operand_reg: Register = undefined;
   2059             var dest_reg: Register = undefined;
   2060 
   2061             const read_args = [_]ReadArg{
   2062                 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
   2063             };
   2064             const write_args = [_]WriteArg{
   2065                 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   2066             };
   2067             try self.allocRegs(
   2068                 &read_args,
   2069                 &write_args,
   2070                 if (maybe_inst) |inst| .{
   2071                     .corresponding_inst = inst,
   2072                     .operand_mapping = &.{0},
   2073                 } else null,
   2074             );
   2075 
   2076             const err_bit_offset = err_offset * 8;
   2077             const err_bit_size: u32 = @intCast(err_ty.abiSize(zcu) * 8);
   2078 
   2079             _ = try self.addInst(.{
   2080                 .tag = .ubfx, // errors are unsigned integers
   2081                 .data = .{ .rr_lsb_width = .{
   2082                     .rd = dest_reg,
   2083                     .rn = operand_reg,
   2084                     .lsb = @intCast(err_bit_offset),
   2085                     .width = @intCast(err_bit_size),
   2086                 } },
   2087             });
   2088 
   2089             return MCValue{ .register = dest_reg };
   2090         },
   2091         .stack_argument_offset => |off| {
   2092             return MCValue{ .stack_argument_offset = off + err_offset };
   2093         },
   2094         .stack_offset => |off| {
   2095             return MCValue{ .stack_offset = off - err_offset };
   2096         },
   2097         .memory => |addr| {
   2098             return MCValue{ .memory = addr + err_offset };
   2099         },
   2100         else => unreachable, // invalid MCValue for an error union
   2101     }
   2102 }
   2103 
   2104 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
   2105     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2106     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2107         const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
   2108         const error_union_ty = self.typeOf(ty_op.operand);
   2109 
   2110         break :result try self.errUnionErr(error_union_bind, error_union_ty, inst);
   2111     };
   2112     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2113 }
   2114 
   2115 /// Given an error union, returns the payload
   2116 fn errUnionPayload(
   2117     self: *Self,
   2118     error_union_bind: ReadArg.Bind,
   2119     error_union_ty: Type,
   2120     maybe_inst: ?Air.Inst.Index,
   2121 ) !MCValue {
   2122     const pt = self.pt;
   2123     const zcu = pt.zcu;
   2124     const err_ty = error_union_ty.errorUnionSet(zcu);
   2125     const payload_ty = error_union_ty.errorUnionPayload(zcu);
   2126     if (err_ty.errorSetIsEmpty(zcu)) {
   2127         return try error_union_bind.resolveToMcv(self);
   2128     }
   2129     if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   2130         return MCValue.none;
   2131     }
   2132 
   2133     const payload_offset: u32 = @intCast(errUnionPayloadOffset(payload_ty, zcu));
   2134     switch (try error_union_bind.resolveToMcv(self)) {
   2135         .register => {
   2136             var operand_reg: Register = undefined;
   2137             var dest_reg: Register = undefined;
   2138 
   2139             const read_args = [_]ReadArg{
   2140                 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
   2141             };
   2142             const write_args = [_]WriteArg{
   2143                 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   2144             };
   2145             try self.allocRegs(
   2146                 &read_args,
   2147                 &write_args,
   2148                 if (maybe_inst) |inst| .{
   2149                     .corresponding_inst = inst,
   2150                     .operand_mapping = &.{0},
   2151                 } else null,
   2152             );
   2153 
   2154             const payload_bit_offset = payload_offset * 8;
   2155             const payload_bit_size: u32 = @intCast(payload_ty.abiSize(zcu) * 8);
   2156 
   2157             _ = try self.addInst(.{
   2158                 .tag = if (payload_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx,
   2159                 .data = .{ .rr_lsb_width = .{
   2160                     .rd = dest_reg,
   2161                     .rn = operand_reg,
   2162                     .lsb = @intCast(payload_bit_offset),
   2163                     .width = @intCast(payload_bit_size),
   2164                 } },
   2165             });
   2166 
   2167             return MCValue{ .register = dest_reg };
   2168         },
   2169         .stack_argument_offset => |off| {
   2170             return MCValue{ .stack_argument_offset = off + payload_offset };
   2171         },
   2172         .stack_offset => |off| {
   2173             return MCValue{ .stack_offset = off - payload_offset };
   2174         },
   2175         .memory => |addr| {
   2176             return MCValue{ .memory = addr + payload_offset };
   2177         },
   2178         else => unreachable, // invalid MCValue for an error union
   2179     }
   2180 }
   2181 
   2182 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
   2183     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2184     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2185         const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
   2186         const error_union_ty = self.typeOf(ty_op.operand);
   2187 
   2188         break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst);
   2189     };
   2190     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2191 }
   2192 
   2193 // *(E!T) -> E
   2194 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   2195     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2196     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
   2197     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2198 }
   2199 
   2200 // *(E!T) -> *T
   2201 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   2202     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2203     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
   2204     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2205 }
   2206 
   2207 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   2208     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2209     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
   2210     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2211 }
   2212 
   2213 fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
   2214     const result: MCValue = if (self.liveness.isUnused(inst))
   2215         .dead
   2216     else
   2217         return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
   2218     return self.finishAir(inst, result, .{ .none, .none, .none });
   2219 }
   2220 
   2221 fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
   2222     _ = inst;
   2223     return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
   2224 }
   2225 
   2226 fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
   2227     _ = inst;
   2228     return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
   2229 }
   2230 
   2231 /// T to E!T
   2232 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
   2233     const pt = self.pt;
   2234     const zcu = pt.zcu;
   2235     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2236     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2237         const error_union_ty = ty_op.ty.toType();
   2238         const error_ty = error_union_ty.errorUnionSet(zcu);
   2239         const payload_ty = error_union_ty.errorUnionPayload(zcu);
   2240         const operand = try self.resolveInst(ty_op.operand);
   2241         if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand;
   2242 
   2243         const abi_size: u32 = @intCast(error_union_ty.abiSize(zcu));
   2244         const abi_align = error_union_ty.abiAlignment(zcu);
   2245         const stack_offset: u32 = @intCast(try self.allocMem(abi_size, abi_align, inst));
   2246         const payload_off = errUnionPayloadOffset(payload_ty, zcu);
   2247         const err_off = errUnionErrorOffset(payload_ty, zcu);
   2248         try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), operand);
   2249         try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), .{ .immediate = 0 });
   2250 
   2251         break :result MCValue{ .stack_offset = stack_offset };
   2252     };
   2253     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2254 }
   2255 
   2256 /// E to E!T
   2257 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
   2258     const pt = self.pt;
   2259     const zcu = pt.zcu;
   2260     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2261     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2262         const error_union_ty = ty_op.ty.toType();
   2263         const error_ty = error_union_ty.errorUnionSet(zcu);
   2264         const payload_ty = error_union_ty.errorUnionPayload(zcu);
   2265         const operand = try self.resolveInst(ty_op.operand);
   2266         if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand;
   2267 
   2268         const abi_size: u32 = @intCast(error_union_ty.abiSize(zcu));
   2269         const abi_align = error_union_ty.abiAlignment(zcu);
   2270         const stack_offset: u32 = @intCast(try self.allocMem(abi_size, abi_align, inst));
   2271         const payload_off = errUnionPayloadOffset(payload_ty, zcu);
   2272         const err_off = errUnionErrorOffset(payload_ty, zcu);
   2273         try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), operand);
   2274         try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), .undef);
   2275 
   2276         break :result MCValue{ .stack_offset = stack_offset };
   2277     };
   2278     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2279 }
   2280 
   2281 /// Given a slice, returns the length
   2282 fn slicePtr(mcv: MCValue) MCValue {
   2283     switch (mcv) {
   2284         .register => unreachable, // a slice doesn't fit in one register
   2285         .stack_argument_offset => |off| {
   2286             return MCValue{ .stack_argument_offset = off };
   2287         },
   2288         .stack_offset => |off| {
   2289             return MCValue{ .stack_offset = off };
   2290         },
   2291         .memory => |addr| {
   2292             return MCValue{ .memory = addr };
   2293         },
   2294         else => unreachable, // invalid MCValue for a slice
   2295     }
   2296 }
   2297 
   2298 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
   2299     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2300     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2301         const mcv = try self.resolveInst(ty_op.operand);
   2302         break :result slicePtr(mcv);
   2303     };
   2304     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2305 }
   2306 
   2307 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
   2308     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2309     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2310         const mcv = try self.resolveInst(ty_op.operand);
   2311         switch (mcv) {
   2312             .register => unreachable, // a slice doesn't fit in one register
   2313             .stack_argument_offset => |off| {
   2314                 break :result MCValue{ .stack_argument_offset = off + 4 };
   2315             },
   2316             .stack_offset => |off| {
   2317                 break :result MCValue{ .stack_offset = off - 4 };
   2318             },
   2319             .memory => |addr| {
   2320                 break :result MCValue{ .memory = addr + 4 };
   2321             },
   2322             else => unreachable, // invalid MCValue for a slice
   2323         }
   2324     };
   2325     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2326 }
   2327 
   2328 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
   2329     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2330     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2331         const mcv = try self.resolveInst(ty_op.operand);
   2332         switch (mcv) {
   2333             .dead, .unreach => unreachable,
   2334             .ptr_stack_offset => |off| {
   2335                 break :result MCValue{ .ptr_stack_offset = off - 4 };
   2336             },
   2337             else => {
   2338                 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
   2339                 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 4 } };
   2340 
   2341                 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
   2342             },
   2343         }
   2344     };
   2345     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2346 }
   2347 
   2348 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
   2349     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2350     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2351         const mcv = try self.resolveInst(ty_op.operand);
   2352         switch (mcv) {
   2353             .dead, .unreach => unreachable,
   2354             .ptr_stack_offset => |off| {
   2355                 break :result MCValue{ .ptr_stack_offset = off };
   2356             },
   2357             else => {
   2358                 if (self.reuseOperand(inst, ty_op.operand, 0, mcv)) {
   2359                     break :result mcv;
   2360                 } else {
   2361                     break :result MCValue{ .register = try self.copyToTmpRegister(Type.usize, mcv) };
   2362                 }
   2363             },
   2364         }
   2365     };
   2366     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2367 }
   2368 
   2369 fn ptrElemVal(
   2370     self: *Self,
   2371     ptr_bind: ReadArg.Bind,
   2372     index_bind: ReadArg.Bind,
   2373     ptr_ty: Type,
   2374     maybe_inst: ?Air.Inst.Index,
   2375 ) !MCValue {
   2376     const pt = self.pt;
   2377     const zcu = pt.zcu;
   2378     const elem_ty = ptr_ty.childType(zcu);
   2379     const elem_size: u32 = @intCast(elem_ty.abiSize(zcu));
   2380 
   2381     switch (elem_size) {
   2382         1, 4 => {
   2383             var base_reg: Register = undefined;
   2384             var index_reg: Register = undefined;
   2385             var dest_reg: Register = undefined;
   2386 
   2387             const read_args = [_]ReadArg{
   2388                 .{ .ty = ptr_ty, .bind = ptr_bind, .class = gp, .reg = &base_reg },
   2389                 .{ .ty = Type.usize, .bind = index_bind, .class = gp, .reg = &index_reg },
   2390             };
   2391             const write_args = [_]WriteArg{
   2392                 .{ .ty = elem_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   2393             };
   2394             try self.allocRegs(
   2395                 &read_args,
   2396                 &write_args,
   2397                 if (maybe_inst) |inst| .{
   2398                     .corresponding_inst = inst,
   2399                     .operand_mapping = &.{ 0, 1 },
   2400                 } else null,
   2401             );
   2402 
   2403             const tag: Mir.Inst.Tag = switch (elem_size) {
   2404                 1 => .ldrb,
   2405                 4 => .ldr,
   2406                 else => unreachable,
   2407             };
   2408             const shift: u5 = switch (elem_size) {
   2409                 1 => 0,
   2410                 4 => 2,
   2411                 else => unreachable,
   2412             };
   2413 
   2414             _ = try self.addInst(.{
   2415                 .tag = tag,
   2416                 .data = .{ .rr_offset = .{
   2417                     .rt = dest_reg,
   2418                     .rn = base_reg,
   2419                     .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
   2420                 } },
   2421             });
   2422 
   2423             return MCValue{ .register = dest_reg };
   2424         },
   2425         else => {
   2426             const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null);
   2427 
   2428             const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst);
   2429             try self.load(dest, addr, ptr_ty);
   2430             return dest;
   2431         },
   2432     }
   2433 }
   2434 
   2435 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
   2436     const pt = self.pt;
   2437     const zcu = pt.zcu;
   2438     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2439     const slice_ty = self.typeOf(bin_op.lhs);
   2440     const result: MCValue = if (!slice_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: {
   2441         const ptr_ty = slice_ty.slicePtrFieldType(zcu);
   2442 
   2443         const slice_mcv = try self.resolveInst(bin_op.lhs);
   2444         const base_mcv = slicePtr(slice_mcv);
   2445 
   2446         const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
   2447         const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
   2448 
   2449         break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
   2450     };
   2451     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2452 }
   2453 
   2454 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   2455     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2456     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   2457     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2458         const slice_mcv = try self.resolveInst(extra.lhs);
   2459         const base_mcv = slicePtr(slice_mcv);
   2460 
   2461         const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
   2462         const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
   2463 
   2464         const slice_ty = self.typeOf(extra.lhs);
   2465         const index_ty = self.typeOf(extra.rhs);
   2466 
   2467         const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null);
   2468         break :result addr;
   2469     };
   2470     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   2471 }
   2472 
   2473 fn arrayElemVal(
   2474     self: *Self,
   2475     array_bind: ReadArg.Bind,
   2476     index_bind: ReadArg.Bind,
   2477     array_ty: Type,
   2478     maybe_inst: ?Air.Inst.Index,
   2479 ) InnerError!MCValue {
   2480     const pt = self.pt;
   2481     const zcu = pt.zcu;
   2482     const elem_ty = array_ty.childType(zcu);
   2483 
   2484     const mcv = try array_bind.resolveToMcv(self);
   2485     switch (mcv) {
   2486         .stack_offset,
   2487         .memory,
   2488         .stack_argument_offset,
   2489         => {
   2490             const ptr_to_mcv = switch (mcv) {
   2491                 .stack_offset => |off| MCValue{ .ptr_stack_offset = off },
   2492                 .memory => |addr| MCValue{ .immediate = @intCast(addr) },
   2493                 .stack_argument_offset => |off| blk: {
   2494                     const reg = try self.register_manager.allocReg(null, gp);
   2495 
   2496                     _ = try self.addInst(.{
   2497                         .tag = .ldr_ptr_stack_argument,
   2498                         .data = .{ .r_stack_offset = .{
   2499                             .rt = reg,
   2500                             .stack_offset = off,
   2501                         } },
   2502                     });
   2503 
   2504                     break :blk MCValue{ .register = reg };
   2505                 },
   2506                 else => unreachable,
   2507             };
   2508             const ptr_to_mcv_lock: ?RegisterLock = switch (ptr_to_mcv) {
   2509                 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
   2510                 else => null,
   2511             };
   2512             defer if (ptr_to_mcv_lock) |lock| self.register_manager.unlockReg(lock);
   2513 
   2514             const base_bind: ReadArg.Bind = .{ .mcv = ptr_to_mcv };
   2515 
   2516             const ptr_ty = try pt.singleMutPtrType(elem_ty);
   2517 
   2518             return try self.ptrElemVal(base_bind, index_bind, ptr_ty, maybe_inst);
   2519         },
   2520         else => return self.fail("TODO implement array_elem_val for {}", .{mcv}),
   2521     }
   2522 }
   2523 
   2524 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
   2525     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2526     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2527         const array_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
   2528         const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
   2529         const array_ty = self.typeOf(bin_op.lhs);
   2530 
   2531         break :result try self.arrayElemVal(array_bind, index_bind, array_ty, inst);
   2532     };
   2533     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2534 }
   2535 
   2536 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
   2537     const pt = self.pt;
   2538     const zcu = pt.zcu;
   2539     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2540     const ptr_ty = self.typeOf(bin_op.lhs);
   2541     const result: MCValue = if (!ptr_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: {
   2542         const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
   2543         const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
   2544 
   2545         break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
   2546     };
   2547     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2548 }
   2549 
   2550 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   2551     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2552     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   2553     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2554         const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
   2555         const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
   2556 
   2557         const ptr_ty = self.typeOf(extra.lhs);
   2558         const index_ty = self.typeOf(extra.rhs);
   2559 
   2560         const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null);
   2561         break :result addr;
   2562     };
   2563     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   2564 }
   2565 
   2566 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   2567     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2568     _ = bin_op;
   2569     return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
   2570     // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2571 }
   2572 
   2573 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   2574     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2575     _ = ty_op;
   2576     return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
   2577     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2578 }
   2579 
   2580 fn airClz(self: *Self, inst: Air.Inst.Index) !void {
   2581     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2582     _ = ty_op;
   2583     return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
   2584     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2585 }
   2586 
   2587 fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
   2588     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2589     _ = ty_op;
   2590     return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
   2591     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2592 }
   2593 
   2594 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
   2595     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2596     _ = ty_op;
   2597     return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
   2598     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2599 }
   2600 
   2601 fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
   2602     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2603     _ = ty_op;
   2604     return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
   2605     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2606 }
   2607 
   2608 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
   2609     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2610     _ = ty_op;
   2611     return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
   2612     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2613 }
   2614 
   2615 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
   2616     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2617     _ = ty_op;
   2618     return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
   2619     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2620 }
   2621 
   2622 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
   2623     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2624     const result: MCValue = if (self.liveness.isUnused(inst))
   2625         .dead
   2626     else
   2627         return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
   2628     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2629 }
   2630 
   2631 fn reuseOperand(
   2632     self: *Self,
   2633     inst: Air.Inst.Index,
   2634     operand: Air.Inst.Ref,
   2635     op_index: Liveness.OperandInt,
   2636     mcv: MCValue,
   2637 ) bool {
   2638     if (!self.liveness.operandDies(inst, op_index))
   2639         return false;
   2640 
   2641     switch (mcv) {
   2642         .register => |reg| {
   2643             // We assert that this register is allocatable by asking
   2644             // for its index
   2645             const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above
   2646             if (!self.register_manager.isRegFree(reg)) {
   2647                 self.register_manager.registers[index] = inst;
   2648             }
   2649 
   2650             log.debug("%{d} => {} (reused)", .{ inst, reg });
   2651         },
   2652         .stack_offset => |off| {
   2653             log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
   2654         },
   2655         .cpsr_flags => {
   2656             log.debug("%{d} => cpsr_flags (reused)", .{inst});
   2657         },
   2658         else => return false,
   2659     }
   2660 
   2661     // Prevent the operand deaths processing code from deallocating it.
   2662     self.liveness.clearOperandDeath(inst, op_index);
   2663 
   2664     // That makes us responsible for doing the rest of the stuff that processDeath would have done.
   2665     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   2666     branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead);
   2667 
   2668     return true;
   2669 }
   2670 
   2671 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
   2672     const pt = self.pt;
   2673     const zcu = pt.zcu;
   2674     const elem_ty = ptr_ty.childType(zcu);
   2675     const elem_size: u32 = @intCast(elem_ty.abiSize(zcu));
   2676 
   2677     switch (ptr) {
   2678         .none => unreachable,
   2679         .undef => unreachable,
   2680         .unreach => unreachable,
   2681         .dead => unreachable,
   2682         .cpsr_flags,
   2683         .register_c_flag,
   2684         .register_v_flag,
   2685         => unreachable, // cannot hold an address
   2686         .immediate => |imm| {
   2687             try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
   2688         },
   2689         .ptr_stack_offset => |off| {
   2690             try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
   2691         },
   2692         .register => |reg| {
   2693             const reg_lock = self.register_manager.lockReg(reg);
   2694             defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked);
   2695 
   2696             switch (dst_mcv) {
   2697                 .register => |dst_reg| {
   2698                     try self.genLdrRegister(dst_reg, reg, elem_ty);
   2699                 },
   2700                 .stack_offset => |off| {
   2701                     if (elem_size <= 4) {
   2702                         const tmp_reg = try self.register_manager.allocReg(null, gp);
   2703                         const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
   2704                         defer self.register_manager.unlockReg(tmp_reg_lock);
   2705 
   2706                         try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
   2707                         try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg });
   2708                     } else {
   2709                         // TODO optimize the register allocation
   2710                         const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
   2711                         const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
   2712                         defer for (regs_locks) |reg_locked| {
   2713                             self.register_manager.unlockReg(reg_locked);
   2714                         };
   2715 
   2716                         const src_reg = reg;
   2717                         const dst_reg = regs[0];
   2718                         const len_reg = regs[1];
   2719                         const count_reg = regs[2];
   2720                         const tmp_reg = regs[3];
   2721 
   2722                         // sub dst_reg, fp, #off
   2723                         try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
   2724 
   2725                         // mov len, #elem_size
   2726                         try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
   2727 
   2728                         // memcpy(src, dst, len)
   2729                         try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   2730                     }
   2731                 },
   2732                 else => unreachable, // attempting to load into non-register or non-stack MCValue
   2733             }
   2734         },
   2735         .memory,
   2736         .stack_offset,
   2737         .stack_argument_offset,
   2738         => {
   2739             const reg = try self.register_manager.allocReg(null, gp);
   2740             const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
   2741             defer self.register_manager.unlockReg(reg_lock);
   2742 
   2743             try self.genSetReg(ptr_ty, reg, ptr);
   2744             try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
   2745         },
   2746     }
   2747 }
   2748 
   2749 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
   2750     const pt = self.pt;
   2751     const zcu = pt.zcu;
   2752     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2753     const elem_ty = self.typeOfIndex(inst);
   2754     const result: MCValue = result: {
   2755         if (!elem_ty.hasRuntimeBits(zcu))
   2756             break :result MCValue.none;
   2757 
   2758         const ptr = try self.resolveInst(ty_op.operand);
   2759         const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(zcu);
   2760         if (self.liveness.isUnused(inst) and !is_volatile)
   2761             break :result MCValue.dead;
   2762 
   2763         const dest_mcv: MCValue = blk: {
   2764             const ptr_fits_dest = elem_ty.abiSize(zcu) <= 4;
   2765             if (ptr_fits_dest and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
   2766                 // The MCValue that holds the pointer can be re-used as the value.
   2767                 break :blk ptr;
   2768             } else {
   2769                 break :blk try self.allocRegOrMem(elem_ty, true, inst);
   2770             }
   2771         };
   2772         try self.load(dest_mcv, ptr, self.typeOf(ty_op.operand));
   2773 
   2774         break :result dest_mcv;
   2775     };
   2776     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2777 }
   2778 
   2779 fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
   2780     const pt = self.pt;
   2781     const elem_size: u32 = @intCast(value_ty.abiSize(pt.zcu));
   2782 
   2783     switch (ptr) {
   2784         .none => unreachable,
   2785         .undef => unreachable,
   2786         .unreach => unreachable,
   2787         .dead => unreachable,
   2788         .cpsr_flags,
   2789         .register_c_flag,
   2790         .register_v_flag,
   2791         => unreachable, // cannot hold an address
   2792         .immediate => |imm| {
   2793             try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
   2794         },
   2795         .ptr_stack_offset => |off| {
   2796             try self.genSetStack(value_ty, off, value);
   2797         },
   2798         .register => |addr_reg| {
   2799             const addr_reg_lock = self.register_manager.lockReg(addr_reg);
   2800             defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
   2801 
   2802             switch (value) {
   2803                 .dead => unreachable,
   2804                 .undef => {
   2805                     try self.genSetReg(value_ty, addr_reg, value);
   2806                 },
   2807                 .register => |value_reg| {
   2808                     try self.genStrRegister(value_reg, addr_reg, value_ty);
   2809                 },
   2810                 else => {
   2811                     if (elem_size <= 4) {
   2812                         const tmp_reg = try self.register_manager.allocReg(null, gp);
   2813                         const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
   2814                         defer self.register_manager.unlockReg(tmp_reg_lock);
   2815 
   2816                         try self.genSetReg(value_ty, tmp_reg, value);
   2817                         try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
   2818                     } else {
   2819                         const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
   2820                         const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
   2821                         defer for (regs_locks) |reg| {
   2822                             self.register_manager.unlockReg(reg);
   2823                         };
   2824 
   2825                         const src_reg = regs[0];
   2826                         const dst_reg = addr_reg;
   2827                         const len_reg = regs[1];
   2828                         const count_reg = regs[2];
   2829                         const tmp_reg = regs[3];
   2830 
   2831                         switch (value) {
   2832                             .stack_offset => |off| {
   2833                                 // sub src_reg, fp, #off
   2834                                 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
   2835                             },
   2836                             .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }),
   2837                             .stack_argument_offset => |off| {
   2838                                 _ = try self.addInst(.{
   2839                                     .tag = .ldr_ptr_stack_argument,
   2840                                     .data = .{ .r_stack_offset = .{
   2841                                         .rt = src_reg,
   2842                                         .stack_offset = off,
   2843                                     } },
   2844                                 });
   2845                             },
   2846                             else => return self.fail("TODO store {} to register", .{value}),
   2847                         }
   2848 
   2849                         // mov len, #elem_size
   2850                         try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
   2851 
   2852                         // memcpy(src, dst, len)
   2853                         try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   2854                     }
   2855                 },
   2856             }
   2857         },
   2858         .memory,
   2859         .stack_offset,
   2860         .stack_argument_offset,
   2861         => {
   2862             const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr);
   2863             try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty);
   2864         },
   2865     }
   2866 }
   2867 
   2868 fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
   2869     if (safety) {
   2870         // TODO if the value is undef, write 0xaa bytes to dest
   2871     } else {
   2872         // TODO if the value is undef, don't lower this instruction
   2873     }
   2874     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2875     const ptr = try self.resolveInst(bin_op.lhs);
   2876     const value = try self.resolveInst(bin_op.rhs);
   2877     const ptr_ty = self.typeOf(bin_op.lhs);
   2878     const value_ty = self.typeOf(bin_op.rhs);
   2879 
   2880     try self.store(ptr, value, ptr_ty, value_ty);
   2881 
   2882     return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   2883 }
   2884 
   2885 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
   2886     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2887     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   2888     const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
   2889     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
   2890 }
   2891 
   2892 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
   2893     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2894     const result = try self.structFieldPtr(inst, ty_op.operand, index);
   2895     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2896 }
   2897 
   2898 fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
   2899     return if (self.liveness.isUnused(inst)) .dead else result: {
   2900         const pt = self.pt;
   2901         const zcu = pt.zcu;
   2902         const mcv = try self.resolveInst(operand);
   2903         const ptr_ty = self.typeOf(operand);
   2904         const struct_ty = ptr_ty.childType(zcu);
   2905         const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(index, zcu));
   2906         switch (mcv) {
   2907             .ptr_stack_offset => |off| {
   2908                 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
   2909             },
   2910             else => {
   2911                 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
   2912                 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
   2913 
   2914                 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
   2915             },
   2916         }
   2917     };
   2918 }
   2919 
   2920 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
   2921     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2922     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   2923     const operand = extra.struct_operand;
   2924     const index = extra.field_index;
   2925     const pt = self.pt;
   2926     const zcu = pt.zcu;
   2927     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2928         const mcv = try self.resolveInst(operand);
   2929         const struct_ty = self.typeOf(operand);
   2930         const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(index, zcu));
   2931         const struct_field_ty = struct_ty.fieldType(index, zcu);
   2932 
   2933         switch (mcv) {
   2934             .dead, .unreach => unreachable,
   2935             .stack_argument_offset => |off| {
   2936                 break :result MCValue{ .stack_argument_offset = off + struct_field_offset };
   2937             },
   2938             .stack_offset => |off| {
   2939                 break :result MCValue{ .stack_offset = off - struct_field_offset };
   2940             },
   2941             .memory => |addr| {
   2942                 break :result MCValue{ .memory = addr + struct_field_offset };
   2943             },
   2944             .register_c_flag,
   2945             .register_v_flag,
   2946             => |reg| {
   2947                 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
   2948                 defer self.register_manager.unlockReg(reg_lock);
   2949 
   2950                 const field: MCValue = switch (index) {
   2951                     // get wrapped value: return register
   2952                     0 => MCValue{ .register = reg },
   2953 
   2954                     // get overflow bit: return C or V flag
   2955                     1 => MCValue{ .cpsr_flags = switch (mcv) {
   2956                         .register_c_flag => .cs,
   2957                         .register_v_flag => .vs,
   2958                         else => unreachable,
   2959                     } },
   2960 
   2961                     else => unreachable,
   2962                 };
   2963 
   2964                 if (self.reuseOperand(inst, operand, 0, field)) {
   2965                     break :result field;
   2966                 } else {
   2967                     // Copy to new register
   2968                     const dest_reg = try self.register_manager.allocReg(null, gp);
   2969                     try self.genSetReg(struct_field_ty, dest_reg, field);
   2970 
   2971                     break :result MCValue{ .register = dest_reg };
   2972                 }
   2973             },
   2974             .register => {
   2975                 var operand_reg: Register = undefined;
   2976                 var dest_reg: Register = undefined;
   2977 
   2978                 const read_args = [_]ReadArg{
   2979                     .{ .ty = struct_ty, .bind = .{ .mcv = mcv }, .class = gp, .reg = &operand_reg },
   2980                 };
   2981                 const write_args = [_]WriteArg{
   2982                     .{ .ty = struct_field_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   2983                 };
   2984                 try self.allocRegs(
   2985                     &read_args,
   2986                     &write_args,
   2987                     ReuseMetadata{
   2988                         .corresponding_inst = inst,
   2989                         .operand_mapping = &.{0},
   2990                     },
   2991                 );
   2992 
   2993                 const field_bit_offset = struct_field_offset * 8;
   2994                 const field_bit_size: u32 = @intCast(struct_field_ty.abiSize(zcu) * 8);
   2995 
   2996                 _ = try self.addInst(.{
   2997                     .tag = if (struct_field_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx,
   2998                     .data = .{ .rr_lsb_width = .{
   2999                         .rd = dest_reg,
   3000                         .rn = operand_reg,
   3001                         .lsb = @intCast(field_bit_offset),
   3002                         .width = @intCast(field_bit_size),
   3003                     } },
   3004                 });
   3005 
   3006                 break :result MCValue{ .register = dest_reg };
   3007             },
   3008             else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
   3009         }
   3010     };
   3011 
   3012     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
   3013 }
   3014 
   3015 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
   3016     const pt = self.pt;
   3017     const zcu = pt.zcu;
   3018     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3019     const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
   3020     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3021         const field_ptr = try self.resolveInst(extra.field_ptr);
   3022         const struct_ty = ty_pl.ty.toType().childType(zcu);
   3023 
   3024         if (struct_ty.zigTypeTag(zcu) == .Union) {
   3025             return self.fail("TODO implement @fieldParentPtr codegen for unions", .{});
   3026         }
   3027 
   3028         const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(extra.field_index, zcu));
   3029         switch (field_ptr) {
   3030             .ptr_stack_offset => |off| {
   3031                 break :result MCValue{ .ptr_stack_offset = off + struct_field_offset };
   3032             },
   3033             else => {
   3034                 const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr };
   3035                 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
   3036 
   3037                 break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
   3038             },
   3039         }
   3040     };
   3041     return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
   3042 }
   3043 
   3044 /// An argument to a Mir instruction which is read (and possibly also
   3045 /// written to) by the respective instruction
   3046 const ReadArg = struct {
   3047     ty: Type,
   3048     bind: Bind,
   3049     class: RegisterManager.RegisterBitSet,
   3050     reg: *Register,
   3051 
   3052     const Bind = union(enum) {
   3053         inst: Air.Inst.Ref,
   3054         mcv: MCValue,
   3055 
   3056         fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue {
   3057             return switch (bind) {
   3058                 .inst => |inst| try function.resolveInst(inst),
   3059                 .mcv => |mcv| mcv,
   3060             };
   3061         }
   3062 
   3063         fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u32 {
   3064             switch (bind) {
   3065                 .inst => |inst| {
   3066                     // TODO resolve independently of inst_table
   3067                     const mcv = try function.resolveInst(inst);
   3068                     switch (mcv) {
   3069                         .immediate => |imm| return imm,
   3070                         else => return null,
   3071                     }
   3072                 },
   3073                 .mcv => |mcv| {
   3074                     switch (mcv) {
   3075                         .immediate => |imm| return imm,
   3076                         else => return null,
   3077                     }
   3078                 },
   3079             }
   3080         }
   3081     };
   3082 };
   3083 
   3084 /// An argument to a Mir instruction which is written to (but not read
   3085 /// from) by the respective instruction
   3086 const WriteArg = struct {
   3087     ty: Type,
   3088     bind: Bind,
   3089     class: RegisterManager.RegisterBitSet,
   3090     reg: *Register,
   3091 
   3092     const Bind = union(enum) {
   3093         reg: Register,
   3094         none: void,
   3095     };
   3096 };
   3097 
   3098 /// Holds all data necessary for enabling the potential reuse of
   3099 /// operand registers as destinations
   3100 const ReuseMetadata = struct {
   3101     corresponding_inst: Air.Inst.Index,
   3102 
   3103     /// Maps every element index of read_args to the corresponding
   3104     /// index in the Air instruction
   3105     ///
   3106     /// When the order of read_args corresponds exactly to the order
   3107     /// of the inputs of the Air instruction, this would be e.g.
   3108     /// &.{ 0, 1 }. However, when the order is not the same or some
   3109     /// inputs to the Air instruction are omitted (e.g. when they can
   3110     /// be represented as immediates to the Mir instruction),
   3111     /// operand_mapping should reflect that fact.
   3112     operand_mapping: []const Liveness.OperandInt,
   3113 };
   3114 
   3115 /// Allocate a set of registers for use as arguments for a Mir
   3116 /// instruction
   3117 ///
   3118 /// If the Mir instruction these registers are allocated for
   3119 /// corresponds exactly to a single Air instruction, populate
   3120 /// reuse_metadata in order to enable potential reuse of an operand as
   3121 /// the destination (provided that that operand dies in this
   3122 /// instruction).
   3123 ///
   3124 /// Reusing an operand register as destination is the only time two
   3125 /// arguments may share the same register. In all other cases,
   3126 /// allocRegs guarantees that a register will never be allocated to
   3127 /// more than one argument.
   3128 ///
   3129 /// Furthermore, allocReg guarantees that all arguments which are
   3130 /// already bound to registers before calling allocRegs will not
   3131 /// change their register binding. This is done by locking these
   3132 /// registers.
   3133 fn allocRegs(
   3134     self: *Self,
   3135     read_args: []const ReadArg,
   3136     write_args: []const WriteArg,
   3137     reuse_metadata: ?ReuseMetadata,
   3138 ) InnerError!void {
   3139     // Air instructions have exactly one output
   3140     assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
   3141 
   3142     // The operand mapping is a 1:1 mapping of read args to their
   3143     // corresponding operand index in the Air instruction
   3144     assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above
   3145 
   3146     const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len);
   3147     defer self.gpa.free(locks);
   3148     const read_locks = locks[0..read_args.len];
   3149     const write_locks = locks[read_args.len..];
   3150 
   3151     @memset(locks, null);
   3152     defer for (locks) |lock| {
   3153         if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
   3154     };
   3155 
   3156     // When we reuse a read_arg as a destination, the corresponding
   3157     // MCValue of the read_arg will be set to .dead. In that case, we
   3158     // skip allocating this read_arg.
   3159     var reused_read_arg: ?usize = null;
   3160 
   3161     // Lock all args which are already allocated to registers
   3162     for (read_args, 0..) |arg, i| {
   3163         const mcv = try arg.bind.resolveToMcv(self);
   3164         if (mcv == .register) {
   3165             read_locks[i] = self.register_manager.lockReg(mcv.register);
   3166         }
   3167     }
   3168 
   3169     for (write_args, 0..) |arg, i| {
   3170         if (arg.bind == .reg) {
   3171             write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
   3172         }
   3173     }
   3174 
   3175     // Allocate registers for all args which aren't allocated to
   3176     // registers yet
   3177     for (read_args, 0..) |arg, i| {
   3178         const mcv = try arg.bind.resolveToMcv(self);
   3179         if (mcv == .register) {
   3180             arg.reg.* = mcv.register;
   3181         } else {
   3182             const track_inst: ?Air.Inst.Index = switch (arg.bind) {
   3183                 .inst => |inst| inst.toIndex().?,
   3184                 else => null,
   3185             };
   3186             arg.reg.* = try self.register_manager.allocReg(track_inst, arg.class);
   3187             read_locks[i] = self.register_manager.lockReg(arg.reg.*);
   3188         }
   3189     }
   3190 
   3191     if (reuse_metadata != null) {
   3192         const inst = reuse_metadata.?.corresponding_inst;
   3193         const operand_mapping = reuse_metadata.?.operand_mapping;
   3194         const arg = write_args[0];
   3195         if (arg.bind == .reg) {
   3196             arg.reg.* = arg.bind.reg;
   3197         } else {
   3198             reuse_operand: for (read_args, 0..) |read_arg, i| {
   3199                 if (read_arg.bind == .inst) {
   3200                     const operand = read_arg.bind.inst;
   3201                     const mcv = try self.resolveInst(operand);
   3202                     if (mcv == .register and
   3203                         std.meta.eql(arg.class, read_arg.class) and
   3204                         self.reuseOperand(inst, operand, operand_mapping[i], mcv))
   3205                     {
   3206                         arg.reg.* = mcv.register;
   3207                         write_locks[0] = null;
   3208                         reused_read_arg = i;
   3209                         break :reuse_operand;
   3210                     }
   3211                 }
   3212             } else {
   3213                 arg.reg.* = try self.register_manager.allocReg(inst, arg.class);
   3214                 write_locks[0] = self.register_manager.lockReg(arg.reg.*);
   3215             }
   3216         }
   3217     } else {
   3218         for (write_args, 0..) |arg, i| {
   3219             if (arg.bind == .reg) {
   3220                 arg.reg.* = arg.bind.reg;
   3221             } else {
   3222                 arg.reg.* = try self.register_manager.allocReg(null, arg.class);
   3223                 write_locks[i] = self.register_manager.lockReg(arg.reg.*);
   3224             }
   3225         }
   3226     }
   3227 
   3228     // For all read_args which need to be moved from non-register to
   3229     // register, perform the move
   3230     for (read_args, 0..) |arg, i| {
   3231         if (reused_read_arg) |j| {
   3232             // Check whether this read_arg was reused
   3233             if (i == j) continue;
   3234         }
   3235 
   3236         const mcv = try arg.bind.resolveToMcv(self);
   3237         if (mcv != .register) {
   3238             if (arg.bind == .inst) {
   3239                 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   3240                 const inst = arg.bind.inst.toIndex().?;
   3241 
   3242                 // Overwrite the MCValue associated with this inst
   3243                 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* });
   3244 
   3245                 // If the previous MCValue occupied some space we track, we
   3246                 // need to make sure it is marked as free now.
   3247                 switch (mcv) {
   3248                     .cpsr_flags => {
   3249                         assert(self.cpsr_flags_inst.? == inst);
   3250                         self.cpsr_flags_inst = null;
   3251                     },
   3252                     .register => |prev_reg| {
   3253                         assert(!self.register_manager.isRegFree(prev_reg));
   3254                         self.register_manager.freeReg(prev_reg);
   3255                     },
   3256                     else => {},
   3257                 }
   3258             }
   3259 
   3260             try self.genSetReg(arg.ty, arg.reg.*, mcv);
   3261         }
   3262     }
   3263 }
   3264 
   3265 /// Wrapper around allocRegs and addInst tailored for specific Mir
   3266 /// instructions which are binary operations acting on two registers
   3267 ///
   3268 /// Returns the destination register
   3269 fn binOpRegister(
   3270     self: *Self,
   3271     mir_tag: Mir.Inst.Tag,
   3272     lhs_bind: ReadArg.Bind,
   3273     rhs_bind: ReadArg.Bind,
   3274     lhs_ty: Type,
   3275     rhs_ty: Type,
   3276     maybe_inst: ?Air.Inst.Index,
   3277 ) !MCValue {
   3278     var lhs_reg: Register = undefined;
   3279     var rhs_reg: Register = undefined;
   3280     var dest_reg: Register = undefined;
   3281 
   3282     const read_args = [_]ReadArg{
   3283         .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   3284         .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
   3285     };
   3286     const write_args = [_]WriteArg{
   3287         .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   3288     };
   3289     try self.allocRegs(
   3290         &read_args,
   3291         &write_args,
   3292         if (maybe_inst) |inst| .{
   3293             .corresponding_inst = inst,
   3294             .operand_mapping = &.{ 0, 1 },
   3295         } else null,
   3296     );
   3297 
   3298     const mir_data: Mir.Inst.Data = switch (mir_tag) {
   3299         .add,
   3300         .adds,
   3301         .sub,
   3302         .subs,
   3303         .@"and",
   3304         .orr,
   3305         .eor,
   3306         => .{ .rr_op = .{
   3307             .rd = dest_reg,
   3308             .rn = lhs_reg,
   3309             .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
   3310         } },
   3311         .lsl,
   3312         .asr,
   3313         .lsr,
   3314         => .{ .rr_shift = .{
   3315             .rd = dest_reg,
   3316             .rm = lhs_reg,
   3317             .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
   3318         } },
   3319         .mul,
   3320         .smulbb,
   3321         => .{ .rrr = .{
   3322             .rd = dest_reg,
   3323             .rn = lhs_reg,
   3324             .rm = rhs_reg,
   3325         } },
   3326         else => unreachable,
   3327     };
   3328 
   3329     _ = try self.addInst(.{
   3330         .tag = mir_tag,
   3331         .data = mir_data,
   3332     });
   3333 
   3334     return MCValue{ .register = dest_reg };
   3335 }
   3336 
   3337 /// Wrapper around allocRegs and addInst tailored for specific Mir
   3338 /// instructions which are binary operations acting on a register and
   3339 /// an immediate
   3340 ///
   3341 /// Returns the destination register
   3342 fn binOpImmediate(
   3343     self: *Self,
   3344     mir_tag: Mir.Inst.Tag,
   3345     lhs_bind: ReadArg.Bind,
   3346     rhs_immediate: u32,
   3347     lhs_ty: Type,
   3348     lhs_and_rhs_swapped: bool,
   3349     maybe_inst: ?Air.Inst.Index,
   3350 ) !MCValue {
   3351     var lhs_reg: Register = undefined;
   3352     var dest_reg: Register = undefined;
   3353 
   3354     const read_args = [_]ReadArg{
   3355         .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   3356     };
   3357     const write_args = [_]WriteArg{
   3358         .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   3359     };
   3360     const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
   3361     try self.allocRegs(
   3362         &read_args,
   3363         &write_args,
   3364         if (maybe_inst) |inst| .{
   3365             .corresponding_inst = inst,
   3366             .operand_mapping = operand_mapping,
   3367         } else null,
   3368     );
   3369 
   3370     const mir_data: Mir.Inst.Data = switch (mir_tag) {
   3371         .add,
   3372         .adds,
   3373         .sub,
   3374         .subs,
   3375         .@"and",
   3376         .orr,
   3377         .eor,
   3378         => .{ .rr_op = .{
   3379             .rd = dest_reg,
   3380             .rn = lhs_reg,
   3381             .op = Instruction.Operand.fromU32(rhs_immediate).?,
   3382         } },
   3383         .lsl,
   3384         .asr,
   3385         .lsr,
   3386         => .{ .rr_shift = .{
   3387             .rd = dest_reg,
   3388             .rm = lhs_reg,
   3389             .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_immediate)),
   3390         } },
   3391         else => unreachable,
   3392     };
   3393 
   3394     _ = try self.addInst(.{
   3395         .tag = mir_tag,
   3396         .data = mir_data,
   3397     });
   3398 
   3399     return MCValue{ .register = dest_reg };
   3400 }
   3401 
   3402 fn addSub(
   3403     self: *Self,
   3404     tag: Air.Inst.Tag,
   3405     lhs_bind: ReadArg.Bind,
   3406     rhs_bind: ReadArg.Bind,
   3407     lhs_ty: Type,
   3408     rhs_ty: Type,
   3409     maybe_inst: ?Air.Inst.Index,
   3410 ) InnerError!MCValue {
   3411     const pt = self.pt;
   3412     const zcu = pt.zcu;
   3413     switch (lhs_ty.zigTypeTag(zcu)) {
   3414         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3415         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3416         .Int => {
   3417             assert(lhs_ty.eql(rhs_ty, zcu));
   3418             const int_info = lhs_ty.intInfo(zcu);
   3419             if (int_info.bits <= 32) {
   3420                 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
   3421                 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3422 
   3423                 // Only say yes if the operation is
   3424                 // commutative, i.e. we can swap both of the
   3425                 // operands
   3426                 const lhs_immediate_ok = switch (tag) {
   3427                     .add => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
   3428                     .sub => false,
   3429                     else => unreachable,
   3430                 };
   3431                 const rhs_immediate_ok = switch (tag) {
   3432                     .add,
   3433                     .sub,
   3434                     => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
   3435                     else => unreachable,
   3436                 };
   3437 
   3438                 const mir_tag: Mir.Inst.Tag = switch (tag) {
   3439                     .add => .add,
   3440                     .sub => .sub,
   3441                     else => unreachable,
   3442                 };
   3443 
   3444                 if (rhs_immediate_ok) {
   3445                     return try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
   3446                 } else if (lhs_immediate_ok) {
   3447                     // swap lhs and rhs
   3448                     return try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
   3449                 } else {
   3450                     return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
   3451                 }
   3452             } else {
   3453                 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   3454             }
   3455         },
   3456         else => unreachable,
   3457     }
   3458 }
   3459 
   3460 fn mul(
   3461     self: *Self,
   3462     lhs_bind: ReadArg.Bind,
   3463     rhs_bind: ReadArg.Bind,
   3464     lhs_ty: Type,
   3465     rhs_ty: Type,
   3466     maybe_inst: ?Air.Inst.Index,
   3467 ) InnerError!MCValue {
   3468     const pt = self.pt;
   3469     const zcu = pt.zcu;
   3470     switch (lhs_ty.zigTypeTag(zcu)) {
   3471         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3472         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3473         .Int => {
   3474             assert(lhs_ty.eql(rhs_ty, zcu));
   3475             const int_info = lhs_ty.intInfo(zcu);
   3476             if (int_info.bits <= 32) {
   3477                 // TODO add optimisations for multiplication
   3478                 // with immediates, for example a * 2 can be
   3479                 // lowered to a << 1
   3480                 return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
   3481             } else {
   3482                 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   3483             }
   3484         },
   3485         else => unreachable,
   3486     }
   3487 }
   3488 
   3489 fn divFloat(
   3490     self: *Self,
   3491     lhs_bind: ReadArg.Bind,
   3492     rhs_bind: ReadArg.Bind,
   3493     lhs_ty: Type,
   3494     rhs_ty: Type,
   3495     maybe_inst: ?Air.Inst.Index,
   3496 ) InnerError!MCValue {
   3497     _ = lhs_bind;
   3498     _ = rhs_bind;
   3499     _ = rhs_ty;
   3500     _ = maybe_inst;
   3501 
   3502     const pt = self.pt;
   3503     const zcu = pt.zcu;
   3504     switch (lhs_ty.zigTypeTag(zcu)) {
   3505         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3506         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3507         else => unreachable,
   3508     }
   3509 }
   3510 
   3511 fn divTrunc(
   3512     self: *Self,
   3513     lhs_bind: ReadArg.Bind,
   3514     rhs_bind: ReadArg.Bind,
   3515     lhs_ty: Type,
   3516     rhs_ty: Type,
   3517     maybe_inst: ?Air.Inst.Index,
   3518 ) InnerError!MCValue {
   3519     const pt = self.pt;
   3520     const zcu = pt.zcu;
   3521     switch (lhs_ty.zigTypeTag(zcu)) {
   3522         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3523         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3524         .Int => {
   3525             assert(lhs_ty.eql(rhs_ty, zcu));
   3526             const int_info = lhs_ty.intInfo(zcu);
   3527             if (int_info.bits <= 32) {
   3528                 switch (int_info.signedness) {
   3529                     .signed => {
   3530                         return self.fail("TODO ARM signed integer division", .{});
   3531                     },
   3532                     .unsigned => {
   3533                         const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3534 
   3535                         if (rhs_immediate) |imm| {
   3536                             if (std.math.isPowerOfTwo(imm)) {
   3537                                 const shift = std.math.log2_int(u32, imm);
   3538                                 return try self.binOpImmediate(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst);
   3539                             } else {
   3540                                 return self.fail("TODO ARM integer division by constants", .{});
   3541                             }
   3542                         } else {
   3543                             return self.fail("TODO ARM integer division", .{});
   3544                         }
   3545                     },
   3546                 }
   3547             } else {
   3548                 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
   3549             }
   3550         },
   3551         else => unreachable,
   3552     }
   3553 }
   3554 
   3555 fn divFloor(
   3556     self: *Self,
   3557     lhs_bind: ReadArg.Bind,
   3558     rhs_bind: ReadArg.Bind,
   3559     lhs_ty: Type,
   3560     rhs_ty: Type,
   3561     maybe_inst: ?Air.Inst.Index,
   3562 ) InnerError!MCValue {
   3563     const pt = self.pt;
   3564     const zcu = pt.zcu;
   3565     switch (lhs_ty.zigTypeTag(zcu)) {
   3566         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3567         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3568         .Int => {
   3569             assert(lhs_ty.eql(rhs_ty, zcu));
   3570             const int_info = lhs_ty.intInfo(zcu);
   3571             if (int_info.bits <= 32) {
   3572                 switch (int_info.signedness) {
   3573                     .signed => {
   3574                         return self.fail("TODO ARM signed integer division", .{});
   3575                     },
   3576                     .unsigned => {
   3577                         const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3578 
   3579                         if (rhs_immediate) |imm| {
   3580                             if (std.math.isPowerOfTwo(imm)) {
   3581                                 const shift = std.math.log2_int(u32, imm);
   3582                                 return try self.binOpImmediate(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst);
   3583                             } else {
   3584                                 return self.fail("TODO ARM integer division by constants", .{});
   3585                             }
   3586                         } else {
   3587                             return self.fail("TODO ARM integer division", .{});
   3588                         }
   3589                     },
   3590                 }
   3591             } else {
   3592                 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
   3593             }
   3594         },
   3595         else => unreachable,
   3596     }
   3597 }
   3598 
   3599 fn divExact(
   3600     self: *Self,
   3601     lhs_bind: ReadArg.Bind,
   3602     rhs_bind: ReadArg.Bind,
   3603     lhs_ty: Type,
   3604     rhs_ty: Type,
   3605     maybe_inst: ?Air.Inst.Index,
   3606 ) InnerError!MCValue {
   3607     _ = lhs_bind;
   3608     _ = rhs_bind;
   3609     _ = rhs_ty;
   3610     _ = maybe_inst;
   3611 
   3612     const pt = self.pt;
   3613     const zcu = pt.zcu;
   3614     switch (lhs_ty.zigTypeTag(zcu)) {
   3615         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3616         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3617         .Int => return self.fail("TODO ARM div_exact", .{}),
   3618         else => unreachable,
   3619     }
   3620 }
   3621 
   3622 fn rem(
   3623     self: *Self,
   3624     lhs_bind: ReadArg.Bind,
   3625     rhs_bind: ReadArg.Bind,
   3626     lhs_ty: Type,
   3627     rhs_ty: Type,
   3628     maybe_inst: ?Air.Inst.Index,
   3629 ) InnerError!MCValue {
   3630     const pt = self.pt;
   3631     const zcu = pt.zcu;
   3632     switch (lhs_ty.zigTypeTag(zcu)) {
   3633         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3634         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3635         .Int => {
   3636             assert(lhs_ty.eql(rhs_ty, zcu));
   3637             const int_info = lhs_ty.intInfo(zcu);
   3638             if (int_info.bits <= 32) {
   3639                 switch (int_info.signedness) {
   3640                     .signed => {
   3641                         return self.fail("TODO ARM signed integer zcu", .{});
   3642                     },
   3643                     .unsigned => {
   3644                         const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3645 
   3646                         if (rhs_immediate) |imm| {
   3647                             if (std.math.isPowerOfTwo(imm)) {
   3648                                 const log2 = std.math.log2_int(u32, imm);
   3649 
   3650                                 var lhs_reg: Register = undefined;
   3651                                 var dest_reg: Register = undefined;
   3652 
   3653                                 const read_args = [_]ReadArg{
   3654                                     .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
   3655                                 };
   3656                                 const write_args = [_]WriteArg{
   3657                                     .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
   3658                                 };
   3659                                 try self.allocRegs(
   3660                                     &read_args,
   3661                                     &write_args,
   3662                                     if (maybe_inst) |inst| .{
   3663                                         .corresponding_inst = inst,
   3664                                         .operand_mapping = &.{0},
   3665                                     } else null,
   3666                                 );
   3667 
   3668                                 try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2);
   3669 
   3670                                 return MCValue{ .register = dest_reg };
   3671                             } else {
   3672                                 return self.fail("TODO ARM integer zcu by constants", .{});
   3673                             }
   3674                         } else {
   3675                             return self.fail("TODO ARM integer zcu", .{});
   3676                         }
   3677                     },
   3678                 }
   3679             } else {
   3680                 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
   3681             }
   3682         },
   3683         else => unreachable,
   3684     }
   3685 }
   3686 
   3687 fn modulo(
   3688     self: *Self,
   3689     lhs_bind: ReadArg.Bind,
   3690     rhs_bind: ReadArg.Bind,
   3691     lhs_ty: Type,
   3692     rhs_ty: Type,
   3693     maybe_inst: ?Air.Inst.Index,
   3694 ) InnerError!MCValue {
   3695     _ = lhs_bind;
   3696     _ = rhs_bind;
   3697     _ = rhs_ty;
   3698     _ = maybe_inst;
   3699 
   3700     const pt = self.pt;
   3701     const zcu = pt.zcu;
   3702     switch (lhs_ty.zigTypeTag(zcu)) {
   3703         .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   3704         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3705         .Int => return self.fail("TODO ARM zcu", .{}),
   3706         else => unreachable,
   3707     }
   3708 }
   3709 
   3710 fn wrappingArithmetic(
   3711     self: *Self,
   3712     tag: Air.Inst.Tag,
   3713     lhs_bind: ReadArg.Bind,
   3714     rhs_bind: ReadArg.Bind,
   3715     lhs_ty: Type,
   3716     rhs_ty: Type,
   3717     maybe_inst: ?Air.Inst.Index,
   3718 ) InnerError!MCValue {
   3719     const pt = self.pt;
   3720     const zcu = pt.zcu;
   3721     switch (lhs_ty.zigTypeTag(zcu)) {
   3722         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3723         .Int => {
   3724             const int_info = lhs_ty.intInfo(zcu);
   3725             if (int_info.bits <= 32) {
   3726                 // Generate an add/sub/mul
   3727                 const result: MCValue = switch (tag) {
   3728                     .add_wrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
   3729                     .sub_wrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
   3730                     .mul_wrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
   3731                     else => unreachable,
   3732                 };
   3733 
   3734                 // Truncate if necessary
   3735                 const result_reg = result.register;
   3736                 if (int_info.bits < 32) {
   3737                     try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
   3738                 }
   3739 
   3740                 return result;
   3741             } else {
   3742                 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   3743             }
   3744         },
   3745         else => unreachable,
   3746     }
   3747 }
   3748 
   3749 fn bitwise(
   3750     self: *Self,
   3751     tag: Air.Inst.Tag,
   3752     lhs_bind: ReadArg.Bind,
   3753     rhs_bind: ReadArg.Bind,
   3754     lhs_ty: Type,
   3755     rhs_ty: Type,
   3756     maybe_inst: ?Air.Inst.Index,
   3757 ) InnerError!MCValue {
   3758     const pt = self.pt;
   3759     const zcu = pt.zcu;
   3760     switch (lhs_ty.zigTypeTag(zcu)) {
   3761         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3762         .Int => {
   3763             assert(lhs_ty.eql(rhs_ty, zcu));
   3764             const int_info = lhs_ty.intInfo(zcu);
   3765             if (int_info.bits <= 32) {
   3766                 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
   3767                 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3768 
   3769                 const lhs_immediate_ok = if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
   3770                 const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
   3771 
   3772                 const mir_tag: Mir.Inst.Tag = switch (tag) {
   3773                     .bit_and => .@"and",
   3774                     .bit_or => .orr,
   3775                     .xor => .eor,
   3776                     else => unreachable,
   3777                 };
   3778 
   3779                 if (rhs_immediate_ok) {
   3780                     return try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
   3781                 } else if (lhs_immediate_ok) {
   3782                     // swap lhs and rhs
   3783                     return try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
   3784                 } else {
   3785                     return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
   3786                 }
   3787             } else {
   3788                 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   3789             }
   3790         },
   3791         else => unreachable,
   3792     }
   3793 }
   3794 
   3795 fn shiftExact(
   3796     self: *Self,
   3797     tag: Air.Inst.Tag,
   3798     lhs_bind: ReadArg.Bind,
   3799     rhs_bind: ReadArg.Bind,
   3800     lhs_ty: Type,
   3801     rhs_ty: Type,
   3802     maybe_inst: ?Air.Inst.Index,
   3803 ) InnerError!MCValue {
   3804     const pt = self.pt;
   3805     const zcu = pt.zcu;
   3806     switch (lhs_ty.zigTypeTag(zcu)) {
   3807         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3808         .Int => {
   3809             const int_info = lhs_ty.intInfo(zcu);
   3810             if (int_info.bits <= 32) {
   3811                 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3812 
   3813                 const mir_tag: Mir.Inst.Tag = switch (tag) {
   3814                     .shl_exact => .lsl,
   3815                     .shr_exact => switch (lhs_ty.intInfo(zcu).signedness) {
   3816                         .signed => Mir.Inst.Tag.asr,
   3817                         .unsigned => Mir.Inst.Tag.lsr,
   3818                     },
   3819                     else => unreachable,
   3820                 };
   3821 
   3822                 if (rhs_immediate) |imm| {
   3823                     return try self.binOpImmediate(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
   3824                 } else {
   3825                     return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
   3826                 }
   3827             } else {
   3828                 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   3829             }
   3830         },
   3831         else => unreachable,
   3832     }
   3833 }
   3834 
   3835 fn shiftNormal(
   3836     self: *Self,
   3837     tag: Air.Inst.Tag,
   3838     lhs_bind: ReadArg.Bind,
   3839     rhs_bind: ReadArg.Bind,
   3840     lhs_ty: Type,
   3841     rhs_ty: Type,
   3842     maybe_inst: ?Air.Inst.Index,
   3843 ) InnerError!MCValue {
   3844     const pt = self.pt;
   3845     const zcu = pt.zcu;
   3846     switch (lhs_ty.zigTypeTag(zcu)) {
   3847         .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   3848         .Int => {
   3849             const int_info = lhs_ty.intInfo(zcu);
   3850             if (int_info.bits <= 32) {
   3851                 // Generate a shl_exact/shr_exact
   3852                 const result: MCValue = switch (tag) {
   3853                     .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
   3854                     .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
   3855                     else => unreachable,
   3856                 };
   3857 
   3858                 // Truncate if necessary
   3859                 switch (tag) {
   3860                     .shr => return result,
   3861                     .shl => {
   3862                         const result_reg = result.register;
   3863                         if (int_info.bits < 32) {
   3864                             try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
   3865                         }
   3866 
   3867                         return result;
   3868                     },
   3869                     else => unreachable,
   3870                 }
   3871             } else {
   3872                 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   3873             }
   3874         },
   3875         else => unreachable,
   3876     }
   3877 }
   3878 
   3879 fn booleanOp(
   3880     self: *Self,
   3881     tag: Air.Inst.Tag,
   3882     lhs_bind: ReadArg.Bind,
   3883     rhs_bind: ReadArg.Bind,
   3884     lhs_ty: Type,
   3885     rhs_ty: Type,
   3886     maybe_inst: ?Air.Inst.Index,
   3887 ) InnerError!MCValue {
   3888     const pt = self.pt;
   3889     const zcu = pt.zcu;
   3890     switch (lhs_ty.zigTypeTag(zcu)) {
   3891         .Bool => {
   3892             const lhs_immediate = try lhs_bind.resolveToImmediate(self);
   3893             const rhs_immediate = try rhs_bind.resolveToImmediate(self);
   3894 
   3895             const mir_tag: Mir.Inst.Tag = switch (tag) {
   3896                 .bool_and => .@"and",
   3897                 .bool_or => .orr,
   3898                 else => unreachable,
   3899             };
   3900 
   3901             if (rhs_immediate) |imm| {
   3902                 return try self.binOpImmediate(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
   3903             } else if (lhs_immediate) |imm| {
   3904                 // swap lhs and rhs
   3905                 return try self.binOpImmediate(mir_tag, rhs_bind, imm, rhs_ty, true, maybe_inst);
   3906             } else {
   3907                 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
   3908             }
   3909         },
   3910         else => unreachable,
   3911     }
   3912 }
   3913 
   3914 fn ptrArithmetic(
   3915     self: *Self,
   3916     tag: Air.Inst.Tag,
   3917     lhs_bind: ReadArg.Bind,
   3918     rhs_bind: ReadArg.Bind,
   3919     lhs_ty: Type,
   3920     rhs_ty: Type,
   3921     maybe_inst: ?Air.Inst.Index,
   3922 ) InnerError!MCValue {
   3923     const pt = self.pt;
   3924     const zcu = pt.zcu;
   3925     switch (lhs_ty.zigTypeTag(zcu)) {
   3926         .Pointer => {
   3927             assert(rhs_ty.eql(Type.usize, zcu));
   3928 
   3929             const ptr_ty = lhs_ty;
   3930             const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
   3931                 .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type
   3932                 else => ptr_ty.childType(zcu),
   3933             };
   3934             const elem_size: u32 = @intCast(elem_ty.abiSize(zcu));
   3935 
   3936             const base_tag: Air.Inst.Tag = switch (tag) {
   3937                 .ptr_add => .add,
   3938                 .ptr_sub => .sub,
   3939                 else => unreachable,
   3940             };
   3941 
   3942             if (elem_size == 1) {
   3943                 return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst);
   3944             } else {
   3945                 // convert the offset into a byte offset by
   3946                 // multiplying it with elem_size
   3947                 const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
   3948 
   3949                 const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
   3950                 const offset_bind = ReadArg.Bind{ .mcv = offset };
   3951 
   3952                 const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null);
   3953                 return addr;
   3954             }
   3955         },
   3956         else => unreachable,
   3957     }
   3958 }
   3959 
   3960 fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void {
   3961     const pt = self.pt;
   3962     const zcu = pt.zcu;
   3963     const abi_size = ty.abiSize(zcu);
   3964 
   3965     const tag: Mir.Inst.Tag = switch (abi_size) {
   3966         1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb else .ldrb,
   3967         2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh else .ldrh,
   3968         3, 4 => .ldr,
   3969         else => unreachable,
   3970     };
   3971 
   3972     const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
   3973         .rt = dest_reg,
   3974         .rn = addr_reg,
   3975         .offset = .{ .offset = Instruction.Offset.none },
   3976     } };
   3977     const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
   3978         .rt = dest_reg,
   3979         .rn = addr_reg,
   3980         .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
   3981     } };
   3982 
   3983     const data: Mir.Inst.Data = switch (abi_size) {
   3984         1 => if (ty.isSignedInt(zcu)) rr_extra_offset else rr_offset,
   3985         2 => rr_extra_offset,
   3986         3, 4 => rr_offset,
   3987         else => unreachable,
   3988     };
   3989 
   3990     _ = try self.addInst(.{
   3991         .tag = tag,
   3992         .data = data,
   3993     });
   3994 }
   3995 
   3996 fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void {
   3997     const pt = self.pt;
   3998     const abi_size = ty.abiSize(pt.zcu);
   3999 
   4000     const tag: Mir.Inst.Tag = switch (abi_size) {
   4001         1 => .strb,
   4002         2 => .strh,
   4003         4 => .str,
   4004         3 => return self.fail("TODO: genStrRegister for abi_size={}", .{abi_size}),
   4005         else => unreachable,
   4006     };
   4007 
   4008     const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
   4009         .rt = source_reg,
   4010         .rn = addr_reg,
   4011         .offset = .{ .offset = Instruction.Offset.none },
   4012     } };
   4013     const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
   4014         .rt = source_reg,
   4015         .rn = addr_reg,
   4016         .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
   4017     } };
   4018 
   4019     const data: Mir.Inst.Data = switch (abi_size) {
   4020         1, 4 => rr_offset,
   4021         2 => rr_extra_offset,
   4022         else => unreachable,
   4023     };
   4024 
   4025     _ = try self.addInst(.{
   4026         .tag = tag,
   4027         .data = data,
   4028     });
   4029 }
   4030 
   4031 fn genInlineMemcpy(
   4032     self: *Self,
   4033     src: Register,
   4034     dst: Register,
   4035     len: Register,
   4036     count: Register,
   4037     tmp: Register,
   4038 ) !void {
   4039     // mov count, #0
   4040     _ = try self.addInst(.{
   4041         .tag = .mov,
   4042         .data = .{ .r_op_mov = .{
   4043             .rd = count,
   4044             .op = Instruction.Operand.imm(0, 0),
   4045         } },
   4046     });
   4047 
   4048     // loop:
   4049     // cmp count, len
   4050     _ = try self.addInst(.{
   4051         .tag = .cmp,
   4052         .data = .{ .r_op_cmp = .{
   4053             .rn = count,
   4054             .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
   4055         } },
   4056     });
   4057 
   4058     // bge end
   4059     _ = try self.addInst(.{
   4060         .tag = .b,
   4061         .cond = .ge,
   4062         .data = .{ .inst = @intCast(self.mir_instructions.len + 5) },
   4063     });
   4064 
   4065     // ldrb tmp, [src, count]
   4066     _ = try self.addInst(.{
   4067         .tag = .ldrb,
   4068         .data = .{ .rr_offset = .{
   4069             .rt = tmp,
   4070             .rn = src,
   4071             .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
   4072         } },
   4073     });
   4074 
   4075     // strb tmp, [src, count]
   4076     _ = try self.addInst(.{
   4077         .tag = .strb,
   4078         .data = .{ .rr_offset = .{
   4079             .rt = tmp,
   4080             .rn = dst,
   4081             .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
   4082         } },
   4083     });
   4084 
   4085     // add count, count, #1
   4086     _ = try self.addInst(.{
   4087         .tag = .add,
   4088         .data = .{ .rr_op = .{
   4089             .rd = count,
   4090             .rn = count,
   4091             .op = Instruction.Operand.imm(1, 0),
   4092         } },
   4093     });
   4094 
   4095     // b loop
   4096     _ = try self.addInst(.{
   4097         .tag = .b,
   4098         .data = .{ .inst = @intCast(self.mir_instructions.len - 5) },
   4099     });
   4100 
   4101     // end:
   4102 }
   4103 
   4104 fn genInlineMemset(
   4105     self: *Self,
   4106     dst: MCValue,
   4107     val: MCValue,
   4108     len: MCValue,
   4109 ) !void {
   4110     const dst_reg = switch (dst) {
   4111         .register => |r| r,
   4112         else => try self.copyToTmpRegister(Type.manyptr_u8, dst),
   4113     };
   4114     const dst_reg_lock = self.register_manager.lockReg(dst_reg);
   4115     defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
   4116 
   4117     const val_reg = switch (val) {
   4118         .register => |r| r,
   4119         else => try self.copyToTmpRegister(Type.u8, val),
   4120     };
   4121     const val_reg_lock = self.register_manager.lockReg(val_reg);
   4122     defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock);
   4123 
   4124     const len_reg = switch (len) {
   4125         .register => |r| r,
   4126         else => try self.copyToTmpRegister(Type.usize, len),
   4127     };
   4128     const len_reg_lock = self.register_manager.lockReg(len_reg);
   4129     defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock);
   4130 
   4131     const count_reg = try self.register_manager.allocReg(null, gp);
   4132 
   4133     try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg);
   4134 }
   4135 
   4136 fn genInlineMemsetCode(
   4137     self: *Self,
   4138     dst: Register,
   4139     val: Register,
   4140     len: Register,
   4141     count: Register,
   4142 ) !void {
   4143     // mov count, #0
   4144     _ = try self.addInst(.{
   4145         .tag = .mov,
   4146         .data = .{ .r_op_mov = .{
   4147             .rd = count,
   4148             .op = Instruction.Operand.imm(0, 0),
   4149         } },
   4150     });
   4151 
   4152     // loop:
   4153     // cmp count, len
   4154     _ = try self.addInst(.{
   4155         .tag = .cmp,
   4156         .data = .{ .r_op_cmp = .{
   4157             .rn = count,
   4158             .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
   4159         } },
   4160     });
   4161 
   4162     // bge end
   4163     _ = try self.addInst(.{
   4164         .tag = .b,
   4165         .cond = .ge,
   4166         .data = .{ .inst = @intCast(self.mir_instructions.len + 4) },
   4167     });
   4168 
   4169     // strb val, [src, count]
   4170     _ = try self.addInst(.{
   4171         .tag = .strb,
   4172         .data = .{ .rr_offset = .{
   4173             .rt = val,
   4174             .rn = dst,
   4175             .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
   4176         } },
   4177     });
   4178 
   4179     // add count, count, #1
   4180     _ = try self.addInst(.{
   4181         .tag = .add,
   4182         .data = .{ .rr_op = .{
   4183             .rd = count,
   4184             .rn = count,
   4185             .op = Instruction.Operand.imm(1, 0),
   4186         } },
   4187     });
   4188 
   4189     // b loop
   4190     _ = try self.addInst(.{
   4191         .tag = .b,
   4192         .data = .{ .inst = @intCast(self.mir_instructions.len - 4) },
   4193     });
   4194 
   4195     // end:
   4196 }
   4197 
   4198 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
   4199     // skip zero-bit arguments as they don't have a corresponding arg instruction
   4200     var arg_index = self.arg_index;
   4201     while (self.args[arg_index] == .none) arg_index += 1;
   4202     self.arg_index = arg_index + 1;
   4203 
   4204     const ty = self.typeOfIndex(inst);
   4205     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
   4206 
   4207     const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
   4208     if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{
   4209         .tag = tag,
   4210         .ty = ty,
   4211         .name = name.toSlice(self.air),
   4212         .mcv = self.args[arg_index],
   4213     });
   4214 
   4215     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
   4216     return self.finishAir(inst, result, .{ .none, .none, .none });
   4217 }
   4218 
   4219 fn airTrap(self: *Self) !void {
   4220     _ = try self.addInst(.{
   4221         .tag = .undefined_instruction,
   4222         .data = .{ .nop = {} },
   4223     });
   4224     return self.finishAirBookkeeping();
   4225 }
   4226 
   4227 fn airBreakpoint(self: *Self) !void {
   4228     _ = try self.addInst(.{
   4229         .tag = .bkpt,
   4230         .data = .{ .imm16 = 0 },
   4231     });
   4232     return self.finishAirBookkeeping();
   4233 }
   4234 
   4235 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
   4236     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for arm", .{});
   4237     return self.finishAir(inst, result, .{ .none, .none, .none });
   4238 }
   4239 
   4240 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
   4241     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for arm", .{});
   4242     return self.finishAir(inst, result, .{ .none, .none, .none });
   4243 }
   4244 
   4245 fn airFence(self: *Self) !void {
   4246     return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
   4247     //return self.finishAirBookkeeping();
   4248 }
   4249 
   4250 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
   4251     if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{});
   4252     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   4253     const callee = pl_op.operand;
   4254     const extra = self.air.extraData(Air.Call, pl_op.payload);
   4255     const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]);
   4256     const ty = self.typeOf(callee);
   4257     const pt = self.pt;
   4258     const zcu = pt.zcu;
   4259     const ip = &zcu.intern_pool;
   4260 
   4261     const fn_ty = switch (ty.zigTypeTag(zcu)) {
   4262         .Fn => ty,
   4263         .Pointer => ty.childType(zcu),
   4264         else => unreachable,
   4265     };
   4266 
   4267     var info = try self.resolveCallingConventionValues(fn_ty);
   4268     defer info.deinit(self);
   4269 
   4270     // According to the Procedure Call Standard for the ARM
   4271     // Architecture, compare flags are not preserved across
   4272     // calls. Therefore, if some value is currently stored there, we
   4273     // need to save it.
   4274     try self.spillCompareFlagsIfOccupied();
   4275 
   4276     // Save caller-saved registers, but crucially *after* we save the
   4277     // compare flags as saving compare flags may require a new
   4278     // caller-saved register
   4279     for (caller_preserved_regs) |reg| {
   4280         try self.register_manager.getReg(reg, null);
   4281     }
   4282 
   4283     // If returning by reference, r0 will contain the address of where
   4284     // to put the result into. In that case, make sure that r0 remains
   4285     // untouched by the parameter passing code
   4286     const r0_lock: ?RegisterLock = if (info.return_value == .stack_offset) blk: {
   4287         log.debug("airCall: return by reference", .{});
   4288         const ret_ty = fn_ty.fnReturnType(zcu);
   4289         const ret_abi_size: u32 = @intCast(ret_ty.abiSize(zcu));
   4290         const ret_abi_align = ret_ty.abiAlignment(zcu);
   4291         const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst);
   4292 
   4293         const ptr_ty = try pt.singleMutPtrType(ret_ty);
   4294         try self.register_manager.getReg(.r0, null);
   4295         try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });
   4296 
   4297         info.return_value = .{ .stack_offset = stack_offset };
   4298 
   4299         break :blk self.register_manager.lockRegAssumeUnused(.r0);
   4300     } else null;
   4301     defer if (r0_lock) |reg| self.register_manager.unlockReg(reg);
   4302 
   4303     // Make space for the arguments passed via the stack
   4304     self.max_end_stack += info.stack_byte_count;
   4305 
   4306     for (info.args, 0..) |mc_arg, arg_i| {
   4307         const arg = args[arg_i];
   4308         const arg_ty = self.typeOf(arg);
   4309         const arg_mcv = try self.resolveInst(args[arg_i]);
   4310 
   4311         switch (mc_arg) {
   4312             .none => continue,
   4313             .register => |reg| {
   4314                 try self.register_manager.getReg(reg, null);
   4315                 try self.genSetReg(arg_ty, reg, arg_mcv);
   4316             },
   4317             .stack_offset => unreachable,
   4318             .stack_argument_offset => |offset| try self.genSetStackArgument(
   4319                 arg_ty,
   4320                 offset,
   4321                 arg_mcv,
   4322             ),
   4323             else => unreachable,
   4324         }
   4325     }
   4326 
   4327     // Due to incremental compilation, how function calls are generated depends
   4328     // on linking.
   4329     if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
   4330         .func => {
   4331             return self.fail("TODO implement calling functions", .{});
   4332         },
   4333         .@"extern" => {
   4334             return self.fail("TODO implement calling extern functions", .{});
   4335         },
   4336         else => {
   4337             return self.fail("TODO implement calling bitcasted functions", .{});
   4338         },
   4339     } else {
   4340         assert(ty.zigTypeTag(zcu) == .Pointer);
   4341         const mcv = try self.resolveInst(callee);
   4342 
   4343         try self.genSetReg(Type.usize, .lr, mcv);
   4344     }
   4345 
   4346     // TODO: add Instruction.supportedOn
   4347     // function for ARM
   4348     if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
   4349         _ = try self.addInst(.{
   4350             .tag = .blx,
   4351             .data = .{ .reg = .lr },
   4352         });
   4353     } else {
   4354         return self.fail("TODO fix blx emulation for ARM <v5", .{});
   4355         // _ = try self.addInst(.{
   4356         //     .tag = .mov,
   4357         //     .data = .{ .rr_op = .{
   4358         //         .rd = .lr,
   4359         //         .rn = .r0,
   4360         //         .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
   4361         //     } },
   4362         // });
   4363         // _ = try self.addInst(.{
   4364         //     .tag = .bx,
   4365         //     .data = .{ .reg = .lr },
   4366         // });
   4367     }
   4368 
   4369     const result: MCValue = result: {
   4370         switch (info.return_value) {
   4371             .register => |reg| {
   4372                 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
   4373                     // Save function return value into a tracked register
   4374                     log.debug("airCall: copying {} as it is not tracked", .{reg});
   4375                     const new_reg = try self.copyToTmpRegister(fn_ty.fnReturnType(zcu), info.return_value);
   4376                     break :result MCValue{ .register = new_reg };
   4377                 }
   4378             },
   4379             else => {},
   4380         }
   4381         break :result info.return_value;
   4382     };
   4383 
   4384     if (args.len <= Liveness.bpi - 2) {
   4385         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   4386         buf[0] = callee;
   4387         @memcpy(buf[1..][0..args.len], args);
   4388         return self.finishAir(inst, result, buf);
   4389     }
   4390     var bt = try self.iterateBigTomb(inst, 1 + args.len);
   4391     bt.feed(callee);
   4392     for (args) |arg| {
   4393         bt.feed(arg);
   4394     }
   4395     return bt.finishAir(result);
   4396 }
   4397 
   4398 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
   4399     const pt = self.pt;
   4400     const zcu = pt.zcu;
   4401     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4402     const operand = try self.resolveInst(un_op);
   4403     const ret_ty = self.fn_type.fnReturnType(zcu);
   4404 
   4405     switch (self.ret_mcv) {
   4406         .none => {},
   4407         .immediate => {
   4408             assert(ret_ty.isError(zcu));
   4409         },
   4410         .register => |reg| {
   4411             // Return result by value
   4412             try self.genSetReg(ret_ty, reg, operand);
   4413         },
   4414         .stack_offset => {
   4415             // Return result by reference
   4416             //
   4417             // self.ret_mcv is an address to where this function
   4418             // should store its result into
   4419             const ptr_ty = try pt.singleMutPtrType(ret_ty);
   4420             try self.store(self.ret_mcv, operand, ptr_ty, ret_ty);
   4421         },
   4422         else => unreachable, // invalid return result
   4423     }
   4424 
   4425     // Just add space for an instruction, patch this later
   4426     try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
   4427 
   4428     return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   4429 }
   4430 
   4431 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
   4432     const pt = self.pt;
   4433     const zcu = pt.zcu;
   4434     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4435     const ptr = try self.resolveInst(un_op);
   4436     const ptr_ty = self.typeOf(un_op);
   4437     const ret_ty = self.fn_type.fnReturnType(zcu);
   4438 
   4439     switch (self.ret_mcv) {
   4440         .none => {},
   4441         .register => {
   4442             // Return result by value
   4443             try self.load(self.ret_mcv, ptr, ptr_ty);
   4444         },
   4445         .stack_offset => {
   4446             // Return result by reference
   4447             //
   4448             // self.ret_mcv is an address to where this function
   4449             // should store its result into
   4450             //
   4451             // If the operand is a ret_ptr instruction, we are done
   4452             // here. Else we need to load the result from the location
   4453             // pointed to by the operand and store it to the result
   4454             // location.
   4455             const op_inst = un_op.toIndex().?;
   4456             if (self.air.instructions.items(.tag)[@intFromEnum(op_inst)] != .ret_ptr) {
   4457                 const abi_size: u32 = @intCast(ret_ty.abiSize(zcu));
   4458                 const abi_align = ret_ty.abiAlignment(zcu);
   4459 
   4460                 const offset = try self.allocMem(abi_size, abi_align, null);
   4461 
   4462                 const tmp_mcv = MCValue{ .stack_offset = offset };
   4463                 try self.load(tmp_mcv, ptr, ptr_ty);
   4464                 try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty);
   4465             }
   4466         },
   4467         else => unreachable, // invalid return result
   4468     }
   4469 
   4470     // Just add space for an instruction, patch this later
   4471     try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
   4472 
   4473     return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   4474 }
   4475 
   4476 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
   4477     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4478     const lhs_ty = self.typeOf(bin_op.lhs);
   4479 
   4480     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
   4481         break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
   4482     };
   4483 
   4484     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   4485 }
   4486 
   4487 fn cmp(
   4488     self: *Self,
   4489     lhs: ReadArg.Bind,
   4490     rhs: ReadArg.Bind,
   4491     lhs_ty: Type,
   4492     op: math.CompareOperator,
   4493 ) !MCValue {
   4494     const pt = self.pt;
   4495     const zcu = pt.zcu;
   4496     const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
   4497         .Optional => blk: {
   4498             const payload_ty = lhs_ty.optionalChild(zcu);
   4499             if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   4500                 break :blk Type.u1;
   4501             } else if (lhs_ty.isPtrLikeOptional(zcu)) {
   4502                 break :blk Type.usize;
   4503             } else {
   4504                 return self.fail("TODO ARM cmp non-pointer optionals", .{});
   4505             }
   4506         },
   4507         .Float => return self.fail("TODO ARM cmp floats", .{}),
   4508         .Enum => lhs_ty.intTagType(zcu),
   4509         .Int => lhs_ty,
   4510         .Bool => Type.u1,
   4511         .Pointer => Type.usize,
   4512         .ErrorSet => Type.u16,
   4513         else => unreachable,
   4514     };
   4515 
   4516     const int_info = int_ty.intInfo(zcu);
   4517     if (int_info.bits <= 32) {
   4518         try self.spillCompareFlagsIfOccupied();
   4519 
   4520         var lhs_reg: Register = undefined;
   4521         var rhs_reg: Register = undefined;
   4522 
   4523         const rhs_immediate = try rhs.resolveToImmediate(self);
   4524         const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
   4525 
   4526         if (rhs_immediate_ok) {
   4527             const read_args = [_]ReadArg{
   4528                 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
   4529             };
   4530             try self.allocRegs(
   4531                 &read_args,
   4532                 &.{},
   4533                 null, // we won't be able to reuse a register as there are no write_regs
   4534             );
   4535 
   4536             _ = try self.addInst(.{
   4537                 .tag = .cmp,
   4538                 .data = .{ .r_op_cmp = .{
   4539                     .rn = lhs_reg,
   4540                     .op = Instruction.Operand.fromU32(rhs_immediate.?).?,
   4541                 } },
   4542             });
   4543         } else {
   4544             const read_args = [_]ReadArg{
   4545                 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
   4546                 .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg },
   4547             };
   4548             try self.allocRegs(
   4549                 &read_args,
   4550                 &.{},
   4551                 null, // we won't be able to reuse a register as there are no write_regs
   4552             );
   4553 
   4554             _ = try self.addInst(.{
   4555                 .tag = .cmp,
   4556                 .data = .{ .r_op_cmp = .{
   4557                     .rn = lhs_reg,
   4558                     .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
   4559                 } },
   4560             });
   4561         }
   4562 
   4563         return switch (int_info.signedness) {
   4564             .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) },
   4565             .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) },
   4566         };
   4567     } else {
   4568         return self.fail("TODO ARM cmp for ints > 32 bits", .{});
   4569     }
   4570 }
   4571 
   4572 fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
   4573     _ = inst;
   4574     return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
   4575 }
   4576 
   4577 fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
   4578     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4579     const operand = try self.resolveInst(un_op);
   4580     _ = operand;
   4581     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
   4582     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4583 }
   4584 
   4585 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
   4586     const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   4587 
   4588     _ = try self.addInst(.{
   4589         .tag = .dbg_line,
   4590         .cond = undefined,
   4591         .data = .{ .dbg_line_column = .{
   4592             .line = dbg_stmt.line,
   4593             .column = dbg_stmt.column,
   4594         } },
   4595     });
   4596 
   4597     return self.finishAirBookkeeping();
   4598 }
   4599 
   4600 fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
   4601     const pt = self.pt;
   4602     const zcu = pt.zcu;
   4603     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4604     const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
   4605     const func = zcu.funcInfo(extra.data.func);
   4606     // TODO emit debug info for function change
   4607     _ = func;
   4608     try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
   4609 }
   4610 
   4611 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
   4612     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   4613     const operand = pl_op.operand;
   4614     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
   4615     const ty = self.typeOf(operand);
   4616     const mcv = try self.resolveInst(operand);
   4617     const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
   4618 
   4619     log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
   4620 
   4621     try self.dbg_info_relocs.append(self.gpa, .{
   4622         .tag = tag,
   4623         .ty = ty,
   4624         .name = name.toSlice(self.air),
   4625         .mcv = mcv,
   4626     });
   4627 
   4628     return self.finishAir(inst, .dead, .{ operand, .none, .none });
   4629 }
   4630 
   4631 /// Given a boolean condition, emit a jump that is taken when that
   4632 /// condition is false.
   4633 fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
   4634     const condition_code: Condition = switch (condition) {
   4635         .cpsr_flags => |cond| cond.negate(),
   4636         else => blk: {
   4637             const reg = switch (condition) {
   4638                 .register => |r| r,
   4639                 else => try self.copyToTmpRegister(Type.bool, condition),
   4640             };
   4641 
   4642             try self.spillCompareFlagsIfOccupied();
   4643 
   4644             // cmp reg, 1
   4645             // bne ...
   4646             _ = try self.addInst(.{
   4647                 .tag = .cmp,
   4648                 .data = .{ .r_op_cmp = .{
   4649                     .rn = reg,
   4650                     .op = Instruction.Operand.imm(1, 0),
   4651                 } },
   4652             });
   4653 
   4654             break :blk .ne;
   4655         },
   4656     };
   4657 
   4658     return try self.addInst(.{
   4659         .tag = .b,
   4660         .cond = condition_code,
   4661         .data = .{ .inst = undefined }, // populated later through performReloc
   4662     });
   4663 }
   4664 
   4665 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
   4666     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   4667     const cond_inst = try self.resolveInst(pl_op.operand);
   4668     const extra = self.air.extraData(Air.CondBr, pl_op.payload);
   4669     const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]);
   4670     const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);
   4671     const liveness_condbr = self.liveness.getCondBr(inst);
   4672 
   4673     const reloc: Mir.Inst.Index = try self.condBr(cond_inst);
   4674 
   4675     // If the condition dies here in this condbr instruction, process
   4676     // that death now instead of later as this has an effect on
   4677     // whether it needs to be spilled in the branches
   4678     if (self.liveness.operandDies(inst, 0)) {
   4679         if (pl_op.operand.toIndex()) |op_index| {
   4680             self.processDeath(op_index);
   4681         }
   4682     }
   4683 
   4684     // Capture the state of register and stack allocation state so that we can revert to it.
   4685     const parent_next_stack_offset = self.next_stack_offset;
   4686     const parent_free_registers = self.register_manager.free_registers;
   4687     var parent_stack = try self.stack.clone(self.gpa);
   4688     defer parent_stack.deinit(self.gpa);
   4689     const parent_registers = self.register_manager.registers;
   4690     const parent_cpsr_flags_inst = self.cpsr_flags_inst;
   4691 
   4692     try self.branch_stack.append(.{});
   4693     errdefer {
   4694         _ = self.branch_stack.pop();
   4695     }
   4696 
   4697     try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
   4698     for (liveness_condbr.then_deaths) |operand| {
   4699         self.processDeath(operand);
   4700     }
   4701     try self.genBody(then_body);
   4702 
   4703     // Revert to the previous register and stack allocation state.
   4704 
   4705     var saved_then_branch = self.branch_stack.pop();
   4706     defer saved_then_branch.deinit(self.gpa);
   4707 
   4708     self.register_manager.registers = parent_registers;
   4709     self.cpsr_flags_inst = parent_cpsr_flags_inst;
   4710 
   4711     self.stack.deinit(self.gpa);
   4712     self.stack = parent_stack;
   4713     parent_stack = .{};
   4714 
   4715     self.next_stack_offset = parent_next_stack_offset;
   4716     self.register_manager.free_registers = parent_free_registers;
   4717 
   4718     try self.performReloc(reloc);
   4719     const else_branch = self.branch_stack.addOneAssumeCapacity();
   4720     else_branch.* = .{};
   4721 
   4722     try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
   4723     for (liveness_condbr.else_deaths) |operand| {
   4724         self.processDeath(operand);
   4725     }
   4726     try self.genBody(else_body);
   4727 
   4728     // At this point, each branch will possibly have conflicting values for where
   4729     // each instruction is stored. They agree, however, on which instructions are alive/dead.
   4730     // We use the first ("then") branch as canonical, and here emit
   4731     // instructions into the second ("else") branch to make it conform.
   4732     // We continue respect the data structure semantic guarantees of the else_branch so
   4733     // that we can use all the code emitting abstractions. This is why at the bottom we
   4734     // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
   4735     // rather than assigning it.
   4736     const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
   4737     try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
   4738 
   4739     const else_slice = else_branch.inst_table.entries.slice();
   4740     const else_keys = else_slice.items(.key);
   4741     const else_values = else_slice.items(.value);
   4742     for (else_keys, 0..) |else_key, else_idx| {
   4743         const else_value = else_values[else_idx];
   4744         const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
   4745             // The instruction's MCValue is overridden in both branches.
   4746             parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
   4747             if (else_value == .dead) {
   4748                 assert(then_entry.value == .dead);
   4749                 continue;
   4750             }
   4751             break :blk then_entry.value;
   4752         } else blk: {
   4753             if (else_value == .dead)
   4754                 continue;
   4755             // The instruction is only overridden in the else branch.
   4756             var i: usize = self.branch_stack.items.len - 1;
   4757             while (true) {
   4758                 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
   4759                 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
   4760                     assert(mcv != .dead);
   4761                     break :blk mcv;
   4762                 }
   4763             }
   4764         };
   4765         log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
   4766         // TODO make sure the destination stack offset / register does not already have something
   4767         // going on there.
   4768         try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value);
   4769         // TODO track the new register / stack allocation
   4770     }
   4771     try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
   4772     const then_slice = saved_then_branch.inst_table.entries.slice();
   4773     const then_keys = then_slice.items(.key);
   4774     const then_values = then_slice.items(.value);
   4775     for (then_keys, 0..) |then_key, then_idx| {
   4776         const then_value = then_values[then_idx];
   4777         // We already deleted the items from this table that matched the else_branch.
   4778         // So these are all instructions that are only overridden in the then branch.
   4779         parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
   4780         if (then_value == .dead)
   4781             continue;
   4782         const parent_mcv = blk: {
   4783             var i: usize = self.branch_stack.items.len - 1;
   4784             while (true) {
   4785                 i -= 1;
   4786                 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
   4787                     assert(mcv != .dead);
   4788                     break :blk mcv;
   4789                 }
   4790             }
   4791         };
   4792         log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
   4793         // TODO make sure the destination stack offset / register does not already have something
   4794         // going on there.
   4795         try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value);
   4796         // TODO track the new register / stack allocation
   4797     }
   4798 
   4799     {
   4800         var item = self.branch_stack.pop();
   4801         item.deinit(self.gpa);
   4802     }
   4803 
   4804     // We already took care of pl_op.operand earlier, so we're going
   4805     // to pass .none here
   4806     return self.finishAir(inst, .unreach, .{ .none, .none, .none });
   4807 }
   4808 
   4809 fn isNull(
   4810     self: *Self,
   4811     operand_bind: ReadArg.Bind,
   4812     operand_ty: Type,
   4813 ) !MCValue {
   4814     const pt = self.pt;
   4815     const zcu = pt.zcu;
   4816     if (operand_ty.isPtrLikeOptional(zcu)) {
   4817         assert(operand_ty.abiSize(zcu) == 4);
   4818 
   4819         const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } };
   4820         return self.cmp(operand_bind, imm_bind, Type.usize, .eq);
   4821     } else {
   4822         return self.fail("TODO implement non-pointer optionals", .{});
   4823     }
   4824 }
   4825 
   4826 fn isNonNull(
   4827     self: *Self,
   4828     operand_bind: ReadArg.Bind,
   4829     operand_ty: Type,
   4830 ) !MCValue {
   4831     const is_null_result = try self.isNull(operand_bind, operand_ty);
   4832     assert(is_null_result.cpsr_flags == .eq);
   4833 
   4834     return MCValue{ .cpsr_flags = .ne };
   4835 }
   4836 
   4837 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
   4838     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4839     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4840         const operand_bind: ReadArg.Bind = .{ .inst = un_op };
   4841         const operand_ty = self.typeOf(un_op);
   4842 
   4843         break :result try self.isNull(operand_bind, operand_ty);
   4844     };
   4845     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4846 }
   4847 
   4848 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   4849     const pt = self.pt;
   4850     const zcu = pt.zcu;
   4851     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4852     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4853         const operand_ptr = try self.resolveInst(un_op);
   4854         const ptr_ty = self.typeOf(un_op);
   4855         const elem_ty = ptr_ty.childType(zcu);
   4856 
   4857         const operand = try self.allocRegOrMem(elem_ty, true, null);
   4858         try self.load(operand, operand_ptr, ptr_ty);
   4859 
   4860         break :result try self.isNull(.{ .mcv = operand }, elem_ty);
   4861     };
   4862     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4863 }
   4864 
   4865 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
   4866     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4867     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4868         const operand_bind: ReadArg.Bind = .{ .inst = un_op };
   4869         const operand_ty = self.typeOf(un_op);
   4870 
   4871         break :result try self.isNonNull(operand_bind, operand_ty);
   4872     };
   4873     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4874 }
   4875 
   4876 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   4877     const pt = self.pt;
   4878     const zcu = pt.zcu;
   4879     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4880     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4881         const operand_ptr = try self.resolveInst(un_op);
   4882         const ptr_ty = self.typeOf(un_op);
   4883         const elem_ty = ptr_ty.childType(zcu);
   4884 
   4885         const operand = try self.allocRegOrMem(elem_ty, true, null);
   4886         try self.load(operand, operand_ptr, ptr_ty);
   4887 
   4888         break :result try self.isNonNull(.{ .mcv = operand }, elem_ty);
   4889     };
   4890     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4891 }
   4892 
   4893 fn isErr(
   4894     self: *Self,
   4895     error_union_bind: ReadArg.Bind,
   4896     error_union_ty: Type,
   4897 ) !MCValue {
   4898     const pt = self.pt;
   4899     const zcu = pt.zcu;
   4900     const error_type = error_union_ty.errorUnionSet(zcu);
   4901 
   4902     if (error_type.errorSetIsEmpty(zcu)) {
   4903         return MCValue{ .immediate = 0 }; // always false
   4904     }
   4905 
   4906     const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null);
   4907     return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
   4908 }
   4909 
   4910 fn isNonErr(
   4911     self: *Self,
   4912     error_union_bind: ReadArg.Bind,
   4913     error_union_ty: Type,
   4914 ) !MCValue {
   4915     const is_err_result = try self.isErr(error_union_bind, error_union_ty);
   4916     switch (is_err_result) {
   4917         .cpsr_flags => |cond| {
   4918             assert(cond == .hi);
   4919             return MCValue{ .cpsr_flags = cond.negate() };
   4920         },
   4921         .immediate => |imm| {
   4922             assert(imm == 0);
   4923             return MCValue{ .immediate = 1 };
   4924         },
   4925         else => unreachable,
   4926     }
   4927 }
   4928 
   4929 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
   4930     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4931     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4932         const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
   4933         const error_union_ty = self.typeOf(un_op);
   4934 
   4935         break :result try self.isErr(error_union_bind, error_union_ty);
   4936     };
   4937     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4938 }
   4939 
   4940 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   4941     const pt = self.pt;
   4942     const zcu = pt.zcu;
   4943     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4944     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4945         const operand_ptr = try self.resolveInst(un_op);
   4946         const ptr_ty = self.typeOf(un_op);
   4947         const elem_ty = ptr_ty.childType(zcu);
   4948 
   4949         const operand = try self.allocRegOrMem(elem_ty, true, null);
   4950         try self.load(operand, operand_ptr, ptr_ty);
   4951 
   4952         break :result try self.isErr(.{ .mcv = operand }, elem_ty);
   4953     };
   4954     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4955 }
   4956 
   4957 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
   4958     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4959     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4960         const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
   4961         const error_union_ty = self.typeOf(un_op);
   4962 
   4963         break :result try self.isNonErr(error_union_bind, error_union_ty);
   4964     };
   4965     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4966 }
   4967 
   4968 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   4969     const pt = self.pt;
   4970     const zcu = pt.zcu;
   4971     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4972     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   4973         const operand_ptr = try self.resolveInst(un_op);
   4974         const ptr_ty = self.typeOf(un_op);
   4975         const elem_ty = ptr_ty.childType(zcu);
   4976 
   4977         const operand = try self.allocRegOrMem(elem_ty, true, null);
   4978         try self.load(operand, operand_ptr, ptr_ty);
   4979 
   4980         break :result try self.isNonErr(.{ .mcv = operand }, elem_ty);
   4981     };
   4982     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4983 }
   4984 
   4985 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
   4986     // A loop is a setup to be able to jump back to the beginning.
   4987     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4988     const loop = self.air.extraData(Air.Block, ty_pl.payload);
   4989     const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]);
   4990     const start_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
   4991 
   4992     try self.genBody(body);
   4993     try self.jump(start_index);
   4994 
   4995     return self.finishAirBookkeeping();
   4996 }
   4997 
   4998 /// Send control flow to `inst`.
   4999 fn jump(self: *Self, inst: Mir.Inst.Index) !void {
   5000     _ = try self.addInst(.{
   5001         .tag = .b,
   5002         .data = .{ .inst = inst },
   5003     });
   5004 }
   5005 
   5006 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
   5007     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5008     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   5009     try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
   5010 }
   5011 
   5012 fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
   5013     try self.blocks.putNoClobber(self.gpa, inst, .{
   5014         // A block is a setup to be able to jump to the end.
   5015         .relocs = .{},
   5016         // It also acts as a receptacle for break operands.
   5017         // Here we use `MCValue.none` to represent a null value so that the first
   5018         // break instruction will choose a MCValue for the block result and overwrite
   5019         // this field. Following break instructions will use that MCValue to put their
   5020         // block results.
   5021         .mcv = MCValue{ .none = {} },
   5022     });
   5023     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
   5024 
   5025     // TODO emit debug info lexical block
   5026     try self.genBody(body);
   5027 
   5028     // relocations for `br` instructions
   5029     const relocs = &self.blocks.getPtr(inst).?.relocs;
   5030     if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
   5031         // If the last Mir instruction is the last relocation (which
   5032         // would just jump one instruction further), it can be safely
   5033         // removed
   5034         self.mir_instructions.orderedRemove(relocs.pop());
   5035     }
   5036     for (relocs.items) |reloc| {
   5037         try self.performReloc(reloc);
   5038     }
   5039 
   5040     const result = self.blocks.getPtr(inst).?.mcv;
   5041     return self.finishAir(inst, result, .{ .none, .none, .none });
   5042 }
   5043 
   5044 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
   5045     const switch_br = self.air.unwrapSwitch(inst);
   5046     const condition_ty = self.typeOf(switch_br.operand);
   5047     const liveness = try self.liveness.getSwitchBr(
   5048         self.gpa,
   5049         inst,
   5050         switch_br.cases_len + 1,
   5051     );
   5052     defer self.gpa.free(liveness.deaths);
   5053 
   5054     var it = switch_br.iterateCases();
   5055     while (it.next()) |case| {
   5056         // For every item, we compare it to condition and branch into
   5057         // the prong if they are equal. After we compared to all
   5058         // items, we branch into the next prong (or if no other prongs
   5059         // exist out of the switch statement).
   5060         //
   5061         //             cmp condition, item1
   5062         //             beq prong
   5063         //             cmp condition, item2
   5064         //             beq prong
   5065         //             cmp condition, item3
   5066         //             beq prong
   5067         //             b out
   5068         // prong:      ...
   5069         //             ...
   5070         // out:        ...
   5071         const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len);
   5072         defer self.gpa.free(branch_into_prong_relocs);
   5073 
   5074         for (case.items, 0..) |item, idx| {
   5075             const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq);
   5076             branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
   5077         }
   5078 
   5079         const branch_away_from_prong_reloc = try self.addInst(.{
   5080             .tag = .b,
   5081             .data = .{ .inst = undefined }, // populated later through performReloc
   5082         });
   5083 
   5084         for (branch_into_prong_relocs) |reloc| {
   5085             try self.performReloc(reloc);
   5086         }
   5087 
   5088         // Capture the state of register and stack allocation state so that we can revert to it.
   5089         const parent_next_stack_offset = self.next_stack_offset;
   5090         const parent_free_registers = self.register_manager.free_registers;
   5091         const parent_cpsr_flags_inst = self.cpsr_flags_inst;
   5092         var parent_stack = try self.stack.clone(self.gpa);
   5093         defer parent_stack.deinit(self.gpa);
   5094         const parent_registers = self.register_manager.registers;
   5095 
   5096         try self.branch_stack.append(.{});
   5097         errdefer {
   5098             _ = self.branch_stack.pop();
   5099         }
   5100 
   5101         try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len);
   5102         for (liveness.deaths[case.idx]) |operand| {
   5103             self.processDeath(operand);
   5104         }
   5105         try self.genBody(case.body);
   5106 
   5107         // Revert to the previous register and stack allocation state.
   5108         var saved_case_branch = self.branch_stack.pop();
   5109         defer saved_case_branch.deinit(self.gpa);
   5110 
   5111         self.register_manager.registers = parent_registers;
   5112         self.cpsr_flags_inst = parent_cpsr_flags_inst;
   5113         self.stack.deinit(self.gpa);
   5114         self.stack = parent_stack;
   5115         parent_stack = .{};
   5116 
   5117         self.next_stack_offset = parent_next_stack_offset;
   5118         self.register_manager.free_registers = parent_free_registers;
   5119 
   5120         try self.performReloc(branch_away_from_prong_reloc);
   5121     }
   5122 
   5123     if (switch_br.else_body_len > 0) {
   5124         const else_body = it.elseBody();
   5125 
   5126         // Capture the state of register and stack allocation state so that we can revert to it.
   5127         const parent_next_stack_offset = self.next_stack_offset;
   5128         const parent_free_registers = self.register_manager.free_registers;
   5129         const parent_cpsr_flags_inst = self.cpsr_flags_inst;
   5130         var parent_stack = try self.stack.clone(self.gpa);
   5131         defer parent_stack.deinit(self.gpa);
   5132         const parent_registers = self.register_manager.registers;
   5133 
   5134         try self.branch_stack.append(.{});
   5135         errdefer {
   5136             _ = self.branch_stack.pop();
   5137         }
   5138 
   5139         const else_deaths = liveness.deaths.len - 1;
   5140         try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
   5141         for (liveness.deaths[else_deaths]) |operand| {
   5142             self.processDeath(operand);
   5143         }
   5144         try self.genBody(else_body);
   5145 
   5146         // Revert to the previous register and stack allocation state.
   5147         var saved_case_branch = self.branch_stack.pop();
   5148         defer saved_case_branch.deinit(self.gpa);
   5149 
   5150         self.register_manager.registers = parent_registers;
   5151         self.cpsr_flags_inst = parent_cpsr_flags_inst;
   5152         self.stack.deinit(self.gpa);
   5153         self.stack = parent_stack;
   5154         parent_stack = .{};
   5155 
   5156         self.next_stack_offset = parent_next_stack_offset;
   5157         self.register_manager.free_registers = parent_free_registers;
   5158 
   5159         // TODO consolidate returned MCValues between prongs and else branch like we do
   5160         // in airCondBr.
   5161     }
   5162 
   5163     return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none });
   5164 }
   5165 
   5166 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
   5167     const tag = self.mir_instructions.items(.tag)[inst];
   5168     switch (tag) {
   5169         .b => self.mir_instructions.items(.data)[inst].inst = @intCast(self.mir_instructions.len),
   5170         else => unreachable,
   5171     }
   5172 }
   5173 
   5174 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
   5175     const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
   5176     try self.br(branch.block_inst, branch.operand);
   5177     return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
   5178 }
   5179 
   5180 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
   5181     const zcu = self.pt.zcu;
   5182     const block_data = self.blocks.getPtr(block).?;
   5183 
   5184     if (self.typeOf(operand).hasRuntimeBits(zcu)) {
   5185         const operand_mcv = try self.resolveInst(operand);
   5186         const block_mcv = block_data.mcv;
   5187         if (block_mcv == .none) {
   5188             block_data.mcv = switch (operand_mcv) {
   5189                 .none, .dead, .unreach => unreachable,
   5190                 .register, .stack_offset, .memory => operand_mcv,
   5191                 .immediate, .stack_argument_offset, .cpsr_flags => blk: {
   5192                     const new_mcv = try self.allocRegOrMem(self.typeOfIndex(block), true, block);
   5193                     try self.setRegOrMem(self.typeOfIndex(block), new_mcv, operand_mcv);
   5194                     break :blk new_mcv;
   5195                 },
   5196                 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
   5197             };
   5198         } else {
   5199             try self.setRegOrMem(self.typeOfIndex(block), block_mcv, operand_mcv);
   5200         }
   5201     }
   5202     return self.brVoid(block);
   5203 }
   5204 
   5205 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
   5206     const block_data = self.blocks.getPtr(block).?;
   5207 
   5208     // Emit a jump with a relocation. It will be patched up after the block ends.
   5209     try block_data.relocs.append(self.gpa, try self.addInst(.{
   5210         .tag = .b,
   5211         .data = .{ .inst = undefined }, // populated later through performReloc
   5212     }));
   5213 }
   5214 
   5215 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
   5216     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5217     const extra = self.air.extraData(Air.Asm, ty_pl.payload);
   5218     const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
   5219     const clobbers_len: u31 = @truncate(extra.data.flags);
   5220     var extra_i: usize = extra.end;
   5221     const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]);
   5222     extra_i += outputs.len;
   5223     const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]);
   5224     extra_i += inputs.len;
   5225 
   5226     const dead = !is_volatile and self.liveness.isUnused(inst);
   5227     const result: MCValue = if (dead) .dead else result: {
   5228         if (outputs.len > 1) {
   5229             return self.fail("TODO implement codegen for asm with more than 1 output", .{});
   5230         }
   5231 
   5232         const output_constraint: ?[]const u8 = for (outputs) |output| {
   5233             if (output != .none) {
   5234                 return self.fail("TODO implement codegen for non-expr asm", .{});
   5235             }
   5236             const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
   5237             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   5238             const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5239             // This equation accounts for the fact that even if we have exactly 4 bytes
   5240             // for the string, we still use the next u32 for the null terminator.
   5241             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5242 
   5243             break constraint;
   5244         } else null;
   5245 
   5246         for (inputs) |input| {
   5247             const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
   5248             const constraint = std.mem.sliceTo(input_bytes, 0);
   5249             const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
   5250             // This equation accounts for the fact that even if we have exactly 4 bytes
   5251             // for the string, we still use the next u32 for the null terminator.
   5252             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5253 
   5254             if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
   5255                 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
   5256             }
   5257             const reg_name = constraint[1 .. constraint.len - 1];
   5258             const reg = parseRegName(reg_name) orelse
   5259                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   5260 
   5261             const arg_mcv = try self.resolveInst(input);
   5262             try self.register_manager.getReg(reg, null);
   5263             try self.genSetReg(self.typeOf(input), reg, arg_mcv);
   5264         }
   5265 
   5266         {
   5267             var clobber_i: u32 = 0;
   5268             while (clobber_i < clobbers_len) : (clobber_i += 1) {
   5269                 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   5270                 // This equation accounts for the fact that even if we have exactly 4 bytes
   5271                 // for the string, we still use the next u32 for the null terminator.
   5272                 extra_i += clobber.len / 4 + 1;
   5273 
   5274                 // TODO honor these
   5275             }
   5276         }
   5277 
   5278         const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
   5279 
   5280         if (mem.eql(u8, asm_source, "svc #0")) {
   5281             _ = try self.addInst(.{
   5282                 .tag = .svc,
   5283                 .data = .{ .imm24 = 0 },
   5284             });
   5285         } else {
   5286             return self.fail("TODO implement support for more arm assembly instructions", .{});
   5287         }
   5288 
   5289         if (output_constraint) |output| {
   5290             if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
   5291                 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
   5292             }
   5293             const reg_name = output[2 .. output.len - 1];
   5294             const reg = parseRegName(reg_name) orelse
   5295                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   5296 
   5297             break :result MCValue{ .register = reg };
   5298         } else {
   5299             break :result MCValue{ .none = {} };
   5300         }
   5301     };
   5302 
   5303     simple: {
   5304         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   5305         var buf_index: usize = 0;
   5306         for (outputs) |output| {
   5307             if (output == .none) continue;
   5308 
   5309             if (buf_index >= buf.len) break :simple;
   5310             buf[buf_index] = output;
   5311             buf_index += 1;
   5312         }
   5313         if (buf_index + inputs.len > buf.len) break :simple;
   5314         @memcpy(buf[buf_index..][0..inputs.len], inputs);
   5315         return self.finishAir(inst, result, buf);
   5316     }
   5317     var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
   5318     for (outputs) |output| {
   5319         if (output == .none) continue;
   5320 
   5321         bt.feed(output);
   5322     }
   5323     for (inputs) |input| {
   5324         bt.feed(input);
   5325     }
   5326     return bt.finishAir(result);
   5327 }
   5328 
   5329 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
   5330     try self.ensureProcessDeathCapacity(operand_count + 1);
   5331     return BigTomb{
   5332         .function = self,
   5333         .inst = inst,
   5334         .lbt = self.liveness.iterateBigTomb(inst),
   5335     };
   5336 }
   5337 
   5338 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
   5339 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
   5340     switch (loc) {
   5341         .none => return,
   5342         .register => |reg| return self.genSetReg(ty, reg, val),
   5343         .stack_offset => |off| return self.genSetStack(ty, off, val),
   5344         .memory => {
   5345             return self.fail("TODO implement setRegOrMem for memory", .{});
   5346         },
   5347         else => unreachable,
   5348     }
   5349 }
   5350 
   5351 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
   5352     const pt = self.pt;
   5353     const zcu = pt.zcu;
   5354     const abi_size: u32 = @intCast(ty.abiSize(zcu));
   5355     switch (mcv) {
   5356         .dead => unreachable,
   5357         .unreach, .none => return, // Nothing to do.
   5358         .undef => {
   5359             if (!self.wantSafety())
   5360                 return; // The already existing value will do just fine.
   5361             // TODO Upgrade this to a memset call when we have that available.
   5362             switch (abi_size) {
   5363                 1 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
   5364                 2 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
   5365                 4 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
   5366                 else => try self.genInlineMemset(
   5367                     .{ .ptr_stack_offset = stack_offset },
   5368                     .{ .immediate = 0xaa },
   5369                     .{ .immediate = abi_size },
   5370                 ),
   5371             }
   5372         },
   5373         .cpsr_flags,
   5374         .immediate,
   5375         .ptr_stack_offset,
   5376         => {
   5377             const reg = try self.copyToTmpRegister(ty, mcv);
   5378             return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
   5379         },
   5380         .register => |reg| {
   5381             switch (abi_size) {
   5382                 1, 4 => {
   5383                     const offset = if (math.cast(u12, stack_offset)) |imm| blk: {
   5384                         break :blk Instruction.Offset.imm(imm);
   5385                     } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }), .none);
   5386 
   5387                     const tag: Mir.Inst.Tag = switch (abi_size) {
   5388                         1 => .strb,
   5389                         4 => .str,
   5390                         else => unreachable,
   5391                     };
   5392 
   5393                     _ = try self.addInst(.{
   5394                         .tag = tag,
   5395                         .data = .{ .rr_offset = .{
   5396                             .rt = reg,
   5397                             .rn = .fp,
   5398                             .offset = .{
   5399                                 .offset = offset,
   5400                                 .positive = false,
   5401                             },
   5402                         } },
   5403                     });
   5404                 },
   5405                 2 => {
   5406                     const offset = if (stack_offset <= math.maxInt(u8)) blk: {
   5407                         break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(stack_offset));
   5408                     } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }));
   5409 
   5410                     _ = try self.addInst(.{
   5411                         .tag = .strh,
   5412                         .data = .{ .rr_extra_offset = .{
   5413                             .rt = reg,
   5414                             .rn = .fp,
   5415                             .offset = .{
   5416                                 .offset = offset,
   5417                                 .positive = false,
   5418                             },
   5419                         } },
   5420                     });
   5421                 },
   5422                 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
   5423             }
   5424         },
   5425         .register_c_flag,
   5426         .register_v_flag,
   5427         => |reg| {
   5428             const reg_lock = self.register_manager.lockReg(reg);
   5429             defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
   5430 
   5431             const wrapped_ty = ty.fieldType(0, zcu);
   5432             try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg });
   5433 
   5434             const overflow_bit_ty = ty.fieldType(1, zcu);
   5435             const overflow_bit_offset: u32 = @intCast(ty.structFieldOffset(1, zcu));
   5436             const cond_reg = try self.register_manager.allocReg(null, gp);
   5437 
   5438             // C flag: movcs reg, #1
   5439             // V flag: movvs reg, #1
   5440             _ = try self.addInst(.{
   5441                 .tag = .mov,
   5442                 .cond = switch (mcv) {
   5443                     .register_c_flag => .cs,
   5444                     .register_v_flag => .vs,
   5445                     else => unreachable,
   5446                 },
   5447                 .data = .{ .r_op_mov = .{
   5448                     .rd = cond_reg,
   5449                     .op = Instruction.Operand.fromU32(1).?,
   5450                 } },
   5451             });
   5452 
   5453             try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
   5454                 .register = cond_reg,
   5455             });
   5456         },
   5457         .memory,
   5458         .stack_argument_offset,
   5459         .stack_offset,
   5460         => {
   5461             switch (mcv) {
   5462                 .stack_offset => |off| {
   5463                     if (stack_offset == off)
   5464                         return; // Copy stack variable to itself; nothing to do.
   5465                 },
   5466                 else => {},
   5467             }
   5468 
   5469             if (abi_size <= 4) {
   5470                 const reg = try self.copyToTmpRegister(ty, mcv);
   5471                 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
   5472             } else {
   5473                 const ptr_ty = try pt.singleMutPtrType(ty);
   5474 
   5475                 // TODO call extern memcpy
   5476                 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
   5477                 const src_reg = regs[0];
   5478                 const dst_reg = regs[1];
   5479                 const len_reg = regs[2];
   5480                 const count_reg = regs[3];
   5481                 const tmp_reg = regs[4];
   5482 
   5483                 switch (mcv) {
   5484                     .stack_offset => |off| {
   5485                         // sub src_reg, fp, #off
   5486                         try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
   5487                     },
   5488                     .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }),
   5489                     .stack_argument_offset => |off| {
   5490                         _ = try self.addInst(.{
   5491                             .tag = .ldr_ptr_stack_argument,
   5492                             .data = .{ .r_stack_offset = .{
   5493                                 .rt = src_reg,
   5494                                 .stack_offset = off,
   5495                             } },
   5496                         });
   5497                     },
   5498                     else => unreachable,
   5499                 }
   5500 
   5501                 // sub dst_reg, fp, #stack_offset
   5502                 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });
   5503 
   5504                 // mov len, #abi_size
   5505                 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
   5506 
   5507                 // memcpy(src, dst, len)
   5508                 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   5509             }
   5510         },
   5511     }
   5512 }
   5513 
   5514 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
   5515     const pt = self.pt;
   5516     const zcu = pt.zcu;
   5517     switch (mcv) {
   5518         .dead => unreachable,
   5519         .unreach, .none => return, // Nothing to do.
   5520         .undef => {
   5521             if (!self.wantSafety())
   5522                 return; // The already existing value will do just fine.
   5523             // Write the debug undefined value.
   5524             return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa });
   5525         },
   5526         .ptr_stack_offset => |off| {
   5527             // TODO: maybe addressing from sp instead of fp
   5528             const op = Instruction.Operand.fromU32(off) orelse
   5529                 return self.fail("TODO larger stack offsets", .{});
   5530 
   5531             _ = try self.addInst(.{
   5532                 .tag = .sub,
   5533                 .data = .{ .rr_op = .{
   5534                     .rd = reg,
   5535                     .rn = .fp,
   5536                     .op = op,
   5537                 } },
   5538             });
   5539         },
   5540         .cpsr_flags => |condition| {
   5541             const zero = Instruction.Operand.imm(0, 0);
   5542             const one = Instruction.Operand.imm(1, 0);
   5543 
   5544             // mov reg, 0
   5545             _ = try self.addInst(.{
   5546                 .tag = .mov,
   5547                 .data = .{ .r_op_mov = .{
   5548                     .rd = reg,
   5549                     .op = zero,
   5550                 } },
   5551             });
   5552 
   5553             // moveq reg, 1
   5554             _ = try self.addInst(.{
   5555                 .tag = .mov,
   5556                 .cond = condition,
   5557                 .data = .{ .r_op_mov = .{
   5558                     .rd = reg,
   5559                     .op = one,
   5560                 } },
   5561             });
   5562         },
   5563         .immediate => |x| {
   5564             if (Instruction.Operand.fromU32(x)) |op| {
   5565                 _ = try self.addInst(.{
   5566                     .tag = .mov,
   5567                     .data = .{ .r_op_mov = .{
   5568                         .rd = reg,
   5569                         .op = op,
   5570                     } },
   5571                 });
   5572             } else if (Instruction.Operand.fromU32(~x)) |op| {
   5573                 _ = try self.addInst(.{
   5574                     .tag = .mvn,
   5575                     .data = .{ .r_op_mov = .{
   5576                         .rd = reg,
   5577                         .op = op,
   5578                     } },
   5579                 });
   5580             } else if (x <= math.maxInt(u16)) {
   5581                 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
   5582                     _ = try self.addInst(.{
   5583                         .tag = .movw,
   5584                         .data = .{ .r_imm16 = .{
   5585                             .rd = reg,
   5586                             .imm16 = @intCast(x),
   5587                         } },
   5588                     });
   5589                 } else {
   5590                     _ = try self.addInst(.{
   5591                         .tag = .mov,
   5592                         .data = .{ .r_op_mov = .{
   5593                             .rd = reg,
   5594                             .op = Instruction.Operand.imm(@truncate(x), 0),
   5595                         } },
   5596                     });
   5597                     _ = try self.addInst(.{
   5598                         .tag = .orr,
   5599                         .data = .{ .rr_op = .{
   5600                             .rd = reg,
   5601                             .rn = reg,
   5602                             .op = Instruction.Operand.imm(@truncate(x >> 8), 12),
   5603                         } },
   5604                     });
   5605                 }
   5606             } else {
   5607                 // TODO write constant to code and load
   5608                 // relative to pc
   5609                 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
   5610                     // immediate: 0xaaaabbbb
   5611                     // movw reg, #0xbbbb
   5612                     // movt reg, #0xaaaa
   5613                     _ = try self.addInst(.{
   5614                         .tag = .movw,
   5615                         .data = .{ .r_imm16 = .{
   5616                             .rd = reg,
   5617                             .imm16 = @truncate(x),
   5618                         } },
   5619                     });
   5620                     _ = try self.addInst(.{
   5621                         .tag = .movt,
   5622                         .data = .{ .r_imm16 = .{
   5623                             .rd = reg,
   5624                             .imm16 = @truncate(x >> 16),
   5625                         } },
   5626                     });
   5627                 } else {
   5628                     // immediate: 0xaabbccdd
   5629                     // mov reg, #0xaa
   5630                     // orr reg, reg, #0xbb, 24
   5631                     // orr reg, reg, #0xcc, 16
   5632                     // orr reg, reg, #0xdd, 8
   5633                     _ = try self.addInst(.{
   5634                         .tag = .mov,
   5635                         .data = .{ .r_op_mov = .{
   5636                             .rd = reg,
   5637                             .op = Instruction.Operand.imm(@truncate(x), 0),
   5638                         } },
   5639                     });
   5640                     _ = try self.addInst(.{
   5641                         .tag = .orr,
   5642                         .data = .{ .rr_op = .{
   5643                             .rd = reg,
   5644                             .rn = reg,
   5645                             .op = Instruction.Operand.imm(@truncate(x >> 8), 12),
   5646                         } },
   5647                     });
   5648                     _ = try self.addInst(.{
   5649                         .tag = .orr,
   5650                         .data = .{ .rr_op = .{
   5651                             .rd = reg,
   5652                             .rn = reg,
   5653                             .op = Instruction.Operand.imm(@truncate(x >> 16), 8),
   5654                         } },
   5655                     });
   5656                     _ = try self.addInst(.{
   5657                         .tag = .orr,
   5658                         .data = .{ .rr_op = .{
   5659                             .rd = reg,
   5660                             .rn = reg,
   5661                             .op = Instruction.Operand.imm(@truncate(x >> 24), 4),
   5662                         } },
   5663                     });
   5664                 }
   5665             }
   5666         },
   5667         .register => |src_reg| {
   5668             // If the registers are the same, nothing to do.
   5669             if (src_reg.id() == reg.id())
   5670                 return;
   5671 
   5672             // mov reg, src_reg
   5673             _ = try self.addInst(.{
   5674                 .tag = .mov,
   5675                 .data = .{ .r_op_mov = .{
   5676                     .rd = reg,
   5677                     .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),
   5678                 } },
   5679             });
   5680         },
   5681         .register_c_flag => unreachable, // doesn't fit into a register
   5682         .register_v_flag => unreachable, // doesn't fit into a register
   5683         .memory => |addr| {
   5684             // The value is in memory at a hard-coded address.
   5685             // If the type is a pointer, it means the pointer address is at this memory location.
   5686             try self.genSetReg(ty, reg, .{ .immediate = @intCast(addr) });
   5687             try self.genLdrRegister(reg, reg, ty);
   5688         },
   5689         .stack_offset => |off| {
   5690             // TODO: maybe addressing from sp instead of fp
   5691             const abi_size: u32 = @intCast(ty.abiSize(zcu));
   5692 
   5693             const tag: Mir.Inst.Tag = switch (abi_size) {
   5694                 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb else .ldrb,
   5695                 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh else .ldrh,
   5696                 3, 4 => .ldr,
   5697                 else => unreachable,
   5698             };
   5699 
   5700             const extra_offset = switch (abi_size) {
   5701                 1 => ty.isSignedInt(zcu),
   5702                 2 => true,
   5703                 3, 4 => false,
   5704                 else => unreachable,
   5705             };
   5706 
   5707             if (extra_offset) {
   5708                 const offset = if (off <= math.maxInt(u8)) blk: {
   5709                     break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(off));
   5710                 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.usize, MCValue{ .immediate = off }));
   5711 
   5712                 _ = try self.addInst(.{
   5713                     .tag = tag,
   5714                     .data = .{ .rr_extra_offset = .{
   5715                         .rt = reg,
   5716                         .rn = .fp,
   5717                         .offset = .{
   5718                             .offset = offset,
   5719                             .positive = false,
   5720                         },
   5721                     } },
   5722                 });
   5723             } else {
   5724                 const offset = if (off <= math.maxInt(u12)) blk: {
   5725                     break :blk Instruction.Offset.imm(@intCast(off));
   5726                 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.usize, MCValue{ .immediate = off }), .none);
   5727 
   5728                 _ = try self.addInst(.{
   5729                     .tag = tag,
   5730                     .data = .{ .rr_offset = .{
   5731                         .rt = reg,
   5732                         .rn = .fp,
   5733                         .offset = .{
   5734                             .offset = offset,
   5735                             .positive = false,
   5736                         },
   5737                     } },
   5738                 });
   5739             }
   5740         },
   5741         .stack_argument_offset => |off| {
   5742             const abi_size = ty.abiSize(zcu);
   5743 
   5744             const tag: Mir.Inst.Tag = switch (abi_size) {
   5745                 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument,
   5746                 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument,
   5747                 3, 4 => .ldr_stack_argument,
   5748                 else => unreachable,
   5749             };
   5750 
   5751             _ = try self.addInst(.{
   5752                 .tag = tag,
   5753                 .data = .{ .r_stack_offset = .{
   5754                     .rt = reg,
   5755                     .stack_offset = off,
   5756                 } },
   5757             });
   5758         },
   5759     }
   5760 }
   5761 
   5762 fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
   5763     const pt = self.pt;
   5764     const abi_size: u32 = @intCast(ty.abiSize(pt.zcu));
   5765     switch (mcv) {
   5766         .dead => unreachable,
   5767         .none, .unreach => return,
   5768         .undef => {
   5769             if (!self.wantSafety())
   5770                 return; // The already existing value will do just fine.
   5771             // TODO Upgrade this to a memset call when we have that available.
   5772             switch (abi_size) {
   5773                 1 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }),
   5774                 2 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }),
   5775                 4 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
   5776                 else => return self.fail("TODO implement memset", .{}),
   5777             }
   5778         },
   5779         .register => |reg| {
   5780             switch (abi_size) {
   5781                 1, 4 => {
   5782                     const offset = if (math.cast(u12, stack_offset)) |imm| blk: {
   5783                         break :blk Instruction.Offset.imm(imm);
   5784                     } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }), .none);
   5785 
   5786                     const tag: Mir.Inst.Tag = switch (abi_size) {
   5787                         1 => .strb,
   5788                         4 => .str,
   5789                         else => unreachable,
   5790                     };
   5791 
   5792                     _ = try self.addInst(.{
   5793                         .tag = tag,
   5794                         .data = .{ .rr_offset = .{
   5795                             .rt = reg,
   5796                             .rn = .sp,
   5797                             .offset = .{ .offset = offset },
   5798                         } },
   5799                     });
   5800                 },
   5801                 2 => {
   5802                     const offset = if (stack_offset <= math.maxInt(u8)) blk: {
   5803                         break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(stack_offset));
   5804                     } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }));
   5805 
   5806                     _ = try self.addInst(.{
   5807                         .tag = .strh,
   5808                         .data = .{ .rr_extra_offset = .{
   5809                             .rt = reg,
   5810                             .rn = .sp,
   5811                             .offset = .{ .offset = offset },
   5812                         } },
   5813                     });
   5814                 },
   5815                 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
   5816             }
   5817         },
   5818         .register_c_flag,
   5819         .register_v_flag,
   5820         => {
   5821             return self.fail("TODO implement genSetStack {}", .{mcv});
   5822         },
   5823         .stack_offset,
   5824         .memory,
   5825         .stack_argument_offset,
   5826         => {
   5827             if (abi_size <= 4) {
   5828                 const reg = try self.copyToTmpRegister(ty, mcv);
   5829                 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
   5830             } else {
   5831                 const ptr_ty = try pt.singleMutPtrType(ty);
   5832 
   5833                 // TODO call extern memcpy
   5834                 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
   5835                 const src_reg = regs[0];
   5836                 const dst_reg = regs[1];
   5837                 const len_reg = regs[2];
   5838                 const count_reg = regs[3];
   5839                 const tmp_reg = regs[4];
   5840 
   5841                 switch (mcv) {
   5842                     .stack_offset => |off| {
   5843                         // sub src_reg, fp, #off
   5844                         try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
   5845                     },
   5846                     .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }),
   5847                     .stack_argument_offset => |off| {
   5848                         _ = try self.addInst(.{
   5849                             .tag = .ldr_ptr_stack_argument,
   5850                             .data = .{ .r_stack_offset = .{
   5851                                 .rt = src_reg,
   5852                                 .stack_offset = off,
   5853                             } },
   5854                         });
   5855                     },
   5856                     else => unreachable,
   5857                 }
   5858 
   5859                 // add dst_reg, sp, #stack_offset
   5860                 const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(stack_offset)) |x| x else {
   5861                     return self.fail("TODO load: set reg to stack offset with all possible offsets", .{});
   5862                 };
   5863                 _ = try self.addInst(.{
   5864                     .tag = .add,
   5865                     .data = .{ .rr_op = .{
   5866                         .rd = dst_reg,
   5867                         .rn = .sp,
   5868                         .op = dst_offset_op,
   5869                     } },
   5870                 });
   5871 
   5872                 // mov len, #abi_size
   5873                 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
   5874 
   5875                 // memcpy(src, dst, len)
   5876                 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   5877             }
   5878         },
   5879         .cpsr_flags,
   5880         .immediate,
   5881         .ptr_stack_offset,
   5882         => {
   5883             const reg = try self.copyToTmpRegister(ty, mcv);
   5884             return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
   5885         },
   5886     }
   5887 }
   5888 
   5889 fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
   5890     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5891     const result = try self.resolveInst(un_op);
   5892     return self.finishAir(inst, result, .{ un_op, .none, .none });
   5893 }
   5894 
   5895 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
   5896     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5897     const result = if (self.liveness.isUnused(inst)) .dead else result: {
   5898         const operand = try self.resolveInst(ty_op.operand);
   5899         if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
   5900 
   5901         const operand_lock = switch (operand) {
   5902             .register,
   5903             .register_c_flag,
   5904             .register_v_flag,
   5905             => |reg| self.register_manager.lockReg(reg),
   5906             else => null,
   5907         };
   5908         defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
   5909 
   5910         const dest_ty = self.typeOfIndex(inst);
   5911         const dest = try self.allocRegOrMem(dest_ty, true, inst);
   5912         try self.setRegOrMem(dest_ty, dest, operand);
   5913         break :result dest;
   5914     };
   5915     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   5916 }
   5917 
   5918 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
   5919     const pt = self.pt;
   5920     const zcu = pt.zcu;
   5921     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5922     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   5923         const ptr_ty = self.typeOf(ty_op.operand);
   5924         const ptr = try self.resolveInst(ty_op.operand);
   5925         const array_ty = ptr_ty.childType(zcu);
   5926         const array_len: u32 = @intCast(array_ty.arrayLen(zcu));
   5927 
   5928         const stack_offset = try self.allocMem(8, .@"8", inst);
   5929         try self.genSetStack(ptr_ty, stack_offset, ptr);
   5930         try self.genSetStack(Type.usize, stack_offset - 4, .{ .immediate = array_len });
   5931         break :result MCValue{ .stack_offset = stack_offset };
   5932     };
   5933     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   5934 }
   5935 
   5936 fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
   5937     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5938     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
   5939         self.target.cpu.arch,
   5940     });
   5941     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   5942 }
   5943 
   5944 fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
   5945     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5946     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
   5947         self.target.cpu.arch,
   5948     });
   5949     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   5950 }
   5951 
   5952 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
   5953     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5954     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   5955     _ = extra;
   5956 
   5957     return self.fail("TODO implement airCmpxchg for {}", .{
   5958         self.target.cpu.arch,
   5959     });
   5960 }
   5961 
   5962 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
   5963     _ = inst;
   5964     return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
   5965 }
   5966 
   5967 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
   5968     _ = inst;
   5969     return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
   5970 }
   5971 
   5972 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
   5973     _ = inst;
   5974     _ = order;
   5975     return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
   5976 }
   5977 
   5978 fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
   5979     if (safety) {
   5980         // TODO if the value is undef, write 0xaa bytes to dest
   5981     } else {
   5982         // TODO if the value is undef, don't lower this instruction
   5983     }
   5984     _ = inst;
   5985     return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
   5986 }
   5987 
   5988 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
   5989     _ = inst;
   5990     return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
   5991 }
   5992 
   5993 fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
   5994     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5995     const operand = try self.resolveInst(un_op);
   5996     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   5997         _ = operand;
   5998         return self.fail("TODO implement airTagName for arm", .{});
   5999     };
   6000     return self.finishAir(inst, result, .{ un_op, .none, .none });
   6001 }
   6002 
   6003 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
   6004     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   6005     const operand = try self.resolveInst(un_op);
   6006     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   6007         _ = operand;
   6008         return self.fail("TODO implement airErrorName for arm", .{});
   6009     };
   6010     return self.finishAir(inst, result, .{ un_op, .none, .none });
   6011 }
   6012 
   6013 fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
   6014     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6015     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for arm", .{});
   6016     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   6017 }
   6018 
   6019 fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
   6020     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6021     const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
   6022     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for arm", .{});
   6023     return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
   6024 }
   6025 
   6026 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
   6027     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6028     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for arm", .{});
   6029     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   6030 }
   6031 
   6032 fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
   6033     const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
   6034     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for arm", .{});
   6035     return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
   6036 }
   6037 
   6038 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
   6039     const pt = self.pt;
   6040     const zcu = pt.zcu;
   6041     const vector_ty = self.typeOfIndex(inst);
   6042     const len = vector_ty.vectorLen(zcu);
   6043     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   6044     const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]);
   6045     const result: MCValue = res: {
   6046         if (self.liveness.isUnused(inst)) break :res MCValue.dead;
   6047         return self.fail("TODO implement airAggregateInit for arm", .{});
   6048     };
   6049 
   6050     if (elements.len <= Liveness.bpi - 1) {
   6051         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   6052         @memcpy(buf[0..elements.len], elements);
   6053         return self.finishAir(inst, result, buf);
   6054     }
   6055     var bt = try self.iterateBigTomb(inst, elements.len);
   6056     for (elements) |elem| {
   6057         bt.feed(elem);
   6058     }
   6059     return bt.finishAir(result);
   6060 }
   6061 
   6062 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
   6063     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   6064     const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
   6065     _ = extra;
   6066 
   6067     return self.fail("TODO implement airUnionInit for arm", .{});
   6068 }
   6069 
   6070 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
   6071     const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
   6072     return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
   6073 }
   6074 
   6075 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
   6076     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6077     const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
   6078     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   6079         return self.fail("TODO implement airMulAdd for arm", .{});
   6080     };
   6081     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
   6082 }
   6083 
   6084 fn airTry(self: *Self, inst: Air.Inst.Index) !void {
   6085     const pt = self.pt;
   6086     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6087     const extra = self.air.extraData(Air.Try, pl_op.payload);
   6088     const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
   6089     const result: MCValue = result: {
   6090         const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand };
   6091         const error_union_ty = self.typeOf(pl_op.operand);
   6092         const error_union_size: u32 = @intCast(error_union_ty.abiSize(pt.zcu));
   6093         const error_union_align = error_union_ty.abiAlignment(pt.zcu);
   6094 
   6095         // The error union will die in the body. However, we need the
   6096         // error union after the body in order to extract the payload
   6097         // of the error union, so we create a copy of it
   6098         const error_union_copy = try self.allocMem(error_union_size, error_union_align, null);
   6099         try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self));
   6100 
   6101         const is_err_result = try self.isErr(error_union_bind, error_union_ty);
   6102         const reloc = try self.condBr(is_err_result);
   6103 
   6104         try self.genBody(body);
   6105         try self.performReloc(reloc);
   6106 
   6107         break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null);
   6108     };
   6109     return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
   6110 }
   6111 
   6112 fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
   6113     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   6114     const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
   6115     const body = self.air.extra[extra.end..][0..extra.data.body_len];
   6116     _ = body;
   6117     return self.fail("TODO implement airTryPtr for arm", .{});
   6118     // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
   6119 }
   6120 
   6121 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
   6122     const pt = self.pt;
   6123     const zcu = pt.zcu;
   6124 
   6125     // If the type has no codegen bits, no need to store it.
   6126     const inst_ty = self.typeOf(inst);
   6127     if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !inst_ty.isError(zcu))
   6128         return MCValue{ .none = {} };
   6129 
   6130     const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, pt)).?);
   6131 
   6132     return self.getResolvedInstValue(inst_index);
   6133 }
   6134 
   6135 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
   6136     // Treat each stack item as a "layer" on top of the previous one.
   6137     var i: usize = self.branch_stack.items.len;
   6138     while (true) {
   6139         i -= 1;
   6140         if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
   6141             assert(mcv != .dead);
   6142             return mcv;
   6143         }
   6144     }
   6145 }
   6146 
   6147 fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
   6148     const pt = self.pt;
   6149     const mcv: MCValue = switch (try codegen.genTypedValue(
   6150         self.bin_file,
   6151         pt,
   6152         self.src_loc,
   6153         val,
   6154         self.target.*,
   6155     )) {
   6156         .mcv => |mcv| switch (mcv) {
   6157             .none => .none,
   6158             .undef => .undef,
   6159             .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO
   6160             .immediate => |imm| .{ .immediate = @truncate(imm) },
   6161             .memory => |addr| .{ .memory = addr },
   6162         },
   6163         .fail => |msg| {
   6164             self.err_msg = msg;
   6165             return error.CodegenFail;
   6166         },
   6167     };
   6168     return mcv;
   6169 }
   6170 
   6171 const CallMCValues = struct {
   6172     args: []MCValue,
   6173     return_value: MCValue,
   6174     stack_byte_count: u32,
   6175     stack_align: u32,
   6176 
   6177     fn deinit(self: *CallMCValues, func: *Self) void {
   6178         func.gpa.free(self.args);
   6179         self.* = undefined;
   6180     }
   6181 };
   6182 
   6183 /// Caller must call `CallMCValues.deinit`.
   6184 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
   6185     const pt = self.pt;
   6186     const zcu = pt.zcu;
   6187     const ip = &zcu.intern_pool;
   6188     const fn_info = zcu.typeToFunc(fn_ty).?;
   6189     const cc = fn_info.cc;
   6190     var result: CallMCValues = .{
   6191         .args = try self.gpa.alloc(MCValue, fn_info.param_types.len),
   6192         // These undefined values must be populated before returning from this function.
   6193         .return_value = undefined,
   6194         .stack_byte_count = undefined,
   6195         .stack_align = undefined,
   6196     };
   6197     errdefer self.gpa.free(result.args);
   6198 
   6199     const ret_ty = fn_ty.fnReturnType(zcu);
   6200 
   6201     switch (cc) {
   6202         .Naked => {
   6203             assert(result.args.len == 0);
   6204             result.return_value = .{ .unreach = {} };
   6205             result.stack_byte_count = 0;
   6206             result.stack_align = 1;
   6207             return result;
   6208         },
   6209         .C => {
   6210             // ARM Procedure Call Standard, Chapter 6.5
   6211             var ncrn: usize = 0; // Next Core Register Number
   6212             var nsaa: u32 = 0; // Next stacked argument address
   6213 
   6214             if (ret_ty.zigTypeTag(zcu) == .NoReturn) {
   6215                 result.return_value = .{ .unreach = {} };
   6216             } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   6217                 result.return_value = .{ .none = {} };
   6218             } else {
   6219                 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu));
   6220                 // TODO handle cases where multiple registers are used
   6221                 if (ret_ty_size <= 4) {
   6222                     result.return_value = .{ .register = c_abi_int_return_regs[0] };
   6223                 } else {
   6224                     // The result is returned by reference, not by
   6225                     // value. This means that r0 will contain the
   6226                     // address of where this function should write the
   6227                     // result into.
   6228                     result.return_value = .{ .stack_offset = 0 };
   6229                     ncrn = 1;
   6230                 }
   6231             }
   6232 
   6233             for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
   6234                 if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8")
   6235                     ncrn = std.mem.alignForward(usize, ncrn, 2);
   6236 
   6237                 const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu));
   6238                 if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
   6239                     if (param_size <= 4) {
   6240                         result_arg.* = .{ .register = c_abi_int_param_regs[ncrn] };
   6241                         ncrn += 1;
   6242                     } else {
   6243                         return self.fail("TODO MCValues with multiple registers", .{});
   6244                     }
   6245                 } else if (ncrn < 4 and nsaa == 0) {
   6246                     return self.fail("TODO MCValues split between registers and stack", .{});
   6247                 } else {
   6248                     ncrn = 4;
   6249                     if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8")
   6250                         nsaa = std.mem.alignForward(u32, nsaa, 8);
   6251 
   6252                     result_arg.* = .{ .stack_argument_offset = nsaa };
   6253                     nsaa += param_size;
   6254                 }
   6255             }
   6256 
   6257             result.stack_byte_count = nsaa;
   6258             result.stack_align = 8;
   6259         },
   6260         .Unspecified => {
   6261             if (ret_ty.zigTypeTag(zcu) == .NoReturn) {
   6262                 result.return_value = .{ .unreach = {} };
   6263             } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
   6264                 result.return_value = .{ .none = {} };
   6265             } else {
   6266                 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu));
   6267                 if (ret_ty_size == 0) {
   6268                     assert(ret_ty.isError(zcu));
   6269                     result.return_value = .{ .immediate = 0 };
   6270                 } else if (ret_ty_size <= 4) {
   6271                     result.return_value = .{ .register = .r0 };
   6272                 } else {
   6273                     // The result is returned by reference, not by
   6274                     // value. This means that r0 will contain the
   6275                     // address of where this function should write the
   6276                     // result into.
   6277                     result.return_value = .{ .stack_offset = 0 };
   6278                 }
   6279             }
   6280 
   6281             var stack_offset: u32 = 0;
   6282 
   6283             for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
   6284                 if (Type.fromInterned(ty).abiSize(zcu) > 0) {
   6285                     const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu));
   6286                     const param_alignment = Type.fromInterned(ty).abiAlignment(zcu);
   6287 
   6288                     stack_offset = @intCast(param_alignment.forward(stack_offset));
   6289                     result_arg.* = .{ .stack_argument_offset = stack_offset };
   6290                     stack_offset += param_size;
   6291                 } else {
   6292                     result_arg.* = .{ .none = {} };
   6293                 }
   6294             }
   6295 
   6296             result.stack_byte_count = stack_offset;
   6297             result.stack_align = 8;
   6298         },
   6299         else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
   6300     }
   6301 
   6302     return result;
   6303 }
   6304 
   6305 /// TODO support scope overrides. Also note this logic is duplicated with `Zcu.wantSafety`.
   6306 fn wantSafety(self: *Self) bool {
   6307     return switch (self.bin_file.comp.root_mod.optimize_mode) {
   6308         .Debug => true,
   6309         .ReleaseSafe => true,
   6310         .ReleaseFast => false,
   6311         .ReleaseSmall => false,
   6312     };
   6313 }
   6314 
   6315 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   6316     @branchHint(.cold);
   6317     assert(self.err_msg == null);
   6318     const gpa = self.gpa;
   6319     self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
   6320     return error.CodegenFail;
   6321 }
   6322 
   6323 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   6324     @branchHint(.cold);
   6325     assert(self.err_msg == null);
   6326     const gpa = self.gpa;
   6327     self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
   6328     return error.CodegenFail;
   6329 }
   6330 
   6331 fn parseRegName(name: []const u8) ?Register {
   6332     if (@hasDecl(Register, "parseRegName")) {
   6333         return Register.parseRegName(name);
   6334     }
   6335     return std.meta.stringToEnum(Register, name);
   6336 }
   6337 
   6338 fn typeOf(self: *Self, inst: Air.Inst.Ref) Type {
   6339     return self.air.typeOf(inst, &self.pt.zcu.intern_pool);
   6340 }
   6341 
   6342 fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {
   6343     return self.air.typeOfIndex(inst, &self.pt.zcu.intern_pool);
   6344 }