zig

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

blob 4e91c085 (125087B) - 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");
     12 const link = @import("../../link.zig");
     13 const Module = @import("../../Module.zig");
     14 const InternPool = @import("../../InternPool.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 codegen = @import("../../codegen.zig");
     25 const Alignment = InternPool.Alignment;
     26 
     27 const CodeGenError = codegen.CodeGenError;
     28 const Result = codegen.Result;
     29 const DebugInfoOutput = codegen.DebugInfoOutput;
     30 
     31 const bits = @import("bits.zig");
     32 const abi = @import("abi.zig");
     33 const Register = bits.Register;
     34 const RegisterManager = abi.RegisterManager;
     35 const RegisterLock = RegisterManager.RegisterLock;
     36 const callee_preserved_regs = abi.callee_preserved_regs;
     37 /// General Purpose
     38 const gp = abi.RegisterClass.gp;
     39 /// Function Args
     40 const fa = abi.RegisterClass.fa;
     41 
     42 const InnerError = CodeGenError || error{OutOfRegisters};
     43 
     44 gpa: Allocator,
     45 air: Air,
     46 liveness: Liveness,
     47 bin_file: *link.File,
     48 target: *const std.Target,
     49 func_index: InternPool.Index,
     50 code: *std.ArrayList(u8),
     51 debug_output: DebugInfoOutput,
     52 err_msg: ?*ErrorMsg,
     53 args: []MCValue,
     54 ret_mcv: MCValue,
     55 fn_type: Type,
     56 arg_index: usize,
     57 src_loc: Module.SrcLoc,
     58 stack_align: Alignment,
     59 
     60 /// MIR Instructions
     61 mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
     62 /// MIR extra data
     63 mir_extra: std.ArrayListUnmanaged(u32) = .{},
     64 
     65 /// Byte offset within the source file of the ending curly.
     66 end_di_line: u32,
     67 end_di_column: u32,
     68 
     69 /// The value is an offset into the `Function` `code` from the beginning.
     70 /// To perform the reloc, write 32-bit signed little-endian integer
     71 /// which is a relative jump, based on the address following the reloc.
     72 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
     73 
     74 /// Whenever there is a runtime branch, we push a Branch onto this stack,
     75 /// and pop it off when the runtime branch joins. This provides an "overlay"
     76 /// of the table of mappings from instructions to `MCValue` from within the branch.
     77 /// This way we can modify the `MCValue` for an instruction in different ways
     78 /// within different branches. Special consideration is needed when a branch
     79 /// joins with its parent, to make sure all instructions have the same MCValue
     80 /// across each runtime branch upon joining.
     81 branch_stack: *std.ArrayList(Branch),
     82 
     83 // Key is the block instruction
     84 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
     85 
     86 register_manager: RegisterManager = .{},
     87 /// Maps offset to what is stored there.
     88 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
     89 
     90 /// Offset from the stack base, representing the end of the stack frame.
     91 max_end_stack: u32 = 0,
     92 /// Represents the current end stack offset. If there is no existing slot
     93 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
     94 next_stack_offset: u32 = 0,
     95 
     96 /// Debug field, used to find bugs in the compiler.
     97 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
     98 
     99 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
    100 
    101 const SymbolOffset = struct { sym: u32, off: i32 = 0 };
    102 
    103 const MCValue = union(enum) {
    104     /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
    105     /// TODO Look into deleting this tag and using `dead` instead, since every use
    106     /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
    107     none,
    108     /// Control flow will not allow this value to be observed.
    109     unreach,
    110     /// No more references to this value remain.
    111     dead,
    112     /// The value is undefined.
    113     undef,
    114     /// A pointer-sized integer that fits in a register.
    115     /// If the type is a pointer, this is the pointer address in virtual address space.
    116     immediate: u64,
    117     /// The value is in memory at an address not-yet-allocated by the linker.
    118     /// This traditionally corresponds to a relocation emitted in a relocatable object file.
    119     load_symbol: SymbolOffset,
    120     /// The value is in a target-specific register.
    121     register: Register,
    122     /// The value is in memory at a hard-coded address.
    123     /// If the type is a pointer, it means the pointer address is at this memory location.
    124     memory: u64,
    125     /// The value is one of the stack variables.
    126     /// If the type is a pointer, it means the pointer address is in the stack at this offset.
    127     stack_offset: u32,
    128     /// The value is a pointer to one of the stack variables (payload is stack offset).
    129     ptr_stack_offset: u32,
    130 
    131     fn isMemory(mcv: MCValue) bool {
    132         return switch (mcv) {
    133             .memory, .stack_offset => true,
    134             else => false,
    135         };
    136     }
    137 
    138     fn isImmediate(mcv: MCValue) bool {
    139         return switch (mcv) {
    140             .immediate => true,
    141             else => false,
    142         };
    143     }
    144 
    145     fn isMutable(mcv: MCValue) bool {
    146         return switch (mcv) {
    147             .none => unreachable,
    148             .unreach => unreachable,
    149             .dead => unreachable,
    150 
    151             .immediate,
    152             .memory,
    153             .ptr_stack_offset,
    154             .undef,
    155             .load_symbol,
    156             => false,
    157 
    158             .register,
    159             .stack_offset,
    160             => true,
    161         };
    162     }
    163 };
    164 
    165 const Branch = struct {
    166     inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
    167 
    168     fn deinit(self: *Branch, gpa: Allocator) void {
    169         self.inst_table.deinit(gpa);
    170         self.* = undefined;
    171     }
    172 };
    173 
    174 const StackAllocation = struct {
    175     inst: Air.Inst.Index,
    176     /// TODO: make the size inferred from the bits of the inst
    177     size: u32,
    178 };
    179 
    180 const BlockData = struct {
    181     relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
    182     /// The first break instruction encounters `null` here and chooses a
    183     /// machine code value for the block result, populating this field.
    184     /// Following break instructions encounter that value and use it for
    185     /// the location to store their block results.
    186     mcv: MCValue,
    187 };
    188 
    189 const BigTomb = struct {
    190     function: *Self,
    191     inst: Air.Inst.Index,
    192     lbt: Liveness.BigTomb,
    193 
    194     fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
    195         const dies = bt.lbt.feed();
    196         const op_index = op_ref.toIndex() orelse return;
    197         if (!dies) return;
    198         bt.function.processDeath(op_index);
    199     }
    200 
    201     fn finishAir(bt: *BigTomb, result: MCValue) void {
    202         const is_used = !bt.function.liveness.isUnused(bt.inst);
    203         if (is_used) {
    204             log.debug("%{d} => {}", .{ bt.inst, result });
    205             const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
    206             branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
    207         }
    208         bt.function.finishAirBookkeeping();
    209     }
    210 };
    211 
    212 const Self = @This();
    213 
    214 pub fn generate(
    215     lf: *link.File,
    216     src_loc: Module.SrcLoc,
    217     func_index: InternPool.Index,
    218     air: Air,
    219     liveness: Liveness,
    220     code: *std.ArrayList(u8),
    221     debug_output: DebugInfoOutput,
    222 ) CodeGenError!Result {
    223     const gpa = lf.comp.gpa;
    224     const zcu = lf.comp.module.?;
    225     const func = zcu.funcInfo(func_index);
    226     const fn_owner_decl = zcu.declPtr(func.owner_decl);
    227     assert(fn_owner_decl.has_tv);
    228     const fn_type = fn_owner_decl.typeOf(zcu);
    229     const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace);
    230     const target = &namespace.file_scope.mod.resolved_target.result;
    231 
    232     var branch_stack = std.ArrayList(Branch).init(gpa);
    233     defer {
    234         assert(branch_stack.items.len == 1);
    235         branch_stack.items[0].deinit(gpa);
    236         branch_stack.deinit();
    237     }
    238     try branch_stack.append(.{});
    239 
    240     var function = Self{
    241         .gpa = gpa,
    242         .air = air,
    243         .liveness = liveness,
    244         .target = target,
    245         .bin_file = lf,
    246         .func_index = func_index,
    247         .code = code,
    248         .debug_output = debug_output,
    249         .err_msg = null,
    250         .args = undefined, // populated after `resolveCallingConventionValues`
    251         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
    252         .fn_type = fn_type,
    253         .arg_index = 0,
    254         .branch_stack = &branch_stack,
    255         .src_loc = src_loc,
    256         .stack_align = undefined,
    257         .end_di_line = func.rbrace_line,
    258         .end_di_column = func.rbrace_column,
    259     };
    260     defer function.stack.deinit(gpa);
    261     defer function.blocks.deinit(gpa);
    262     defer function.exitlude_jump_relocs.deinit(gpa);
    263 
    264     var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
    265         error.CodegenFail => return Result{ .fail = function.err_msg.? },
    266         error.OutOfRegisters => return Result{
    267             .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    268         },
    269         else => |e| return e,
    270     };
    271 
    272     defer call_info.deinit(&function);
    273 
    274     function.args = call_info.args;
    275     function.ret_mcv = call_info.return_value;
    276     function.stack_align = call_info.stack_align;
    277     function.max_end_stack = call_info.stack_byte_count;
    278 
    279     function.gen() catch |err| switch (err) {
    280         error.CodegenFail => return Result{ .fail = function.err_msg.? },
    281         error.OutOfRegisters => return Result{
    282             .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    283         },
    284         else => |e| return e,
    285     };
    286 
    287     var mir = Mir{
    288         .instructions = function.mir_instructions.toOwnedSlice(),
    289         .extra = try function.mir_extra.toOwnedSlice(gpa),
    290     };
    291     defer mir.deinit(gpa);
    292 
    293     var emit = Emit{
    294         .mir = mir,
    295         .bin_file = lf,
    296         .debug_output = debug_output,
    297         .target = target,
    298         .src_loc = src_loc,
    299         .code = code,
    300         .prev_di_pc = 0,
    301         .prev_di_line = func.lbrace_line,
    302         .prev_di_column = func.lbrace_column,
    303         .stack_size = @max(32, function.max_end_stack),
    304     };
    305     defer emit.deinit();
    306 
    307     emit.emitMir() catch |err| switch (err) {
    308         error.EmitFail => return Result{ .fail = emit.err_msg.? },
    309         else => |e| return e,
    310     };
    311 
    312     if (function.err_msg) |em| {
    313         return Result{ .fail = em };
    314     } else {
    315         return Result.ok;
    316     }
    317 }
    318 
    319 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
    320     const gpa = self.gpa;
    321 
    322     try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
    323 
    324     const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
    325     self.mir_instructions.appendAssumeCapacity(inst);
    326     return result_index;
    327 }
    328 
    329 fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
    330     return try self.addInst(.{
    331         .tag = .nop,
    332         .data = .{ .nop = {} },
    333     });
    334 }
    335 
    336 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
    337     const fields = std.meta.fields(@TypeOf(extra));
    338     try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
    339     return self.addExtraAssumeCapacity(extra);
    340 }
    341 
    342 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
    343     const fields = std.meta.fields(@TypeOf(extra));
    344     const result: u32 = @intCast(self.mir_extra.items.len);
    345     inline for (fields) |field| {
    346         self.mir_extra.appendAssumeCapacity(switch (field.type) {
    347             u32 => @field(extra, field.name),
    348             i32 => @bitCast(@field(extra, field.name)),
    349             else => @compileError("bad field type"),
    350         });
    351     }
    352     return result;
    353 }
    354 
    355 fn gen(self: *Self) !void {
    356     _ = try self.addInst(.{
    357         .tag = .psuedo_prologue,
    358         .data = .{ .nop = {} }, // Backpatched later.
    359     });
    360 
    361     _ = try self.addInst(.{
    362         .tag = .dbg_prologue_end,
    363         .data = .{ .nop = {} },
    364     });
    365 
    366     try self.genBody(self.air.getMainBody());
    367 
    368     // Drop them off at the rbrace.
    369     _ = try self.addInst(.{
    370         .tag = .dbg_line,
    371         .data = .{ .dbg_line_column = .{
    372             .line = self.end_di_line,
    373             .column = self.end_di_column,
    374         } },
    375     });
    376 }
    377 
    378 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
    379     const mod = self.bin_file.comp.module.?;
    380     const ip = &mod.intern_pool;
    381     const air_tags = self.air.instructions.items(.tag);
    382 
    383     for (body) |inst| {
    384         // TODO: remove now-redundant isUnused calls from AIR handler functions
    385         if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip))
    386             continue;
    387 
    388         const old_air_bookkeeping = self.air_bookkeeping;
    389         try self.ensureProcessDeathCapacity(Liveness.bpi);
    390 
    391         switch (air_tags[@intFromEnum(inst)]) {
    392             // zig fmt: off
    393             .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
    394             .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
    395 
    396             .add => try self.airBinOp(inst, .add),
    397             .sub => try self.airBinOp(inst, .sub),
    398 
    399             .add_safe,
    400             .sub_safe,
    401             .mul_safe,
    402             => return self.fail("TODO implement safety_checked_instructions", .{}),
    403 
    404             .add_wrap        => try self.airAddWrap(inst),
    405             .add_sat         => try self.airAddSat(inst),
    406             .sub_wrap        => try self.airSubWrap(inst),
    407             .sub_sat         => try self.airSubSat(inst),
    408             .mul             => try self.airMul(inst),
    409             .mul_wrap        => try self.airMulWrap(inst),
    410             .mul_sat         => try self.airMulSat(inst),
    411             .rem             => try self.airRem(inst),
    412             .mod             => try self.airMod(inst),
    413             .shl, .shl_exact => try self.airShl(inst),
    414             .shl_sat         => try self.airShlSat(inst),
    415             .min             => try self.airMin(inst),
    416             .max             => try self.airMax(inst),
    417             .slice           => try self.airSlice(inst),
    418 
    419             .sqrt,
    420             .sin,
    421             .cos,
    422             .tan,
    423             .exp,
    424             .exp2,
    425             .log,
    426             .log2,
    427             .log10,
    428             .floor,
    429             .ceil,
    430             .round,
    431             .trunc_float,
    432             .neg,
    433             => try self.airUnaryMath(inst),
    434 
    435             .add_with_overflow => try self.airAddWithOverflow(inst),
    436             .sub_with_overflow => try self.airSubWithOverflow(inst),
    437             .mul_with_overflow => try self.airMulWithOverflow(inst),
    438             .shl_with_overflow => try self.airShlWithOverflow(inst),
    439 
    440             .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
    441 
    442             .cmp_lt  => try self.airCmp(inst),
    443             .cmp_lte => try self.airCmp(inst),
    444             .cmp_eq  => try self.airCmp(inst),
    445             .cmp_gte => try self.airCmp(inst),
    446             .cmp_gt  => try self.airCmp(inst),
    447             .cmp_neq => try self.airCmp(inst),
    448 
    449             .cmp_vector => try self.airCmpVector(inst),
    450             .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
    451 
    452             .bool_and        => try self.airBoolOp(inst),
    453             .bool_or         => try self.airBoolOp(inst),
    454             .bit_and         => try self.airBitAnd(inst),
    455             .bit_or          => try self.airBitOr(inst),
    456             .xor             => try self.airXor(inst),
    457             .shr, .shr_exact => try self.airShr(inst),
    458 
    459             .alloc           => try self.airAlloc(inst),
    460             .ret_ptr         => try self.airRetPtr(inst),
    461             .arg             => try self.airArg(inst),
    462             .assembly        => try self.airAsm(inst),
    463             .bitcast         => try self.airBitCast(inst),
    464             .block           => try self.airBlock(inst),
    465             .br              => try self.airBr(inst),
    466             .trap            => try self.airTrap(),
    467             .breakpoint      => try self.airBreakpoint(),
    468             .ret_addr        => try self.airRetAddr(inst),
    469             .frame_addr      => try self.airFrameAddress(inst),
    470             .fence           => try self.airFence(),
    471             .cond_br         => try self.airCondBr(inst),
    472             .dbg_stmt        => try self.airDbgStmt(inst),
    473             .fptrunc         => try self.airFptrunc(inst),
    474             .fpext           => try self.airFpext(inst),
    475             .intcast         => try self.airIntCast(inst),
    476             .trunc           => try self.airTrunc(inst),
    477             .int_from_bool   => try self.airIntFromBool(inst),
    478             .is_non_null     => try self.airIsNonNull(inst),
    479             .is_non_null_ptr => try self.airIsNonNullPtr(inst),
    480             .is_null         => try self.airIsNull(inst),
    481             .is_null_ptr     => try self.airIsNullPtr(inst),
    482             .is_non_err      => try self.airIsNonErr(inst),
    483             .is_non_err_ptr  => try self.airIsNonErrPtr(inst),
    484             .is_err          => try self.airIsErr(inst),
    485             .is_err_ptr      => try self.airIsErrPtr(inst),
    486             .load            => try self.airLoad(inst),
    487             .loop            => try self.airLoop(inst),
    488             .not             => try self.airNot(inst),
    489             .int_from_ptr    => try self.airIntFromPtr(inst),
    490             .ret             => try self.airRet(inst, false),
    491             .ret_safe        => try self.airRet(inst, true),
    492             .ret_load        => try self.airRetLoad(inst),
    493             .store           => try self.airStore(inst, false),
    494             .store_safe      => try self.airStore(inst, true),
    495             .struct_field_ptr=> try self.airStructFieldPtr(inst),
    496             .struct_field_val=> try self.airStructFieldVal(inst),
    497             .array_to_slice  => try self.airArrayToSlice(inst),
    498             .float_from_int  => try self.airFloatFromInt(inst),
    499             .int_from_float  => try self.airIntFromFloat(inst),
    500             .cmpxchg_strong  => try self.airCmpxchg(inst),
    501             .cmpxchg_weak    => try self.airCmpxchg(inst),
    502             .atomic_rmw      => try self.airAtomicRmw(inst),
    503             .atomic_load     => try self.airAtomicLoad(inst),
    504             .memcpy          => try self.airMemcpy(inst),
    505             .memset          => try self.airMemset(inst, false),
    506             .memset_safe     => try self.airMemset(inst, true),
    507             .set_union_tag   => try self.airSetUnionTag(inst),
    508             .get_union_tag   => try self.airGetUnionTag(inst),
    509             .clz             => try self.airClz(inst),
    510             .ctz             => try self.airCtz(inst),
    511             .popcount        => try self.airPopcount(inst),
    512             .abs             => try self.airAbs(inst),
    513             .byte_swap       => try self.airByteSwap(inst),
    514             .bit_reverse     => try self.airBitReverse(inst),
    515             .tag_name        => try self.airTagName(inst),
    516             .error_name      => try self.airErrorName(inst),
    517             .splat           => try self.airSplat(inst),
    518             .select          => try self.airSelect(inst),
    519             .shuffle         => try self.airShuffle(inst),
    520             .reduce          => try self.airReduce(inst),
    521             .aggregate_init  => try self.airAggregateInit(inst),
    522             .union_init      => try self.airUnionInit(inst),
    523             .prefetch        => try self.airPrefetch(inst),
    524             .mul_add         => try self.airMulAdd(inst),
    525             .addrspace_cast  => return self.fail("TODO: addrspace_cast", .{}),
    526 
    527             .@"try"          =>  return self.fail("TODO: try", .{}),
    528             .try_ptr         =>  return self.fail("TODO: try_ptr", .{}),
    529 
    530             .dbg_var_ptr,
    531             .dbg_var_val,
    532             => try self.airDbgVar(inst),
    533 
    534             .dbg_inline_block => try self.airDbgInlineBlock(inst),
    535 
    536             .call              => try self.airCall(inst, .auto),
    537             .call_always_tail  => try self.airCall(inst, .always_tail),
    538             .call_never_tail   => try self.airCall(inst, .never_tail),
    539             .call_never_inline => try self.airCall(inst, .never_inline),
    540 
    541             .atomic_store_unordered => try self.airAtomicStore(inst, .unordered),
    542             .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic),
    543             .atomic_store_release   => try self.airAtomicStore(inst, .release),
    544             .atomic_store_seq_cst   => try self.airAtomicStore(inst, .seq_cst),
    545 
    546             .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
    547             .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
    548             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
    549             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
    550 
    551             .field_parent_ptr => try self.airFieldParentPtr(inst),
    552 
    553             .switch_br       => try self.airSwitch(inst),
    554             .slice_ptr       => try self.airSlicePtr(inst),
    555             .slice_len       => try self.airSliceLen(inst),
    556 
    557             .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
    558             .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
    559 
    560             .array_elem_val      => try self.airArrayElemVal(inst),
    561             .slice_elem_val      => try self.airSliceElemVal(inst),
    562             .slice_elem_ptr      => try self.airSliceElemPtr(inst),
    563             .ptr_elem_val        => try self.airPtrElemVal(inst),
    564             .ptr_elem_ptr        => try self.airPtrElemPtr(inst),
    565 
    566             .inferred_alloc, .inferred_alloc_comptime => unreachable,
    567             .unreach  => self.finishAirBookkeeping(),
    568 
    569             .optional_payload           => try self.airOptionalPayload(inst),
    570             .optional_payload_ptr       => try self.airOptionalPayloadPtr(inst),
    571             .optional_payload_ptr_set   => try self.airOptionalPayloadPtrSet(inst),
    572             .unwrap_errunion_err        => try self.airUnwrapErrErr(inst),
    573             .unwrap_errunion_payload    => try self.airUnwrapErrPayload(inst),
    574             .unwrap_errunion_err_ptr    => try self.airUnwrapErrErrPtr(inst),
    575             .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
    576             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
    577             .err_return_trace           => try self.airErrReturnTrace(inst),
    578             .set_err_return_trace       => try self.airSetErrReturnTrace(inst),
    579             .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
    580 
    581             .wrap_optional         => try self.airWrapOptional(inst),
    582             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
    583             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
    584 
    585             .add_optimized,
    586             .sub_optimized,
    587             .mul_optimized,
    588             .div_float_optimized,
    589             .div_trunc_optimized,
    590             .div_floor_optimized,
    591             .div_exact_optimized,
    592             .rem_optimized,
    593             .mod_optimized,
    594             .neg_optimized,
    595             .cmp_lt_optimized,
    596             .cmp_lte_optimized,
    597             .cmp_eq_optimized,
    598             .cmp_gte_optimized,
    599             .cmp_gt_optimized,
    600             .cmp_neq_optimized,
    601             .cmp_vector_optimized,
    602             .reduce_optimized,
    603             .int_from_float_optimized,
    604             => return self.fail("TODO implement optimized float mode", .{}),
    605 
    606             .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
    607             .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
    608             .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
    609 
    610             .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
    611             .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
    612             .c_va_end => return self.fail("TODO implement c_va_end", .{}),
    613             .c_va_start => return self.fail("TODO implement c_va_start", .{}),
    614 
    615             .wasm_memory_size => unreachable,
    616             .wasm_memory_grow => unreachable,
    617 
    618             .work_item_id => unreachable,
    619             .work_group_size => unreachable,
    620             .work_group_id => unreachable,
    621             // zig fmt: on
    622         }
    623         if (std.debug.runtime_safety) {
    624             if (self.air_bookkeeping < old_air_bookkeeping + 1) {
    625                 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
    626             }
    627         }
    628     }
    629 }
    630 
    631 /// Asserts there is already capacity to insert into top branch inst_table.
    632 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
    633     // When editing this function, note that the logic must synchronize with `reuseOperand`.
    634     const prev_value = self.getResolvedInstValue(inst);
    635     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    636     branch.inst_table.putAssumeCapacity(inst, .dead);
    637     switch (prev_value) {
    638         .register => |reg| {
    639             self.register_manager.freeReg(reg);
    640         },
    641         else => {}, // TODO process stack allocation death
    642     }
    643 }
    644 
    645 /// Called when there are no operands, and the instruction is always unreferenced.
    646 fn finishAirBookkeeping(self: *Self) void {
    647     if (std.debug.runtime_safety) {
    648         self.air_bookkeeping += 1;
    649     }
    650 }
    651 
    652 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
    653     var tomb_bits = self.liveness.getTombBits(inst);
    654     for (operands) |op| {
    655         const dies = @as(u1, @truncate(tomb_bits)) != 0;
    656         tomb_bits >>= 1;
    657         if (!dies) continue;
    658         const op_index = op.toIndex() orelse continue;
    659         self.processDeath(op_index);
    660     }
    661     const is_used = @as(u1, @truncate(tomb_bits)) == 0;
    662     if (is_used) {
    663         log.debug("%{d} => {}", .{ inst, result });
    664         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    665         branch.inst_table.putAssumeCapacityNoClobber(inst, result);
    666 
    667         switch (result) {
    668             .register => |reg| {
    669                 // In some cases (such as bitcast), an operand
    670                 // may be the same MCValue as the result. If
    671                 // that operand died and was a register, it
    672                 // was freed by processDeath. We have to
    673                 // "re-allocate" the register.
    674                 if (self.register_manager.isRegFree(reg)) {
    675                     self.register_manager.getRegAssumeFree(reg, inst);
    676                 }
    677             },
    678             else => {},
    679         }
    680     }
    681     self.finishAirBookkeeping();
    682 }
    683 
    684 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
    685     const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
    686     try table.ensureUnusedCapacity(self.gpa, additional_count);
    687 }
    688 
    689 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: Alignment) !u32 {
    690     self.stack_align = self.stack_align.max(abi_align);
    691     // TODO find a free slot instead of always appending
    692     const offset: u32 = @intCast(abi_align.forward(self.next_stack_offset));
    693     self.next_stack_offset = offset + abi_size;
    694     if (self.next_stack_offset > self.max_end_stack)
    695         self.max_end_stack = self.next_stack_offset;
    696     try self.stack.putNoClobber(self.gpa, offset, .{
    697         .inst = inst,
    698         .size = abi_size,
    699     });
    700     return offset;
    701 }
    702 
    703 /// Use a pointer instruction as the basis for allocating stack memory.
    704 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
    705     const mod = self.bin_file.comp.module.?;
    706     const elem_ty = self.typeOfIndex(inst).childType(mod);
    707     const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {
    708         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
    709     };
    710     // TODO swap this for inst.ty.ptrAlign
    711     const abi_align = elem_ty.abiAlignment(mod);
    712     return self.allocMem(inst, abi_size, abi_align);
    713 }
    714 
    715 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
    716     const mod = self.bin_file.comp.module.?;
    717     const elem_ty = self.typeOfIndex(inst);
    718     const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {
    719         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
    720     };
    721     const abi_align = elem_ty.abiAlignment(mod);
    722     self.stack_align = self.stack_align.max(abi_align);
    723 
    724     if (reg_ok) {
    725         // Make sure the type can fit in a register before we try to allocate one.
    726         const ptr_bits = self.target.ptrBitWidth();
    727         const ptr_bytes: u64 = @divExact(ptr_bits, 8);
    728         if (abi_size <= ptr_bytes) {
    729             if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
    730                 return MCValue{ .register = reg };
    731             }
    732         }
    733     }
    734     const stack_offset = try self.allocMem(inst, abi_size, abi_align);
    735     return MCValue{ .stack_offset = stack_offset };
    736 }
    737 
    738 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
    739     const stack_mcv = try self.allocRegOrMem(inst, false);
    740     log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
    741     const reg_mcv = self.getResolvedInstValue(inst);
    742     assert(reg == reg_mcv.register);
    743     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    744     try branch.inst_table.put(self.gpa, inst, stack_mcv);
    745     try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
    746 }
    747 
    748 /// Copies a value to a register without tracking the register. The register is not considered
    749 /// allocated. A second call to `copyToTmpRegister` may return the same register.
    750 /// This can have a side effect of spilling instructions to the stack to free up a register.
    751 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
    752     const reg = try self.register_manager.allocReg(null, gp);
    753     try self.genSetReg(ty, reg, mcv);
    754     return reg;
    755 }
    756 
    757 /// Allocates a new register and copies `mcv` into it.
    758 /// `reg_owner` is the instruction that gets associated with the register in the register table.
    759 /// This can have a side effect of spilling instructions to the stack to free up a register.
    760 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
    761     const reg = try self.register_manager.allocReg(reg_owner, gp);
    762     try self.genSetReg(self.typeOfIndex(reg_owner), reg, mcv);
    763     return MCValue{ .register = reg };
    764 }
    765 
    766 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
    767     const stack_offset = try self.allocMemPtr(inst);
    768     log.debug("airAlloc offset: {}", .{stack_offset});
    769     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
    770 }
    771 
    772 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
    773     const stack_offset = try self.allocMemPtr(inst);
    774     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
    775 }
    776 
    777 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
    778     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
    779     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
    780     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    781 }
    782 
    783 fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
    784     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
    785     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
    786     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    787 }
    788 
    789 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
    790     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
    791     if (self.liveness.isUnused(inst))
    792         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
    793 
    794     const mod = self.bin_file.comp.module.?;
    795     const operand_ty = self.typeOf(ty_op.operand);
    796     const operand = try self.resolveInst(ty_op.operand);
    797     const info_a = operand_ty.intInfo(mod);
    798     const info_b = self.typeOfIndex(inst).intInfo(mod);
    799     if (info_a.signedness != info_b.signedness)
    800         return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
    801 
    802     if (info_a.bits == info_b.bits)
    803         return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
    804 
    805     return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
    806     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    807 }
    808 
    809 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
    810     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
    811     if (self.liveness.isUnused(inst))
    812         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
    813 
    814     const operand = try self.resolveInst(ty_op.operand);
    815     _ = operand;
    816     return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
    817     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    818 }
    819 
    820 fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void {
    821     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
    822     const operand = try self.resolveInst(un_op);
    823     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
    824     return self.finishAir(inst, result, .{ un_op, .none, .none });
    825 }
    826 
    827 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
    828     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
    829     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch});
    830     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    831 }
    832 
    833 fn airMin(self: *Self, inst: Air.Inst.Index) !void {
    834     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
    835     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
    836     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    837 }
    838 
    839 fn airMax(self: *Self, inst: Air.Inst.Index) !void {
    840     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
    841     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
    842     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    843 }
    844 
    845 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
    846     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
    847     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
    848     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
    849     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    850 }
    851 
    852 /// Don't call this function directly. Use binOp instead.
    853 ///
    854 /// Calling this function signals an intention to generate a Mir
    855 /// instruction of the form
    856 ///
    857 ///     op dest, lhs, rhs
    858 ///
    859 /// Asserts that generating an instruction of that form is possible.
    860 fn binOpRegister(
    861     self: *Self,
    862     tag: Air.Inst.Tag,
    863     maybe_inst: ?Air.Inst.Index,
    864     lhs: MCValue,
    865     rhs: MCValue,
    866     lhs_ty: Type,
    867     rhs_ty: Type,
    868 ) !MCValue {
    869     const lhs_is_register = lhs == .register;
    870     const rhs_is_register = rhs == .register;
    871 
    872     const lhs_lock: ?RegisterLock = if (lhs_is_register)
    873         self.register_manager.lockReg(lhs.register)
    874     else
    875         null;
    876     defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
    877 
    878     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    879 
    880     const lhs_reg = if (lhs_is_register) lhs.register else blk: {
    881         const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
    882             const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
    883             break :inst bin_op.lhs.toIndex().?;
    884         } else null;
    885 
    886         const reg = try self.register_manager.allocReg(track_inst, gp);
    887 
    888         if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
    889 
    890         break :blk reg;
    891     };
    892     const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
    893     defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
    894 
    895     const rhs_reg = if (rhs_is_register) rhs.register else blk: {
    896         const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
    897             const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
    898             break :inst bin_op.rhs.toIndex().?;
    899         } else null;
    900 
    901         const reg = try self.register_manager.allocReg(track_inst, gp);
    902 
    903         if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
    904 
    905         break :blk reg;
    906     };
    907     const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
    908     defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
    909 
    910     const dest_reg = if (maybe_inst) |inst| blk: {
    911         const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
    912 
    913         if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
    914             break :blk lhs_reg;
    915         } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
    916             break :blk rhs_reg;
    917         } else {
    918             break :blk try self.register_manager.allocReg(inst, gp);
    919         }
    920     } else try self.register_manager.allocReg(null, gp);
    921 
    922     if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
    923     if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
    924 
    925     const mir_tag: Mir.Inst.Tag = switch (tag) {
    926         .add => .add,
    927         .sub => .sub,
    928         .cmp_eq => .cmp_eq,
    929         .cmp_gt => .cmp_gt,
    930         else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
    931     };
    932     const mir_data: Mir.Inst.Data = switch (tag) {
    933         .add,
    934         .sub,
    935         .cmp_eq,
    936         => .{ .r_type = .{
    937             .rd = dest_reg,
    938             .rs1 = lhs_reg,
    939             .rs2 = rhs_reg,
    940         } },
    941         else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
    942     };
    943 
    944     _ = try self.addInst(.{
    945         .tag = mir_tag,
    946         .data = mir_data,
    947     });
    948 
    949     return MCValue{ .register = dest_reg };
    950 }
    951 
    952 /// For all your binary operation needs, this function will generate
    953 /// the corresponding Mir instruction(s). Returns the location of the
    954 /// result.
    955 ///
    956 /// If the binary operation itself happens to be an Air instruction,
    957 /// pass the corresponding index in the inst parameter. That helps
    958 /// this function do stuff like reusing operands.
    959 ///
    960 /// This function does not do any lowering to Mir itself, but instead
    961 /// looks at the lhs and rhs and determines which kind of lowering
    962 /// would be best suitable and then delegates the lowering to other
    963 /// functions.
    964 ///
    965 /// `maybe_inst` **needs** to be a bin_op, make sure of that.
    966 fn binOp(
    967     self: *Self,
    968     tag: Air.Inst.Tag,
    969     maybe_inst: ?Air.Inst.Index,
    970     lhs: MCValue,
    971     rhs: MCValue,
    972     lhs_ty: Type,
    973     rhs_ty: Type,
    974 ) InnerError!MCValue {
    975     const mod = self.bin_file.comp.module.?;
    976     switch (tag) {
    977         // Arithmetic operations on integers and floats
    978         .add,
    979         .sub,
    980         .cmp_eq,
    981         .cmp_neq,
    982         .cmp_gt,
    983         .cmp_gte,
    984         .cmp_lt,
    985         .cmp_lte,
    986         => {
    987             switch (lhs_ty.zigTypeTag(mod)) {
    988                 .Float => return self.fail("TODO binary operations on floats", .{}),
    989                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
    990                 .Int => {
    991                     assert(lhs_ty.eql(rhs_ty, mod));
    992                     const int_info = lhs_ty.intInfo(mod);
    993                     if (int_info.bits <= 64) {
    994                         // TODO immediate operands
    995                         return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
    996                     } else {
    997                         return self.fail("TODO binary operations on int with bits > 64", .{});
    998                     }
    999                 },
   1000                 else => unreachable,
   1001             }
   1002         },
   1003         .ptr_add,
   1004         .ptr_sub,
   1005         => {
   1006             switch (lhs_ty.zigTypeTag(mod)) {
   1007                 .Pointer => {
   1008                     const ptr_ty = lhs_ty;
   1009                     const elem_ty = switch (ptr_ty.ptrSize(mod)) {
   1010                         .One => ptr_ty.childType(mod).childType(mod), // ptr to array, so get array element type
   1011                         else => ptr_ty.childType(mod),
   1012                     };
   1013                     const elem_size = elem_ty.abiSize(mod);
   1014 
   1015                     if (elem_size == 1) {
   1016                         const base_tag: Air.Inst.Tag = switch (tag) {
   1017                             .ptr_add => .add,
   1018                             .ptr_sub => .sub,
   1019                             else => unreachable,
   1020                         };
   1021 
   1022                         return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
   1023                     } else {
   1024                         return self.fail("TODO ptr_add with elem_size > 1", .{});
   1025                     }
   1026                 },
   1027                 else => unreachable,
   1028             }
   1029         },
   1030         else => unreachable,
   1031     }
   1032 }
   1033 
   1034 fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
   1035     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1036     const lhs = try self.resolveInst(bin_op.lhs);
   1037     const rhs = try self.resolveInst(bin_op.rhs);
   1038     const lhs_ty = self.typeOf(bin_op.lhs);
   1039     const rhs_ty = self.typeOf(bin_op.rhs);
   1040 
   1041     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
   1042     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1043 }
   1044 
   1045 fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
   1046     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1047     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1048     const lhs = try self.resolveInst(bin_op.lhs);
   1049     const rhs = try self.resolveInst(bin_op.rhs);
   1050     const lhs_ty = self.typeOf(bin_op.lhs);
   1051     const rhs_ty = self.typeOf(bin_op.rhs);
   1052 
   1053     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
   1054     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1055 }
   1056 
   1057 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
   1058     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1059     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
   1060     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1061 }
   1062 
   1063 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
   1064     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1065     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
   1066     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1067 }
   1068 
   1069 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
   1070     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1071     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
   1072     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1073 }
   1074 
   1075 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
   1076     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1077     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
   1078     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1079 }
   1080 
   1081 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
   1082     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1083     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch});
   1084     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1085 }
   1086 
   1087 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
   1088     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1089     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
   1090     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1091 }
   1092 
   1093 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
   1094     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1095     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
   1096     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1097 }
   1098 
   1099 fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1100     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1101     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1102 
   1103     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1104         const lhs = try self.resolveInst(extra.lhs);
   1105         const rhs = try self.resolveInst(extra.rhs);
   1106         const lhs_ty = self.typeOf(extra.lhs);
   1107         const rhs_ty = self.typeOf(extra.rhs);
   1108 
   1109         break :result try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty);
   1110     };
   1111 
   1112     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1113 }
   1114 
   1115 fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1116     _ = inst;
   1117     return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
   1118 }
   1119 
   1120 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1121     _ = inst;
   1122     return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
   1123 }
   1124 
   1125 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
   1126     _ = inst;
   1127     return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
   1128 }
   1129 
   1130 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
   1131     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1132     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
   1133     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1134 }
   1135 
   1136 fn airRem(self: *Self, inst: Air.Inst.Index) !void {
   1137     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1138     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
   1139     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1140 }
   1141 
   1142 fn airMod(self: *Self, inst: Air.Inst.Index) !void {
   1143     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1144     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
   1145     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1146 }
   1147 
   1148 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
   1149     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1150     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch});
   1151     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1152 }
   1153 
   1154 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
   1155     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1156     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch});
   1157     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1158 }
   1159 
   1160 fn airXor(self: *Self, inst: Air.Inst.Index) !void {
   1161     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1162     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch});
   1163     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1164 }
   1165 
   1166 fn airShl(self: *Self, inst: Air.Inst.Index) !void {
   1167     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1168     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch});
   1169     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1170 }
   1171 
   1172 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
   1173     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1174     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
   1175     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1176 }
   1177 
   1178 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
   1179     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1180     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
   1181     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1182 }
   1183 
   1184 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
   1185     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1186     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
   1187     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1188 }
   1189 
   1190 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   1191     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1192     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
   1193     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1194 }
   1195 
   1196 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   1197     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1198     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
   1199     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1200 }
   1201 
   1202 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
   1203     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1204     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});
   1205     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1206 }
   1207 
   1208 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
   1209     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1210     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
   1211     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1212 }
   1213 
   1214 // *(E!T) -> E
   1215 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1216     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1217     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});
   1218     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1219 }
   1220 
   1221 // *(E!T) -> *T
   1222 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   1223     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1224     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});
   1225     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1226 }
   1227 
   1228 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   1229     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1230     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
   1231     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1232 }
   1233 
   1234 fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
   1235     const result: MCValue = if (self.liveness.isUnused(inst))
   1236         .dead
   1237     else
   1238         return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
   1239     return self.finishAir(inst, result, .{ .none, .none, .none });
   1240 }
   1241 
   1242 fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
   1243     _ = inst;
   1244     return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
   1245 }
   1246 
   1247 fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
   1248     _ = inst;
   1249     return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
   1250 }
   1251 
   1252 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
   1253     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1254     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1255         const mod = self.bin_file.comp.module.?;
   1256         const optional_ty = self.typeOfIndex(inst);
   1257 
   1258         // Optional with a zero-bit payload type is just a boolean true
   1259         if (optional_ty.abiSize(mod) == 1)
   1260             break :result MCValue{ .immediate = 1 };
   1261 
   1262         return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
   1263     };
   1264     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1265 }
   1266 
   1267 /// T to E!T
   1268 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
   1269     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1270     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
   1271     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1272 }
   1273 
   1274 /// E to E!T
   1275 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
   1276     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1277     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch});
   1278     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1279 }
   1280 
   1281 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
   1282     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1283     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1284         const mcv = try self.resolveInst(ty_op.operand);
   1285         break :result try self.slicePtr(mcv);
   1286     };
   1287     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1288 }
   1289 
   1290 fn slicePtr(self: *Self, mcv: MCValue) !MCValue {
   1291     switch (mcv) {
   1292         .dead, .unreach, .none => unreachable,
   1293         .register => unreachable, // a slice doesn't fit in one register
   1294         .stack_offset => |off| {
   1295             return MCValue{ .stack_offset = off };
   1296         },
   1297         .memory => |addr| {
   1298             return MCValue{ .memory = addr };
   1299         },
   1300         else => return self.fail("TODO slicePtr {s}", .{@tagName(mcv)}),
   1301     }
   1302 }
   1303 
   1304 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
   1305     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1306     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSliceLen for {}", .{self.target.cpu.arch});
   1307     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1308 }
   1309 
   1310 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
   1311     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1312     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch});
   1313     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1314 }
   1315 
   1316 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1317     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1318     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch});
   1319     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1320 }
   1321 
   1322 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1323     const is_volatile = false; // TODO
   1324     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1325     const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch});
   1326     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1327 }
   1328 
   1329 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   1330     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1331     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1332     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
   1333     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1334 }
   1335 
   1336 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1337     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1338     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
   1339     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1340 }
   1341 
   1342 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1343     const is_volatile = false; // TODO
   1344     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1345     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});
   1346     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1347 }
   1348 
   1349 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   1350     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1351     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1352     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
   1353     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1354 }
   1355 
   1356 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   1357     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1358     _ = bin_op;
   1359     return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
   1360     // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1361 }
   1362 
   1363 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   1364     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1365     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
   1366     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1367 }
   1368 
   1369 fn airClz(self: *Self, inst: Air.Inst.Index) !void {
   1370     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1371     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
   1372     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1373 }
   1374 
   1375 fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
   1376     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1377     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
   1378     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1379 }
   1380 
   1381 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
   1382     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1383     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
   1384     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1385 }
   1386 
   1387 fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
   1388     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1389     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
   1390     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1391 }
   1392 
   1393 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
   1394     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1395     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
   1396     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1397 }
   1398 
   1399 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
   1400     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1401     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
   1402     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1403 }
   1404 
   1405 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
   1406     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   1407     const result: MCValue = if (self.liveness.isUnused(inst))
   1408         .dead
   1409     else
   1410         return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
   1411     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1412 }
   1413 
   1414 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
   1415     if (!self.liveness.operandDies(inst, op_index))
   1416         return false;
   1417 
   1418     switch (mcv) {
   1419         .register => |reg| {
   1420             // If it's in the registers table, need to associate the register with the
   1421             // new instruction.
   1422             if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
   1423                 if (!self.register_manager.isRegFree(reg)) {
   1424                     self.register_manager.registers[index] = inst;
   1425                 }
   1426             }
   1427             log.debug("%{d} => {} (reused)", .{ inst, reg });
   1428         },
   1429         .stack_offset => |off| {
   1430             log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
   1431         },
   1432         else => return false,
   1433     }
   1434 
   1435     // Prevent the operand deaths processing code from deallocating it.
   1436     self.liveness.clearOperandDeath(inst, op_index);
   1437 
   1438     // That makes us responsible for doing the rest of the stuff that processDeath would have done.
   1439     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   1440     branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead);
   1441 
   1442     return true;
   1443 }
   1444 
   1445 fn load(self: *Self, dst_mcv: MCValue, src_ptr: MCValue, ptr_ty: Type) InnerError!void {
   1446     const mod = self.bin_file.comp.module.?;
   1447     const elem_ty = ptr_ty.childType(mod);
   1448 
   1449     switch (src_ptr) {
   1450         .none => unreachable,
   1451         .undef => unreachable,
   1452         .unreach => unreachable,
   1453         .dead => unreachable,
   1454         .immediate => |imm| try self.setValue(elem_ty, dst_mcv, .{ .memory = imm }),
   1455         .ptr_stack_offset => |off| try self.setValue(elem_ty, dst_mcv, .{ .stack_offset = off }),
   1456         .register => try self.setValue(elem_ty, dst_mcv, src_ptr),
   1457         .memory,
   1458         .stack_offset,
   1459         => {
   1460             const reg = try self.register_manager.allocReg(null, gp);
   1461             const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
   1462             errdefer self.register_manager.unlockReg(reg_lock);
   1463 
   1464             try self.genSetReg(ptr_ty, reg, src_ptr);
   1465             try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
   1466         },
   1467         .load_symbol => {
   1468             const reg = try self.copyToTmpRegister(ptr_ty, src_ptr);
   1469             try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
   1470         },
   1471     }
   1472 }
   1473 
   1474 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
   1475     const mod = self.bin_file.comp.module.?;
   1476     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1477     const elem_ty = self.typeOfIndex(inst);
   1478     const result: MCValue = result: {
   1479         if (!elem_ty.hasRuntimeBits(mod))
   1480             break :result MCValue.none;
   1481 
   1482         const ptr = try self.resolveInst(ty_op.operand);
   1483         const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod);
   1484         if (self.liveness.isUnused(inst) and !is_volatile)
   1485             break :result MCValue.dead;
   1486 
   1487         const dst_mcv: MCValue = blk: {
   1488             if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
   1489                 // The MCValue that holds the pointer can be re-used as the value.
   1490                 break :blk ptr;
   1491             } else {
   1492                 break :blk try self.allocRegOrMem(inst, true);
   1493             }
   1494         };
   1495         try self.load(dst_mcv, ptr, self.typeOf(ty_op.operand));
   1496         break :result dst_mcv;
   1497     };
   1498     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1499 }
   1500 
   1501 fn store(self: *Self, dst_ptr: MCValue, src_val: MCValue, ptr_ty: Type, value_ty: Type) !void {
   1502     _ = ptr_ty;
   1503 
   1504     log.debug("storing {s}", .{@tagName(dst_ptr)});
   1505 
   1506     switch (dst_ptr) {
   1507         .none => unreachable,
   1508         .undef => unreachable,
   1509         .unreach => unreachable,
   1510         .dead => unreachable,
   1511         .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, src_val),
   1512         else => return self.fail("TODO implement storing to MCValue.{s}", .{@tagName(dst_ptr)}),
   1513     }
   1514 }
   1515 
   1516 fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
   1517     if (safety) {
   1518         // TODO if the value is undef, write 0xaa bytes to dest
   1519     } else {
   1520         // TODO if the value is undef, don't lower this instruction
   1521     }
   1522     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1523     const ptr = try self.resolveInst(bin_op.lhs);
   1524     const value = try self.resolveInst(bin_op.rhs);
   1525     const ptr_ty = self.typeOf(bin_op.lhs);
   1526     const value_ty = self.typeOf(bin_op.rhs);
   1527 
   1528     try self.store(ptr, value, ptr_ty, value_ty);
   1529 
   1530     return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   1531 }
   1532 
   1533 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
   1534     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1535     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   1536     const result = try self.structFieldPtr(inst, extra.struct_operand, ty_pl.ty, extra.field_index);
   1537     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
   1538 }
   1539 
   1540 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
   1541     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   1542     const result = try self.structFieldPtr(inst, ty_op.operand, ty_op.ty, index);
   1543     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1544 }
   1545 
   1546 fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !MCValue {
   1547     _ = inst;
   1548     _ = operand;
   1549     _ = ty;
   1550     _ = index;
   1551 
   1552     return self.fail("TODO: structFieldPtr", .{});
   1553 }
   1554 
   1555 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
   1556     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1557     _ = ty_pl;
   1558 
   1559     return self.fail("TODO: airStructFieldVal", .{});
   1560 
   1561     // return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
   1562 }
   1563 
   1564 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
   1565     _ = inst;
   1566     return self.fail("TODO implement codegen airFieldParentPtr", .{});
   1567 }
   1568 
   1569 fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
   1570     const mod = self.bin_file.comp.module.?;
   1571     const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
   1572     const ty = arg.ty.toType();
   1573     const owner_decl = mod.funcOwnerDeclIndex(self.func_index);
   1574     const name = mod.getParamName(self.func_index, arg.src_index);
   1575 
   1576     switch (self.debug_output) {
   1577         .dwarf => |dw| switch (mcv) {
   1578             .register => |reg| try dw.genArgDbgInfo(name, ty, owner_decl, .{
   1579                 .register = reg.dwarfLocOp(),
   1580             }),
   1581             .stack_offset => {},
   1582             else => {},
   1583         },
   1584         .plan9 => {},
   1585         .none => {},
   1586     }
   1587 }
   1588 
   1589 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
   1590     var arg_index = self.arg_index;
   1591 
   1592     // we skip over args that have no bits
   1593     while (self.args[arg_index] == .none) arg_index += 1;
   1594     self.arg_index = arg_index + 1;
   1595 
   1596     const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
   1597         const arg_ty = self.typeOfIndex(inst);
   1598         _ = arg_ty;
   1599         const src_mcv = self.args[arg_index];
   1600 
   1601         const dst_mcv = switch (src_mcv) {
   1602             .register => |src_reg| dst: {
   1603                 self.register_manager.getRegAssumeFree(src_reg, inst);
   1604                 break :dst src_mcv;
   1605             },
   1606             // don't need to allocate anything, can just be used immediately.
   1607             .stack_offset => src_mcv,
   1608             else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
   1609         };
   1610 
   1611         try self.genArgDbgInfo(inst, src_mcv);
   1612         break :result dst_mcv;
   1613     };
   1614 
   1615     return self.finishAir(inst, result, .{ .none, .none, .none });
   1616 }
   1617 
   1618 fn airTrap(self: *Self) !void {
   1619     _ = try self.addInst(.{
   1620         .tag = .unimp,
   1621         .data = .{ .nop = {} },
   1622     });
   1623     return self.finishAirBookkeeping();
   1624 }
   1625 
   1626 fn airBreakpoint(self: *Self) !void {
   1627     _ = try self.addInst(.{
   1628         .tag = .ebreak,
   1629         .data = .{ .nop = {} },
   1630     });
   1631     return self.finishAirBookkeeping();
   1632 }
   1633 
   1634 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
   1635     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{});
   1636     return self.finishAir(inst, result, .{ .none, .none, .none });
   1637 }
   1638 
   1639 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
   1640     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{});
   1641     return self.finishAir(inst, result, .{ .none, .none, .none });
   1642 }
   1643 
   1644 fn airFence(self: *Self) !void {
   1645     return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
   1646     //return self.finishAirBookkeeping();
   1647 }
   1648 
   1649 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
   1650     const mod = self.bin_file.comp.module.?;
   1651     if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});
   1652     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   1653     const fn_ty = self.typeOf(pl_op.operand);
   1654     const callee = pl_op.operand;
   1655     const extra = self.air.extraData(Air.Call, pl_op.payload);
   1656     const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]);
   1657 
   1658     var info = try self.resolveCallingConventionValues(fn_ty);
   1659     defer info.deinit(self);
   1660 
   1661     // Due to incremental compilation, how function calls are generated depends
   1662     // on linking.
   1663     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
   1664         for (info.args, 0..) |mc_arg, arg_i| {
   1665             const arg = args[arg_i];
   1666             const arg_ty = self.typeOf(arg);
   1667             const arg_mcv = try self.resolveInst(args[arg_i]);
   1668             try self.setValue(arg_ty, mc_arg, arg_mcv);
   1669         }
   1670 
   1671         if (try self.air.value(callee, mod)) |func_value| {
   1672             switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
   1673                 .func => |func| {
   1674                     const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
   1675                     const sym = elf_file.symbol(sym_index);
   1676                     _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
   1677                     const got_addr = sym.zigGotAddress(elf_file);
   1678                     try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
   1679                     _ = try self.addInst(.{
   1680                         .tag = .jalr,
   1681                         .data = .{ .i_type = .{
   1682                             .rd = .ra,
   1683                             .rs1 = .ra,
   1684                             .imm12 = 0,
   1685                         } },
   1686                     });
   1687                 },
   1688                 .extern_func => {
   1689                     return self.fail("TODO implement calling extern functions", .{});
   1690                 },
   1691                 else => {
   1692                     return self.fail("TODO implement calling bitcasted functions", .{});
   1693                 },
   1694             }
   1695         } else {
   1696             return self.fail("TODO implement calling runtime-known function pointer", .{});
   1697         }
   1698     } else if (self.bin_file.cast(link.File.Coff)) |_| {
   1699         return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
   1700     } else if (self.bin_file.cast(link.File.MachO)) |_| {
   1701         unreachable; // unsupported architecture for MachO
   1702     } else if (self.bin_file.cast(link.File.Plan9)) |_| {
   1703         return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
   1704     } else unreachable;
   1705 
   1706     const result: MCValue = result: {
   1707         switch (info.return_value) {
   1708             .register => |reg| {
   1709                 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
   1710                     // Save function return value in a callee saved register
   1711                     break :result try self.copyToNewRegister(inst, info.return_value);
   1712                 }
   1713             },
   1714             else => {},
   1715         }
   1716         break :result info.return_value;
   1717     };
   1718 
   1719     if (args.len <= Liveness.bpi - 2) {
   1720         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   1721         buf[0] = callee;
   1722         @memcpy(buf[1..][0..args.len], args);
   1723         return self.finishAir(inst, result, buf);
   1724     }
   1725     var bt = try self.iterateBigTomb(inst, 1 + args.len);
   1726     bt.feed(callee);
   1727     for (args) |arg| {
   1728         bt.feed(arg);
   1729     }
   1730     return bt.finishAir(result);
   1731 }
   1732 
   1733 fn ret(self: *Self, mcv: MCValue) !void {
   1734     const mod = self.bin_file.comp.module.?;
   1735     const ret_ty = self.fn_type.fnReturnType(mod);
   1736     try self.setValue(ret_ty, self.ret_mcv, mcv);
   1737 
   1738     // Just add space for an instruction, patch this later
   1739     const index = try self.addInst(.{
   1740         .tag = .ret,
   1741         .data = .{ .nop = {} },
   1742     });
   1743 
   1744     try self.exitlude_jump_relocs.append(self.gpa, index);
   1745 }
   1746 
   1747 fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
   1748     if (safety) {
   1749         // safe
   1750     } else {
   1751         // not safe
   1752     }
   1753 
   1754     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   1755     const operand = try self.resolveInst(un_op);
   1756 
   1757     _ = try self.addInst(.{
   1758         .tag = .dbg_epilogue_begin,
   1759         .data = .{ .nop = {} },
   1760     });
   1761 
   1762     _ = try self.addInst(.{
   1763         .tag = .psuedo_epilogue,
   1764         .data = .{ .nop = {} },
   1765     });
   1766 
   1767     try self.ret(operand);
   1768 
   1769     return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   1770 }
   1771 
   1772 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
   1773     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   1774     const ptr = try self.resolveInst(un_op);
   1775     _ = ptr;
   1776     return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
   1777     //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   1778 }
   1779 
   1780 fn airCmp(self: *Self, inst: Air.Inst.Index) !void {
   1781     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
   1782     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   1783     if (self.liveness.isUnused(inst))
   1784         return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   1785     const ty = self.typeOf(bin_op.lhs);
   1786     const mod = self.bin_file.comp.module.?;
   1787     assert(ty.eql(self.typeOf(bin_op.rhs), mod));
   1788     if (ty.zigTypeTag(mod) == .ErrorSet)
   1789         return self.fail("TODO implement cmp for errors", .{});
   1790 
   1791     const lhs = try self.resolveInst(bin_op.lhs);
   1792     const rhs = try self.resolveInst(bin_op.rhs);
   1793     const lhs_ty = self.typeOf(bin_op.lhs);
   1794     const rhs_ty = self.typeOf(bin_op.rhs);
   1795 
   1796     const result = try self.binOp(tag, null, lhs, rhs, lhs_ty, rhs_ty);
   1797 
   1798     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1799 }
   1800 
   1801 fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
   1802     _ = inst;
   1803     return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
   1804 }
   1805 
   1806 fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
   1807     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   1808     const operand = try self.resolveInst(un_op);
   1809     _ = operand;
   1810     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
   1811     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1812 }
   1813 
   1814 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
   1815     const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   1816 
   1817     _ = try self.addInst(.{
   1818         .tag = .dbg_line,
   1819         .data = .{ .dbg_line_column = .{
   1820             .line = dbg_stmt.line,
   1821             .column = dbg_stmt.column,
   1822         } },
   1823     });
   1824 
   1825     return self.finishAirBookkeeping();
   1826 }
   1827 
   1828 fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
   1829     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   1830     const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
   1831     _ = extra;
   1832     // TODO: emit debug info for this block
   1833     return self.finishAir(inst, .dead, .{ .none, .none, .none });
   1834 }
   1835 
   1836 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
   1837     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   1838     const name = self.air.nullTerminatedString(pl_op.payload);
   1839     const operand = pl_op.operand;
   1840     // TODO emit debug info for this variable
   1841     _ = name;
   1842     return self.finishAir(inst, .dead, .{ operand, .none, .none });
   1843 }
   1844 
   1845 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
   1846     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   1847     const cond = try self.resolveInst(pl_op.operand);
   1848     const cond_ty = self.typeOf(pl_op.operand);
   1849     const extra = self.air.extraData(Air.CondBr, pl_op.payload);
   1850     const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]);
   1851     const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);
   1852     const liveness_condbr = self.liveness.getCondBr(inst);
   1853 
   1854     // A branch to the false section. Uses beq
   1855     const reloc = try self.condBr(cond_ty, cond);
   1856 
   1857     // If the condition dies here in this condbr instruction, process
   1858     // that death now instead of later as this has an effect on
   1859     // whether it needs to be spilled in the branches
   1860     if (self.liveness.operandDies(inst, 0)) {
   1861         if (pl_op.operand.toIndex()) |op_index| {
   1862             self.processDeath(op_index);
   1863         }
   1864     }
   1865 
   1866     // Save state
   1867     const parent_next_stack_offset = self.next_stack_offset;
   1868     const parent_free_registers = self.register_manager.free_registers;
   1869     var parent_stack = try self.stack.clone(self.gpa);
   1870     defer parent_stack.deinit(self.gpa);
   1871     const parent_registers = self.register_manager.registers;
   1872 
   1873     try self.branch_stack.append(.{});
   1874     errdefer {
   1875         _ = self.branch_stack.pop();
   1876     }
   1877 
   1878     try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
   1879     for (liveness_condbr.then_deaths) |operand| {
   1880         self.processDeath(operand);
   1881     }
   1882     try self.genBody(then_body);
   1883 
   1884     // Revert to the previous register and stack allocation state.
   1885 
   1886     var saved_then_branch = self.branch_stack.pop();
   1887     defer saved_then_branch.deinit(self.gpa);
   1888 
   1889     self.register_manager.registers = parent_registers;
   1890 
   1891     self.stack.deinit(self.gpa);
   1892     self.stack = parent_stack;
   1893     parent_stack = .{};
   1894 
   1895     self.next_stack_offset = parent_next_stack_offset;
   1896     self.register_manager.free_registers = parent_free_registers;
   1897 
   1898     try self.performReloc(reloc);
   1899     const else_branch = self.branch_stack.addOneAssumeCapacity();
   1900     else_branch.* = .{};
   1901 
   1902     try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
   1903     for (liveness_condbr.else_deaths) |operand| {
   1904         self.processDeath(operand);
   1905     }
   1906     try self.genBody(else_body);
   1907 
   1908     // At this point, each branch will possibly have conflicting values for where
   1909     // each instruction is stored. They agree, however, on which instructions are alive/dead.
   1910     // We use the first ("then") branch as canonical, and here emit
   1911     // instructions into the second ("else") branch to make it conform.
   1912     // We continue respect the data structure semantic guarantees of the else_branch so
   1913     // that we can use all the code emitting abstractions. This is why at the bottom we
   1914     // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
   1915     // rather than assigning it.
   1916     const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
   1917     try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
   1918     const else_slice = else_branch.inst_table.entries.slice();
   1919     const else_keys = else_slice.items(.key);
   1920     const else_values = else_slice.items(.value);
   1921     for (else_keys, 0..) |else_key, else_idx| {
   1922         const else_value = else_values[else_idx];
   1923         const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
   1924             // The instruction's MCValue is overridden in both branches.
   1925             log.debug("condBr put branch table (key = %{d}, value = {})", .{ else_key, then_entry.value });
   1926             parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
   1927             if (else_value == .dead) {
   1928                 assert(then_entry.value == .dead);
   1929                 continue;
   1930             }
   1931             break :blk then_entry.value;
   1932         } else blk: {
   1933             if (else_value == .dead)
   1934                 continue;
   1935             // The instruction is only overridden in the else branch.
   1936             var i: usize = self.branch_stack.items.len - 2;
   1937             while (true) {
   1938                 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
   1939                 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
   1940                     assert(mcv != .dead);
   1941                     break :blk mcv;
   1942                 }
   1943             }
   1944         };
   1945         log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
   1946         // TODO make sure the destination stack offset / register does not already have something
   1947         // going on there.
   1948         try self.setValue(self.typeOfIndex(else_key), canon_mcv, else_value);
   1949         // TODO track the new register / stack allocation
   1950     }
   1951     try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
   1952     const then_slice = saved_then_branch.inst_table.entries.slice();
   1953     const then_keys = then_slice.items(.key);
   1954     const then_values = then_slice.items(.value);
   1955     for (then_keys, 0..) |then_key, then_idx| {
   1956         const then_value = then_values[then_idx];
   1957         // We already deleted the items from this table that matched the else_branch.
   1958         // So these are all instructions that are only overridden in the then branch.
   1959         parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
   1960         if (then_value == .dead)
   1961             continue;
   1962         const parent_mcv = blk: {
   1963             var i: usize = self.branch_stack.items.len - 2;
   1964             while (true) {
   1965                 i -= 1;
   1966                 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
   1967                     assert(mcv != .dead);
   1968                     break :blk mcv;
   1969                 }
   1970             }
   1971         };
   1972         log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
   1973         // TODO make sure the destination stack offset / register does not already have something
   1974         // going on there.
   1975         try self.setValue(self.typeOfIndex(then_key), parent_mcv, then_value);
   1976         // TODO track the new register / stack allocation
   1977     }
   1978 
   1979     {
   1980         var item = self.branch_stack.pop();
   1981         item.deinit(self.gpa);
   1982     }
   1983 
   1984     return self.finishAir(inst, .unreach, .{ .none, .none, .none });
   1985 }
   1986 
   1987 fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
   1988     _ = cond_ty;
   1989 
   1990     const reg = switch (condition) {
   1991         .register => |r| r,
   1992         else => try self.copyToTmpRegister(Type.bool, condition),
   1993     };
   1994 
   1995     return try self.addInst(.{
   1996         .tag = .beq,
   1997         .data = .{
   1998             .b_type = .{
   1999                 .rs1 = reg,
   2000                 .rs2 = .zero,
   2001                 .imm12 = 0, // patched later.
   2002             },
   2003         },
   2004     });
   2005 }
   2006 
   2007 fn isNull(self: *Self, operand: MCValue) !MCValue {
   2008     _ = operand;
   2009     // Here you can specialize this instruction if it makes sense to, otherwise the default
   2010     // will call isNonNull and invert the result.
   2011     return self.fail("TODO call isNonNull and invert the result", .{});
   2012 }
   2013 
   2014 fn isNonNull(self: *Self, operand: MCValue) !MCValue {
   2015     _ = operand;
   2016     // Here you can specialize this instruction if it makes sense to, otherwise the default
   2017     // will call isNull and invert the result.
   2018     return self.fail("TODO call isNull and invert the result", .{});
   2019 }
   2020 
   2021 fn isErr(self: *Self, operand: MCValue) !MCValue {
   2022     _ = operand;
   2023     // Here you can specialize this instruction if it makes sense to, otherwise the default
   2024     // will call isNonNull and invert the result.
   2025     return self.fail("TODO call isNonErr and invert the result", .{});
   2026 }
   2027 
   2028 fn isNonErr(self: *Self, operand: MCValue) !MCValue {
   2029     _ = operand;
   2030     // Here you can specialize this instruction if it makes sense to, otherwise the default
   2031     // will call isNull and invert the result.
   2032     return self.fail("TODO call isErr and invert the result", .{});
   2033 }
   2034 
   2035 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
   2036     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2037     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2038         const operand = try self.resolveInst(un_op);
   2039         break :result try self.isNull(operand);
   2040     };
   2041     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2042 }
   2043 
   2044 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   2045     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2046     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2047         const operand_ptr = try self.resolveInst(un_op);
   2048         const operand: MCValue = blk: {
   2049             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   2050                 // The MCValue that holds the pointer can be re-used as the value.
   2051                 break :blk operand_ptr;
   2052             } else {
   2053                 break :blk try self.allocRegOrMem(inst, true);
   2054             }
   2055         };
   2056         try self.load(operand, operand_ptr, self.typeOf(un_op));
   2057         break :result try self.isNull(operand);
   2058     };
   2059     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2060 }
   2061 
   2062 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
   2063     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2064     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2065         const operand = try self.resolveInst(un_op);
   2066         break :result try self.isNonNull(operand);
   2067     };
   2068     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2069 }
   2070 
   2071 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   2072     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2073     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2074         const operand_ptr = try self.resolveInst(un_op);
   2075         const operand: MCValue = blk: {
   2076             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   2077                 // The MCValue that holds the pointer can be re-used as the value.
   2078                 break :blk operand_ptr;
   2079             } else {
   2080                 break :blk try self.allocRegOrMem(inst, true);
   2081             }
   2082         };
   2083         try self.load(operand, operand_ptr, self.typeOf(un_op));
   2084         break :result try self.isNonNull(operand);
   2085     };
   2086     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2087 }
   2088 
   2089 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
   2090     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2091     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2092         const operand = try self.resolveInst(un_op);
   2093         break :result try self.isErr(operand);
   2094     };
   2095     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2096 }
   2097 
   2098 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   2099     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2100     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2101         const operand_ptr = try self.resolveInst(un_op);
   2102         const operand: MCValue = blk: {
   2103             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   2104                 // The MCValue that holds the pointer can be re-used as the value.
   2105                 break :blk operand_ptr;
   2106             } else {
   2107                 break :blk try self.allocRegOrMem(inst, true);
   2108             }
   2109         };
   2110         try self.load(operand, operand_ptr, self.typeOf(un_op));
   2111         break :result try self.isErr(operand);
   2112     };
   2113     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2114 }
   2115 
   2116 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
   2117     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2118     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2119         const operand = try self.resolveInst(un_op);
   2120         break :result try self.isNonErr(operand);
   2121     };
   2122     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2123 }
   2124 
   2125 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   2126     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2127     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   2128         const operand_ptr = try self.resolveInst(un_op);
   2129         const operand: MCValue = blk: {
   2130             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   2131                 // The MCValue that holds the pointer can be re-used as the value.
   2132                 break :blk operand_ptr;
   2133             } else {
   2134                 break :blk try self.allocRegOrMem(inst, true);
   2135             }
   2136         };
   2137         try self.load(operand, operand_ptr, self.typeOf(un_op));
   2138         break :result try self.isNonErr(operand);
   2139     };
   2140     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2141 }
   2142 
   2143 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
   2144     // A loop is a setup to be able to jump back to the beginning.
   2145     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2146     const loop = self.air.extraData(Air.Block, ty_pl.payload);
   2147     const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]);
   2148 
   2149     const start_index: Mir.Inst.Index = @intCast(self.code.items.len);
   2150 
   2151     try self.genBody(body);
   2152     try self.jump(start_index);
   2153 
   2154     return self.finishAirBookkeeping();
   2155 }
   2156 
   2157 /// Send control flow to the `index` of `self.code`.
   2158 fn jump(self: *Self, index: Mir.Inst.Index) !void {
   2159     _ = try self.addInst(.{
   2160         .tag = .j,
   2161         .data = .{
   2162             .inst = index,
   2163         },
   2164     });
   2165 }
   2166 
   2167 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
   2168     try self.blocks.putNoClobber(self.gpa, inst, .{
   2169         // A block is a setup to be able to jump to the end.
   2170         .relocs = .{},
   2171         // It also acts as a receptacle for break operands.
   2172         // Here we use `MCValue.none` to represent a null value so that the first
   2173         // break instruction will choose a MCValue for the block result and overwrite
   2174         // this field. Following break instructions will use that MCValue to put their
   2175         // block results.
   2176         .mcv = MCValue{ .none = {} },
   2177     });
   2178     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
   2179 
   2180     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2181     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   2182     const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
   2183     // TODO emit debug info lexical block
   2184     try self.genBody(body);
   2185 
   2186     for (self.blocks.getPtr(inst).?.relocs.items) |reloc| {
   2187         try self.performReloc(reloc);
   2188     }
   2189 
   2190     const result = self.blocks.getPtr(inst).?.mcv;
   2191     return self.finishAir(inst, result, .{ .none, .none, .none });
   2192 }
   2193 
   2194 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
   2195     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   2196     const condition = pl_op.operand;
   2197     _ = condition;
   2198     return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
   2199     // return self.finishAir(inst, .dead, .{ condition, .none, .none });
   2200 }
   2201 
   2202 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
   2203     const tag = self.mir_instructions.items(.tag)[inst];
   2204 
   2205     switch (tag) {
   2206         .beq => self.mir_instructions.items(.data)[inst].b_type.imm12 = @intCast(inst),
   2207         else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),
   2208     }
   2209 }
   2210 
   2211 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
   2212     const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
   2213     try self.br(branch.block_inst, branch.operand);
   2214     return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
   2215 }
   2216 
   2217 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
   2218     const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2219     const air_tags = self.air.instructions.items(.tag);
   2220     _ = air_tags;
   2221     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
   2222     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   2223 }
   2224 
   2225 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
   2226     const block_data = self.blocks.getPtr(block).?;
   2227 
   2228     const mod = self.bin_file.comp.module.?;
   2229     if (self.typeOf(operand).hasRuntimeBits(mod)) {
   2230         const operand_mcv = try self.resolveInst(operand);
   2231         const block_mcv = block_data.mcv;
   2232         if (block_mcv == .none) {
   2233             block_data.mcv = operand_mcv;
   2234         } else {
   2235             try self.setValue(self.typeOfIndex(block), block_mcv, operand_mcv);
   2236         }
   2237     }
   2238     return self.brVoid(block);
   2239 }
   2240 
   2241 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
   2242     const block_data = self.blocks.getPtr(block).?;
   2243 
   2244     // Emit a jump with a relocation. It will be patched up after the block ends.
   2245     try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
   2246 
   2247     block_data.relocs.appendAssumeCapacity(try self.addInst(.{
   2248         .tag = .jal,
   2249         .data = .{
   2250             .j_type = .{
   2251                 .rd = .ra,
   2252                 .imm21 = undefined, // populated later through performReloc
   2253             },
   2254         },
   2255     }));
   2256 }
   2257 
   2258 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
   2259     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2260     const extra = self.air.extraData(Air.Asm, ty_pl.payload);
   2261     const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
   2262     const clobbers_len: u31 = @truncate(extra.data.flags);
   2263     var extra_i: usize = extra.end;
   2264     const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]);
   2265     extra_i += outputs.len;
   2266     const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]);
   2267     extra_i += inputs.len;
   2268 
   2269     const dead = !is_volatile and self.liveness.isUnused(inst);
   2270     const result: MCValue = if (dead) .dead else result: {
   2271         if (outputs.len > 1) {
   2272             return self.fail("TODO implement codegen for asm with more than 1 output", .{});
   2273         }
   2274 
   2275         const output_constraint: ?[]const u8 = for (outputs) |output| {
   2276             if (output != .none) {
   2277                 return self.fail("TODO implement codegen for non-expr asm", .{});
   2278             }
   2279             const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
   2280             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   2281             const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   2282             // This equation accounts for the fact that even if we have exactly 4 bytes
   2283             // for the string, we still use the next u32 for the null terminator.
   2284             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   2285 
   2286             break constraint;
   2287         } else null;
   2288 
   2289         for (inputs) |input| {
   2290             const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
   2291             const constraint = std.mem.sliceTo(input_bytes, 0);
   2292             const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
   2293             // This equation accounts for the fact that even if we have exactly 4 bytes
   2294             // for the string, we still use the next u32 for the null terminator.
   2295             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   2296 
   2297             if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
   2298                 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
   2299             }
   2300             const reg_name = constraint[1 .. constraint.len - 1];
   2301             const reg = parseRegName(reg_name) orelse
   2302                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   2303 
   2304             const arg_mcv = try self.resolveInst(input);
   2305             try self.register_manager.getReg(reg, null);
   2306             try self.genSetReg(self.typeOf(input), reg, arg_mcv);
   2307         }
   2308 
   2309         {
   2310             var clobber_i: u32 = 0;
   2311             while (clobber_i < clobbers_len) : (clobber_i += 1) {
   2312                 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   2313                 // This equation accounts for the fact that even if we have exactly 4 bytes
   2314                 // for the string, we still use the next u32 for the null terminator.
   2315                 extra_i += clobber.len / 4 + 1;
   2316 
   2317                 // TODO honor these
   2318             }
   2319         }
   2320 
   2321         const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
   2322 
   2323         if (mem.eql(u8, asm_source, "ecall")) {
   2324             _ = try self.addInst(.{
   2325                 .tag = .ecall,
   2326                 .data = .{ .nop = {} },
   2327             });
   2328         } else {
   2329             return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
   2330         }
   2331 
   2332         if (output_constraint) |output| {
   2333             if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
   2334                 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
   2335             }
   2336             const reg_name = output[2 .. output.len - 1];
   2337             const reg = parseRegName(reg_name) orelse
   2338                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   2339             break :result MCValue{ .register = reg };
   2340         } else {
   2341             break :result MCValue{ .none = {} };
   2342         }
   2343     };
   2344     simple: {
   2345         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   2346         var buf_index: usize = 0;
   2347         for (outputs) |output| {
   2348             if (output == .none) continue;
   2349 
   2350             if (buf_index >= buf.len) break :simple;
   2351             buf[buf_index] = output;
   2352             buf_index += 1;
   2353         }
   2354         if (buf_index + inputs.len > buf.len) break :simple;
   2355         @memcpy(buf[buf_index..][0..inputs.len], inputs);
   2356         return self.finishAir(inst, result, buf);
   2357     }
   2358     var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
   2359     for (outputs) |output| {
   2360         if (output == .none) continue;
   2361 
   2362         bt.feed(output);
   2363     }
   2364     for (inputs) |input| {
   2365         bt.feed(input);
   2366     }
   2367     return bt.finishAir(result);
   2368 }
   2369 
   2370 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
   2371     try self.ensureProcessDeathCapacity(operand_count + 1);
   2372     return BigTomb{
   2373         .function = self,
   2374         .inst = inst,
   2375         .lbt = self.liveness.iterateBigTomb(inst),
   2376     };
   2377 }
   2378 
   2379 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
   2380 fn setValue(self: *Self, ty: Type, dst_val: MCValue, src_val: MCValue) !void {
   2381     // There isn't anything to store
   2382     if (dst_val == .none) return;
   2383 
   2384     if (!dst_val.isMutable()) {
   2385         return std.debug.panic("tried to setValue immutable: {s}", .{@tagName(dst_val)});
   2386     }
   2387 
   2388     switch (dst_val) {
   2389         .register => |reg| return self.genSetReg(ty, reg, src_val),
   2390         .stack_offset => |off| return self.genSetStack(ty, off, src_val),
   2391         .memory => |addr| return self.genSetMem(ty, addr, src_val),
   2392         else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}),
   2393     }
   2394 }
   2395 
   2396 /// Sets the value of `src_val` into stack memory at `stack_offset`.
   2397 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void {
   2398     const mod = self.bin_file.comp.module.?;
   2399     const abi_size: u32 = @intCast(ty.abiSize(mod));
   2400 
   2401     switch (src_val) {
   2402         .none => return,
   2403         .dead => unreachable,
   2404         .immediate => {
   2405             const reg = try self.copyToTmpRegister(ty, src_val);
   2406             return self.genSetStack(ty, stack_offset, .{ .register = reg });
   2407         },
   2408         .register => |reg| {
   2409             switch (abi_size) {
   2410                 1, 2, 4, 8 => {
   2411                     assert(std.mem.isAlignedGeneric(u32, stack_offset, abi_size));
   2412 
   2413                     const tag: Mir.Inst.Tag = switch (abi_size) {
   2414                         1 => .sb,
   2415                         2 => .sh,
   2416                         4 => .sw,
   2417                         8 => .sd,
   2418                         else => unreachable,
   2419                     };
   2420 
   2421                     _ = try self.addInst(.{
   2422                         .tag = tag,
   2423                         .data = .{ .i_type = .{
   2424                             .rd = reg,
   2425                             .rs1 = .s0,
   2426                             .imm12 = math.cast(i12, stack_offset) orelse {
   2427                                 return self.fail("TODO: genSetStack bigger stack values", .{});
   2428                             },
   2429                         } },
   2430                     });
   2431                 },
   2432                 else => return self.fail("TODO: genSetStack for size={d}", .{abi_size}),
   2433             }
   2434         },
   2435         .stack_offset, .load_symbol => {
   2436             if (abi_size <= 8) {
   2437                 const reg = try self.copyToTmpRegister(ty, src_val);
   2438                 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
   2439             }
   2440 
   2441             const ptr_ty = try mod.singleMutPtrType(ty);
   2442 
   2443             // TODO call extern memcpy
   2444             const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
   2445             const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs);
   2446             defer for (regs_locks) |reg| {
   2447                 self.register_manager.unlockReg(reg);
   2448             };
   2449 
   2450             const src_reg = regs[0];
   2451             const dst_reg = regs[1];
   2452             const len_reg = regs[2];
   2453             const count_reg = regs[3];
   2454             const tmp_reg = regs[4];
   2455 
   2456             switch (src_val) {
   2457                 .stack_offset => |offset| {
   2458                     if (offset == stack_offset) return;
   2459                     try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });
   2460                 },
   2461                 .load_symbol => |sym_off| {
   2462                     const atom_index = atom: {
   2463                         const decl_index = mod.funcOwnerDeclIndex(self.func_index);
   2464 
   2465                         if (self.bin_file.cast(link.File.Elf)) |elf_file| {
   2466                             const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
   2467                             break :atom atom_index;
   2468                         } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)});
   2469                     };
   2470 
   2471                     _ = try self.addInst(.{
   2472                         .tag = .load_symbol,
   2473                         .data = .{
   2474                             .payload = try self.addExtra(Mir.LoadSymbolPayload{
   2475                                 .register = @intFromEnum(src_reg),
   2476                                 .atom_index = atom_index,
   2477                                 .sym_index = sym_off.sym,
   2478                             }),
   2479                         },
   2480                     });
   2481                 },
   2482                 else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_val)}),
   2483             }
   2484 
   2485             try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });
   2486             try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
   2487 
   2488             // memcpy(src, dst, len)
   2489             try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
   2490         },
   2491         else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)}),
   2492     }
   2493 }
   2494 
   2495 fn genSetMem(self: *Self, ty: Type, addr: u64, src_val: MCValue) InnerError!void {
   2496     const mod = self.bin_file.comp.module.?;
   2497     const abi_size: u32 = @intCast(ty.abiSize(mod));
   2498     _ = abi_size;
   2499     _ = addr;
   2500     _ = src_val;
   2501 
   2502     return self.fail("TODO: genSetMem", .{});
   2503 }
   2504 
   2505 fn genInlineMemcpy(
   2506     self: *Self,
   2507     src: Register,
   2508     dst: Register,
   2509     len: Register,
   2510     count: Register,
   2511     tmp: Register,
   2512 ) !void {
   2513     _ = src;
   2514     _ = dst;
   2515     _ = len;
   2516     _ = count;
   2517     _ = tmp;
   2518 
   2519     return self.fail("TODO: genInlineMemcpy", .{});
   2520 }
   2521 
   2522 /// Sets the value of `src_val` into `reg`. Assumes you have a lock on it.
   2523 fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void {
   2524     const mod = self.bin_file.comp.module.?;
   2525     const abi_size: u32 = @intCast(ty.abiSize(mod));
   2526 
   2527     switch (src_val) {
   2528         .dead => unreachable,
   2529         .ptr_stack_offset => return self.fail("TODO genSetReg ptr_stack_offset", .{}),
   2530         .unreach, .none => return, // Nothing to do.
   2531         .undef => {
   2532             if (!self.wantSafety())
   2533                 return; // The already existing value will do just fine.
   2534             // Write the debug undefined value.
   2535             return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
   2536         },
   2537         .immediate => |unsigned_x| {
   2538             const x: i64 = @bitCast(unsigned_x);
   2539             if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
   2540                 _ = try self.addInst(.{
   2541                     .tag = .addi,
   2542                     .data = .{ .i_type = .{
   2543                         .rd = reg,
   2544                         .rs1 = .zero,
   2545                         .imm12 = @intCast(x),
   2546                     } },
   2547                 });
   2548             } else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
   2549                 const lo12: i12 = @truncate(x);
   2550                 const carry: i32 = if (lo12 < 0) 1 else 0;
   2551                 const hi20: i20 = @truncate((x >> 12) +% carry);
   2552 
   2553                 // TODO: add test case for 32-bit immediate
   2554                 _ = try self.addInst(.{
   2555                     .tag = .lui,
   2556                     .data = .{ .u_type = .{
   2557                         .rd = reg,
   2558                         .imm20 = hi20,
   2559                     } },
   2560                 });
   2561                 _ = try self.addInst(.{
   2562                     .tag = .addi,
   2563                     .data = .{ .i_type = .{
   2564                         .rd = reg,
   2565                         .rs1 = reg,
   2566                         .imm12 = lo12,
   2567                     } },
   2568                 });
   2569             } else {
   2570                 // li rd, immediate
   2571                 // "Myriad sequences"
   2572                 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
   2573             }
   2574         },
   2575         .register => |src_reg| {
   2576             // If the registers are the same, nothing to do.
   2577             if (src_reg.id() == reg.id())
   2578                 return;
   2579 
   2580             // mov reg, src_reg
   2581             _ = try self.addInst(.{
   2582                 .tag = .mv,
   2583                 .data = .{ .rr = .{
   2584                     .rd = reg,
   2585                     .rs = src_reg,
   2586                 } },
   2587             });
   2588         },
   2589         .memory => |addr| {
   2590             try self.genSetReg(ty, reg, .{ .immediate = addr });
   2591 
   2592             _ = try self.addInst(.{
   2593                 .tag = .ld,
   2594                 .data = .{ .i_type = .{
   2595                     .rd = reg,
   2596                     .rs1 = reg,
   2597                     .imm12 = 0,
   2598                 } },
   2599             });
   2600 
   2601             // LOAD imm=[i12 offset = 0], rs1
   2602         },
   2603         .stack_offset => |off| {
   2604             const tag: Mir.Inst.Tag = switch (abi_size) {
   2605                 1 => .lb,
   2606                 2 => .lh,
   2607                 4 => .lw,
   2608                 8 => .ld,
   2609                 else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}),
   2610             };
   2611 
   2612             _ = try self.addInst(.{
   2613                 .tag = tag,
   2614                 .data = .{ .i_type = .{
   2615                     .rd = reg,
   2616                     .rs1 = .s0,
   2617                     .imm12 = math.cast(i12, off) orelse {
   2618                         return self.fail("TODO: genSetReg support larger stack sizes", .{});
   2619                     },
   2620                 } },
   2621             });
   2622         },
   2623         .load_symbol => |sym_off| {
   2624             assert(sym_off.off == 0);
   2625 
   2626             const decl_index = mod.funcOwnerDeclIndex(self.func_index);
   2627 
   2628             const atom_index = switch (self.bin_file.tag) {
   2629                 .elf => blk: {
   2630                     const elf_file = self.bin_file.cast(link.File.Elf).?;
   2631                     const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
   2632                     break :blk atom_index;
   2633                 },
   2634                 else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}),
   2635             };
   2636             _ = try self.addInst(.{
   2637                 .tag = .load_symbol,
   2638                 .data = .{
   2639                     .payload = try self.addExtra(Mir.LoadSymbolPayload{
   2640                         .register = @intFromEnum(reg),
   2641                         .atom_index = atom_index,
   2642                         .sym_index = sym_off.sym,
   2643                     }),
   2644                 },
   2645             });
   2646         },
   2647     }
   2648 }
   2649 
   2650 fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
   2651     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2652     const result = try self.resolveInst(un_op);
   2653     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2654 }
   2655 
   2656 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
   2657     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2658     const result = if (self.liveness.isUnused(inst)) .dead else result: {
   2659         const operand = try self.resolveInst(ty_op.operand);
   2660         if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
   2661 
   2662         const operand_lock = switch (operand) {
   2663             .register => |reg| self.register_manager.lockReg(reg),
   2664             else => null,
   2665         };
   2666         defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
   2667 
   2668         const dest = try self.allocRegOrMem(inst, true);
   2669         try self.setValue(self.typeOfIndex(inst), dest, operand);
   2670         break :result dest;
   2671     };
   2672     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2673 }
   2674 
   2675 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
   2676     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2677     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{
   2678         self.target.cpu.arch,
   2679     });
   2680     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2681 }
   2682 
   2683 fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
   2684     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2685     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
   2686         self.target.cpu.arch,
   2687     });
   2688     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2689 }
   2690 
   2691 fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
   2692     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2693     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
   2694         self.target.cpu.arch,
   2695     });
   2696     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2697 }
   2698 
   2699 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
   2700     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2701     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   2702     _ = extra;
   2703     return self.fail("TODO implement airCmpxchg for {}", .{
   2704         self.target.cpu.arch,
   2705     });
   2706     // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
   2707 }
   2708 
   2709 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
   2710     _ = inst;
   2711     return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
   2712 }
   2713 
   2714 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
   2715     _ = inst;
   2716     return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
   2717 }
   2718 
   2719 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
   2720     _ = inst;
   2721     _ = order;
   2722     return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
   2723 }
   2724 
   2725 fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
   2726     _ = inst;
   2727     if (safety) {
   2728         // TODO if the value is undef, write 0xaa bytes to dest
   2729     } else {
   2730         // TODO if the value is undef, don't lower this instruction
   2731     }
   2732     return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
   2733 }
   2734 
   2735 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
   2736     _ = inst;
   2737     return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
   2738 }
   2739 
   2740 fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
   2741     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2742     const operand = try self.resolveInst(un_op);
   2743     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   2744         _ = operand;
   2745         return self.fail("TODO implement airTagName for riscv64", .{});
   2746     };
   2747     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2748 }
   2749 
   2750 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
   2751     const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   2752     const operand = try self.resolveInst(un_op);
   2753     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   2754         _ = operand;
   2755         return self.fail("TODO implement airErrorName for riscv64", .{});
   2756     };
   2757     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2758 }
   2759 
   2760 fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
   2761     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2762     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for riscv64", .{});
   2763     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2764 }
   2765 
   2766 fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
   2767     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   2768     const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
   2769     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for riscv64", .{});
   2770     return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
   2771 }
   2772 
   2773 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
   2774     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   2775     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{});
   2776     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2777 }
   2778 
   2779 fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
   2780     const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
   2781     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for riscv64", .{});
   2782     return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
   2783 }
   2784 
   2785 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
   2786     const mod = self.bin_file.comp.module.?;
   2787     const vector_ty = self.typeOfIndex(inst);
   2788     const len = vector_ty.vectorLen(mod);
   2789     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2790     const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]);
   2791     const result: MCValue = res: {
   2792         if (self.liveness.isUnused(inst)) break :res MCValue.dead;
   2793         return self.fail("TODO implement airAggregateInit for riscv64", .{});
   2794     };
   2795 
   2796     if (elements.len <= Liveness.bpi - 1) {
   2797         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   2798         @memcpy(buf[0..elements.len], elements);
   2799         return self.finishAir(inst, result, buf);
   2800     }
   2801     var bt = try self.iterateBigTomb(inst, elements.len);
   2802     for (elements) |elem| {
   2803         bt.feed(elem);
   2804     }
   2805     return bt.finishAir(result);
   2806 }
   2807 
   2808 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
   2809     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   2810     const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
   2811     _ = extra;
   2812     return self.fail("TODO implement airUnionInit for riscv64", .{});
   2813     // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
   2814 }
   2815 
   2816 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
   2817     const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
   2818     return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
   2819 }
   2820 
   2821 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
   2822     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   2823     const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
   2824     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   2825         return self.fail("TODO implement airMulAdd for riscv64", .{});
   2826     };
   2827     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
   2828 }
   2829 
   2830 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
   2831     const mod = self.bin_file.comp.module.?;
   2832 
   2833     // If the type has no codegen bits, no need to store it.
   2834     const inst_ty = self.typeOf(inst);
   2835     if (!inst_ty.hasRuntimeBits(mod))
   2836         return MCValue{ .none = {} };
   2837 
   2838     const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, mod)).?);
   2839 
   2840     return self.getResolvedInstValue(inst_index);
   2841 }
   2842 
   2843 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
   2844     // Treat each stack item as a "layer" on top of the previous one.
   2845     var i: usize = self.branch_stack.items.len;
   2846     while (true) {
   2847         i -= 1;
   2848         if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
   2849             assert(mcv != .dead);
   2850             return mcv;
   2851         }
   2852     }
   2853 }
   2854 
   2855 fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
   2856     const mod = self.bin_file.comp.module.?;
   2857     const mcv: MCValue = switch (try codegen.genTypedValue(
   2858         self.bin_file,
   2859         self.src_loc,
   2860         val,
   2861         mod.funcOwnerDeclIndex(self.func_index),
   2862     )) {
   2863         .mcv => |mcv| switch (mcv) {
   2864             .none => .none,
   2865             .undef => .undef,
   2866             .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
   2867             .immediate => |imm| .{ .immediate = imm },
   2868             .memory => |addr| .{ .memory = addr },
   2869             .load_got, .load_direct, .load_tlv => {
   2870                 return self.fail("TODO: genTypedValue {s}", .{@tagName(mcv)});
   2871             },
   2872         },
   2873         .fail => |msg| {
   2874             self.err_msg = msg;
   2875             return error.CodegenFail;
   2876         },
   2877     };
   2878     return mcv;
   2879 }
   2880 
   2881 const CallMCValues = struct {
   2882     args: []MCValue,
   2883     return_value: MCValue,
   2884     stack_byte_count: u32,
   2885     stack_align: Alignment,
   2886 
   2887     fn deinit(self: *CallMCValues, func: *Self) void {
   2888         func.gpa.free(self.args);
   2889         self.* = undefined;
   2890     }
   2891 };
   2892 
   2893 /// Caller must call `CallMCValues.deinit`.
   2894 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
   2895     const mod = self.bin_file.comp.module.?;
   2896     const fn_info = mod.typeToFunc(fn_ty).?;
   2897     const cc = fn_info.cc;
   2898     var result: CallMCValues = .{
   2899         .args = try self.gpa.alloc(MCValue, fn_info.param_types.len),
   2900         // These undefined values must be populated before returning from this function.
   2901         .return_value = undefined,
   2902         .stack_byte_count = undefined,
   2903         .stack_align = undefined,
   2904     };
   2905     errdefer self.gpa.free(result.args);
   2906 
   2907     const ret_ty = fn_ty.fnReturnType(mod);
   2908 
   2909     switch (cc) {
   2910         .Naked => {
   2911             assert(result.args.len == 0);
   2912             result.return_value = .{ .unreach = {} };
   2913             result.stack_byte_count = 0;
   2914             result.stack_align = .@"1";
   2915             return result;
   2916         },
   2917         .Unspecified, .C => {
   2918             if (result.args.len > 8) {
   2919                 return self.fail("TODO: support more than 8 function args", .{});
   2920             }
   2921 
   2922             for (0..result.args.len) |i| {
   2923                 const arg_reg = try self.register_manager.allocReg(null, fa);
   2924                 result.args[i] = .{ .register = arg_reg };
   2925             }
   2926 
   2927             // stack_offset = num s registers spilled + local var space
   2928             var stack_offset: u32 = 0;
   2929             _ = &stack_offset;
   2930             // TODO: spill used s registers here
   2931 
   2932             result.stack_byte_count = stack_offset;
   2933             result.stack_align = .@"16";
   2934         },
   2935         else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}),
   2936     }
   2937 
   2938     if (ret_ty.zigTypeTag(mod) == .NoReturn) {
   2939         result.return_value = .{ .unreach = {} };
   2940     } else if (!ret_ty.hasRuntimeBits(mod)) {
   2941         result.return_value = .{ .none = {} };
   2942     } else switch (cc) {
   2943         .Naked => unreachable,
   2944         .Unspecified, .C => {
   2945             const ret_ty_size: u32 = @intCast(ret_ty.abiSize(mod));
   2946             if (ret_ty_size <= 8) {
   2947                 result.return_value = .{ .register = .a0 };
   2948             } else if (ret_ty_size <= 16) {
   2949                 return self.fail("TODO support MCValue 2 registers", .{});
   2950             } else {
   2951                 return self.fail("TODO support return by reference", .{});
   2952             }
   2953         },
   2954         else => return self.fail("TODO implement function return values for {}", .{cc}),
   2955     }
   2956     return result;
   2957 }
   2958 
   2959 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
   2960 fn wantSafety(self: *Self) bool {
   2961     return switch (self.bin_file.comp.root_mod.optimize_mode) {
   2962         .Debug => true,
   2963         .ReleaseSafe => true,
   2964         .ReleaseFast => false,
   2965         .ReleaseSmall => false,
   2966     };
   2967 }
   2968 
   2969 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   2970     @setCold(true);
   2971     assert(self.err_msg == null);
   2972     self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args);
   2973     return error.CodegenFail;
   2974 }
   2975 
   2976 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   2977     @setCold(true);
   2978     assert(self.err_msg == null);
   2979     self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args);
   2980     return error.CodegenFail;
   2981 }
   2982 
   2983 fn parseRegName(name: []const u8) ?Register {
   2984     if (@hasDecl(Register, "parseRegName")) {
   2985         return Register.parseRegName(name);
   2986     }
   2987     return std.meta.stringToEnum(Register, name);
   2988 }
   2989 
   2990 fn typeOf(self: *Self, inst: Air.Inst.Ref) Type {
   2991     const mod = self.bin_file.comp.module.?;
   2992     return self.air.typeOf(inst, &mod.intern_pool);
   2993 }
   2994 
   2995 fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {
   2996     const mod = self.bin_file.comp.module.?;
   2997     return self.air.typeOfIndex(inst, &mod.intern_pool);
   2998 }