zig

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

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