zig

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

blob c4220d23 (181483B) - 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 Air = @import("../../Air.zig");
      7 const Mir = @import("Mir.zig");
      8 const Emit = @import("Emit.zig");
      9 const Liveness = @import("../../Liveness.zig");
     10 const Type = @import("../../type.zig").Type;
     11 const Value = @import("../../value.zig").Value;
     12 const TypedValue = @import("../../TypedValue.zig");
     13 const link = @import("../../link.zig");
     14 const Module = @import("../../Module.zig");
     15 const Compilation = @import("../../Compilation.zig");
     16 const ErrorMsg = Module.ErrorMsg;
     17 const Target = std.Target;
     18 const Allocator = mem.Allocator;
     19 const trace = @import("../../tracy.zig").trace;
     20 const DW = std.dwarf;
     21 const leb128 = std.leb;
     22 const log = std.log.scoped(.codegen);
     23 const build_options = @import("build_options");
     24 const RegisterManager = @import("../../register_manager.zig").RegisterManager;
     25 
     26 const FnResult = @import("../../codegen.zig").FnResult;
     27 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
     28 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
     29 
     30 const InnerError = error{
     31     OutOfMemory,
     32     CodegenFail,
     33     OutOfRegisters,
     34 };
     35 
     36 gpa: Allocator,
     37 air: Air,
     38 liveness: Liveness,
     39 bin_file: *link.File,
     40 target: *const std.Target,
     41 mod_fn: *const Module.Fn,
     42 err_msg: ?*ErrorMsg,
     43 args: []MCValue,
     44 ret_mcv: MCValue,
     45 fn_type: Type,
     46 arg_index: u32,
     47 src_loc: Module.SrcLoc,
     48 stack_align: u32,
     49 
     50 /// MIR Instructions
     51 mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
     52 /// MIR extra data
     53 mir_extra: std.ArrayListUnmanaged(u32) = .{},
     54 
     55 /// Byte offset within the source file of the ending curly.
     56 end_di_line: u32,
     57 end_di_column: u32,
     58 
     59 /// The value is an offset into the `Function` `code` from the beginning.
     60 /// To perform the reloc, write 32-bit signed little-endian integer
     61 /// which is a relative jump, based on the address following the reloc.
     62 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
     63 
     64 /// Whenever there is a runtime branch, we push a Branch onto this stack,
     65 /// and pop it off when the runtime branch joins. This provides an "overlay"
     66 /// of the table of mappings from instructions to `MCValue` from within the branch.
     67 /// This way we can modify the `MCValue` for an instruction in different ways
     68 /// within different branches. Special consideration is needed when a branch
     69 /// joins with its parent, to make sure all instructions have the same MCValue
     70 /// across each runtime branch upon joining.
     71 branch_stack: *std.ArrayList(Branch),
     72 
     73 // Key is the block instruction
     74 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
     75 
     76 register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
     77 /// Maps offset to what is stored there.
     78 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
     79 /// Tracks the current instruction allocated to the compare flags
     80 compare_flags_inst: ?Air.Inst.Index = null,
     81 
     82 /// Offset from the stack base, representing the end of the stack frame.
     83 max_end_stack: u32 = 0,
     84 /// Represents the current end stack offset. If there is no existing slot
     85 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
     86 next_stack_offset: u32 = 0,
     87 
     88 saved_regs_stack_space: u32 = 0,
     89 
     90 /// Debug field, used to find bugs in the compiler.
     91 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
     92 
     93 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
     94 
     95 const MCValue = union(enum) {
     96     /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
     97     /// TODO Look into deleting this tag and using `dead` instead, since every use
     98     /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
     99     none,
    100     /// Control flow will not allow this value to be observed.
    101     unreach,
    102     /// No more references to this value remain.
    103     dead,
    104     /// The value is undefined.
    105     undef,
    106     /// A pointer-sized integer that fits in a register.
    107     /// If the type is a pointer, this is the pointer address in virtual address space.
    108     immediate: u32,
    109     /// The constant was emitted into the code, at this offset.
    110     /// If the type is a pointer, it means the pointer address is embedded in the code.
    111     embedded_in_code: usize,
    112     /// The value is a pointer to a constant which was emitted into the code, at this offset.
    113     ptr_embedded_in_code: usize,
    114     /// The value is in a target-specific register.
    115     register: Register,
    116     /// The value is in memory at a hard-coded address.
    117     /// If the type is a pointer, it means the pointer address is at this memory location.
    118     memory: u64,
    119     /// The value is one of the stack variables.
    120     /// If the type is a pointer, it means the pointer address is in the stack at this offset.
    121     stack_offset: u32,
    122     /// The value is a pointer to one of the stack variables (payload is stack offset).
    123     ptr_stack_offset: u32,
    124     /// The value is in the compare flags assuming an unsigned operation,
    125     /// with this operator applied on top of it.
    126     compare_flags_unsigned: math.CompareOperator,
    127     /// The value is in the compare flags assuming a signed operation,
    128     /// with this operator applied on top of it.
    129     compare_flags_signed: math.CompareOperator,
    130     /// The value is a function argument passed via the stack.
    131     stack_argument_offset: u32,
    132 
    133     fn isMemory(mcv: MCValue) bool {
    134         return switch (mcv) {
    135             .embedded_in_code, .memory, .stack_offset, .stack_argument_offset => true,
    136             else => false,
    137         };
    138     }
    139 
    140     fn isImmediate(mcv: MCValue) bool {
    141         return switch (mcv) {
    142             .immediate => true,
    143             else => false,
    144         };
    145     }
    146 
    147     fn isMutable(mcv: MCValue) bool {
    148         return switch (mcv) {
    149             .none => unreachable,
    150             .unreach => unreachable,
    151             .dead => unreachable,
    152 
    153             .immediate,
    154             .embedded_in_code,
    155             .memory,
    156             .compare_flags_unsigned,
    157             .compare_flags_signed,
    158             .ptr_stack_offset,
    159             .ptr_embedded_in_code,
    160             .undef,
    161             .stack_argument_offset,
    162             => false,
    163 
    164             .register,
    165             .stack_offset,
    166             => true,
    167         };
    168     }
    169 };
    170 
    171 const Branch = struct {
    172     inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
    173 
    174     fn deinit(self: *Branch, gpa: Allocator) void {
    175         self.inst_table.deinit(gpa);
    176         self.* = undefined;
    177     }
    178 };
    179 
    180 const StackAllocation = struct {
    181     inst: Air.Inst.Index,
    182     /// TODO do we need size? should be determined by inst.ty.abiSize()
    183     size: u32,
    184 };
    185 
    186 const BlockData = struct {
    187     relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
    188     /// The first break instruction encounters `null` here and chooses a
    189     /// machine code value for the block result, populating this field.
    190     /// Following break instructions encounter that value and use it for
    191     /// the location to store their block results.
    192     mcv: MCValue,
    193 };
    194 
    195 const BigTomb = struct {
    196     function: *Self,
    197     inst: Air.Inst.Index,
    198     tomb_bits: Liveness.Bpi,
    199     big_tomb_bits: u32,
    200     bit_index: usize,
    201 
    202     fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
    203         const this_bit_index = bt.bit_index;
    204         bt.bit_index += 1;
    205 
    206         const op_int = @enumToInt(op_ref);
    207         if (op_int < Air.Inst.Ref.typed_value_map.len) return;
    208         const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
    209 
    210         if (this_bit_index < Liveness.bpi - 1) {
    211             const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
    212             if (!dies) return;
    213         } else {
    214             const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
    215             const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
    216             if (!dies) return;
    217         }
    218         bt.function.processDeath(op_index);
    219     }
    220 
    221     fn finishAir(bt: *BigTomb, result: MCValue) void {
    222         const is_used = !bt.function.liveness.isUnused(bt.inst);
    223         if (is_used) {
    224             log.debug("%{d} => {}", .{ bt.inst, result });
    225             const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
    226             branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
    227         }
    228         bt.function.finishAirBookkeeping();
    229     }
    230 };
    231 
    232 const Self = @This();
    233 
    234 pub fn generate(
    235     bin_file: *link.File,
    236     src_loc: Module.SrcLoc,
    237     module_fn: *Module.Fn,
    238     air: Air,
    239     liveness: Liveness,
    240     code: *std.ArrayList(u8),
    241     debug_output: DebugInfoOutput,
    242 ) GenerateSymbolError!FnResult {
    243     if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
    244         @panic("Attempted to compile for architecture that was disabled by build configuration");
    245     }
    246 
    247     assert(module_fn.owner_decl.has_tv);
    248     const fn_type = module_fn.owner_decl.ty;
    249 
    250     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
    251     defer {
    252         assert(branch_stack.items.len == 1);
    253         branch_stack.items[0].deinit(bin_file.allocator);
    254         branch_stack.deinit();
    255     }
    256     try branch_stack.append(.{});
    257 
    258     var function = Self{
    259         .gpa = bin_file.allocator,
    260         .air = air,
    261         .liveness = liveness,
    262         .target = &bin_file.options.target,
    263         .bin_file = bin_file,
    264         .mod_fn = module_fn,
    265         .err_msg = null,
    266         .args = undefined, // populated after `resolveCallingConventionValues`
    267         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
    268         .fn_type = fn_type,
    269         .arg_index = 0,
    270         .branch_stack = &branch_stack,
    271         .src_loc = src_loc,
    272         .stack_align = undefined,
    273         .end_di_line = module_fn.rbrace_line,
    274         .end_di_column = module_fn.rbrace_column,
    275     };
    276     defer function.stack.deinit(bin_file.allocator);
    277     defer function.blocks.deinit(bin_file.allocator);
    278     defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
    279 
    280     var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
    281         error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
    282         error.OutOfRegisters => return FnResult{
    283             .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    284         },
    285         else => |e| return e,
    286     };
    287     defer call_info.deinit(&function);
    288 
    289     function.args = call_info.args;
    290     function.ret_mcv = call_info.return_value;
    291     function.stack_align = call_info.stack_align;
    292     function.max_end_stack = call_info.stack_byte_count;
    293 
    294     function.gen() catch |err| switch (err) {
    295         error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
    296         error.OutOfRegisters => return FnResult{
    297             .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    298         },
    299         else => |e| return e,
    300     };
    301 
    302     var mir = Mir{
    303         .instructions = function.mir_instructions.toOwnedSlice(),
    304         .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
    305     };
    306     defer mir.deinit(bin_file.allocator);
    307 
    308     var emit = Emit{
    309         .mir = mir,
    310         .bin_file = bin_file,
    311         .function = &function,
    312         .debug_output = debug_output,
    313         .target = &bin_file.options.target,
    314         .src_loc = src_loc,
    315         .code = code,
    316         .prev_di_pc = 0,
    317         .prev_di_line = module_fn.lbrace_line,
    318         .prev_di_column = module_fn.lbrace_column,
    319         .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space,
    320     };
    321     defer emit.deinit();
    322 
    323     emit.emitMir() catch |err| switch (err) {
    324         error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
    325         else => |e| return e,
    326     };
    327 
    328     if (function.err_msg) |em| {
    329         return FnResult{ .fail = em };
    330     } else {
    331         return FnResult{ .appended = {} };
    332     }
    333 }
    334 
    335 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
    336     const gpa = self.gpa;
    337 
    338     try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
    339 
    340     const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
    341     self.mir_instructions.appendAssumeCapacity(inst);
    342     return result_index;
    343 }
    344 
    345 fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
    346     return try self.addInst(.{
    347         .tag = .nop,
    348         .data = .{ .nop = {} },
    349     });
    350 }
    351 
    352 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
    353     const fields = std.meta.fields(@TypeOf(extra));
    354     try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
    355     return self.addExtraAssumeCapacity(extra);
    356 }
    357 
    358 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
    359     const fields = std.meta.fields(@TypeOf(extra));
    360     const result = @intCast(u32, self.mir_extra.items.len);
    361     inline for (fields) |field| {
    362         self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
    363             u32 => @field(extra, field.name),
    364             i32 => @bitCast(u32, @field(extra, field.name)),
    365             else => @compileError("bad field type"),
    366         });
    367     }
    368     return result;
    369 }
    370 
    371 fn gen(self: *Self) !void {
    372     const cc = self.fn_type.fnCallingConvention();
    373     if (cc != .Naked) {
    374         // push {fp, lr}
    375         const push_reloc = try self.addNop();
    376 
    377         // mov fp, sp
    378         _ = try self.addInst(.{
    379             .tag = .mov,
    380             .data = .{ .rr_op = .{
    381                 .rd = .fp,
    382                 .rn = .r0,
    383                 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),
    384             } },
    385         });
    386 
    387         // sub sp, sp, #reloc
    388         const sub_reloc = try self.addNop();
    389 
    390         if (self.ret_mcv == .stack_offset) {
    391             // The address of where to store the return value is in
    392             // r0. As this register might get overwritten along the
    393             // way, save the address to the stack.
    394             const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4);
    395             self.next_stack_offset = stack_offset + 4;
    396             self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
    397 
    398             try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });
    399             self.ret_mcv = MCValue{ .stack_offset = stack_offset };
    400         }
    401 
    402         _ = try self.addInst(.{
    403             .tag = .dbg_prologue_end,
    404             .cond = undefined,
    405             .data = .{ .nop = {} },
    406         });
    407 
    408         try self.genBody(self.air.getMainBody());
    409 
    410         // Backpatch push callee saved regs
    411         var saved_regs = Instruction.RegisterList{
    412             .r11 = true, // fp
    413             .r14 = true, // lr
    414         };
    415         self.saved_regs_stack_space = 8;
    416         inline for (callee_preserved_regs) |reg| {
    417             if (self.register_manager.isRegAllocated(reg)) {
    418                 @field(saved_regs, @tagName(reg)) = true;
    419                 self.saved_regs_stack_space += 4;
    420             }
    421         }
    422 
    423         self.mir_instructions.set(push_reloc, .{
    424             .tag = .push,
    425             .data = .{ .register_list = saved_regs },
    426         });
    427 
    428         // Backpatch stack offset
    429         const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
    430         const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
    431         const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
    432         if (Instruction.Operand.fromU32(stack_size)) |op| {
    433             self.mir_instructions.set(sub_reloc, .{
    434                 .tag = .sub,
    435                 .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } },
    436             });
    437         } else {
    438             return self.failSymbol("TODO ARM: allow larger stacks", .{});
    439         }
    440 
    441         _ = try self.addInst(.{
    442             .tag = .dbg_epilogue_begin,
    443             .cond = undefined,
    444             .data = .{ .nop = {} },
    445         });
    446 
    447         // exitlude jumps
    448         if (self.exitlude_jump_relocs.items.len > 0 and
    449             self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
    450         {
    451             // If the last Mir instruction (apart from the
    452             // dbg_epilogue_begin) is the last exitlude jump
    453             // relocation (which would just jump one instruction
    454             // further), it can be safely removed
    455             self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop());
    456         }
    457 
    458         for (self.exitlude_jump_relocs.items) |jmp_reloc| {
    459             self.mir_instructions.set(jmp_reloc, .{
    460                 .tag = .b,
    461                 .data = .{ .inst = @intCast(u32, self.mir_instructions.len) },
    462             });
    463         }
    464 
    465         // Epilogue: pop callee saved registers (swap lr with pc in saved_regs)
    466         saved_regs.r14 = false; // lr
    467         saved_regs.r15 = true; // pc
    468 
    469         // mov sp, fp
    470         _ = try self.addInst(.{
    471             .tag = .mov,
    472             .data = .{ .rr_op = .{
    473                 .rd = .sp,
    474                 .rn = .r0,
    475                 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),
    476             } },
    477         });
    478 
    479         // pop {fp, pc}
    480         _ = try self.addInst(.{
    481             .tag = .pop,
    482             .data = .{ .register_list = saved_regs },
    483         });
    484     } else {
    485         _ = try self.addInst(.{
    486             .tag = .dbg_prologue_end,
    487             .cond = undefined,
    488             .data = .{ .nop = {} },
    489         });
    490 
    491         try self.genBody(self.air.getMainBody());
    492 
    493         _ = try self.addInst(.{
    494             .tag = .dbg_epilogue_begin,
    495             .cond = undefined,
    496             .data = .{ .nop = {} },
    497         });
    498     }
    499 
    500     // Drop them off at the rbrace.
    501     _ = try self.addInst(.{
    502         .tag = .dbg_line,
    503         .cond = undefined,
    504         .data = .{ .dbg_line_column = .{
    505             .line = self.end_di_line,
    506             .column = self.end_di_column,
    507         } },
    508     });
    509 }
    510 
    511 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
    512     const air_tags = self.air.instructions.items(.tag);
    513 
    514     for (body) |inst| {
    515         const old_air_bookkeeping = self.air_bookkeeping;
    516         try self.ensureProcessDeathCapacity(Liveness.bpi);
    517 
    518         switch (air_tags[inst]) {
    519             // zig fmt: off
    520             .add, .ptr_add   => try self.airBinOp(inst),
    521             .addwrap         => try self.airAddWrap(inst),
    522             .add_sat         => try self.airAddSat(inst),
    523             .sub, .ptr_sub   => try self.airBinOp(inst),
    524             .subwrap         => try self.airSubWrap(inst),
    525             .sub_sat         => try self.airSubSat(inst),
    526             .mul             => try self.airBinOp(inst),
    527             .mulwrap         => try self.airMulWrap(inst),
    528             .mul_sat         => try self.airMulSat(inst),
    529             .rem             => try self.airRem(inst),
    530             .mod             => try self.airMod(inst),
    531             .shl, .shl_exact => try self.airBinOp(inst),
    532             .shl_sat         => try self.airShlSat(inst),
    533             .min             => try self.airMin(inst),
    534             .max             => try self.airMax(inst),
    535             .slice           => try self.airSlice(inst),
    536 
    537             .sqrt,
    538             .sin,
    539             .cos,
    540             .exp,
    541             .exp2,
    542             .log,
    543             .log2,
    544             .log10,
    545             .fabs,
    546             .floor,
    547             .ceil,
    548             .round,
    549             .trunc_float,
    550             => try self.airUnaryMath(inst),
    551 
    552             .add_with_overflow => try self.airAddWithOverflow(inst),
    553             .sub_with_overflow => try self.airSubWithOverflow(inst),
    554             .mul_with_overflow => try self.airMulWithOverflow(inst),
    555             .shl_with_overflow => try self.airShlWithOverflow(inst),
    556 
    557             .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
    558 
    559             .cmp_lt  => try self.airCmp(inst, .lt),
    560             .cmp_lte => try self.airCmp(inst, .lte),
    561             .cmp_eq  => try self.airCmp(inst, .eq),
    562             .cmp_gte => try self.airCmp(inst, .gte),
    563             .cmp_gt  => try self.airCmp(inst, .gt),
    564             .cmp_neq => try self.airCmp(inst, .neq),
    565 
    566             .bool_and        => try self.airBinOp(inst),
    567             .bool_or         => try self.airBinOp(inst),
    568             .bit_and         => try self.airBinOp(inst),
    569             .bit_or          => try self.airBinOp(inst),
    570             .xor             => try self.airBinOp(inst),
    571             .shr, .shr_exact => try self.airBinOp(inst),
    572 
    573             .alloc           => try self.airAlloc(inst),
    574             .ret_ptr         => try self.airRetPtr(inst),
    575             .arg             => try self.airArg(inst),
    576             .assembly        => try self.airAsm(inst),
    577             .bitcast         => try self.airBitCast(inst),
    578             .block           => try self.airBlock(inst),
    579             .br              => try self.airBr(inst),
    580             .breakpoint      => try self.airBreakpoint(),
    581             .ret_addr        => try self.airRetAddr(inst),
    582             .frame_addr      => try self.airFrameAddress(inst),
    583             .fence           => try self.airFence(),
    584             .call            => try self.airCall(inst),
    585             .cond_br         => try self.airCondBr(inst),
    586             .dbg_stmt        => try self.airDbgStmt(inst),
    587             .fptrunc         => try self.airFptrunc(inst),
    588             .fpext           => try self.airFpext(inst),
    589             .intcast         => try self.airIntCast(inst),
    590             .trunc           => try self.airTrunc(inst),
    591             .bool_to_int     => try self.airBoolToInt(inst),
    592             .is_non_null     => try self.airIsNonNull(inst),
    593             .is_non_null_ptr => try self.airIsNonNullPtr(inst),
    594             .is_null         => try self.airIsNull(inst),
    595             .is_null_ptr     => try self.airIsNullPtr(inst),
    596             .is_non_err      => try self.airIsNonErr(inst),
    597             .is_non_err_ptr  => try self.airIsNonErrPtr(inst),
    598             .is_err          => try self.airIsErr(inst),
    599             .is_err_ptr      => try self.airIsErrPtr(inst),
    600             .load            => try self.airLoad(inst),
    601             .loop            => try self.airLoop(inst),
    602             .not             => try self.airNot(inst),
    603             .ptrtoint        => try self.airPtrToInt(inst),
    604             .ret             => try self.airRet(inst),
    605             .ret_load        => try self.airRetLoad(inst),
    606             .store           => try self.airStore(inst),
    607             .struct_field_ptr=> try self.airStructFieldPtr(inst),
    608             .struct_field_val=> try self.airStructFieldVal(inst),
    609             .array_to_slice  => try self.airArrayToSlice(inst),
    610             .int_to_float    => try self.airIntToFloat(inst),
    611             .float_to_int    => try self.airFloatToInt(inst),
    612             .cmpxchg_strong  => try self.airCmpxchg(inst),
    613             .cmpxchg_weak    => try self.airCmpxchg(inst),
    614             .atomic_rmw      => try self.airAtomicRmw(inst),
    615             .atomic_load     => try self.airAtomicLoad(inst),
    616             .memcpy          => try self.airMemcpy(inst),
    617             .memset          => try self.airMemset(inst),
    618             .set_union_tag   => try self.airSetUnionTag(inst),
    619             .get_union_tag   => try self.airGetUnionTag(inst),
    620             .clz             => try self.airClz(inst),
    621             .ctz             => try self.airCtz(inst),
    622             .popcount        => try self.airPopcount(inst),
    623             .byte_swap       => try self.airByteSwap(inst),
    624             .bit_reverse     => try self.airBitReverse(inst),
    625             .tag_name        => try self.airTagName(inst),
    626             .error_name      => try self.airErrorName(inst),
    627             .splat           => try self.airSplat(inst),
    628             .aggregate_init  => try self.airAggregateInit(inst),
    629             .union_init      => try self.airUnionInit(inst),
    630             .prefetch        => try self.airPrefetch(inst),
    631             .mul_add         => try self.airMulAdd(inst),
    632 
    633             .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
    634             .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
    635             .atomic_store_release   => try self.airAtomicStore(inst, .Release),
    636             .atomic_store_seq_cst   => try self.airAtomicStore(inst, .SeqCst),
    637 
    638             .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
    639             .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
    640             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
    641             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
    642 
    643             .field_parent_ptr => try self.airFieldParentPtr(inst),
    644 
    645             .switch_br       => try self.airSwitch(inst),
    646             .slice_ptr       => try self.airSlicePtr(inst),
    647             .slice_len       => try self.airSliceLen(inst),
    648 
    649             .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
    650             .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
    651 
    652             .array_elem_val      => try self.airArrayElemVal(inst),
    653             .slice_elem_val      => try self.airSliceElemVal(inst),
    654             .slice_elem_ptr      => try self.airSliceElemPtr(inst),
    655             .ptr_elem_val        => try self.airPtrElemVal(inst),
    656             .ptr_elem_ptr        => try self.airPtrElemPtr(inst),
    657 
    658             .constant => unreachable, // excluded from function bodies
    659             .const_ty => unreachable, // excluded from function bodies
    660             .unreach  => self.finishAirBookkeeping(),
    661 
    662             .optional_payload           => try self.airOptionalPayload(inst),
    663             .optional_payload_ptr       => try self.airOptionalPayloadPtr(inst),
    664             .optional_payload_ptr_set   => try self.airOptionalPayloadPtrSet(inst),
    665             .unwrap_errunion_err        => try self.airUnwrapErrErr(inst),
    666             .unwrap_errunion_payload    => try self.airUnwrapErrPayload(inst),
    667             .unwrap_errunion_err_ptr    => try self.airUnwrapErrErrPtr(inst),
    668             .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
    669             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
    670 
    671             .wrap_optional         => try self.airWrapOptional(inst),
    672             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
    673             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
    674 
    675             .wasm_memory_size => unreachable,
    676             .wasm_memory_grow => unreachable,
    677             // zig fmt: on
    678         }
    679 
    680         assert(!self.register_manager.frozenRegsExist());
    681 
    682         if (std.debug.runtime_safety) {
    683             if (self.air_bookkeeping < old_air_bookkeeping + 1) {
    684                 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] });
    685             }
    686         }
    687     }
    688 }
    689 
    690 /// Asserts there is already capacity to insert into top branch inst_table.
    691 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
    692     const air_tags = self.air.instructions.items(.tag);
    693     if (air_tags[inst] == .constant) return; // Constants are immortal.
    694     // When editing this function, note that the logic must synchronize with `reuseOperand`.
    695     const prev_value = self.getResolvedInstValue(inst);
    696     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    697     branch.inst_table.putAssumeCapacity(inst, .dead);
    698     switch (prev_value) {
    699         .register => |reg| {
    700             self.register_manager.freeReg(reg);
    701         },
    702         .compare_flags_signed, .compare_flags_unsigned => {
    703             self.compare_flags_inst = null;
    704         },
    705         else => {}, // TODO process stack allocation death
    706     }
    707 }
    708 
    709 /// Called when there are no operands, and the instruction is always unreferenced.
    710 fn finishAirBookkeeping(self: *Self) void {
    711     if (std.debug.runtime_safety) {
    712         self.air_bookkeeping += 1;
    713     }
    714 }
    715 
    716 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
    717     var tomb_bits = self.liveness.getTombBits(inst);
    718     for (operands) |op| {
    719         const dies = @truncate(u1, tomb_bits) != 0;
    720         tomb_bits >>= 1;
    721         if (!dies) continue;
    722         const op_int = @enumToInt(op);
    723         if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
    724         const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
    725         self.processDeath(op_index);
    726     }
    727     const is_used = @truncate(u1, tomb_bits) == 0;
    728     if (is_used) {
    729         log.debug("%{d} => {}", .{ inst, result });
    730         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    731         branch.inst_table.putAssumeCapacityNoClobber(inst, result);
    732 
    733         switch (result) {
    734             .register => |reg| {
    735                 // In some cases (such as bitcast), an operand
    736                 // may be the same MCValue as the result. If
    737                 // that operand died and was a register, it
    738                 // was freed by processDeath. We have to
    739                 // "re-allocate" the register.
    740                 if (self.register_manager.isRegFree(reg)) {
    741                     self.register_manager.getRegAssumeFree(reg, inst);
    742                 }
    743             },
    744             else => {},
    745         }
    746     }
    747     self.finishAirBookkeeping();
    748 }
    749 
    750 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
    751     const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
    752     try table.ensureUnusedCapacity(self.gpa, additional_count);
    753 }
    754 
    755 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
    756     if (abi_align > self.stack_align)
    757         self.stack_align = abi_align;
    758     // TODO find a free slot instead of always appending
    759     const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
    760     self.next_stack_offset = offset + abi_size;
    761     if (self.next_stack_offset > self.max_end_stack)
    762         self.max_end_stack = self.next_stack_offset;
    763     try self.stack.putNoClobber(self.gpa, offset, .{
    764         .inst = inst,
    765         .size = abi_size,
    766     });
    767     return offset;
    768 }
    769 
    770 /// Use a pointer instruction as the basis for allocating stack memory.
    771 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
    772     const elem_ty = self.air.typeOfIndex(inst).elemType();
    773 
    774     if (!elem_ty.hasRuntimeBits()) {
    775         // As this stack item will never be dereferenced at runtime,
    776         // return the current stack offset
    777         try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{
    778             .inst = inst,
    779             .size = 0,
    780         });
    781         return self.next_stack_offset;
    782     }
    783 
    784     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
    785         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
    786     };
    787     // TODO swap this for inst.ty.ptrAlign
    788     const abi_align = elem_ty.abiAlignment(self.target.*);
    789     return self.allocMem(inst, abi_size, abi_align);
    790 }
    791 
    792 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
    793     const elem_ty = self.air.typeOfIndex(inst);
    794     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
    795         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
    796     };
    797     const abi_align = elem_ty.abiAlignment(self.target.*);
    798     if (abi_align > self.stack_align)
    799         self.stack_align = abi_align;
    800 
    801     if (reg_ok) {
    802         // Make sure the type can fit in a register before we try to allocate one.
    803         const ptr_bits = self.target.cpu.arch.ptrBitWidth();
    804         const ptr_bytes: u64 = @divExact(ptr_bits, 8);
    805         if (abi_size <= ptr_bytes) {
    806             if (self.register_manager.tryAllocReg(inst)) |reg| {
    807                 return MCValue{ .register = reg };
    808             }
    809         }
    810     }
    811     const stack_offset = try self.allocMem(inst, abi_size, abi_align);
    812     return MCValue{ .stack_offset = stack_offset };
    813 }
    814 
    815 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
    816     const stack_mcv = try self.allocRegOrMem(inst, false);
    817     log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
    818     const reg_mcv = self.getResolvedInstValue(inst);
    819     assert(reg == reg_mcv.register);
    820     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    821     try branch.inst_table.put(self.gpa, inst, stack_mcv);
    822     try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
    823 }
    824 
    825 /// Save the current instruction stored in the compare flags if
    826 /// occupied
    827 fn spillCompareFlagsIfOccupied(self: *Self) !void {
    828     if (self.compare_flags_inst) |inst_to_save| {
    829         const mcv = self.getResolvedInstValue(inst_to_save);
    830         assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned);
    831 
    832         const new_mcv = try self.allocRegOrMem(inst_to_save, true);
    833         try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
    834         log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
    835 
    836         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    837         try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
    838 
    839         self.compare_flags_inst = null;
    840     }
    841 }
    842 
    843 /// Copies a value to a register without tracking the register. The register is not considered
    844 /// allocated. A second call to `copyToTmpRegister` may return the same register.
    845 /// This can have a side effect of spilling instructions to the stack to free up a register.
    846 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
    847     const reg = try self.register_manager.allocReg(null);
    848     try self.genSetReg(ty, reg, mcv);
    849     return reg;
    850 }
    851 
    852 /// Allocates a new register and copies `mcv` into it.
    853 /// `reg_owner` is the instruction that gets associated with the register in the register table.
    854 /// This can have a side effect of spilling instructions to the stack to free up a register.
    855 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
    856     const reg = try self.register_manager.allocReg(reg_owner);
    857     try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
    858     return MCValue{ .register = reg };
    859 }
    860 
    861 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
    862     const stack_offset = try self.allocMemPtr(inst);
    863     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
    864 }
    865 
    866 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
    867     const stack_offset = try self.allocMemPtr(inst);
    868     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
    869 }
    870 
    871 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
    872     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    873     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
    874     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    875 }
    876 
    877 fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
    878     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    879     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
    880     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    881 }
    882 
    883 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
    884     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    885     if (self.liveness.isUnused(inst))
    886         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
    887 
    888     const operand_ty = self.air.typeOf(ty_op.operand);
    889     const operand = try self.resolveInst(ty_op.operand);
    890     const info_a = operand_ty.intInfo(self.target.*);
    891     const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
    892     if (info_a.signedness != info_b.signedness)
    893         return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
    894 
    895     if (info_a.bits == info_b.bits)
    896         return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
    897 
    898     return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
    899     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    900 }
    901 
    902 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
    903     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    904     if (self.liveness.isUnused(inst))
    905         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
    906 
    907     const operand_ty = self.air.typeOf(ty_op.operand);
    908     const operand = try self.resolveInst(ty_op.operand);
    909     const info_a = operand_ty.intInfo(self.target.*);
    910     const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
    911 
    912     const result: MCValue = blk: {
    913         if (info_b.bits <= 32) {
    914             const operand_reg = switch (operand) {
    915                 .register => |r| r,
    916                 else => operand_reg: {
    917                     if (info_a.bits <= 32) {
    918                         break :operand_reg try self.copyToTmpRegister(operand_ty, operand);
    919                     } else {
    920                         return self.fail("TODO load least significant word into register", .{});
    921                     }
    922                 },
    923             };
    924             self.register_manager.freezeRegs(&.{operand_reg});
    925             defer self.register_manager.unfreezeRegs(&.{operand_reg});
    926 
    927             const dest_reg = dest_reg: {
    928                 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
    929                     break :dest_reg operand_reg;
    930                 }
    931 
    932                 break :dest_reg try self.register_manager.allocReg(null);
    933             };
    934 
    935             switch (info_b.bits) {
    936                 32 => {
    937                     try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
    938                     break :blk MCValue{ .register = dest_reg };
    939                 },
    940                 else => {
    941                     _ = try self.addInst(.{
    942                         .tag = switch (info_b.signedness) {
    943                             .signed => .sbfx,
    944                             .unsigned => .ubfx,
    945                         },
    946                         .data = .{ .rr_lsb_width = .{
    947                             .rd = dest_reg,
    948                             .rn = operand_reg,
    949                             .lsb = 0,
    950                             .width = @intCast(u6, info_b.bits),
    951                         } },
    952                     });
    953                     break :blk MCValue{ .register = dest_reg };
    954                 },
    955             }
    956         } else {
    957             return self.fail("TODO: truncate to ints > 32 bits", .{});
    958         }
    959     };
    960 
    961     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    962 }
    963 
    964 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
    965     const un_op = self.air.instructions.items(.data)[inst].un_op;
    966     const operand = try self.resolveInst(un_op);
    967     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
    968     return self.finishAir(inst, result, .{ un_op, .none, .none });
    969 }
    970 
    971 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
    972     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    973     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
    974         const operand = try self.resolveInst(ty_op.operand);
    975         const operand_ty = self.air.typeOf(ty_op.operand);
    976         switch (operand) {
    977             .dead => unreachable,
    978             .unreach => unreachable,
    979             .compare_flags_unsigned => |op| {
    980                 const r = MCValue{
    981                     .compare_flags_unsigned = switch (op) {
    982                         .gte => .lt,
    983                         .gt => .lte,
    984                         .neq => .eq,
    985                         .lt => .gte,
    986                         .lte => .gt,
    987                         .eq => .neq,
    988                     },
    989                 };
    990                 break :result r;
    991             },
    992             .compare_flags_signed => |op| {
    993                 const r = MCValue{
    994                     .compare_flags_signed = switch (op) {
    995                         .gte => .lt,
    996                         .gt => .lte,
    997                         .neq => .eq,
    998                         .lt => .gte,
    999                         .lte => .gt,
   1000                         .eq => .neq,
   1001                     },
   1002                 };
   1003                 break :result r;
   1004             },
   1005             else => {
   1006                 switch (operand_ty.zigTypeTag()) {
   1007                     .Bool => {
   1008                         const op_reg = switch (operand) {
   1009                             .register => |r| r,
   1010                             else => try self.copyToTmpRegister(operand_ty, operand),
   1011                         };
   1012                         self.register_manager.freezeRegs(&.{op_reg});
   1013                         defer self.register_manager.unfreezeRegs(&.{op_reg});
   1014 
   1015                         const dest_reg = blk: {
   1016                             if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
   1017                                 break :blk op_reg;
   1018                             }
   1019 
   1020                             break :blk try self.register_manager.allocReg(null);
   1021                         };
   1022 
   1023                         _ = try self.addInst(.{
   1024                             .tag = .eor,
   1025                             .data = .{ .rr_op = .{
   1026                                 .rd = dest_reg,
   1027                                 .rn = op_reg,
   1028                                 .op = Instruction.Operand.fromU32(1).?,
   1029                             } },
   1030                         });
   1031 
   1032                         break :result MCValue{ .register = dest_reg };
   1033                     },
   1034                     .Vector => return self.fail("TODO bitwise not for vectors", .{}),
   1035                     .Int => {
   1036                         const int_info = operand_ty.intInfo(self.target.*);
   1037                         if (int_info.bits <= 32) {
   1038                             const op_reg = switch (operand) {
   1039                                 .register => |r| r,
   1040                                 else => try self.copyToTmpRegister(operand_ty, operand),
   1041                             };
   1042                             self.register_manager.freezeRegs(&.{op_reg});
   1043                             defer self.register_manager.unfreezeRegs(&.{op_reg});
   1044 
   1045                             const dest_reg = blk: {
   1046                                 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
   1047                                     break :blk op_reg;
   1048                                 }
   1049 
   1050                                 break :blk try self.register_manager.allocReg(null);
   1051                             };
   1052 
   1053                             _ = try self.addInst(.{
   1054                                 .tag = .mvn,
   1055                                 .data = .{ .rr_op = .{
   1056                                     .rd = dest_reg,
   1057                                     .rn = undefined,
   1058                                     .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),
   1059                                 } },
   1060                             });
   1061 
   1062                             break :result MCValue{ .register = dest_reg };
   1063                         } else {
   1064                             return self.fail("TODO ARM not on integers > u32/i32", .{});
   1065                         }
   1066                     },
   1067                     else => unreachable,
   1068                 }
   1069             },
   1070         }
   1071     };
   1072     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1073 }
   1074 
   1075 fn airMin(self: *Self, inst: Air.Inst.Index) !void {
   1076     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1077     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
   1078     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1079 }
   1080 
   1081 fn airMax(self: *Self, inst: Air.Inst.Index) !void {
   1082     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1083     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
   1084     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1085 }
   1086 
   1087 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
   1088     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1089     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1090     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1091         const ptr = try self.resolveInst(bin_op.lhs);
   1092         const ptr_ty = self.air.typeOf(bin_op.lhs);
   1093         const len = try self.resolveInst(bin_op.rhs);
   1094         const len_ty = self.air.typeOf(bin_op.rhs);
   1095 
   1096         const stack_offset = try self.allocMem(inst, 8, 8);
   1097         try self.genSetStack(ptr_ty, stack_offset + 4, ptr);
   1098         try self.genSetStack(len_ty, stack_offset, len);
   1099         break :result MCValue{ .stack_offset = stack_offset };
   1100     };
   1101     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1102 }
   1103 
   1104 fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
   1105     const tag = self.air.instructions.items(.tag)[inst];
   1106     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1107     const lhs = try self.resolveInst(bin_op.lhs);
   1108     const rhs = try self.resolveInst(bin_op.rhs);
   1109     const lhs_ty = self.air.typeOf(bin_op.lhs);
   1110     const rhs_ty = self.air.typeOf(bin_op.rhs);
   1111 
   1112     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
   1113     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1114 }
   1115 
   1116 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
   1117     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1118     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
   1119     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1120 }
   1121 
   1122 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
   1123     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1124     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
   1125     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1126 }
   1127 
   1128 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
   1129     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1130     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
   1131     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1132 }
   1133 
   1134 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
   1135     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1136     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
   1137     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1138 }
   1139 
   1140 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
   1141     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1142     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
   1143     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1144 }
   1145 
   1146 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
   1147     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1148     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
   1149     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1150 }
   1151 
   1152 fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1153     _ = inst;
   1154     return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
   1155 }
   1156 
   1157 fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1158     _ = inst;
   1159     return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
   1160 }
   1161 
   1162 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1163     _ = inst;
   1164     return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
   1165 }
   1166 
   1167 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1168     _ = inst;
   1169     return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
   1170 }
   1171 
   1172 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
   1173     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1174     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
   1175     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1176 }
   1177 
   1178 fn airRem(self: *Self, inst: Air.Inst.Index) !void {
   1179     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1180     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
   1181     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1182 }
   1183 
   1184 fn airMod(self: *Self, inst: Air.Inst.Index) !void {
   1185     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1186     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
   1187     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1188 }
   1189 
   1190 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
   1191     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1192     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
   1193     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1194 }
   1195 
   1196 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
   1197     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1198     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
   1199     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1200 }
   1201 
   1202 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   1203     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1204     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
   1205     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1206 }
   1207 
   1208 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   1209     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1210     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
   1211     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1212 }
   1213 
   1214 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
   1215     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1216     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1217         const error_union_ty = self.air.typeOf(ty_op.operand);
   1218         const payload_ty = error_union_ty.errorUnionPayload();
   1219         const mcv = try self.resolveInst(ty_op.operand);
   1220         if (!payload_ty.hasRuntimeBits()) break :result mcv;
   1221 
   1222         return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
   1223     };
   1224     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1225 }
   1226 
   1227 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
   1228     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1229     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1230         const error_union_ty = self.air.typeOf(ty_op.operand);
   1231         const payload_ty = error_union_ty.errorUnionPayload();
   1232         if (!payload_ty.hasRuntimeBits()) break :result MCValue.none;
   1233 
   1234         return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
   1235     };
   1236     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1237 }
   1238 
   1239 // *(E!T) -> E
   1240 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1241     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1242     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});
   1243     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1244 }
   1245 
   1246 // *(E!T) -> *T
   1247 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   1248     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1249     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});
   1250     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1251 }
   1252 
   1253 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   1254     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1255     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
   1256     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1257 }
   1258 
   1259 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
   1260     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1261     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1262         const optional_ty = self.air.typeOfIndex(inst);
   1263 
   1264         // Optional with a zero-bit payload type is just a boolean true
   1265         if (optional_ty.abiSize(self.target.*) == 1)
   1266             break :result MCValue{ .immediate = 1 };
   1267 
   1268         return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
   1269     };
   1270     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1271 }
   1272 
   1273 /// T to E!T
   1274 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
   1275     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1276     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
   1277     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1278 }
   1279 
   1280 /// E to E!T
   1281 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
   1282     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1283     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1284         const error_union_ty = self.air.getRefType(ty_op.ty);
   1285         const payload_ty = error_union_ty.errorUnionPayload();
   1286         const mcv = try self.resolveInst(ty_op.operand);
   1287         if (!payload_ty.hasRuntimeBits()) break :result mcv;
   1288 
   1289         return self.fail("TODO implement wrap errunion error for non-empty payloads", .{});
   1290     };
   1291     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1292 }
   1293 
   1294 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
   1295     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1296     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1297         const mcv = try self.resolveInst(ty_op.operand);
   1298         switch (mcv) {
   1299             .dead, .unreach => unreachable,
   1300             .register => unreachable, // a slice doesn't fit in one register
   1301             .stack_argument_offset => |off| {
   1302                 break :result MCValue{ .stack_argument_offset = off + 4 };
   1303             },
   1304             .stack_offset => |off| {
   1305                 break :result MCValue{ .stack_offset = off + 4 };
   1306             },
   1307             .memory => |addr| {
   1308                 break :result MCValue{ .memory = addr };
   1309             },
   1310             else => return self.fail("TODO implement slice_ptr for {}", .{mcv}),
   1311         }
   1312     };
   1313     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1314 }
   1315 
   1316 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
   1317     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1318     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1319         const mcv = try self.resolveInst(ty_op.operand);
   1320         switch (mcv) {
   1321             .dead, .unreach => unreachable,
   1322             .register => unreachable, // a slice doesn't fit in one register
   1323             .stack_argument_offset => |off| {
   1324                 break :result MCValue{ .stack_argument_offset = off };
   1325             },
   1326             .stack_offset => |off| {
   1327                 break :result MCValue{ .stack_offset = off };
   1328             },
   1329             .memory => |addr| {
   1330                 break :result MCValue{ .memory = addr + 4 };
   1331             },
   1332             else => return self.fail("TODO implement slice_len for {}", .{mcv}),
   1333         }
   1334     };
   1335     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1336 }
   1337 
   1338 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
   1339     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1340     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1341         const mcv = try self.resolveInst(ty_op.operand);
   1342         switch (mcv) {
   1343             .dead, .unreach => unreachable,
   1344             .ptr_stack_offset => |off| {
   1345                 break :result MCValue{ .ptr_stack_offset = off + 4 };
   1346             },
   1347             else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}),
   1348         }
   1349     };
   1350     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1351 }
   1352 
   1353 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1354     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1355     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1356         const mcv = try self.resolveInst(ty_op.operand);
   1357         switch (mcv) {
   1358             .dead, .unreach => unreachable,
   1359             .ptr_stack_offset => |off| {
   1360                 break :result MCValue{ .ptr_stack_offset = off };
   1361             },
   1362             else => return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{mcv}),
   1363         }
   1364     };
   1365     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1366 }
   1367 
   1368 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1369     const is_volatile = false; // TODO
   1370     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1371 
   1372     if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   1373     const result: MCValue = result: {
   1374         const slice_mcv = try self.resolveInst(bin_op.lhs);
   1375 
   1376         // TODO optimize for the case where the index is a constant,
   1377         // i.e. index_mcv == .immediate
   1378         const index_mcv = try self.resolveInst(bin_op.rhs);
   1379         const index_is_register = index_mcv == .register;
   1380 
   1381         const slice_ty = self.air.typeOf(bin_op.lhs);
   1382         const elem_ty = slice_ty.childType();
   1383         const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
   1384 
   1385         var buf: Type.SlicePtrFieldTypeBuffer = undefined;
   1386         const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
   1387 
   1388         if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register});
   1389         defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register});
   1390 
   1391         const base_mcv: MCValue = switch (slice_mcv) {
   1392             .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 4 }) },
   1393             .stack_argument_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_argument_offset = off + 4 }) },
   1394             else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
   1395         };
   1396         self.register_manager.freezeRegs(&.{base_mcv.register});
   1397 
   1398         switch (elem_size) {
   1399             1, 4 => {
   1400                 const dst_reg = try self.register_manager.allocReg(inst);
   1401                 const dst_mcv = MCValue{ .register = dst_reg };
   1402                 self.register_manager.freezeRegs(&.{dst_reg});
   1403                 defer self.register_manager.unfreezeRegs(&.{dst_reg});
   1404 
   1405                 const index_reg: Register = switch (index_mcv) {
   1406                     .register => |reg| reg,
   1407                     else => try self.copyToTmpRegister(Type.usize, index_mcv),
   1408                 };
   1409                 self.register_manager.freezeRegs(&.{index_reg});
   1410                 defer self.register_manager.unfreezeRegs(&.{index_reg});
   1411 
   1412                 const tag: Mir.Inst.Tag = switch (elem_size) {
   1413                     1 => .ldrb,
   1414                     4 => .ldr,
   1415                     else => unreachable,
   1416                 };
   1417                 const shift: u5 = switch (elem_size) {
   1418                     1 => 0,
   1419                     4 => 2,
   1420                     else => unreachable,
   1421                 };
   1422 
   1423                 _ = try self.addInst(.{
   1424                     .tag = tag,
   1425                     .data = .{ .rr_offset = .{
   1426                         .rt = dst_reg,
   1427                         .rn = base_mcv.register,
   1428                         .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
   1429                     } },
   1430                 });
   1431 
   1432                 self.register_manager.unfreezeRegs(&.{base_mcv.register});
   1433 
   1434                 break :result dst_mcv;
   1435             },
   1436             else => {
   1437                 const dest = try self.allocRegOrMem(inst, true);
   1438                 const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize);
   1439                 try self.load(dest, addr, slice_ptr_field_type);
   1440 
   1441                 break :result dest;
   1442             },
   1443         }
   1444     };
   1445     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1446 }
   1447 
   1448 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   1449     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1450     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1451     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
   1452     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1453 }
   1454 
   1455 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1456     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1457     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
   1458     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1459 }
   1460 
   1461 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1462     const is_volatile = false; // TODO
   1463     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1464     const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch});
   1465     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1466 }
   1467 
   1468 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   1469     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1470     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1471     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
   1472     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1473 }
   1474 
   1475 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   1476     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1477     _ = bin_op;
   1478     return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
   1479     // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1480 }
   1481 
   1482 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   1483     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1484     _ = ty_op;
   1485     return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
   1486     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1487 }
   1488 
   1489 fn airClz(self: *Self, inst: Air.Inst.Index) !void {
   1490     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1491     _ = ty_op;
   1492     return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
   1493     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1494 }
   1495 
   1496 fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
   1497     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1498     _ = ty_op;
   1499     return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
   1500     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1501 }
   1502 
   1503 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
   1504     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1505     _ = ty_op;
   1506     return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
   1507     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1508 }
   1509 
   1510 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
   1511     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1512     _ = ty_op;
   1513     return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
   1514     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1515 }
   1516 
   1517 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
   1518     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1519     _ = ty_op;
   1520     return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
   1521     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1522 }
   1523 
   1524 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
   1525     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1526     const result: MCValue = if (self.liveness.isUnused(inst))
   1527         .dead
   1528     else
   1529         return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
   1530     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1531 }
   1532 
   1533 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
   1534     if (!self.liveness.operandDies(inst, op_index))
   1535         return false;
   1536 
   1537     switch (mcv) {
   1538         .register => |reg| {
   1539             // If it's in the registers table, need to associate the register with the
   1540             // new instruction.
   1541             if (reg.allocIndex()) |index| {
   1542                 if (!self.register_manager.isRegFree(reg)) {
   1543                     self.register_manager.registers[index] = inst;
   1544                 }
   1545             }
   1546             log.debug("%{d} => {} (reused)", .{ inst, reg });
   1547         },
   1548         .stack_offset => |off| {
   1549             log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
   1550         },
   1551         else => return false,
   1552     }
   1553 
   1554     // Prevent the operand deaths processing code from deallocating it.
   1555     self.liveness.clearOperandDeath(inst, op_index);
   1556 
   1557     // That makes us responsible for doing the rest of the stuff that processDeath would have done.
   1558     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   1559     branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
   1560 
   1561     return true;
   1562 }
   1563 
   1564 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
   1565     const elem_ty = ptr_ty.elemType();
   1566     const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
   1567 
   1568     switch (ptr) {
   1569         .none => unreachable,
   1570         .undef => unreachable,
   1571         .unreach => unreachable,
   1572         .dead => unreachable,
   1573         .compare_flags_unsigned => unreachable,
   1574         .compare_flags_signed => unreachable,
   1575         .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
   1576         .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
   1577         .ptr_embedded_in_code => |off| {
   1578             try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
   1579         },
   1580         .embedded_in_code => {
   1581             return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
   1582         },
   1583         .register => |reg| {
   1584             self.register_manager.freezeRegs(&.{reg});
   1585             defer self.register_manager.unfreezeRegs(&.{reg});
   1586 
   1587             switch (dst_mcv) {
   1588                 .dead => unreachable,
   1589                 .undef => unreachable,
   1590                 .compare_flags_signed, .compare_flags_unsigned => unreachable,
   1591                 .embedded_in_code => unreachable,
   1592                 .register => |dst_reg| {
   1593                     try self.genLdrRegister(dst_reg, reg, elem_ty);
   1594                 },
   1595                 .stack_offset => |off| {
   1596                     if (elem_size <= 4) {
   1597                         const tmp_reg = try self.register_manager.allocReg(null);
   1598                         self.register_manager.freezeRegs(&.{tmp_reg});
   1599                         defer self.register_manager.unfreezeRegs(&.{tmp_reg});
   1600 
   1601                         try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
   1602                         try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg });
   1603                     } else {
   1604                         // TODO optimize the register allocation
   1605                         const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
   1606                         self.register_manager.freezeRegs(&regs);
   1607                         defer self.register_manager.unfreezeRegs(&regs);
   1608 
   1609                         const src_reg = reg;
   1610                         const dst_reg = regs[0];
   1611                         const len_reg = regs[1];
   1612                         const count_reg = regs[2];
   1613                         const tmp_reg = regs[3];
   1614 
   1615                         // sub dst_reg, fp, #off
   1616                         try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
   1617 
   1618                         // mov len, #elem_size
   1619                         try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
   1620 
   1621                         // memcpy(src, dst, len)
   1622                         try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   1623                     }
   1624                 },
   1625                 else => return self.fail("TODO load from register into {}", .{dst_mcv}),
   1626             }
   1627         },
   1628         .memory,
   1629         .stack_offset,
   1630         .stack_argument_offset,
   1631         => {
   1632             const reg = try self.register_manager.allocReg(null);
   1633             self.register_manager.freezeRegs(&.{reg});
   1634             defer self.register_manager.unfreezeRegs(&.{reg});
   1635 
   1636             try self.genSetReg(ptr_ty, reg, ptr);
   1637             try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
   1638         },
   1639     }
   1640 }
   1641 
   1642 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
   1643     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1644     const elem_ty = self.air.typeOfIndex(inst);
   1645     const result: MCValue = result: {
   1646         if (!elem_ty.hasRuntimeBits())
   1647             break :result MCValue.none;
   1648 
   1649         const ptr = try self.resolveInst(ty_op.operand);
   1650         const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
   1651         if (self.liveness.isUnused(inst) and !is_volatile)
   1652             break :result MCValue.dead;
   1653 
   1654         const dst_mcv: MCValue = blk: {
   1655             if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
   1656                 // The MCValue that holds the pointer can be re-used as the value.
   1657                 break :blk ptr;
   1658             } else {
   1659                 break :blk try self.allocRegOrMem(inst, true);
   1660             }
   1661         };
   1662         try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
   1663         break :result dst_mcv;
   1664     };
   1665     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1666 }
   1667 
   1668 fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
   1669     const elem_size = @intCast(u32, value_ty.abiSize(self.target.*));
   1670 
   1671     switch (ptr) {
   1672         .none => unreachable,
   1673         .undef => unreachable,
   1674         .unreach => unreachable,
   1675         .dead => unreachable,
   1676         .compare_flags_unsigned => unreachable,
   1677         .compare_flags_signed => unreachable,
   1678         .immediate => |imm| {
   1679             try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
   1680         },
   1681         .ptr_stack_offset => |off| {
   1682             try self.genSetStack(value_ty, off, value);
   1683         },
   1684         .ptr_embedded_in_code => |off| {
   1685             try self.setRegOrMem(value_ty, .{ .embedded_in_code = off }, value);
   1686         },
   1687         .embedded_in_code => {
   1688             return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
   1689         },
   1690         .register => |addr_reg| {
   1691             self.register_manager.freezeRegs(&.{addr_reg});
   1692             defer self.register_manager.unfreezeRegs(&.{addr_reg});
   1693 
   1694             switch (value) {
   1695                 .dead => unreachable,
   1696                 .undef => unreachable,
   1697                 .register => |value_reg| {
   1698                     try self.genStrRegister(value_reg, addr_reg, value_ty);
   1699                 },
   1700                 else => {
   1701                     if (value_ty.abiSize(self.target.*) <= 4) {
   1702                         const tmp_reg = try self.register_manager.allocReg(null);
   1703                         self.register_manager.freezeRegs(&.{tmp_reg});
   1704                         defer self.register_manager.unfreezeRegs(&.{tmp_reg});
   1705 
   1706                         try self.genSetReg(value_ty, tmp_reg, value);
   1707                         try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
   1708                     } else {
   1709                         const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
   1710                         self.register_manager.freezeRegs(&regs);
   1711                         defer self.register_manager.unfreezeRegs(&regs);
   1712 
   1713                         const src_reg = regs[0];
   1714                         const dst_reg = addr_reg;
   1715                         const len_reg = regs[1];
   1716                         const count_reg = regs[2];
   1717                         const tmp_reg = regs[3];
   1718 
   1719                         switch (value) {
   1720                             .stack_offset => |off| {
   1721                                 // sub src_reg, fp, #off
   1722                                 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
   1723                             },
   1724                             .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }),
   1725                             else => return self.fail("TODO store {} to register", .{value}),
   1726                         }
   1727 
   1728                         // mov len, #elem_size
   1729                         try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
   1730 
   1731                         // memcpy(src, dst, len)
   1732                         try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   1733                     }
   1734                 },
   1735             }
   1736         },
   1737         .memory,
   1738         .stack_offset,
   1739         .stack_argument_offset,
   1740         => {
   1741             const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr);
   1742             try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty);
   1743         },
   1744     }
   1745 }
   1746 
   1747 fn airStore(self: *Self, inst: Air.Inst.Index) !void {
   1748     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1749     const ptr = try self.resolveInst(bin_op.lhs);
   1750     const value = try self.resolveInst(bin_op.rhs);
   1751     const ptr_ty = self.air.typeOf(bin_op.lhs);
   1752     const value_ty = self.air.typeOf(bin_op.rhs);
   1753 
   1754     try self.store(ptr, value, ptr_ty, value_ty);
   1755 
   1756     return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   1757 }
   1758 
   1759 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
   1760     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1761     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   1762     const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
   1763     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
   1764 }
   1765 
   1766 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
   1767     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1768     const result = try self.structFieldPtr(inst, ty_op.operand, index);
   1769     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1770 }
   1771 
   1772 fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
   1773     return if (self.liveness.isUnused(inst)) .dead else result: {
   1774         const mcv = try self.resolveInst(operand);
   1775         const ptr_ty = self.air.typeOf(operand);
   1776         const struct_ty = ptr_ty.childType();
   1777         const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*));
   1778         const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
   1779         const struct_field_ty = struct_ty.structFieldType(index);
   1780         const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
   1781         switch (mcv) {
   1782             .ptr_stack_offset => |off| {
   1783                 break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size };
   1784             },
   1785             else => {
   1786                 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
   1787                     .immediate = struct_field_offset,
   1788                 });
   1789                 self.register_manager.freezeRegs(&.{offset_reg});
   1790                 defer self.register_manager.unfreezeRegs(&.{offset_reg});
   1791 
   1792                 const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv);
   1793                 self.register_manager.freezeRegs(&.{addr_reg});
   1794                 defer self.register_manager.unfreezeRegs(&.{addr_reg});
   1795 
   1796                 const dest = try self.binOp(
   1797                     .add,
   1798                     null,
   1799                     .{ .register = addr_reg },
   1800                     .{ .register = offset_reg },
   1801                     Type.usize,
   1802                     Type.usize,
   1803                 );
   1804 
   1805                 break :result dest;
   1806             },
   1807         }
   1808     };
   1809 }
   1810 
   1811 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
   1812     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1813     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   1814     const operand = extra.struct_operand;
   1815     const index = extra.field_index;
   1816     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1817         const mcv = try self.resolveInst(operand);
   1818         const struct_ty = self.air.typeOf(operand);
   1819         const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*));
   1820         const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
   1821         const struct_field_ty = struct_ty.structFieldType(index);
   1822         const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
   1823         const adjusted_field_offset = struct_size - struct_field_offset - struct_field_size;
   1824 
   1825         switch (mcv) {
   1826             .dead, .unreach => unreachable,
   1827             .stack_argument_offset => |off| {
   1828                 break :result MCValue{ .stack_argument_offset = off + adjusted_field_offset };
   1829             },
   1830             .stack_offset => |off| {
   1831                 break :result MCValue{ .stack_offset = off + adjusted_field_offset };
   1832             },
   1833             .memory => |addr| {
   1834                 break :result MCValue{ .memory = addr + adjusted_field_offset };
   1835             },
   1836             else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
   1837         }
   1838     };
   1839 
   1840     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
   1841 }
   1842 
   1843 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
   1844     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1845     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1846     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFieldParentPtr", .{});
   1847     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1848 }
   1849 
   1850 /// Don't call this function directly. Use binOp instead.
   1851 ///
   1852 /// Calling this function signals an intention to generate a Mir
   1853 /// instruction of the form
   1854 ///
   1855 ///     op dest, lhs, rhs
   1856 ///
   1857 /// Asserts that generating an instruction of that form is possible.
   1858 fn binOpRegister(
   1859     self: *Self,
   1860     tag: Air.Inst.Tag,
   1861     maybe_inst: ?Air.Inst.Index,
   1862     lhs: MCValue,
   1863     rhs: MCValue,
   1864     lhs_ty: Type,
   1865     rhs_ty: Type,
   1866 ) !MCValue {
   1867     const lhs_is_register = lhs == .register;
   1868     const rhs_is_register = rhs == .register;
   1869 
   1870     if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
   1871     if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
   1872 
   1873     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   1874 
   1875     const lhs_reg = if (lhs_is_register) lhs.register else blk: {
   1876         const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
   1877             const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1878             break :inst Air.refToIndex(bin_op.lhs).?;
   1879         } else null;
   1880 
   1881         const reg = try self.register_manager.allocReg(track_inst);
   1882         self.register_manager.freezeRegs(&.{reg});
   1883 
   1884         if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
   1885 
   1886         break :blk reg;
   1887     };
   1888     defer self.register_manager.unfreezeRegs(&.{lhs_reg});
   1889 
   1890     const rhs_reg = if (rhs_is_register) rhs.register else blk: {
   1891         const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
   1892             const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1893             break :inst Air.refToIndex(bin_op.rhs).?;
   1894         } else null;
   1895 
   1896         const reg = try self.register_manager.allocReg(track_inst);
   1897         self.register_manager.freezeRegs(&.{reg});
   1898 
   1899         if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
   1900 
   1901         break :blk reg;
   1902     };
   1903     defer self.register_manager.unfreezeRegs(&.{rhs_reg});
   1904 
   1905     const dest_reg = switch (tag) {
   1906         .cmp_eq => .r0, // cmp has no destination regardless
   1907         else => if (maybe_inst) |inst| blk: {
   1908             const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1909 
   1910             if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
   1911                 break :blk lhs_reg;
   1912             } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
   1913                 break :blk rhs_reg;
   1914             } else {
   1915                 break :blk try self.register_manager.allocReg(inst);
   1916             }
   1917         } else try self.register_manager.allocReg(null),
   1918     };
   1919 
   1920     if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
   1921     if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
   1922 
   1923     const mir_tag: Mir.Inst.Tag = switch (tag) {
   1924         .add, .ptr_add => .add,
   1925         .sub, .ptr_sub => .sub,
   1926         .cmp_eq => .cmp,
   1927         .mul => .mul,
   1928         .bit_and,
   1929         .bool_and,
   1930         => .@"and",
   1931         .bit_or,
   1932         .bool_or,
   1933         => .orr,
   1934         .shl,
   1935         .shl_exact,
   1936         => .lsl,
   1937         .shr,
   1938         .shr_exact,
   1939         => switch (lhs_ty.intInfo(self.target.*).signedness) {
   1940             .signed => Mir.Inst.Tag.asr,
   1941             .unsigned => Mir.Inst.Tag.lsr,
   1942         },
   1943         .xor => .eor,
   1944         else => unreachable,
   1945     };
   1946     const mir_data: Mir.Inst.Data = switch (tag) {
   1947         .add,
   1948         .sub,
   1949         .cmp_eq,
   1950         .bit_and,
   1951         .bool_and,
   1952         .bit_or,
   1953         .bool_or,
   1954         .xor,
   1955         .ptr_add,
   1956         .ptr_sub,
   1957         => .{ .rr_op = .{
   1958             .rd = dest_reg,
   1959             .rn = lhs_reg,
   1960             .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
   1961         } },
   1962         .shl,
   1963         .shl_exact,
   1964         .shr,
   1965         .shr_exact,
   1966         => .{ .rr_shift = .{
   1967             .rd = dest_reg,
   1968             .rm = lhs_reg,
   1969             .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
   1970         } },
   1971         .mul => .{ .rrr = .{
   1972             .rd = dest_reg,
   1973             .rn = lhs_reg,
   1974             .rm = rhs_reg,
   1975         } },
   1976         else => unreachable,
   1977     };
   1978 
   1979     _ = try self.addInst(.{
   1980         .tag = mir_tag,
   1981         .data = mir_data,
   1982     });
   1983 
   1984     return MCValue{ .register = dest_reg };
   1985 }
   1986 
   1987 /// Don't call this function directly. Use binOp instead.
   1988 ///
   1989 /// Calling this function signals an intention to generate a Mir
   1990 /// instruction of the form
   1991 ///
   1992 ///     op dest, lhs, #rhs_imm
   1993 ///
   1994 /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to
   1995 /// rhs and vice versa. This parameter is only used when maybe_inst !=
   1996 /// null.
   1997 ///
   1998 /// Asserts that generating an instruction of that form is possible.
   1999 fn binOpImmediate(
   2000     self: *Self,
   2001     tag: Air.Inst.Tag,
   2002     maybe_inst: ?Air.Inst.Index,
   2003     lhs: MCValue,
   2004     rhs: MCValue,
   2005     lhs_ty: Type,
   2006     lhs_and_rhs_swapped: bool,
   2007 ) !MCValue {
   2008     const lhs_is_register = lhs == .register;
   2009 
   2010     if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
   2011 
   2012     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   2013 
   2014     const lhs_reg = if (lhs_is_register) lhs.register else blk: {
   2015         const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
   2016             const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2017             break :inst Air.refToIndex(
   2018                 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
   2019             ).?;
   2020         } else null;
   2021 
   2022         const reg = try self.register_manager.allocReg(track_inst);
   2023         self.register_manager.freezeRegs(&.{reg});
   2024 
   2025         if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
   2026 
   2027         break :blk reg;
   2028     };
   2029     defer self.register_manager.unfreezeRegs(&.{lhs_reg});
   2030 
   2031     const dest_reg = switch (tag) {
   2032         .cmp_eq => .r0, // cmp has no destination reg
   2033         else => if (maybe_inst) |inst| blk: {
   2034             const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2035 
   2036             if (lhs_is_register and self.reuseOperand(
   2037                 inst,
   2038                 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
   2039                 if (lhs_and_rhs_swapped) 1 else 0,
   2040                 lhs,
   2041             )) {
   2042                 break :blk lhs_reg;
   2043             } else {
   2044                 break :blk try self.register_manager.allocReg(inst);
   2045             }
   2046         } else try self.register_manager.allocReg(null),
   2047     };
   2048 
   2049     if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
   2050 
   2051     const mir_tag: Mir.Inst.Tag = switch (tag) {
   2052         .add => .add,
   2053         .sub => .sub,
   2054         .cmp_eq => .cmp,
   2055         .bit_and,
   2056         .bool_and,
   2057         => .@"and",
   2058         .bit_or,
   2059         .bool_or,
   2060         => .orr,
   2061         .shl,
   2062         .shl_exact,
   2063         => .lsl,
   2064         .shr,
   2065         .shr_exact,
   2066         => switch (lhs_ty.intInfo(self.target.*).signedness) {
   2067             .signed => Mir.Inst.Tag.asr,
   2068             .unsigned => Mir.Inst.Tag.lsr,
   2069         },
   2070         .xor => .eor,
   2071         else => unreachable,
   2072     };
   2073     const mir_data: Mir.Inst.Data = switch (tag) {
   2074         .add,
   2075         .sub,
   2076         .cmp_eq,
   2077         .bit_and,
   2078         .bool_and,
   2079         .bit_or,
   2080         .bool_or,
   2081         .xor,
   2082         => .{ .rr_op = .{
   2083             .rd = dest_reg,
   2084             .rn = lhs_reg,
   2085             .op = Instruction.Operand.fromU32(rhs.immediate).?,
   2086         } },
   2087         .shl,
   2088         .shl_exact,
   2089         .shr,
   2090         .shr_exact,
   2091         => .{ .rr_shift = .{
   2092             .rd = dest_reg,
   2093             .rm = lhs_reg,
   2094             .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)),
   2095         } },
   2096         else => unreachable,
   2097     };
   2098 
   2099     _ = try self.addInst(.{
   2100         .tag = mir_tag,
   2101         .data = mir_data,
   2102     });
   2103 
   2104     return MCValue{ .register = dest_reg };
   2105 }
   2106 
   2107 /// For all your binary operation needs, this function will generate
   2108 /// the corresponding Mir instruction(s). Returns the location of the
   2109 /// result.
   2110 ///
   2111 /// If the binary operation itself happens to be an Air instruction,
   2112 /// pass the corresponding index in the inst parameter. That helps
   2113 /// this function do stuff like reusing operands.
   2114 ///
   2115 /// This function does not do any lowering to Mir itself, but instead
   2116 /// looks at the lhs and rhs and determines which kind of lowering
   2117 /// would be best suitable and then delegates the lowering to other
   2118 /// functions.
   2119 fn binOp(
   2120     self: *Self,
   2121     tag: Air.Inst.Tag,
   2122     maybe_inst: ?Air.Inst.Index,
   2123     lhs: MCValue,
   2124     rhs: MCValue,
   2125     lhs_ty: Type,
   2126     rhs_ty: Type,
   2127 ) InnerError!MCValue {
   2128     switch (tag) {
   2129         .add,
   2130         .sub,
   2131         .cmp_eq,
   2132         => {
   2133             switch (lhs_ty.zigTypeTag()) {
   2134                 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   2135                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   2136                 .Int => {
   2137                     assert(lhs_ty.eql(rhs_ty));
   2138                     const int_info = lhs_ty.intInfo(self.target.*);
   2139                     if (int_info.bits <= 32) {
   2140                         // Only say yes if the operation is
   2141                         // commutative, i.e. we can swap both of the
   2142                         // operands
   2143                         const lhs_immediate_ok = switch (tag) {
   2144                             .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null,
   2145                             .sub,
   2146                             .cmp_eq,
   2147                             => false,
   2148                             else => unreachable,
   2149                         };
   2150                         const rhs_immediate_ok = switch (tag) {
   2151                             .add,
   2152                             .sub,
   2153                             .cmp_eq,
   2154                             => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null,
   2155                             else => unreachable,
   2156                         };
   2157 
   2158                         if (rhs_immediate_ok) {
   2159                             return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
   2160                         } else if (lhs_immediate_ok) {
   2161                             // swap lhs and rhs
   2162                             return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
   2163                         } else {
   2164                             return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   2165                         }
   2166                     } else {
   2167                         return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   2168                     }
   2169                 },
   2170                 else => unreachable,
   2171             }
   2172         },
   2173         .mul => {
   2174             switch (lhs_ty.zigTypeTag()) {
   2175                 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
   2176                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   2177                 .Int => {
   2178                     assert(lhs_ty.eql(rhs_ty));
   2179                     const int_info = lhs_ty.intInfo(self.target.*);
   2180                     if (int_info.bits <= 32) {
   2181                         // TODO add optimisations for multiplication
   2182                         // with immediates, for example a * 2 can be
   2183                         // lowered to a << 1
   2184                         return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   2185                     } else {
   2186                         return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   2187                     }
   2188                 },
   2189                 else => unreachable,
   2190             }
   2191         },
   2192         .bit_and,
   2193         .bit_or,
   2194         .xor,
   2195         => {
   2196             switch (lhs_ty.zigTypeTag()) {
   2197                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   2198                 .Int => {
   2199                     assert(lhs_ty.eql(rhs_ty));
   2200                     const int_info = lhs_ty.intInfo(self.target.*);
   2201                     if (int_info.bits <= 32) {
   2202                         const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
   2203                         const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null;
   2204 
   2205                         if (rhs_immediate_ok) {
   2206                             return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
   2207                         } else if (lhs_immediate_ok) {
   2208                             // swap lhs and rhs
   2209                             return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
   2210                         } else {
   2211                             return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   2212                         }
   2213                     } else {
   2214                         return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   2215                     }
   2216                 },
   2217                 else => unreachable,
   2218             }
   2219         },
   2220         .shl,
   2221         .shr,
   2222         => {
   2223             switch (lhs_ty.zigTypeTag()) {
   2224                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
   2225                 .Int => {
   2226                     const int_info = lhs_ty.intInfo(self.target.*);
   2227                     if (int_info.bits <= 32) {
   2228                         const rhs_immediate_ok = rhs == .immediate;
   2229 
   2230                         if (rhs_immediate_ok) {
   2231                             return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
   2232                         } else {
   2233                             return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   2234                         }
   2235                     } else {
   2236                         return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
   2237                     }
   2238                 },
   2239                 else => unreachable,
   2240             }
   2241         },
   2242         .bool_and,
   2243         .bool_or,
   2244         => {
   2245             switch (lhs_ty.zigTypeTag()) {
   2246                 .Bool => {
   2247                     const lhs_immediate_ok = lhs == .immediate;
   2248                     const rhs_immediate_ok = rhs == .immediate;
   2249 
   2250                     if (rhs_immediate_ok) {
   2251                         return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
   2252                     } else if (lhs_immediate_ok) {
   2253                         // swap lhs and rhs
   2254                         return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
   2255                     } else {
   2256                         return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   2257                     }
   2258                 },
   2259                 else => unreachable,
   2260             }
   2261         },
   2262         .ptr_add,
   2263         .ptr_sub,
   2264         => {
   2265             switch (lhs_ty.zigTypeTag()) {
   2266                 .Pointer => {
   2267                     const ptr_ty = lhs_ty;
   2268                     const elem_ty = switch (ptr_ty.ptrSize()) {
   2269                         .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
   2270                         else => ptr_ty.childType(),
   2271                     };
   2272                     const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
   2273 
   2274                     if (elem_size == 1) {
   2275                         return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   2276                     } else {
   2277                         // convert the offset into a byte offset by
   2278                         // multiplying it with elem_size
   2279                         const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize);
   2280                         const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize);
   2281                         return addr;
   2282                     }
   2283                 },
   2284                 else => unreachable,
   2285             }
   2286         },
   2287         else => unreachable,
   2288     }
   2289 }
   2290 
   2291 fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void {
   2292     const abi_size = ty.abiSize(self.target.*);
   2293 
   2294     const tag: Mir.Inst.Tag = switch (abi_size) {
   2295         1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb,
   2296         2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh else .ldrh,
   2297         3, 4 => .ldr,
   2298         else => unreachable,
   2299     };
   2300 
   2301     const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
   2302         .rt = dest_reg,
   2303         .rn = addr_reg,
   2304         .offset = .{ .offset = Instruction.Offset.none },
   2305     } };
   2306     const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
   2307         .rt = dest_reg,
   2308         .rn = addr_reg,
   2309         .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
   2310     } };
   2311 
   2312     const data: Mir.Inst.Data = switch (abi_size) {
   2313         1 => if (ty.isSignedInt()) rr_extra_offset else rr_offset,
   2314         2 => rr_extra_offset,
   2315         3, 4 => rr_offset,
   2316         else => unreachable,
   2317     };
   2318 
   2319     _ = try self.addInst(.{
   2320         .tag = tag,
   2321         .data = data,
   2322     });
   2323 }
   2324 
   2325 fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void {
   2326     const abi_size = ty.abiSize(self.target.*);
   2327 
   2328     const tag: Mir.Inst.Tag = switch (abi_size) {
   2329         1 => .strb,
   2330         2 => .strh,
   2331         3, 4 => .str,
   2332         else => unreachable,
   2333     };
   2334 
   2335     const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
   2336         .rt = source_reg,
   2337         .rn = addr_reg,
   2338         .offset = .{ .offset = Instruction.Offset.none },
   2339     } };
   2340     const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
   2341         .rt = source_reg,
   2342         .rn = addr_reg,
   2343         .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
   2344     } };
   2345 
   2346     const data: Mir.Inst.Data = switch (abi_size) {
   2347         1, 3, 4 => rr_offset,
   2348         2 => rr_extra_offset,
   2349         else => unreachable,
   2350     };
   2351 
   2352     _ = try self.addInst(.{
   2353         .tag = tag,
   2354         .data = data,
   2355     });
   2356 }
   2357 
   2358 fn genInlineMemcpy(
   2359     self: *Self,
   2360     src: Register,
   2361     dst: Register,
   2362     len: Register,
   2363     count: Register,
   2364     tmp: Register,
   2365 ) !void {
   2366     // mov count, #0
   2367     _ = try self.addInst(.{
   2368         .tag = .mov,
   2369         .data = .{ .rr_op = .{
   2370             .rd = count,
   2371             .rn = .r0,
   2372             .op = Instruction.Operand.imm(0, 0),
   2373         } },
   2374     });
   2375 
   2376     // loop:
   2377     // cmp count, len
   2378     _ = try self.addInst(.{
   2379         .tag = .cmp,
   2380         .data = .{ .rr_op = .{
   2381             .rd = .r0,
   2382             .rn = count,
   2383             .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
   2384         } },
   2385     });
   2386 
   2387     // bge end
   2388     _ = try self.addInst(.{
   2389         .tag = .b,
   2390         .cond = .ge,
   2391         .data = .{ .inst = @intCast(u32, self.mir_instructions.len + 5) },
   2392     });
   2393 
   2394     // ldrb tmp, [src, count]
   2395     _ = try self.addInst(.{
   2396         .tag = .ldrb,
   2397         .data = .{ .rr_offset = .{
   2398             .rt = tmp,
   2399             .rn = src,
   2400             .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
   2401         } },
   2402     });
   2403 
   2404     // strb tmp, [src, count]
   2405     _ = try self.addInst(.{
   2406         .tag = .strb,
   2407         .data = .{ .rr_offset = .{
   2408             .rt = tmp,
   2409             .rn = dst,
   2410             .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
   2411         } },
   2412     });
   2413 
   2414     // add count, count, #1
   2415     _ = try self.addInst(.{
   2416         .tag = .add,
   2417         .data = .{ .rr_op = .{
   2418             .rd = count,
   2419             .rn = count,
   2420             .op = Instruction.Operand.imm(1, 0),
   2421         } },
   2422     });
   2423 
   2424     // b loop
   2425     _ = try self.addInst(.{
   2426         .tag = .b,
   2427         .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 5) },
   2428     });
   2429 
   2430     // end:
   2431 }
   2432 
   2433 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
   2434     const arg_index = self.arg_index;
   2435     self.arg_index += 1;
   2436 
   2437     const ty = self.air.typeOfIndex(inst);
   2438 
   2439     const result = self.args[arg_index];
   2440     const mcv = switch (result) {
   2441         // Copy registers to the stack
   2442         .register => |reg| blk: {
   2443             const abi_size = @intCast(u32, ty.abiSize(self.target.*));
   2444             const abi_align = ty.abiAlignment(self.target.*);
   2445             const stack_offset = try self.allocMem(inst, abi_size, abi_align);
   2446             try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
   2447 
   2448             break :blk MCValue{ .stack_offset = stack_offset };
   2449         },
   2450         else => result,
   2451     };
   2452 
   2453     _ = try self.addInst(.{
   2454         .tag = .dbg_arg,
   2455         .cond = undefined,
   2456         .data = .{ .dbg_arg_info = .{
   2457             .air_inst = inst,
   2458             .arg_index = arg_index,
   2459         } },
   2460     });
   2461 
   2462     if (self.liveness.isUnused(inst))
   2463         return self.finishAirBookkeeping();
   2464 
   2465     switch (mcv) {
   2466         .register => |reg| {
   2467             self.register_manager.getRegAssumeFree(reg, inst);
   2468         },
   2469         else => {},
   2470     }
   2471 
   2472     return self.finishAir(inst, mcv, .{ .none, .none, .none });
   2473 }
   2474 
   2475 fn airBreakpoint(self: *Self) !void {
   2476     _ = try self.addInst(.{
   2477         .tag = .bkpt,
   2478         .data = .{ .imm16 = 0 },
   2479     });
   2480     return self.finishAirBookkeeping();
   2481 }
   2482 
   2483 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
   2484     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for arm", .{});
   2485     return self.finishAir(inst, result, .{ .none, .none, .none });
   2486 }
   2487 
   2488 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
   2489     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for arm", .{});
   2490     return self.finishAir(inst, result, .{ .none, .none, .none });
   2491 }
   2492 
   2493 fn airFence(self: *Self) !void {
   2494     return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
   2495     //return self.finishAirBookkeeping();
   2496 }
   2497 
   2498 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
   2499     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   2500     const callee = pl_op.operand;
   2501     const extra = self.air.extraData(Air.Call, pl_op.payload);
   2502     const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
   2503     const ty = self.air.typeOf(callee);
   2504 
   2505     const fn_ty = switch (ty.zigTypeTag()) {
   2506         .Fn => ty,
   2507         .Pointer => ty.childType(),
   2508         else => unreachable,
   2509     };
   2510 
   2511     var info = try self.resolveCallingConventionValues(fn_ty);
   2512     defer info.deinit(self);
   2513 
   2514     // According to the Procedure Call Standard for the ARM
   2515     // Architecture, compare flags are not preserved across
   2516     // calls. Therefore, if some value is currently stored there, we
   2517     // need to save it.
   2518     //
   2519     // TODO once caller-saved registers are implemented, save them
   2520     // here too, but crucially *after* we save the compare flags as
   2521     // saving compare flags may require a new caller-saved register
   2522     try self.spillCompareFlagsIfOccupied();
   2523 
   2524     if (info.return_value == .stack_offset) {
   2525         const ret_ty = fn_ty.fnReturnType();
   2526         const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
   2527         const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
   2528         const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align);
   2529 
   2530         var ptr_ty_payload: Type.Payload.ElemType = .{
   2531             .base = .{ .tag = .single_mut_pointer },
   2532             .data = ret_ty,
   2533         };
   2534         const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
   2535         try self.register_manager.getReg(.r0, inst);
   2536         try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });
   2537 
   2538         info.return_value = .{ .stack_offset = stack_offset };
   2539     }
   2540 
   2541     // Make space for the arguments passed via the stack
   2542     self.max_end_stack += info.stack_byte_count;
   2543 
   2544     for (info.args) |mc_arg, arg_i| {
   2545         const arg = args[arg_i];
   2546         const arg_ty = self.air.typeOf(arg);
   2547         const arg_mcv = try self.resolveInst(args[arg_i]);
   2548 
   2549         switch (mc_arg) {
   2550             .none => continue,
   2551             .undef => unreachable,
   2552             .immediate => unreachable,
   2553             .unreach => unreachable,
   2554             .dead => unreachable,
   2555             .embedded_in_code => unreachable,
   2556             .memory => unreachable,
   2557             .compare_flags_signed => unreachable,
   2558             .compare_flags_unsigned => unreachable,
   2559             .ptr_stack_offset => unreachable,
   2560             .ptr_embedded_in_code => unreachable,
   2561             .register => |reg| {
   2562                 try self.register_manager.getReg(reg, null);
   2563                 try self.genSetReg(arg_ty, reg, arg_mcv);
   2564             },
   2565             .stack_offset => unreachable,
   2566             .stack_argument_offset => |offset| try self.genSetStackArgument(
   2567                 arg_ty,
   2568                 info.stack_byte_count - offset,
   2569                 arg_mcv,
   2570             ),
   2571         }
   2572     }
   2573 
   2574     // Due to incremental compilation, how function calls are generated depends
   2575     // on linking.
   2576     switch (self.bin_file.tag) {
   2577         .elf, .coff => {
   2578             if (self.air.value(callee)) |func_value| {
   2579                 if (func_value.castTag(.function)) |func_payload| {
   2580                     const func = func_payload.data;
   2581                     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
   2582                     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
   2583                     const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
   2584                         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
   2585                         break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
   2586                     } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
   2587                         coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
   2588                     else
   2589                         unreachable;
   2590 
   2591                     try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
   2592                 } else if (func_value.castTag(.extern_fn)) |_| {
   2593                     return self.fail("TODO implement calling extern functions", .{});
   2594                 } else {
   2595                     return self.fail("TODO implement calling bitcasted functions", .{});
   2596                 }
   2597             } else {
   2598                 assert(ty.zigTypeTag() == .Pointer);
   2599                 const mcv = try self.resolveInst(callee);
   2600 
   2601                 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
   2602             }
   2603 
   2604             // TODO: add Instruction.supportedOn
   2605             // function for ARM
   2606             if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
   2607                 _ = try self.addInst(.{
   2608                     .tag = .blx,
   2609                     .data = .{ .reg = .lr },
   2610                 });
   2611             } else {
   2612                 return self.fail("TODO fix blx emulation for ARM <v5", .{});
   2613                 // _ = try self.addInst(.{
   2614                 //     .tag = .mov,
   2615                 //     .data = .{ .rr_op = .{
   2616                 //         .rd = .lr,
   2617                 //         .rn = .r0,
   2618                 //         .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
   2619                 //     } },
   2620                 // });
   2621                 // _ = try self.addInst(.{
   2622                 //     .tag = .bx,
   2623                 //     .data = .{ .reg = .lr },
   2624                 // });
   2625             }
   2626         },
   2627         .macho => unreachable, // unsupported architecture for MachO
   2628         .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
   2629         else => unreachable,
   2630     }
   2631 
   2632     const result: MCValue = result: {
   2633         switch (info.return_value) {
   2634             .register => |reg| {
   2635                 if (Register.allocIndex(reg) == null) {
   2636                     // Save function return value in a callee saved register
   2637                     break :result try self.copyToNewRegister(inst, info.return_value);
   2638                 }
   2639             },
   2640             else => {},
   2641         }
   2642         break :result info.return_value;
   2643     };
   2644 
   2645     if (args.len <= Liveness.bpi - 2) {
   2646         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   2647         buf[0] = callee;
   2648         std.mem.copy(Air.Inst.Ref, buf[1..], args);
   2649         return self.finishAir(inst, result, buf);
   2650     }
   2651     var bt = try self.iterateBigTomb(inst, 1 + args.len);
   2652     bt.feed(callee);
   2653     for (args) |arg| {
   2654         bt.feed(arg);
   2655     }
   2656     return bt.finishAir(result);
   2657 }
   2658 
   2659 fn ret(self: *Self, mcv: MCValue) !void {
   2660     const ret_ty = self.fn_type.fnReturnType();
   2661     switch (self.ret_mcv) {
   2662         .none => {},
   2663         .register => |reg| {
   2664             // Return result by value
   2665             try self.genSetReg(ret_ty, reg, mcv);
   2666         },
   2667         .stack_offset => {
   2668             // Return result by reference
   2669             //
   2670             // self.ret_mcv is an address to where this function
   2671             // should store its result into
   2672             var ptr_ty_payload: Type.Payload.ElemType = .{
   2673                 .base = .{ .tag = .single_mut_pointer },
   2674                 .data = ret_ty,
   2675             };
   2676             const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
   2677             try self.store(self.ret_mcv, mcv, ptr_ty, ret_ty);
   2678         },
   2679         else => unreachable, // invalid return result
   2680     }
   2681 
   2682     // Just add space for an instruction, patch this later
   2683     try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
   2684 }
   2685 
   2686 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
   2687     const un_op = self.air.instructions.items(.data)[inst].un_op;
   2688     const operand = try self.resolveInst(un_op);
   2689     try self.ret(operand);
   2690     return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   2691 }
   2692 
   2693 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
   2694     const un_op = self.air.instructions.items(.data)[inst].un_op;
   2695     const ptr = try self.resolveInst(un_op);
   2696     _ = ptr;
   2697     return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
   2698     //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   2699 }
   2700 
   2701 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
   2702     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2703     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2704         const lhs = try self.resolveInst(bin_op.lhs);
   2705         const rhs = try self.resolveInst(bin_op.rhs);
   2706         const lhs_ty = self.air.typeOf(bin_op.lhs);
   2707 
   2708         switch (lhs_ty.zigTypeTag()) {
   2709             .Vector => return self.fail("TODO ARM cmp vectors", .{}),
   2710             .Optional => return self.fail("TODO ARM cmp optionals", .{}),
   2711             .Float => return self.fail("TODO ARM cmp floats", .{}),
   2712             .Int, .Bool, .Pointer, .ErrorSet, .Enum => {
   2713                 var int_buffer: Type.Payload.Bits = undefined;
   2714                 const int_ty = switch (lhs_ty.zigTypeTag()) {
   2715                     .Enum => lhs_ty.intTagType(&int_buffer),
   2716                     .Int => lhs_ty,
   2717                     .Bool => Type.initTag(.u1),
   2718                     .Pointer => Type.usize,
   2719                     .ErrorSet => Type.initTag(.u16),
   2720                     else => unreachable,
   2721                 };
   2722 
   2723                 const int_info = int_ty.intInfo(self.target.*);
   2724                 if (int_info.bits <= 32) {
   2725                     try self.spillCompareFlagsIfOccupied();
   2726                     self.compare_flags_inst = inst;
   2727 
   2728                     _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
   2729 
   2730                     break :result switch (int_info.signedness) {
   2731                         .signed => MCValue{ .compare_flags_signed = op },
   2732                         .unsigned => MCValue{ .compare_flags_unsigned = op },
   2733                     };
   2734                 } else {
   2735                     return self.fail("TODO ARM cmp for ints > 32 bits", .{});
   2736                 }
   2737             },
   2738             else => unreachable,
   2739         }
   2740     };
   2741     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2742 }
   2743 
   2744 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
   2745     const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
   2746 
   2747     _ = try self.addInst(.{
   2748         .tag = .dbg_line,
   2749         .cond = undefined,
   2750         .data = .{ .dbg_line_column = .{
   2751             .line = dbg_stmt.line,
   2752             .column = dbg_stmt.column,
   2753         } },
   2754     });
   2755 
   2756     return self.finishAirBookkeeping();
   2757 }
   2758 
   2759 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
   2760     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   2761     const cond = try self.resolveInst(pl_op.operand);
   2762     const extra = self.air.extraData(Air.CondBr, pl_op.payload);
   2763     const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
   2764     const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
   2765     const liveness_condbr = self.liveness.getCondBr(inst);
   2766 
   2767     const reloc: Mir.Inst.Index = reloc: {
   2768         const condition: Condition = switch (cond) {
   2769             .compare_flags_signed => |cmp_op| blk: {
   2770                 // Here we map to the opposite condition because the jump is to the false branch.
   2771                 const condition = Condition.fromCompareOperatorSigned(cmp_op);
   2772                 break :blk condition.negate();
   2773             },
   2774             .compare_flags_unsigned => |cmp_op| blk: {
   2775                 // Here we map to the opposite condition because the jump is to the false branch.
   2776                 const condition = Condition.fromCompareOperatorUnsigned(cmp_op);
   2777                 break :blk condition.negate();
   2778             },
   2779             .register => |reg| blk: {
   2780                 try self.spillCompareFlagsIfOccupied();
   2781 
   2782                 // cmp reg, 1
   2783                 // bne ...
   2784                 _ = try self.addInst(.{
   2785                     .tag = .cmp,
   2786                     .cond = .al,
   2787                     .data = .{ .rr_op = .{
   2788                         .rd = .r0,
   2789                         .rn = reg,
   2790                         .op = Instruction.Operand.imm(1, 0),
   2791                     } },
   2792                 });
   2793 
   2794                 break :blk .ne;
   2795             },
   2796             .stack_offset,
   2797             .memory,
   2798             .stack_argument_offset,
   2799             => blk: {
   2800                 try self.spillCompareFlagsIfOccupied();
   2801 
   2802                 const reg = try self.copyToTmpRegister(Type.initTag(.bool), cond);
   2803 
   2804                 // cmp reg, 1
   2805                 // bne ...
   2806                 _ = try self.addInst(.{
   2807                     .tag = .cmp,
   2808                     .data = .{ .rr_op = .{
   2809                         .rd = .r0,
   2810                         .rn = reg,
   2811                         .op = Instruction.Operand.imm(1, 0),
   2812                     } },
   2813                 });
   2814 
   2815                 break :blk .ne;
   2816             },
   2817             else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
   2818         };
   2819 
   2820         break :reloc try self.addInst(.{
   2821             .tag = .b,
   2822             .cond = condition,
   2823             .data = .{ .inst = undefined }, // populated later through performReloc
   2824         });
   2825     };
   2826 
   2827     // If the condition dies here in this condbr instruction, process
   2828     // that death now instead of later as this has an effect on
   2829     // whether it needs to be spilled in the branches
   2830     if (self.liveness.operandDies(inst, 0)) {
   2831         const op_int = @enumToInt(pl_op.operand);
   2832         if (op_int >= Air.Inst.Ref.typed_value_map.len) {
   2833             const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
   2834             self.processDeath(op_index);
   2835         }
   2836     }
   2837 
   2838     // Capture the state of register and stack allocation state so that we can revert to it.
   2839     const parent_next_stack_offset = self.next_stack_offset;
   2840     const parent_free_registers = self.register_manager.free_registers;
   2841     var parent_stack = try self.stack.clone(self.gpa);
   2842     defer parent_stack.deinit(self.gpa);
   2843     const parent_registers = self.register_manager.registers;
   2844     const parent_compare_flags_inst = self.compare_flags_inst;
   2845 
   2846     try self.branch_stack.append(.{});
   2847 
   2848     try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
   2849     for (liveness_condbr.then_deaths) |operand| {
   2850         self.processDeath(operand);
   2851     }
   2852     try self.genBody(then_body);
   2853 
   2854     // Revert to the previous register and stack allocation state.
   2855 
   2856     var saved_then_branch = self.branch_stack.pop();
   2857     defer saved_then_branch.deinit(self.gpa);
   2858 
   2859     self.register_manager.registers = parent_registers;
   2860     self.compare_flags_inst = parent_compare_flags_inst;
   2861 
   2862     self.stack.deinit(self.gpa);
   2863     self.stack = parent_stack;
   2864     parent_stack = .{};
   2865 
   2866     self.next_stack_offset = parent_next_stack_offset;
   2867     self.register_manager.free_registers = parent_free_registers;
   2868 
   2869     try self.performReloc(reloc);
   2870     const else_branch = self.branch_stack.addOneAssumeCapacity();
   2871     else_branch.* = .{};
   2872 
   2873     try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
   2874     for (liveness_condbr.else_deaths) |operand| {
   2875         self.processDeath(operand);
   2876     }
   2877     try self.genBody(else_body);
   2878 
   2879     // At this point, each branch will possibly have conflicting values for where
   2880     // each instruction is stored. They agree, however, on which instructions are alive/dead.
   2881     // We use the first ("then") branch as canonical, and here emit
   2882     // instructions into the second ("else") branch to make it conform.
   2883     // We continue respect the data structure semantic guarantees of the else_branch so
   2884     // that we can use all the code emitting abstractions. This is why at the bottom we
   2885     // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
   2886     // rather than assigning it.
   2887     const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
   2888     try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
   2889 
   2890     const else_slice = else_branch.inst_table.entries.slice();
   2891     const else_keys = else_slice.items(.key);
   2892     const else_values = else_slice.items(.value);
   2893     for (else_keys) |else_key, else_idx| {
   2894         const else_value = else_values[else_idx];
   2895         const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
   2896             // The instruction's MCValue is overridden in both branches.
   2897             parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
   2898             if (else_value == .dead) {
   2899                 assert(then_entry.value == .dead);
   2900                 continue;
   2901             }
   2902             break :blk then_entry.value;
   2903         } else blk: {
   2904             if (else_value == .dead)
   2905                 continue;
   2906             // The instruction is only overridden in the else branch.
   2907             var i: usize = self.branch_stack.items.len - 2;
   2908             while (true) {
   2909                 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
   2910                 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
   2911                     assert(mcv != .dead);
   2912                     break :blk mcv;
   2913                 }
   2914             }
   2915         };
   2916         log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
   2917         // TODO make sure the destination stack offset / register does not already have something
   2918         // going on there.
   2919         try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
   2920         // TODO track the new register / stack allocation
   2921     }
   2922     try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
   2923     const then_slice = saved_then_branch.inst_table.entries.slice();
   2924     const then_keys = then_slice.items(.key);
   2925     const then_values = then_slice.items(.value);
   2926     for (then_keys) |then_key, then_idx| {
   2927         const then_value = then_values[then_idx];
   2928         // We already deleted the items from this table that matched the else_branch.
   2929         // So these are all instructions that are only overridden in the then branch.
   2930         parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
   2931         if (then_value == .dead)
   2932             continue;
   2933         const parent_mcv = blk: {
   2934             var i: usize = self.branch_stack.items.len - 2;
   2935             while (true) {
   2936                 i -= 1;
   2937                 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
   2938                     assert(mcv != .dead);
   2939                     break :blk mcv;
   2940                 }
   2941             }
   2942         };
   2943         log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
   2944         // TODO make sure the destination stack offset / register does not already have something
   2945         // going on there.
   2946         try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
   2947         // TODO track the new register / stack allocation
   2948     }
   2949 
   2950     self.branch_stack.pop().deinit(self.gpa);
   2951 
   2952     // We already took care of pl_op.operand earlier, so we're going
   2953     // to pass .none here
   2954     return self.finishAir(inst, .unreach, .{ .none, .none, .none });
   2955 }
   2956 
   2957 fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
   2958     if (ty.isPtrLikeOptional()) {
   2959         assert(ty.abiSize(self.target.*) == 4);
   2960 
   2961         const reg_mcv: MCValue = switch (operand) {
   2962             .register => operand,
   2963             else => .{ .register = try self.copyToTmpRegister(ty, operand) },
   2964         };
   2965 
   2966         _ = try self.addInst(.{
   2967             .tag = .cmp,
   2968             .data = .{ .rr_op = .{
   2969                 .rd = undefined,
   2970                 .rn = reg_mcv.register,
   2971                 .op = Instruction.Operand.fromU32(0).?,
   2972             } },
   2973         });
   2974 
   2975         return MCValue{ .compare_flags_unsigned = .eq };
   2976     } else {
   2977         return self.fail("TODO implement non-pointer optionals", .{});
   2978     }
   2979 }
   2980 
   2981 fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
   2982     const is_null_result = try self.isNull(ty, operand);
   2983     assert(is_null_result.compare_flags_unsigned == .eq);
   2984 
   2985     return MCValue{ .compare_flags_unsigned = .neq };
   2986 }
   2987 
   2988 fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
   2989     const error_type = ty.errorUnionSet();
   2990     const payload_type = ty.errorUnionPayload();
   2991 
   2992     if (!error_type.hasRuntimeBits()) {
   2993         return MCValue{ .immediate = 0 }; // always false
   2994     } else if (!payload_type.hasRuntimeBits()) {
   2995         if (error_type.abiSize(self.target.*) <= 4) {
   2996             const reg_mcv: MCValue = switch (operand) {
   2997                 .register => operand,
   2998                 else => .{ .register = try self.copyToTmpRegister(error_type, operand) },
   2999             };
   3000 
   3001             _ = try self.addInst(.{
   3002                 .tag = .cmp,
   3003                 .data = .{ .rr_op = .{
   3004                     .rd = undefined,
   3005                     .rn = reg_mcv.register,
   3006                     .op = Instruction.Operand.fromU32(0).?,
   3007                 } },
   3008             });
   3009 
   3010             return MCValue{ .compare_flags_unsigned = .gt };
   3011         } else {
   3012             return self.fail("TODO isErr for errors with size > 4", .{});
   3013         }
   3014     } else {
   3015         return self.fail("TODO isErr for non-empty payloads", .{});
   3016     }
   3017 }
   3018 
   3019 fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
   3020     const is_err_result = try self.isErr(ty, operand);
   3021     switch (is_err_result) {
   3022         .compare_flags_unsigned => |op| {
   3023             assert(op == .gt);
   3024             return MCValue{ .compare_flags_unsigned = .lte };
   3025         },
   3026         .immediate => |imm| {
   3027             assert(imm == 0);
   3028             return MCValue{ .immediate = 1 };
   3029         },
   3030         else => unreachable,
   3031     }
   3032 }
   3033 
   3034 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
   3035     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3036 
   3037     try self.spillCompareFlagsIfOccupied();
   3038     self.compare_flags_inst = inst;
   3039 
   3040     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3041         const operand = try self.resolveInst(un_op);
   3042         const ty = self.air.typeOf(un_op);
   3043         break :result try self.isNull(ty, operand);
   3044     };
   3045     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3046 }
   3047 
   3048 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   3049     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3050     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3051         const operand_ptr = try self.resolveInst(un_op);
   3052         const ptr_ty = self.air.typeOf(un_op);
   3053         const operand: MCValue = blk: {
   3054             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   3055                 // The MCValue that holds the pointer can be re-used as the value.
   3056                 break :blk operand_ptr;
   3057             } else {
   3058                 break :blk try self.allocRegOrMem(inst, true);
   3059             }
   3060         };
   3061         try self.load(operand, operand_ptr, ptr_ty);
   3062         break :result try self.isNull(ptr_ty.elemType(), operand);
   3063     };
   3064     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3065 }
   3066 
   3067 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
   3068     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3069     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3070         const operand = try self.resolveInst(un_op);
   3071         const ty = self.air.typeOf(un_op);
   3072         break :result try self.isNonNull(ty, operand);
   3073     };
   3074     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3075 }
   3076 
   3077 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   3078     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3079     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3080         const operand_ptr = try self.resolveInst(un_op);
   3081         const ptr_ty = self.air.typeOf(un_op);
   3082         const operand: MCValue = blk: {
   3083             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   3084                 // The MCValue that holds the pointer can be re-used as the value.
   3085                 break :blk operand_ptr;
   3086             } else {
   3087                 break :blk try self.allocRegOrMem(inst, true);
   3088             }
   3089         };
   3090         try self.load(operand, operand_ptr, ptr_ty);
   3091         break :result try self.isNonNull(ptr_ty.elemType(), operand);
   3092     };
   3093     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3094 }
   3095 
   3096 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
   3097     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3098     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3099         const operand = try self.resolveInst(un_op);
   3100         const ty = self.air.typeOf(un_op);
   3101         break :result try self.isErr(ty, operand);
   3102     };
   3103     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3104 }
   3105 
   3106 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   3107     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3108     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3109         const operand_ptr = try self.resolveInst(un_op);
   3110         const ptr_ty = self.air.typeOf(un_op);
   3111         const operand: MCValue = blk: {
   3112             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   3113                 // The MCValue that holds the pointer can be re-used as the value.
   3114                 break :blk operand_ptr;
   3115             } else {
   3116                 break :blk try self.allocRegOrMem(inst, true);
   3117             }
   3118         };
   3119         try self.load(operand, operand_ptr, ptr_ty);
   3120         break :result try self.isErr(ptr_ty.elemType(), operand);
   3121     };
   3122     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3123 }
   3124 
   3125 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
   3126     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3127     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3128         const operand = try self.resolveInst(un_op);
   3129         const ty = self.air.typeOf(un_op);
   3130         break :result try self.isNonErr(ty, operand);
   3131     };
   3132     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3133 }
   3134 
   3135 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   3136     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3137     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3138         const operand_ptr = try self.resolveInst(un_op);
   3139         const ptr_ty = self.air.typeOf(un_op);
   3140         const operand: MCValue = blk: {
   3141             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   3142                 // The MCValue that holds the pointer can be re-used as the value.
   3143                 break :blk operand_ptr;
   3144             } else {
   3145                 break :blk try self.allocRegOrMem(inst, true);
   3146             }
   3147         };
   3148         try self.load(operand, operand_ptr, ptr_ty);
   3149         break :result try self.isNonErr(ptr_ty.elemType(), operand);
   3150     };
   3151     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3152 }
   3153 
   3154 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
   3155     // A loop is a setup to be able to jump back to the beginning.
   3156     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3157     const loop = self.air.extraData(Air.Block, ty_pl.payload);
   3158     const body = self.air.extra[loop.end..][0..loop.data.body_len];
   3159     const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);
   3160     try self.genBody(body);
   3161     try self.jump(start_index);
   3162     return self.finishAirBookkeeping();
   3163 }
   3164 
   3165 /// Send control flow to `inst`.
   3166 fn jump(self: *Self, inst: Mir.Inst.Index) !void {
   3167     _ = try self.addInst(.{
   3168         .tag = .b,
   3169         .data = .{ .inst = inst },
   3170     });
   3171 }
   3172 
   3173 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
   3174     try self.blocks.putNoClobber(self.gpa, inst, .{
   3175         // A block is a setup to be able to jump to the end.
   3176         .relocs = .{},
   3177         // It also acts as a receptacle for break operands.
   3178         // Here we use `MCValue.none` to represent a null value so that the first
   3179         // break instruction will choose a MCValue for the block result and overwrite
   3180         // this field. Following break instructions will use that MCValue to put their
   3181         // block results.
   3182         .mcv = MCValue{ .none = {} },
   3183     });
   3184     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
   3185 
   3186     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3187     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   3188     const body = self.air.extra[extra.end..][0..extra.data.body_len];
   3189     try self.genBody(body);
   3190 
   3191     // relocations for `br` instructions
   3192     const relocs = &self.blocks.getPtr(inst).?.relocs;
   3193     if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
   3194         // If the last Mir instruction is the last relocation (which
   3195         // would just jump one instruction further), it can be safely
   3196         // removed
   3197         self.mir_instructions.orderedRemove(relocs.pop());
   3198     }
   3199     for (relocs.items) |reloc| {
   3200         try self.performReloc(reloc);
   3201     }
   3202 
   3203     const result = self.blocks.getPtr(inst).?.mcv;
   3204     return self.finishAir(inst, result, .{ .none, .none, .none });
   3205 }
   3206 
   3207 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
   3208     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   3209     const condition = pl_op.operand;
   3210     _ = condition;
   3211     return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
   3212     // return self.finishAir(inst, .dead, .{ condition, .none, .none });
   3213 }
   3214 
   3215 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
   3216     const tag = self.mir_instructions.items(.tag)[inst];
   3217     switch (tag) {
   3218         .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Air.Inst.Index, self.mir_instructions.len),
   3219         else => unreachable,
   3220     }
   3221 }
   3222 
   3223 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
   3224     const branch = self.air.instructions.items(.data)[inst].br;
   3225     try self.br(branch.block_inst, branch.operand);
   3226     return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
   3227 }
   3228 
   3229 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
   3230     const block_data = self.blocks.getPtr(block).?;
   3231 
   3232     if (self.air.typeOf(operand).hasRuntimeBits()) {
   3233         const operand_mcv = try self.resolveInst(operand);
   3234         const block_mcv = block_data.mcv;
   3235         if (block_mcv == .none) {
   3236             block_data.mcv = switch (operand_mcv) {
   3237                 .none, .dead, .unreach => unreachable,
   3238                 .register, .stack_offset, .memory => operand_mcv,
   3239                 .immediate, .stack_argument_offset => blk: {
   3240                     const new_mcv = try self.allocRegOrMem(block, true);
   3241                     try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
   3242                     break :blk new_mcv;
   3243                 },
   3244                 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
   3245             };
   3246         } else {
   3247             try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
   3248         }
   3249     }
   3250     return self.brVoid(block);
   3251 }
   3252 
   3253 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
   3254     const block_data = self.blocks.getPtr(block).?;
   3255 
   3256     // Emit a jump with a relocation. It will be patched up after the block ends.
   3257     try block_data.relocs.append(self.gpa, try self.addInst(.{
   3258         .tag = .b,
   3259         .data = .{ .inst = undefined }, // populated later through performReloc
   3260     }));
   3261 }
   3262 
   3263 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
   3264     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3265     const extra = self.air.extraData(Air.Asm, ty_pl.payload);
   3266     const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
   3267     const clobbers_len = @truncate(u31, extra.data.flags);
   3268     var extra_i: usize = extra.end;
   3269     const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
   3270     extra_i += outputs.len;
   3271     const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
   3272     extra_i += inputs.len;
   3273 
   3274     const dead = !is_volatile and self.liveness.isUnused(inst);
   3275     const result: MCValue = if (dead) .dead else result: {
   3276         if (outputs.len > 1) {
   3277             return self.fail("TODO implement codegen for asm with more than 1 output", .{});
   3278         }
   3279 
   3280         const output_constraint: ?[]const u8 = for (outputs) |output| {
   3281             if (output != .none) {
   3282                 return self.fail("TODO implement codegen for non-expr asm", .{});
   3283             }
   3284             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   3285             // This equation accounts for the fact that even if we have exactly 4 bytes
   3286             // for the string, we still use the next u32 for the null terminator.
   3287             extra_i += constraint.len / 4 + 1;
   3288 
   3289             break constraint;
   3290         } else null;
   3291 
   3292         for (inputs) |input| {
   3293             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   3294             // This equation accounts for the fact that even if we have exactly 4 bytes
   3295             // for the string, we still use the next u32 for the null terminator.
   3296             extra_i += constraint.len / 4 + 1;
   3297 
   3298             if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
   3299                 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
   3300             }
   3301             const reg_name = constraint[1 .. constraint.len - 1];
   3302             const reg = parseRegName(reg_name) orelse
   3303                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   3304 
   3305             const arg_mcv = try self.resolveInst(input);
   3306             try self.register_manager.getReg(reg, null);
   3307             try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
   3308         }
   3309 
   3310         {
   3311             var clobber_i: u32 = 0;
   3312             while (clobber_i < clobbers_len) : (clobber_i += 1) {
   3313                 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   3314                 // This equation accounts for the fact that even if we have exactly 4 bytes
   3315                 // for the string, we still use the next u32 for the null terminator.
   3316                 extra_i += clobber.len / 4 + 1;
   3317 
   3318                 // TODO honor these
   3319             }
   3320         }
   3321 
   3322         const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
   3323 
   3324         if (mem.eql(u8, asm_source, "svc #0")) {
   3325             _ = try self.addInst(.{
   3326                 .tag = .svc,
   3327                 .data = .{ .imm24 = 0 },
   3328             });
   3329         } else {
   3330             return self.fail("TODO implement support for more arm assembly instructions", .{});
   3331         }
   3332 
   3333         if (output_constraint) |output| {
   3334             if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
   3335                 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
   3336             }
   3337             const reg_name = output[2 .. output.len - 1];
   3338             const reg = parseRegName(reg_name) orelse
   3339                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   3340 
   3341             break :result MCValue{ .register = reg };
   3342         } else {
   3343             break :result MCValue{ .none = {} };
   3344         }
   3345     };
   3346 
   3347     simple: {
   3348         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   3349         var buf_index: usize = 0;
   3350         for (outputs) |output| {
   3351             if (output == .none) continue;
   3352 
   3353             if (buf_index >= buf.len) break :simple;
   3354             buf[buf_index] = output;
   3355             buf_index += 1;
   3356         }
   3357         if (buf_index + inputs.len > buf.len) break :simple;
   3358         std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
   3359         return self.finishAir(inst, result, buf);
   3360     }
   3361     var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
   3362     for (outputs) |output| {
   3363         if (output == .none) continue;
   3364 
   3365         bt.feed(output);
   3366     }
   3367     for (inputs) |input| {
   3368         bt.feed(input);
   3369     }
   3370     return bt.finishAir(result);
   3371 }
   3372 
   3373 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
   3374     try self.ensureProcessDeathCapacity(operand_count + 1);
   3375     return BigTomb{
   3376         .function = self,
   3377         .inst = inst,
   3378         .tomb_bits = self.liveness.getTombBits(inst),
   3379         .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
   3380         .bit_index = 0,
   3381     };
   3382 }
   3383 
   3384 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
   3385 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
   3386     switch (loc) {
   3387         .none => return,
   3388         .register => |reg| return self.genSetReg(ty, reg, val),
   3389         .stack_offset => |off| return self.genSetStack(ty, off, val),
   3390         .memory => {
   3391             return self.fail("TODO implement setRegOrMem for memory", .{});
   3392         },
   3393         else => unreachable,
   3394     }
   3395 }
   3396 
   3397 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
   3398     const abi_size = @intCast(u32, ty.abiSize(self.target.*));
   3399     switch (mcv) {
   3400         .dead => unreachable,
   3401         .unreach, .none => return, // Nothing to do.
   3402         .undef => {
   3403             if (!self.wantSafety())
   3404                 return; // The already existing value will do just fine.
   3405             // TODO Upgrade this to a memset call when we have that available.
   3406             switch (ty.abiSize(self.target.*)) {
   3407                 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
   3408                 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
   3409                 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
   3410                 else => return self.fail("TODO implement memset", .{}),
   3411             }
   3412         },
   3413         .compare_flags_unsigned,
   3414         .compare_flags_signed,
   3415         .immediate,
   3416         .ptr_stack_offset,
   3417         .ptr_embedded_in_code,
   3418         => {
   3419             const reg = try self.copyToTmpRegister(ty, mcv);
   3420             return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
   3421         },
   3422         .register => |reg| {
   3423             const adj_off = stack_offset + abi_size;
   3424 
   3425             switch (abi_size) {
   3426                 1, 4 => {
   3427                     const offset = if (math.cast(u12, adj_off)) |imm| blk: {
   3428                         break :blk Instruction.Offset.imm(imm);
   3429                     } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
   3430 
   3431                     const tag: Mir.Inst.Tag = switch (abi_size) {
   3432                         1 => .strb,
   3433                         4 => .str,
   3434                         else => unreachable,
   3435                     };
   3436 
   3437                     _ = try self.addInst(.{
   3438                         .tag = tag,
   3439                         .data = .{ .rr_offset = .{
   3440                             .rt = reg,
   3441                             .rn = .fp,
   3442                             .offset = .{
   3443                                 .offset = offset,
   3444                                 .positive = false,
   3445                             },
   3446                         } },
   3447                     });
   3448                 },
   3449                 2 => {
   3450                     const offset = if (adj_off <= math.maxInt(u8)) blk: {
   3451                         break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
   3452                     } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
   3453 
   3454                     _ = try self.addInst(.{
   3455                         .tag = .strh,
   3456                         .data = .{ .rr_extra_offset = .{
   3457                             .rt = reg,
   3458                             .rn = .fp,
   3459                             .offset = .{
   3460                                 .offset = offset,
   3461                                 .positive = false,
   3462                             },
   3463                         } },
   3464                     });
   3465                 },
   3466                 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
   3467             }
   3468         },
   3469         .memory,
   3470         .embedded_in_code,
   3471         .stack_argument_offset,
   3472         .stack_offset,
   3473         => {
   3474             switch (mcv) {
   3475                 .stack_offset => |off| {
   3476                     if (stack_offset == off)
   3477                         return; // Copy stack variable to itself; nothing to do.
   3478                 },
   3479                 else => {},
   3480             }
   3481 
   3482             if (abi_size <= 4) {
   3483                 const reg = try self.copyToTmpRegister(ty, mcv);
   3484                 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
   3485             } else {
   3486                 var ptr_ty_payload: Type.Payload.ElemType = .{
   3487                     .base = .{ .tag = .single_mut_pointer },
   3488                     .data = ty,
   3489                 };
   3490                 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
   3491 
   3492                 // TODO call extern memcpy
   3493                 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null });
   3494                 const src_reg = regs[0];
   3495                 const dst_reg = regs[1];
   3496                 const len_reg = regs[2];
   3497                 const count_reg = regs[3];
   3498                 const tmp_reg = regs[4];
   3499 
   3500                 switch (mcv) {
   3501                     .stack_offset => |off| {
   3502                         // sub src_reg, fp, #off
   3503                         try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
   3504                     },
   3505                     .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }),
   3506                     .embedded_in_code,
   3507                     .stack_argument_offset,
   3508                     => return self.fail("TODO genSetStack with src={}", .{mcv}),
   3509                     else => unreachable,
   3510                 }
   3511 
   3512                 // sub dst_reg, fp, #stack_offset
   3513                 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });
   3514 
   3515                 // mov len, #abi_size
   3516                 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
   3517 
   3518                 // memcpy(src, dst, len)
   3519                 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   3520             }
   3521         },
   3522     }
   3523 }
   3524 
   3525 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
   3526     switch (mcv) {
   3527         .dead => unreachable,
   3528         .ptr_embedded_in_code => unreachable,
   3529         .unreach, .none => return, // Nothing to do.
   3530         .undef => {
   3531             if (!self.wantSafety())
   3532                 return; // The already existing value will do just fine.
   3533             // Write the debug undefined value.
   3534             return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa });
   3535         },
   3536         .ptr_stack_offset => |unadjusted_off| {
   3537             // TODO: maybe addressing from sp instead of fp
   3538             const elem_ty = ty.childType();
   3539             const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
   3540             const adj_off = unadjusted_off + abi_size;
   3541 
   3542             const op = Instruction.Operand.fromU32(adj_off) orelse
   3543                 return self.fail("TODO larger stack offsets", .{});
   3544 
   3545             _ = try self.addInst(.{
   3546                 .tag = .sub,
   3547                 .data = .{ .rr_op = .{
   3548                     .rd = reg,
   3549                     .rn = .fp,
   3550                     .op = op,
   3551                 } },
   3552             });
   3553         },
   3554         .compare_flags_unsigned,
   3555         .compare_flags_signed,
   3556         => |op| {
   3557             const condition = switch (mcv) {
   3558                 .compare_flags_unsigned => Condition.fromCompareOperatorUnsigned(op),
   3559                 .compare_flags_signed => Condition.fromCompareOperatorSigned(op),
   3560                 else => unreachable,
   3561             };
   3562 
   3563             const zero = Instruction.Operand.imm(0, 0);
   3564             const one = Instruction.Operand.imm(1, 0);
   3565 
   3566             // mov reg, 0
   3567             _ = try self.addInst(.{
   3568                 .tag = .mov,
   3569                 .data = .{ .rr_op = .{
   3570                     .rd = reg,
   3571                     .rn = .r0,
   3572                     .op = zero,
   3573                 } },
   3574             });
   3575 
   3576             // moveq reg, 1
   3577             _ = try self.addInst(.{
   3578                 .tag = .mov,
   3579                 .cond = condition,
   3580                 .data = .{ .rr_op = .{
   3581                     .rd = reg,
   3582                     .rn = .r0,
   3583                     .op = one,
   3584                 } },
   3585             });
   3586         },
   3587         .immediate => |x| {
   3588             if (Instruction.Operand.fromU32(x)) |op| {
   3589                 _ = try self.addInst(.{
   3590                     .tag = .mov,
   3591                     .data = .{ .rr_op = .{
   3592                         .rd = reg,
   3593                         .rn = .r0,
   3594                         .op = op,
   3595                     } },
   3596                 });
   3597             } else if (Instruction.Operand.fromU32(~x)) |op| {
   3598                 _ = try self.addInst(.{
   3599                     .tag = .mvn,
   3600                     .data = .{ .rr_op = .{
   3601                         .rd = reg,
   3602                         .rn = .r0,
   3603                         .op = op,
   3604                     } },
   3605                 });
   3606             } else if (x <= math.maxInt(u16)) {
   3607                 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
   3608                     _ = try self.addInst(.{
   3609                         .tag = .movw,
   3610                         .data = .{ .r_imm16 = .{
   3611                             .rd = reg,
   3612                             .imm16 = @intCast(u16, x),
   3613                         } },
   3614                     });
   3615                 } else {
   3616                     _ = try self.addInst(.{
   3617                         .tag = .mov,
   3618                         .data = .{ .rr_op = .{
   3619                             .rd = reg,
   3620                             .rn = .r0,
   3621                             .op = Instruction.Operand.imm(@truncate(u8, x), 0),
   3622                         } },
   3623                     });
   3624                     _ = try self.addInst(.{
   3625                         .tag = .orr,
   3626                         .data = .{ .rr_op = .{
   3627                             .rd = reg,
   3628                             .rn = reg,
   3629                             .op = Instruction.Operand.imm(@truncate(u8, x >> 8), 12),
   3630                         } },
   3631                     });
   3632                 }
   3633             } else {
   3634                 // TODO write constant to code and load
   3635                 // relative to pc
   3636                 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
   3637                     // immediate: 0xaaaabbbb
   3638                     // movw reg, #0xbbbb
   3639                     // movt reg, #0xaaaa
   3640                     _ = try self.addInst(.{
   3641                         .tag = .movw,
   3642                         .data = .{ .r_imm16 = .{
   3643                             .rd = reg,
   3644                             .imm16 = @truncate(u16, x),
   3645                         } },
   3646                     });
   3647                     _ = try self.addInst(.{
   3648                         .tag = .movt,
   3649                         .data = .{ .r_imm16 = .{
   3650                             .rd = reg,
   3651                             .imm16 = @truncate(u16, x >> 16),
   3652                         } },
   3653                     });
   3654                 } else {
   3655                     // immediate: 0xaabbccdd
   3656                     // mov reg, #0xaa
   3657                     // orr reg, reg, #0xbb, 24
   3658                     // orr reg, reg, #0xcc, 16
   3659                     // orr reg, reg, #0xdd, 8
   3660                     _ = try self.addInst(.{
   3661                         .tag = .mov,
   3662                         .data = .{ .rr_op = .{
   3663                             .rd = reg,
   3664                             .rn = .r0,
   3665                             .op = Instruction.Operand.imm(@truncate(u8, x), 0),
   3666                         } },
   3667                     });
   3668                     _ = try self.addInst(.{
   3669                         .tag = .orr,
   3670                         .data = .{ .rr_op = .{
   3671                             .rd = reg,
   3672                             .rn = reg,
   3673                             .op = Instruction.Operand.imm(@truncate(u8, x >> 8), 12),
   3674                         } },
   3675                     });
   3676                     _ = try self.addInst(.{
   3677                         .tag = .orr,
   3678                         .data = .{ .rr_op = .{
   3679                             .rd = reg,
   3680                             .rn = reg,
   3681                             .op = Instruction.Operand.imm(@truncate(u8, x >> 16), 8),
   3682                         } },
   3683                     });
   3684                     _ = try self.addInst(.{
   3685                         .tag = .orr,
   3686                         .data = .{ .rr_op = .{
   3687                             .rd = reg,
   3688                             .rn = reg,
   3689                             .op = Instruction.Operand.imm(@truncate(u8, x >> 24), 4),
   3690                         } },
   3691                     });
   3692                 }
   3693             }
   3694         },
   3695         .register => |src_reg| {
   3696             // If the registers are the same, nothing to do.
   3697             if (src_reg.id() == reg.id())
   3698                 return;
   3699 
   3700             // mov reg, src_reg
   3701             _ = try self.addInst(.{
   3702                 .tag = .mov,
   3703                 .data = .{ .rr_op = .{
   3704                     .rd = reg,
   3705                     .rn = .r0,
   3706                     .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),
   3707                 } },
   3708             });
   3709         },
   3710         .memory => |addr| {
   3711             // The value is in memory at a hard-coded address.
   3712             // If the type is a pointer, it means the pointer address is at this memory location.
   3713             try self.genSetReg(ty, reg, .{ .immediate = @intCast(u32, addr) });
   3714             try self.genLdrRegister(reg, reg, ty);
   3715         },
   3716         .stack_offset => |unadjusted_off| {
   3717             // TODO: maybe addressing from sp instead of fp
   3718             const abi_size = @intCast(u32, ty.abiSize(self.target.*));
   3719             const adj_off = unadjusted_off + abi_size;
   3720 
   3721             const tag: Mir.Inst.Tag = switch (abi_size) {
   3722                 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb,
   3723                 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh else .ldrh,
   3724                 3, 4 => .ldr,
   3725                 else => unreachable,
   3726             };
   3727 
   3728             const extra_offset = switch (abi_size) {
   3729                 1 => ty.isSignedInt(),
   3730                 2 => true,
   3731                 3, 4 => false,
   3732                 else => unreachable,
   3733             };
   3734 
   3735             if (extra_offset) {
   3736                 const offset = if (adj_off <= math.maxInt(u8)) blk: {
   3737                     break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
   3738                 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
   3739 
   3740                 _ = try self.addInst(.{
   3741                     .tag = tag,
   3742                     .data = .{ .rr_extra_offset = .{
   3743                         .rt = reg,
   3744                         .rn = .fp,
   3745                         .offset = .{
   3746                             .offset = offset,
   3747                             .positive = false,
   3748                         },
   3749                     } },
   3750                 });
   3751             } else {
   3752                 const offset = if (adj_off <= math.maxInt(u12)) blk: {
   3753                     break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
   3754                 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
   3755 
   3756                 _ = try self.addInst(.{
   3757                     .tag = tag,
   3758                     .data = .{ .rr_offset = .{
   3759                         .rt = reg,
   3760                         .rn = .fp,
   3761                         .offset = .{
   3762                             .offset = offset,
   3763                             .positive = false,
   3764                         },
   3765                     } },
   3766                 });
   3767             }
   3768         },
   3769         .stack_argument_offset => |unadjusted_off| {
   3770             const abi_size = ty.abiSize(self.target.*);
   3771             const adj_off = unadjusted_off + abi_size;
   3772 
   3773             const tag: Mir.Inst.Tag = switch (abi_size) {
   3774                 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument,
   3775                 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument,
   3776                 3, 4 => .ldr_stack_argument,
   3777                 else => unreachable,
   3778             };
   3779 
   3780             _ = try self.addInst(.{
   3781                 .tag = tag,
   3782                 .data = .{ .r_stack_offset = .{
   3783                     .rt = reg,
   3784                     .stack_offset = @intCast(u32, adj_off),
   3785                 } },
   3786             });
   3787         },
   3788         else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
   3789     }
   3790 }
   3791 
   3792 fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
   3793     const abi_size = @intCast(u32, ty.abiSize(self.target.*));
   3794     switch (mcv) {
   3795         .dead => unreachable,
   3796         .none, .unreach => return,
   3797         .undef => {
   3798             if (!self.wantSafety())
   3799                 return; // The already existing value will do just fine.
   3800             // TODO Upgrade this to a memset call when we have that available.
   3801             switch (ty.abiSize(self.target.*)) {
   3802                 1 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }),
   3803                 2 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }),
   3804                 4 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
   3805                 else => return self.fail("TODO implement memset", .{}),
   3806             }
   3807         },
   3808         .register => |reg| {
   3809             const adj_off = stack_offset - abi_size;
   3810 
   3811             switch (abi_size) {
   3812                 1, 4 => {
   3813                     const offset = if (math.cast(u12, adj_off)) |imm| blk: {
   3814                         break :blk Instruction.Offset.imm(imm);
   3815                     } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
   3816 
   3817                     const tag: Mir.Inst.Tag = switch (abi_size) {
   3818                         1 => .strb,
   3819                         4 => .str,
   3820                         else => unreachable,
   3821                     };
   3822 
   3823                     _ = try self.addInst(.{
   3824                         .tag = tag,
   3825                         .data = .{ .rr_offset = .{
   3826                             .rt = reg,
   3827                             .rn = .sp,
   3828                             .offset = .{ .offset = offset },
   3829                         } },
   3830                     });
   3831                 },
   3832                 2 => {
   3833                     const offset = if (adj_off <= math.maxInt(u8)) blk: {
   3834                         break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
   3835                     } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
   3836 
   3837                     _ = try self.addInst(.{
   3838                         .tag = .strh,
   3839                         .data = .{ .rr_extra_offset = .{
   3840                             .rt = reg,
   3841                             .rn = .sp,
   3842                             .offset = .{ .offset = offset },
   3843                         } },
   3844                     });
   3845                 },
   3846                 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
   3847             }
   3848         },
   3849         .stack_offset,
   3850         .memory,
   3851         .stack_argument_offset,
   3852         .embedded_in_code,
   3853         => {
   3854             if (abi_size <= 4) {
   3855                 const reg = try self.copyToTmpRegister(ty, mcv);
   3856                 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
   3857             } else {
   3858                 var ptr_ty_payload: Type.Payload.ElemType = .{
   3859                     .base = .{ .tag = .single_mut_pointer },
   3860                     .data = ty,
   3861                 };
   3862                 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
   3863 
   3864                 // TODO call extern memcpy
   3865                 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null });
   3866                 const src_reg = regs[0];
   3867                 const dst_reg = regs[1];
   3868                 const len_reg = regs[2];
   3869                 const count_reg = regs[3];
   3870                 const tmp_reg = regs[4];
   3871 
   3872                 switch (mcv) {
   3873                     .stack_offset => |off| {
   3874                         // sub src_reg, fp, #off
   3875                         try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
   3876                     },
   3877                     .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }),
   3878                     .stack_argument_offset,
   3879                     .embedded_in_code,
   3880                     => return self.fail("TODO genSetStackArgument src={}", .{mcv}),
   3881                     else => unreachable,
   3882                 }
   3883 
   3884                 // add dst_reg, sp, #stack_offset
   3885                 const adj_dst_offset = stack_offset - abi_size;
   3886                 const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else {
   3887                     return self.fail("TODO load: set reg to stack offset with all possible offsets", .{});
   3888                 };
   3889                 _ = try self.addInst(.{
   3890                     .tag = .add,
   3891                     .data = .{ .rr_op = .{
   3892                         .rd = dst_reg,
   3893                         .rn = .sp,
   3894                         .op = dst_offset_op,
   3895                     } },
   3896                 });
   3897 
   3898                 // mov len, #abi_size
   3899                 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
   3900 
   3901                 // memcpy(src, dst, len)
   3902                 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   3903             }
   3904         },
   3905         .compare_flags_unsigned,
   3906         .compare_flags_signed,
   3907         .immediate,
   3908         .ptr_stack_offset,
   3909         .ptr_embedded_in_code,
   3910         => {
   3911             const reg = try self.copyToTmpRegister(ty, mcv);
   3912             return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
   3913         },
   3914     }
   3915 }
   3916 
   3917 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
   3918     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3919     const result = try self.resolveInst(un_op);
   3920     return self.finishAir(inst, result, .{ un_op, .none, .none });
   3921 }
   3922 
   3923 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
   3924     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3925     const result = try self.resolveInst(ty_op.operand);
   3926     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   3927 }
   3928 
   3929 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
   3930     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3931     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   3932         const ptr_ty = self.air.typeOf(ty_op.operand);
   3933         const ptr = try self.resolveInst(ty_op.operand);
   3934         const array_ty = ptr_ty.childType();
   3935         const array_len = @intCast(u32, array_ty.arrayLen());
   3936 
   3937         const stack_offset = try self.allocMem(inst, 8, 8);
   3938         try self.genSetStack(ptr_ty, stack_offset + 4, ptr);
   3939         try self.genSetStack(Type.initTag(.usize), stack_offset, .{ .immediate = array_len });
   3940         break :result MCValue{ .stack_offset = stack_offset };
   3941     };
   3942     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   3943 }
   3944 
   3945 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
   3946     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3947     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{
   3948         self.target.cpu.arch,
   3949     });
   3950     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   3951 }
   3952 
   3953 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
   3954     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3955     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{
   3956         self.target.cpu.arch,
   3957     });
   3958     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   3959 }
   3960 
   3961 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
   3962     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3963     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   3964     _ = extra;
   3965 
   3966     return self.fail("TODO implement airCmpxchg for {}", .{
   3967         self.target.cpu.arch,
   3968     });
   3969 }
   3970 
   3971 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
   3972     _ = inst;
   3973     return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
   3974 }
   3975 
   3976 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
   3977     _ = inst;
   3978     return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
   3979 }
   3980 
   3981 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
   3982     _ = inst;
   3983     _ = order;
   3984     return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
   3985 }
   3986 
   3987 fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
   3988     _ = inst;
   3989     return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
   3990 }
   3991 
   3992 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
   3993     _ = inst;
   3994     return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
   3995 }
   3996 
   3997 fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
   3998     const un_op = self.air.instructions.items(.data)[inst].un_op;
   3999     const operand = try self.resolveInst(un_op);
   4000     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   4001         _ = operand;
   4002         return self.fail("TODO implement airTagName for arm", .{});
   4003     };
   4004     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4005 }
   4006 
   4007 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
   4008     const un_op = self.air.instructions.items(.data)[inst].un_op;
   4009     const operand = try self.resolveInst(un_op);
   4010     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   4011         _ = operand;
   4012         return self.fail("TODO implement airErrorName for arm", .{});
   4013     };
   4014     return self.finishAir(inst, result, .{ un_op, .none, .none });
   4015 }
   4016 
   4017 fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
   4018     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   4019     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for arm", .{});
   4020     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   4021 }
   4022 
   4023 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
   4024     const vector_ty = self.air.typeOfIndex(inst);
   4025     const len = vector_ty.vectorLen();
   4026     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   4027     const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
   4028     const result: MCValue = res: {
   4029         if (self.liveness.isUnused(inst)) break :res MCValue.dead;
   4030         return self.fail("TODO implement airAggregateInit for arm", .{});
   4031     };
   4032 
   4033     if (elements.len <= Liveness.bpi - 1) {
   4034         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   4035         std.mem.copy(Air.Inst.Ref, &buf, elements);
   4036         return self.finishAir(inst, result, buf);
   4037     }
   4038     var bt = try self.iterateBigTomb(inst, elements.len);
   4039     for (elements) |elem| {
   4040         bt.feed(elem);
   4041     }
   4042     return bt.finishAir(result);
   4043 }
   4044 
   4045 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
   4046     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   4047     const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
   4048     _ = extra;
   4049 
   4050     return self.fail("TODO implement airUnionInit for arm", .{});
   4051 }
   4052 
   4053 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
   4054     const prefetch = self.air.instructions.items(.data)[inst].prefetch;
   4055     return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
   4056 }
   4057 
   4058 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
   4059     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   4060     const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
   4061     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   4062         return self.fail("TODO implement airMulAdd for arm", .{});
   4063     };
   4064     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
   4065 }
   4066 
   4067 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
   4068     // First section of indexes correspond to a set number of constant values.
   4069     const ref_int = @enumToInt(inst);
   4070     if (ref_int < Air.Inst.Ref.typed_value_map.len) {
   4071         const tv = Air.Inst.Ref.typed_value_map[ref_int];
   4072         if (!tv.ty.hasRuntimeBits()) {
   4073             return MCValue{ .none = {} };
   4074         }
   4075         return self.genTypedValue(tv);
   4076     }
   4077 
   4078     // If the type has no codegen bits, no need to store it.
   4079     const inst_ty = self.air.typeOf(inst);
   4080     if (!inst_ty.hasRuntimeBits())
   4081         return MCValue{ .none = {} };
   4082 
   4083     const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
   4084     switch (self.air.instructions.items(.tag)[inst_index]) {
   4085         .constant => {
   4086             // Constants have static lifetimes, so they are always memoized in the outer most table.
   4087             const branch = &self.branch_stack.items[0];
   4088             const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
   4089             if (!gop.found_existing) {
   4090                 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
   4091                 gop.value_ptr.* = try self.genTypedValue(.{
   4092                     .ty = inst_ty,
   4093                     .val = self.air.values[ty_pl.payload],
   4094                 });
   4095             }
   4096             return gop.value_ptr.*;
   4097         },
   4098         .const_ty => unreachable,
   4099         else => return self.getResolvedInstValue(inst_index),
   4100     }
   4101 }
   4102 
   4103 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
   4104     // Treat each stack item as a "layer" on top of the previous one.
   4105     var i: usize = self.branch_stack.items.len;
   4106     while (true) {
   4107         i -= 1;
   4108         if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
   4109             assert(mcv != .dead);
   4110             return mcv;
   4111         }
   4112     }
   4113 }
   4114 
   4115 fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
   4116     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
   4117     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
   4118 
   4119     decl.alive = true;
   4120     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
   4121         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
   4122         const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
   4123         return MCValue{ .memory = got_addr };
   4124     } else if (self.bin_file.cast(link.File.MachO)) |_| {
   4125         unreachable; // unsupported architecture for MachO
   4126     } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
   4127         const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
   4128         return MCValue{ .memory = got_addr };
   4129     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
   4130         try p9.seeDecl(decl);
   4131         const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
   4132         return MCValue{ .memory = got_addr };
   4133     } else {
   4134         return self.fail("TODO codegen non-ELF const Decl pointer", .{});
   4135     }
   4136 
   4137     _ = tv;
   4138 }
   4139 
   4140 fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
   4141     const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
   4142         return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
   4143     };
   4144     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
   4145         const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
   4146         return MCValue{ .memory = vaddr };
   4147     } else if (self.bin_file.cast(link.File.MachO)) |_| {
   4148         unreachable;
   4149     } else if (self.bin_file.cast(link.File.Coff)) |_| {
   4150         return self.fail("TODO lower unnamed const in COFF", .{});
   4151     } else if (self.bin_file.cast(link.File.Plan9)) |_| {
   4152         return self.fail("TODO lower unnamed const in Plan9", .{});
   4153     } else {
   4154         return self.fail("TODO lower unnamed const", .{});
   4155     }
   4156 }
   4157 
   4158 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
   4159     if (typed_value.val.isUndef())
   4160         return MCValue{ .undef = {} };
   4161     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
   4162 
   4163     if (typed_value.val.castTag(.decl_ref)) |payload| {
   4164         return self.lowerDeclRef(typed_value, payload.data);
   4165     }
   4166     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
   4167         return self.lowerDeclRef(typed_value, payload.data.decl);
   4168     }
   4169 
   4170     switch (typed_value.ty.zigTypeTag()) {
   4171         .Array => {
   4172             return self.lowerUnnamedConst(typed_value);
   4173         },
   4174         .Pointer => switch (typed_value.ty.ptrSize()) {
   4175             .Slice => {
   4176                 return self.lowerUnnamedConst(typed_value);
   4177             },
   4178             else => {
   4179                 switch (typed_value.val.tag()) {
   4180                     .int_u64 => {
   4181                         return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) };
   4182                     },
   4183                     .slice => {
   4184                         return self.lowerUnnamedConst(typed_value);
   4185                     },
   4186                     else => {
   4187                         return self.fail("TODO codegen more kinds of const pointers", .{});
   4188                     },
   4189                 }
   4190             },
   4191         },
   4192         .Int => {
   4193             const info = typed_value.ty.intInfo(self.target.*);
   4194             if (info.bits <= ptr_bits) {
   4195                 const unsigned = switch (info.signedness) {
   4196                     .signed => blk: {
   4197                         const signed = @intCast(i32, typed_value.val.toSignedInt());
   4198                         break :blk @bitCast(u32, signed);
   4199                     },
   4200                     .unsigned => @intCast(u32, typed_value.val.toUnsignedInt()),
   4201                 };
   4202 
   4203                 return MCValue{ .immediate = unsigned };
   4204             } else {
   4205                 return self.lowerUnnamedConst(typed_value);
   4206             }
   4207         },
   4208         .Bool => {
   4209             return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
   4210         },
   4211         .ComptimeInt => unreachable, // semantic analysis prevents this
   4212         .ComptimeFloat => unreachable, // semantic analysis prevents this
   4213         .Optional => {
   4214             if (typed_value.ty.isPtrLikeOptional()) {
   4215                 if (typed_value.val.isNull())
   4216                     return MCValue{ .immediate = 0 };
   4217 
   4218                 var buf: Type.Payload.ElemType = undefined;
   4219                 return self.genTypedValue(.{
   4220                     .ty = typed_value.ty.optionalChild(&buf),
   4221                     .val = typed_value.val,
   4222                 });
   4223             } else if (typed_value.ty.abiSize(self.target.*) == 1) {
   4224                 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
   4225             }
   4226             return self.fail("TODO non pointer optionals", .{});
   4227         },
   4228         .Enum => {
   4229             if (typed_value.val.castTag(.enum_field_index)) |field_index| {
   4230                 switch (typed_value.ty.tag()) {
   4231                     .enum_simple => {
   4232                         return MCValue{ .immediate = field_index.data };
   4233                     },
   4234                     .enum_full, .enum_nonexhaustive => {
   4235                         const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
   4236                         if (enum_full.values.count() != 0) {
   4237                             const tag_val = enum_full.values.keys()[field_index.data];
   4238                             return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
   4239                         } else {
   4240                             return MCValue{ .immediate = field_index.data };
   4241                         }
   4242                     },
   4243                     else => unreachable,
   4244                 }
   4245             } else {
   4246                 var int_tag_buffer: Type.Payload.Bits = undefined;
   4247                 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
   4248                 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
   4249             }
   4250         },
   4251         .ErrorSet => {
   4252             const err_name = typed_value.val.castTag(.@"error").?.data.name;
   4253             const module = self.bin_file.options.module.?;
   4254             const global_error_set = module.global_error_set;
   4255             const error_index = global_error_set.get(err_name).?;
   4256             return MCValue{ .immediate = error_index };
   4257         },
   4258         .ErrorUnion => {
   4259             const error_type = typed_value.ty.errorUnionSet();
   4260             const payload_type = typed_value.ty.errorUnionPayload();
   4261 
   4262             if (typed_value.val.castTag(.eu_payload)) |pl| {
   4263                 if (!payload_type.hasRuntimeBits()) {
   4264                     // We use the error type directly as the type.
   4265                     return MCValue{ .immediate = 0 };
   4266                 }
   4267 
   4268                 _ = pl;
   4269                 return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty});
   4270             } else {
   4271                 if (!payload_type.hasRuntimeBits()) {
   4272                     // We use the error type directly as the type.
   4273                     return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
   4274                 }
   4275 
   4276                 return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty});
   4277             }
   4278         },
   4279         .Struct => {
   4280             return self.lowerUnnamedConst(typed_value);
   4281         },
   4282         else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
   4283     }
   4284 }
   4285 
   4286 const CallMCValues = struct {
   4287     args: []MCValue,
   4288     return_value: MCValue,
   4289     stack_byte_count: u32,
   4290     stack_align: u32,
   4291 
   4292     fn deinit(self: *CallMCValues, func: *Self) void {
   4293         func.gpa.free(self.args);
   4294         self.* = undefined;
   4295     }
   4296 };
   4297 
   4298 /// Caller must call `CallMCValues.deinit`.
   4299 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
   4300     const cc = fn_ty.fnCallingConvention();
   4301     const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
   4302     defer self.gpa.free(param_types);
   4303     fn_ty.fnParamTypes(param_types);
   4304     var result: CallMCValues = .{
   4305         .args = try self.gpa.alloc(MCValue, param_types.len),
   4306         // These undefined values must be populated before returning from this function.
   4307         .return_value = undefined,
   4308         .stack_byte_count = undefined,
   4309         .stack_align = undefined,
   4310     };
   4311     errdefer self.gpa.free(result.args);
   4312 
   4313     const ret_ty = fn_ty.fnReturnType();
   4314 
   4315     switch (cc) {
   4316         .Naked => {
   4317             assert(result.args.len == 0);
   4318             result.return_value = .{ .unreach = {} };
   4319             result.stack_byte_count = 0;
   4320             result.stack_align = 1;
   4321             return result;
   4322         },
   4323         .C => {
   4324             // ARM Procedure Call Standard, Chapter 6.5
   4325             var ncrn: usize = 0; // Next Core Register Number
   4326             var nsaa: u32 = 0; // Next stacked argument address
   4327 
   4328             if (ret_ty.zigTypeTag() == .NoReturn) {
   4329                 result.return_value = .{ .unreach = {} };
   4330             } else if (!ret_ty.hasRuntimeBits()) {
   4331                 result.return_value = .{ .none = {} };
   4332             } else {
   4333                 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
   4334                 // TODO handle cases where multiple registers are used
   4335                 if (ret_ty_size <= 4) {
   4336                     result.return_value = .{ .register = c_abi_int_return_regs[0] };
   4337                 } else {
   4338                     // The result is returned by reference, not by
   4339                     // value. This means that r0 will contain the
   4340                     // address of where this function should write the
   4341                     // result into.
   4342                     result.return_value = .{ .stack_offset = 0 };
   4343                     ncrn = 1;
   4344                 }
   4345             }
   4346 
   4347             for (param_types) |ty, i| {
   4348                 if (ty.abiAlignment(self.target.*) == 8)
   4349                     ncrn = std.mem.alignForwardGeneric(usize, ncrn, 2);
   4350 
   4351                 const param_size = @intCast(u32, ty.abiSize(self.target.*));
   4352                 if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
   4353                     if (param_size <= 4) {
   4354                         result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
   4355                         ncrn += 1;
   4356                     } else {
   4357                         return self.fail("TODO MCValues with multiple registers", .{});
   4358                     }
   4359                 } else if (ncrn < 4 and nsaa == 0) {
   4360                     return self.fail("TODO MCValues split between registers and stack", .{});
   4361                 } else {
   4362                     ncrn = 4;
   4363                     if (ty.abiAlignment(self.target.*) == 8)
   4364                         nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8);
   4365 
   4366                     result.args[i] = .{ .stack_argument_offset = nsaa };
   4367                     nsaa += param_size;
   4368                 }
   4369             }
   4370 
   4371             result.stack_byte_count = nsaa;
   4372             result.stack_align = 8;
   4373         },
   4374         .Unspecified => {
   4375             if (ret_ty.zigTypeTag() == .NoReturn) {
   4376                 result.return_value = .{ .unreach = {} };
   4377             } else if (!ret_ty.hasRuntimeBits()) {
   4378                 result.return_value = .{ .none = {} };
   4379             } else {
   4380                 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
   4381                 if (ret_ty_size <= 4) {
   4382                     result.return_value = .{ .register = .r0 };
   4383                 } else {
   4384                     // The result is returned by reference, not by
   4385                     // value. This means that r0 will contain the
   4386                     // address of where this function should write the
   4387                     // result into.
   4388                     result.return_value = .{ .stack_offset = 0 };
   4389                 }
   4390             }
   4391 
   4392             var stack_offset: u32 = 0;
   4393 
   4394             for (param_types) |ty, i| {
   4395                 if (ty.abiSize(self.target.*) > 0) {
   4396                     stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*));
   4397                     result.args[i] = .{ .stack_argument_offset = stack_offset };
   4398                     stack_offset += @intCast(u32, ty.abiSize(self.target.*));
   4399                 } else {
   4400                     result.args[i] = .{ .none = {} };
   4401                 }
   4402             }
   4403 
   4404             result.stack_byte_count = stack_offset;
   4405             result.stack_align = 8;
   4406         },
   4407         else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
   4408     }
   4409 
   4410     return result;
   4411 }
   4412 
   4413 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
   4414 fn wantSafety(self: *Self) bool {
   4415     return switch (self.bin_file.options.optimize_mode) {
   4416         .Debug => true,
   4417         .ReleaseSafe => true,
   4418         .ReleaseFast => false,
   4419         .ReleaseSmall => false,
   4420     };
   4421 }
   4422 
   4423 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   4424     @setCold(true);
   4425     assert(self.err_msg == null);
   4426     self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
   4427     return error.CodegenFail;
   4428 }
   4429 
   4430 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   4431     @setCold(true);
   4432     assert(self.err_msg == null);
   4433     self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
   4434     return error.CodegenFail;
   4435 }
   4436 
   4437 const Register = @import("bits.zig").Register;
   4438 const Instruction = @import("bits.zig").Instruction;
   4439 const Condition = @import("bits.zig").Condition;
   4440 const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
   4441 const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs;
   4442 const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs;
   4443 
   4444 fn parseRegName(name: []const u8) ?Register {
   4445     if (@hasDecl(Register, "parseRegName")) {
   4446         return Register.parseRegName(name);
   4447     }
   4448     return std.meta.stringToEnum(Register, name);
   4449 }