zig

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

blob 1604f045 (110036B) - Raw


      1 const std = @import("std");
      2 const builtin = @import("builtin");
      3 const mem = std.mem;
      4 const math = std.math;
      5 const assert = std.debug.assert;
      6 const Air = @import("../../Air.zig");
      7 const Mir = @import("Mir.zig");
      8 const Emit = @import("Emit.zig");
      9 const Liveness = @import("../../Liveness.zig");
     10 const Type = @import("../../type.zig").Type;
     11 const Value = @import("../../value.zig").Value;
     12 const TypedValue = @import("../../TypedValue.zig");
     13 const link = @import("../../link.zig");
     14 const Module = @import("../../Module.zig");
     15 const Compilation = @import("../../Compilation.zig");
     16 const ErrorMsg = Module.ErrorMsg;
     17 const Target = std.Target;
     18 const Allocator = mem.Allocator;
     19 const trace = @import("../../tracy.zig").trace;
     20 const DW = std.dwarf;
     21 const leb128 = std.leb;
     22 const log = std.log.scoped(.codegen);
     23 const build_options = @import("build_options");
     24 const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
     25 const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
     26 
     27 const FnResult = @import("../../codegen.zig").FnResult;
     28 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
     29 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
     30 
     31 const bits = @import("bits.zig");
     32 const abi = @import("abi.zig");
     33 const Register = bits.Register;
     34 const Instruction = abi.Instruction;
     35 const callee_preserved_regs = abi.callee_preserved_regs;
     36 
     37 const InnerError = error{
     38     OutOfMemory,
     39     CodegenFail,
     40     OutOfRegisters,
     41 };
     42 
     43 gpa: Allocator,
     44 air: Air,
     45 liveness: Liveness,
     46 bin_file: *link.File,
     47 target: *const std.Target,
     48 mod_fn: *const Module.Fn,
     49 code: *std.ArrayList(u8),
     50 debug_output: DebugInfoOutput,
     51 err_msg: ?*ErrorMsg,
     52 args: []MCValue,
     53 ret_mcv: MCValue,
     54 fn_type: Type,
     55 arg_index: usize,
     56 src_loc: Module.SrcLoc,
     57 stack_align: u32,
     58 
     59 /// MIR Instructions
     60 mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
     61 /// MIR extra data
     62 mir_extra: std.ArrayListUnmanaged(u32) = .{},
     63 
     64 /// Byte offset within the source file of the ending curly.
     65 end_di_line: u32,
     66 end_di_column: u32,
     67 
     68 /// The value is an offset into the `Function` `code` from the beginning.
     69 /// To perform the reloc, write 32-bit signed little-endian integer
     70 /// which is a relative jump, based on the address following the reloc.
     71 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
     72 
     73 /// Whenever there is a runtime branch, we push a Branch onto this stack,
     74 /// and pop it off when the runtime branch joins. This provides an "overlay"
     75 /// of the table of mappings from instructions to `MCValue` from within the branch.
     76 /// This way we can modify the `MCValue` for an instruction in different ways
     77 /// within different branches. Special consideration is needed when a branch
     78 /// joins with its parent, to make sure all instructions have the same MCValue
     79 /// across each runtime branch upon joining.
     80 branch_stack: *std.ArrayList(Branch),
     81 
     82 // Key is the block instruction
     83 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
     84 
     85 register_manager: RegisterManager = .{},
     86 /// Maps offset to what is stored there.
     87 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
     88 
     89 /// Offset from the stack base, representing the end of the stack frame.
     90 max_end_stack: u32 = 0,
     91 /// Represents the current end stack offset. If there is no existing slot
     92 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
     93 next_stack_offset: u32 = 0,
     94 
     95 /// Debug field, used to find bugs in the compiler.
     96 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
     97 
     98 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
     99 
    100 const MCValue = union(enum) {
    101     /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
    102     /// TODO Look into deleting this tag and using `dead` instead, since every use
    103     /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
    104     none,
    105     /// Control flow will not allow this value to be observed.
    106     unreach,
    107     /// No more references to this value remain.
    108     dead,
    109     /// The value is undefined.
    110     undef,
    111     /// A pointer-sized integer that fits in a register.
    112     /// If the type is a pointer, this is the pointer address in virtual address space.
    113     immediate: u64,
    114     /// The constant was emitted into the code, at this offset.
    115     /// If the type is a pointer, it means the pointer address is embedded in the code.
    116     embedded_in_code: usize,
    117     /// The value is a pointer to a constant which was emitted into the code, at this offset.
    118     ptr_embedded_in_code: usize,
    119     /// The value is in a target-specific register.
    120     register: Register,
    121     /// The value is in memory at a hard-coded address.
    122     /// If the type is a pointer, it means the pointer address is at this memory location.
    123     memory: u64,
    124     /// The value is one of the stack variables.
    125     /// If the type is a pointer, it means the pointer address is in the stack at this offset.
    126     stack_offset: u32,
    127     /// The value is a pointer to one of the stack variables (payload is stack offset).
    128     ptr_stack_offset: u32,
    129 
    130     fn isMemory(mcv: MCValue) bool {
    131         return switch (mcv) {
    132             .embedded_in_code, .memory, .stack_offset => true,
    133             else => false,
    134         };
    135     }
    136 
    137     fn isImmediate(mcv: MCValue) bool {
    138         return switch (mcv) {
    139             .immediate => true,
    140             else => false,
    141         };
    142     }
    143 
    144     fn isMutable(mcv: MCValue) bool {
    145         return switch (mcv) {
    146             .none => unreachable,
    147             .unreach => unreachable,
    148             .dead => unreachable,
    149 
    150             .immediate,
    151             .embedded_in_code,
    152             .memory,
    153             .ptr_stack_offset,
    154             .ptr_embedded_in_code,
    155             .undef,
    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 do we need size? should be determined by inst.ty.abiSize()
    177     size: u32,
    178 };
    179 
    180 const BlockData = struct {
    181     relocs: std.ArrayListUnmanaged(Reloc),
    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 Reloc = union(enum) {
    190     /// The value is an offset into the `Function` `code` from the beginning.
    191     /// To perform the reloc, write 32-bit signed little-endian integer
    192     /// which is a relative jump, based on the address following the reloc.
    193     rel32: usize,
    194     /// A branch in the ARM instruction set
    195     arm_branch: struct {
    196         pos: usize,
    197         cond: @import("../arm/bits.zig").Condition,
    198     },
    199 };
    200 
    201 const BigTomb = struct {
    202     function: *Self,
    203     inst: Air.Inst.Index,
    204     tomb_bits: Liveness.Bpi,
    205     big_tomb_bits: u32,
    206     bit_index: usize,
    207 
    208     fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
    209         const this_bit_index = bt.bit_index;
    210         bt.bit_index += 1;
    211 
    212         const op_int = @enumToInt(op_ref);
    213         if (op_int < Air.Inst.Ref.typed_value_map.len) return;
    214         const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
    215 
    216         if (this_bit_index < Liveness.bpi - 1) {
    217             const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
    218             if (!dies) return;
    219         } else {
    220             const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
    221             const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
    222             if (!dies) return;
    223         }
    224         bt.function.processDeath(op_index);
    225     }
    226 
    227     fn finishAir(bt: *BigTomb, result: MCValue) void {
    228         const is_used = !bt.function.liveness.isUnused(bt.inst);
    229         if (is_used) {
    230             log.debug("%{d} => {}", .{ bt.inst, result });
    231             const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
    232             branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
    233         }
    234         bt.function.finishAirBookkeeping();
    235     }
    236 };
    237 
    238 const Self = @This();
    239 
    240 pub fn generate(
    241     bin_file: *link.File,
    242     src_loc: Module.SrcLoc,
    243     module_fn: *Module.Fn,
    244     air: Air,
    245     liveness: Liveness,
    246     code: *std.ArrayList(u8),
    247     debug_output: DebugInfoOutput,
    248 ) GenerateSymbolError!FnResult {
    249     if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
    250         @panic("Attempted to compile for architecture that was disabled by build configuration");
    251     }
    252 
    253     assert(module_fn.owner_decl.has_tv);
    254     const fn_type = module_fn.owner_decl.ty;
    255 
    256     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
    257     defer {
    258         assert(branch_stack.items.len == 1);
    259         branch_stack.items[0].deinit(bin_file.allocator);
    260         branch_stack.deinit();
    261     }
    262     try branch_stack.append(.{});
    263 
    264     var function = Self{
    265         .gpa = bin_file.allocator,
    266         .air = air,
    267         .liveness = liveness,
    268         .target = &bin_file.options.target,
    269         .bin_file = bin_file,
    270         .mod_fn = module_fn,
    271         .code = code,
    272         .debug_output = debug_output,
    273         .err_msg = null,
    274         .args = undefined, // populated after `resolveCallingConventionValues`
    275         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
    276         .fn_type = fn_type,
    277         .arg_index = 0,
    278         .branch_stack = &branch_stack,
    279         .src_loc = src_loc,
    280         .stack_align = undefined,
    281         .end_di_line = module_fn.rbrace_line,
    282         .end_di_column = module_fn.rbrace_column,
    283     };
    284     defer function.stack.deinit(bin_file.allocator);
    285     defer function.blocks.deinit(bin_file.allocator);
    286     defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
    287 
    288     var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
    289         error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
    290         error.OutOfRegisters => return FnResult{
    291             .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    292         },
    293         else => |e| return e,
    294     };
    295     defer call_info.deinit(&function);
    296 
    297     function.args = call_info.args;
    298     function.ret_mcv = call_info.return_value;
    299     function.stack_align = call_info.stack_align;
    300     function.max_end_stack = call_info.stack_byte_count;
    301 
    302     function.gen() catch |err| switch (err) {
    303         error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
    304         error.OutOfRegisters => return FnResult{
    305             .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
    306         },
    307         else => |e| return e,
    308     };
    309 
    310     var mir = Mir{
    311         .instructions = function.mir_instructions.toOwnedSlice(),
    312         .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
    313     };
    314     defer mir.deinit(bin_file.allocator);
    315 
    316     var emit = Emit{
    317         .mir = mir,
    318         .bin_file = bin_file,
    319         .debug_output = debug_output,
    320         .target = &bin_file.options.target,
    321         .src_loc = src_loc,
    322         .code = code,
    323         .prev_di_pc = 0,
    324         .prev_di_line = module_fn.lbrace_line,
    325         .prev_di_column = module_fn.lbrace_column,
    326     };
    327     defer emit.deinit();
    328 
    329     emit.emitMir() catch |err| switch (err) {
    330         error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
    331         else => |e| return e,
    332     };
    333 
    334     if (function.err_msg) |em| {
    335         return FnResult{ .fail = em };
    336     } else {
    337         return FnResult{ .appended = {} };
    338     }
    339 }
    340 
    341 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
    342     const gpa = self.gpa;
    343 
    344     try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
    345 
    346     const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
    347     self.mir_instructions.appendAssumeCapacity(inst);
    348     return result_index;
    349 }
    350 
    351 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
    352     const fields = std.meta.fields(@TypeOf(extra));
    353     try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
    354     return self.addExtraAssumeCapacity(extra);
    355 }
    356 
    357 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
    358     const fields = std.meta.fields(@TypeOf(extra));
    359     const result = @intCast(u32, self.mir_extra.items.len);
    360     inline for (fields) |field| {
    361         self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
    362             u32 => @field(extra, field.name),
    363             i32 => @bitCast(u32, @field(extra, field.name)),
    364             else => @compileError("bad field type"),
    365         });
    366     }
    367     return result;
    368 }
    369 
    370 fn gen(self: *Self) !void {
    371     const cc = self.fn_type.fnCallingConvention();
    372     if (cc != .Naked) {
    373         // TODO Finish function prologue and epilogue for riscv64.
    374 
    375         // TODO Backpatch stack offset
    376         // addi sp, sp, -16
    377         _ = try self.addInst(.{
    378             .tag = .addi,
    379             .data = .{ .i_type = .{
    380                 .rd = .sp,
    381                 .rs1 = .sp,
    382                 .imm12 = -16,
    383             } },
    384         });
    385 
    386         // sd ra, 8(sp)
    387         _ = try self.addInst(.{
    388             .tag = .sd,
    389             .data = .{ .i_type = .{
    390                 .rd = .ra,
    391                 .rs1 = .sp,
    392                 .imm12 = 8,
    393             } },
    394         });
    395 
    396         // sd s0, 0(sp)
    397         _ = try self.addInst(.{
    398             .tag = .sd,
    399             .data = .{ .i_type = .{
    400                 .rd = .s0,
    401                 .rs1 = .sp,
    402                 .imm12 = 0,
    403             } },
    404         });
    405 
    406         _ = try self.addInst(.{
    407             .tag = .dbg_prologue_end,
    408             .data = .{ .nop = {} },
    409         });
    410 
    411         try self.genBody(self.air.getMainBody());
    412 
    413         _ = try self.addInst(.{
    414             .tag = .dbg_epilogue_begin,
    415             .data = .{ .nop = {} },
    416         });
    417 
    418         // exitlude jumps
    419         if (self.exitlude_jump_relocs.items.len == 1) {
    420             // There is only one relocation. Hence,
    421             // this relocation must be at the end of
    422             // the code. Therefore, we can just delete
    423             // the space initially reserved for the
    424             // jump
    425             self.mir_instructions.len -= 1;
    426         } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
    427             _ = jmp_reloc;
    428             return self.fail("TODO add branches in RISCV64", .{});
    429         }
    430 
    431         // ld ra, 8(sp)
    432         _ = try self.addInst(.{
    433             .tag = .ld,
    434             .data = .{ .i_type = .{
    435                 .rd = .ra,
    436                 .rs1 = .sp,
    437                 .imm12 = 8,
    438             } },
    439         });
    440 
    441         // ld s0, 0(sp)
    442         _ = try self.addInst(.{
    443             .tag = .ld,
    444             .data = .{ .i_type = .{
    445                 .rd = .s0,
    446                 .rs1 = .sp,
    447                 .imm12 = 0,
    448             } },
    449         });
    450 
    451         // addi sp, sp, 16
    452         _ = try self.addInst(.{
    453             .tag = .addi,
    454             .data = .{ .i_type = .{
    455                 .rd = .sp,
    456                 .rs1 = .sp,
    457                 .imm12 = 16,
    458             } },
    459         });
    460 
    461         // ret
    462         _ = try self.addInst(.{
    463             .tag = .ret,
    464             .data = .{ .nop = {} },
    465         });
    466     } else {
    467         _ = try self.addInst(.{
    468             .tag = .dbg_prologue_end,
    469             .data = .{ .nop = {} },
    470         });
    471 
    472         try self.genBody(self.air.getMainBody());
    473 
    474         _ = try self.addInst(.{
    475             .tag = .dbg_epilogue_begin,
    476             .data = .{ .nop = {} },
    477         });
    478     }
    479 
    480     // Drop them off at the rbrace.
    481     _ = try self.addInst(.{
    482         .tag = .dbg_line,
    483         .data = .{ .dbg_line_column = .{
    484             .line = self.end_di_line,
    485             .column = self.end_di_column,
    486         } },
    487     });
    488 }
    489 
    490 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
    491     const air_tags = self.air.instructions.items(.tag);
    492 
    493     for (body) |inst| {
    494         const old_air_bookkeeping = self.air_bookkeeping;
    495         try self.ensureProcessDeathCapacity(Liveness.bpi);
    496 
    497         switch (air_tags[inst]) {
    498             // zig fmt: off
    499             .add, .ptr_add   => try self.airAdd(inst),
    500             .addwrap         => try self.airAddWrap(inst),
    501             .add_sat         => try self.airAddSat(inst),
    502             .sub, .ptr_sub   => try self.airSub(inst),
    503             .subwrap         => try self.airSubWrap(inst),
    504             .sub_sat         => try self.airSubSat(inst),
    505             .mul             => try self.airMul(inst),
    506             .mulwrap         => try self.airMulWrap(inst),
    507             .mul_sat         => try self.airMulSat(inst),
    508             .rem             => try self.airRem(inst),
    509             .mod             => try self.airMod(inst),
    510             .shl, .shl_exact => try self.airShl(inst),
    511             .shl_sat         => try self.airShlSat(inst),
    512             .min             => try self.airMin(inst),
    513             .max             => try self.airMax(inst),
    514             .slice           => try self.airSlice(inst),
    515 
    516             .sqrt,
    517             .sin,
    518             .cos,
    519             .exp,
    520             .exp2,
    521             .log,
    522             .log2,
    523             .log10,
    524             .fabs,
    525             .floor,
    526             .ceil,
    527             .round,
    528             .trunc_float,
    529             => try self.airUnaryMath(inst),
    530 
    531             .add_with_overflow => try self.airAddWithOverflow(inst),
    532             .sub_with_overflow => try self.airSubWithOverflow(inst),
    533             .mul_with_overflow => try self.airMulWithOverflow(inst),
    534             .shl_with_overflow => try self.airShlWithOverflow(inst),
    535 
    536             .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
    537 
    538             .cmp_lt  => try self.airCmp(inst, .lt),
    539             .cmp_lte => try self.airCmp(inst, .lte),
    540             .cmp_eq  => try self.airCmp(inst, .eq),
    541             .cmp_gte => try self.airCmp(inst, .gte),
    542             .cmp_gt  => try self.airCmp(inst, .gt),
    543             .cmp_neq => try self.airCmp(inst, .neq),
    544 
    545             .bool_and        => try self.airBoolOp(inst),
    546             .bool_or         => try self.airBoolOp(inst),
    547             .bit_and         => try self.airBitAnd(inst),
    548             .bit_or          => try self.airBitOr(inst),
    549             .xor             => try self.airXor(inst),
    550             .shr, .shr_exact => try self.airShr(inst),
    551 
    552             .alloc           => try self.airAlloc(inst),
    553             .ret_ptr         => try self.airRetPtr(inst),
    554             .arg             => try self.airArg(inst),
    555             .assembly        => try self.airAsm(inst),
    556             .bitcast         => try self.airBitCast(inst),
    557             .block           => try self.airBlock(inst),
    558             .br              => try self.airBr(inst),
    559             .breakpoint      => try self.airBreakpoint(),
    560             .ret_addr        => try self.airRetAddr(inst),
    561             .frame_addr      => try self.airFrameAddress(inst),
    562             .fence           => try self.airFence(),
    563             .cond_br         => try self.airCondBr(inst),
    564             .dbg_stmt        => try self.airDbgStmt(inst),
    565             .dbg_func        => try self.airDbgFunc(inst),
    566             .fptrunc         => try self.airFptrunc(inst),
    567             .fpext           => try self.airFpext(inst),
    568             .intcast         => try self.airIntCast(inst),
    569             .trunc           => try self.airTrunc(inst),
    570             .bool_to_int     => try self.airBoolToInt(inst),
    571             .is_non_null     => try self.airIsNonNull(inst),
    572             .is_non_null_ptr => try self.airIsNonNullPtr(inst),
    573             .is_null         => try self.airIsNull(inst),
    574             .is_null_ptr     => try self.airIsNullPtr(inst),
    575             .is_non_err      => try self.airIsNonErr(inst),
    576             .is_non_err_ptr  => try self.airIsNonErrPtr(inst),
    577             .is_err          => try self.airIsErr(inst),
    578             .is_err_ptr      => try self.airIsErrPtr(inst),
    579             .load            => try self.airLoad(inst),
    580             .loop            => try self.airLoop(inst),
    581             .not             => try self.airNot(inst),
    582             .ptrtoint        => try self.airPtrToInt(inst),
    583             .ret             => try self.airRet(inst),
    584             .ret_load        => try self.airRetLoad(inst),
    585             .store           => try self.airStore(inst),
    586             .struct_field_ptr=> try self.airStructFieldPtr(inst),
    587             .struct_field_val=> try self.airStructFieldVal(inst),
    588             .array_to_slice  => try self.airArrayToSlice(inst),
    589             .int_to_float    => try self.airIntToFloat(inst),
    590             .float_to_int    => try self.airFloatToInt(inst),
    591             .cmpxchg_strong  => try self.airCmpxchg(inst),
    592             .cmpxchg_weak    => try self.airCmpxchg(inst),
    593             .atomic_rmw      => try self.airAtomicRmw(inst),
    594             .atomic_load     => try self.airAtomicLoad(inst),
    595             .memcpy          => try self.airMemcpy(inst),
    596             .memset          => try self.airMemset(inst),
    597             .set_union_tag   => try self.airSetUnionTag(inst),
    598             .get_union_tag   => try self.airGetUnionTag(inst),
    599             .clz             => try self.airClz(inst),
    600             .ctz             => try self.airCtz(inst),
    601             .popcount        => try self.airPopcount(inst),
    602             .byte_swap       => try self.airByteSwap(inst),
    603             .bit_reverse     => try self.airBitReverse(inst),
    604             .tag_name        => try self.airTagName(inst),
    605             .error_name      => try self.airErrorName(inst),
    606             .splat           => try self.airSplat(inst),
    607             .shuffle         => try self.airShuffle(inst),
    608             .aggregate_init  => try self.airAggregateInit(inst),
    609             .union_init      => try self.airUnionInit(inst),
    610             .prefetch        => try self.airPrefetch(inst),
    611             .mul_add         => try self.airMulAdd(inst),
    612 
    613             .dbg_var_ptr,
    614             .dbg_var_val,
    615             => try self.airDbgVar(inst),
    616 
    617             .call              => try self.airCall(inst, .auto),
    618             .call_always_tail  => try self.airCall(inst, .always_tail),
    619             .call_never_tail   => try self.airCall(inst, .never_tail),
    620             .call_never_inline => try self.airCall(inst, .never_inline),
    621 
    622             .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
    623             .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
    624             .atomic_store_release   => try self.airAtomicStore(inst, .Release),
    625             .atomic_store_seq_cst   => try self.airAtomicStore(inst, .SeqCst),
    626 
    627             .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
    628             .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
    629             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
    630             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
    631 
    632             .field_parent_ptr => try self.airFieldParentPtr(inst),
    633 
    634             .switch_br       => try self.airSwitch(inst),
    635             .slice_ptr       => try self.airSlicePtr(inst),
    636             .slice_len       => try self.airSliceLen(inst),
    637 
    638             .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
    639             .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
    640 
    641             .array_elem_val      => try self.airArrayElemVal(inst),
    642             .slice_elem_val      => try self.airSliceElemVal(inst),
    643             .slice_elem_ptr      => try self.airSliceElemPtr(inst),
    644             .ptr_elem_val        => try self.airPtrElemVal(inst),
    645             .ptr_elem_ptr        => try self.airPtrElemPtr(inst),
    646 
    647             .constant => unreachable, // excluded from function bodies
    648             .const_ty => unreachable, // excluded from function bodies
    649             .unreach  => self.finishAirBookkeeping(),
    650 
    651             .optional_payload           => try self.airOptionalPayload(inst),
    652             .optional_payload_ptr       => try self.airOptionalPayloadPtr(inst),
    653             .optional_payload_ptr_set   => try self.airOptionalPayloadPtrSet(inst),
    654             .unwrap_errunion_err        => try self.airUnwrapErrErr(inst),
    655             .unwrap_errunion_payload    => try self.airUnwrapErrPayload(inst),
    656             .unwrap_errunion_err_ptr    => try self.airUnwrapErrErrPtr(inst),
    657             .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
    658             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
    659 
    660             .wrap_optional         => try self.airWrapOptional(inst),
    661             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
    662             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
    663 
    664             .wasm_memory_size => unreachable,
    665             .wasm_memory_grow => unreachable,
    666             // zig fmt: on
    667         }
    668         if (std.debug.runtime_safety) {
    669             if (self.air_bookkeeping < old_air_bookkeeping + 1) {
    670                 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
    671             }
    672         }
    673     }
    674 }
    675 
    676 /// Asserts there is already capacity to insert into top branch inst_table.
    677 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
    678     const air_tags = self.air.instructions.items(.tag);
    679     if (air_tags[inst] == .constant) return; // Constants are immortal.
    680     // When editing this function, note that the logic must synchronize with `reuseOperand`.
    681     const prev_value = self.getResolvedInstValue(inst);
    682     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    683     branch.inst_table.putAssumeCapacity(inst, .dead);
    684     switch (prev_value) {
    685         .register => |reg| {
    686             self.register_manager.freeReg(reg);
    687         },
    688         else => {}, // TODO process stack allocation death
    689     }
    690 }
    691 
    692 /// Called when there are no operands, and the instruction is always unreferenced.
    693 fn finishAirBookkeeping(self: *Self) void {
    694     if (std.debug.runtime_safety) {
    695         self.air_bookkeeping += 1;
    696     }
    697 }
    698 
    699 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
    700     var tomb_bits = self.liveness.getTombBits(inst);
    701     for (operands) |op| {
    702         const dies = @truncate(u1, tomb_bits) != 0;
    703         tomb_bits >>= 1;
    704         if (!dies) continue;
    705         const op_int = @enumToInt(op);
    706         if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
    707         const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
    708         self.processDeath(op_index);
    709     }
    710     const is_used = @truncate(u1, tomb_bits) == 0;
    711     if (is_used) {
    712         log.debug("%{d} => {}", .{ inst, result });
    713         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    714         branch.inst_table.putAssumeCapacityNoClobber(inst, result);
    715 
    716         switch (result) {
    717             .register => |reg| {
    718                 // In some cases (such as bitcast), an operand
    719                 // may be the same MCValue as the result. If
    720                 // that operand died and was a register, it
    721                 // was freed by processDeath. We have to
    722                 // "re-allocate" the register.
    723                 if (self.register_manager.isRegFree(reg)) {
    724                     self.register_manager.getRegAssumeFree(reg, inst);
    725                 }
    726             },
    727             else => {},
    728         }
    729     }
    730     self.finishAirBookkeeping();
    731 }
    732 
    733 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
    734     const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
    735     try table.ensureUnusedCapacity(self.gpa, additional_count);
    736 }
    737 
    738 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
    739 /// after codegen for this symbol is done.
    740 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
    741     switch (self.debug_output) {
    742         .dwarf => |dbg_out| {
    743             assert(ty.hasRuntimeBits());
    744             const index = dbg_out.dbg_info.items.len;
    745             try dbg_out.dbg_info.resize(index + 4); // DW.AT.type,  DW.FORM.ref4
    746 
    747             const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
    748             if (!gop.found_existing) {
    749                 gop.value_ptr.* = .{
    750                     .off = undefined,
    751                     .relocs = .{},
    752                 };
    753             }
    754             try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
    755         },
    756         .plan9 => {},
    757         .none => {},
    758     }
    759 }
    760 
    761 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
    762     if (abi_align > self.stack_align)
    763         self.stack_align = abi_align;
    764     // TODO find a free slot instead of always appending
    765     const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
    766     self.next_stack_offset = offset + abi_size;
    767     if (self.next_stack_offset > self.max_end_stack)
    768         self.max_end_stack = self.next_stack_offset;
    769     try self.stack.putNoClobber(self.gpa, offset, .{
    770         .inst = inst,
    771         .size = abi_size,
    772     });
    773     return offset;
    774 }
    775 
    776 /// Use a pointer instruction as the basis for allocating stack memory.
    777 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
    778     const elem_ty = self.air.typeOfIndex(inst).elemType();
    779     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
    780         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
    781     };
    782     // TODO swap this for inst.ty.ptrAlign
    783     const abi_align = elem_ty.abiAlignment(self.target.*);
    784     return self.allocMem(inst, abi_size, abi_align);
    785 }
    786 
    787 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
    788     const elem_ty = self.air.typeOfIndex(inst);
    789     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
    790         return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
    791     };
    792     const abi_align = elem_ty.abiAlignment(self.target.*);
    793     if (abi_align > self.stack_align)
    794         self.stack_align = abi_align;
    795 
    796     if (reg_ok) {
    797         // Make sure the type can fit in a register before we try to allocate one.
    798         const ptr_bits = self.target.cpu.arch.ptrBitWidth();
    799         const ptr_bytes: u64 = @divExact(ptr_bits, 8);
    800         if (abi_size <= ptr_bytes) {
    801             if (self.register_manager.tryAllocReg(inst)) |reg| {
    802                 return MCValue{ .register = reg };
    803             }
    804         }
    805     }
    806     const stack_offset = try self.allocMem(inst, abi_size, abi_align);
    807     return MCValue{ .stack_offset = stack_offset };
    808 }
    809 
    810 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
    811     const stack_mcv = try self.allocRegOrMem(inst, false);
    812     log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
    813     const reg_mcv = self.getResolvedInstValue(inst);
    814     assert(reg == reg_mcv.register);
    815     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
    816     try branch.inst_table.put(self.gpa, inst, stack_mcv);
    817     try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
    818 }
    819 
    820 /// Copies a value to a register without tracking the register. The register is not considered
    821 /// allocated. A second call to `copyToTmpRegister` may return the same register.
    822 /// This can have a side effect of spilling instructions to the stack to free up a register.
    823 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
    824     const reg = try self.register_manager.allocReg(null);
    825     try self.genSetReg(ty, reg, mcv);
    826     return reg;
    827 }
    828 
    829 /// Allocates a new register and copies `mcv` into it.
    830 /// `reg_owner` is the instruction that gets associated with the register in the register table.
    831 /// This can have a side effect of spilling instructions to the stack to free up a register.
    832 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
    833     const reg = try self.register_manager.allocReg(reg_owner);
    834     try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
    835     return MCValue{ .register = reg };
    836 }
    837 
    838 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
    839     const stack_offset = try self.allocMemPtr(inst);
    840     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
    841 }
    842 
    843 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
    844     const stack_offset = try self.allocMemPtr(inst);
    845     return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
    846 }
    847 
    848 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
    849     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    850     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
    851     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    852 }
    853 
    854 fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
    855     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    856     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
    857     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    858 }
    859 
    860 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
    861     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    862     if (self.liveness.isUnused(inst))
    863         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
    864 
    865     const operand_ty = self.air.typeOf(ty_op.operand);
    866     const operand = try self.resolveInst(ty_op.operand);
    867     const info_a = operand_ty.intInfo(self.target.*);
    868     const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
    869     if (info_a.signedness != info_b.signedness)
    870         return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
    871 
    872     if (info_a.bits == info_b.bits)
    873         return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
    874 
    875     return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
    876     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    877 }
    878 
    879 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
    880     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    881     if (self.liveness.isUnused(inst))
    882         return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
    883 
    884     const operand = try self.resolveInst(ty_op.operand);
    885     _ = operand;
    886     return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
    887     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    888 }
    889 
    890 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
    891     const un_op = self.air.instructions.items(.data)[inst].un_op;
    892     const operand = try self.resolveInst(un_op);
    893     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
    894     return self.finishAir(inst, result, .{ un_op, .none, .none });
    895 }
    896 
    897 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
    898     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    899     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch});
    900     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
    901 }
    902 
    903 fn airMin(self: *Self, inst: Air.Inst.Index) !void {
    904     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    905     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
    906     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    907 }
    908 
    909 fn airMax(self: *Self, inst: Air.Inst.Index) !void {
    910     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    911     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
    912     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    913 }
    914 
    915 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
    916     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
    917     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
    918     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
    919     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    920 }
    921 
    922 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
    923     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    924     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch});
    925     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    926 }
    927 
    928 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
    929     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    930     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
    931     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    932 }
    933 
    934 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
    935     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    936     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
    937     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    938 }
    939 
    940 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
    941     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    942     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch});
    943     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    944 }
    945 
    946 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
    947     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    948     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
    949     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    950 }
    951 
    952 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
    953     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    954     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
    955     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    956 }
    957 
    958 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
    959     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    960     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch});
    961     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    962 }
    963 
    964 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
    965     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    966     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
    967     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    968 }
    969 
    970 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
    971     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    972     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
    973     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
    974 }
    975 
    976 fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
    977     _ = inst;
    978     return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
    979 }
    980 
    981 fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
    982     _ = inst;
    983     return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
    984 }
    985 
    986 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
    987     _ = inst;
    988     return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
    989 }
    990 
    991 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
    992     _ = inst;
    993     return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
    994 }
    995 
    996 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
    997     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    998     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
    999     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1000 }
   1001 
   1002 fn airRem(self: *Self, inst: Air.Inst.Index) !void {
   1003     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1004     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
   1005     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1006 }
   1007 
   1008 fn airMod(self: *Self, inst: Air.Inst.Index) !void {
   1009     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1010     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
   1011     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1012 }
   1013 
   1014 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
   1015     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1016     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch});
   1017     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1018 }
   1019 
   1020 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
   1021     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1022     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch});
   1023     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1024 }
   1025 
   1026 fn airXor(self: *Self, inst: Air.Inst.Index) !void {
   1027     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1028     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch});
   1029     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1030 }
   1031 
   1032 fn airShl(self: *Self, inst: Air.Inst.Index) !void {
   1033     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1034     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch});
   1035     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1036 }
   1037 
   1038 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
   1039     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1040     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
   1041     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1042 }
   1043 
   1044 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
   1045     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1046     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
   1047     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1048 }
   1049 
   1050 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
   1051     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1052     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
   1053     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1054 }
   1055 
   1056 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   1057     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1058     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
   1059     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1060 }
   1061 
   1062 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   1063     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1064     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
   1065     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1066 }
   1067 
   1068 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
   1069     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1070     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});
   1071     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1072 }
   1073 
   1074 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
   1075     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1076     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
   1077     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1078 }
   1079 
   1080 // *(E!T) -> E
   1081 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1082     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1083     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});
   1084     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1085 }
   1086 
   1087 // *(E!T) -> *T
   1088 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
   1089     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1090     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});
   1091     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1092 }
   1093 
   1094 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
   1095     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1096     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
   1097     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1098 }
   1099 
   1100 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
   1101     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1102     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1103         const optional_ty = self.air.typeOfIndex(inst);
   1104 
   1105         // Optional with a zero-bit payload type is just a boolean true
   1106         if (optional_ty.abiSize(self.target.*) == 1)
   1107             break :result MCValue{ .immediate = 1 };
   1108 
   1109         return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
   1110     };
   1111     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1112 }
   1113 
   1114 /// T to E!T
   1115 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
   1116     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1117     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
   1118     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1119 }
   1120 
   1121 /// E to E!T
   1122 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
   1123     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1124     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch});
   1125     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1126 }
   1127 
   1128 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
   1129     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1130     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
   1131     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1132 }
   1133 
   1134 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
   1135     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1136     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch});
   1137     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1138 }
   1139 
   1140 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
   1141     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1142     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch});
   1143     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1144 }
   1145 
   1146 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1147     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1148     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch});
   1149     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1150 }
   1151 
   1152 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1153     const is_volatile = false; // TODO
   1154     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1155     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});
   1156     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1157 }
   1158 
   1159 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   1160     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1161     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1162     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
   1163     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1164 }
   1165 
   1166 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1167     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1168     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
   1169     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1170 }
   1171 
   1172 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
   1173     const is_volatile = false; // TODO
   1174     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1175     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});
   1176     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1177 }
   1178 
   1179 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
   1180     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1181     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   1182     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
   1183     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
   1184 }
   1185 
   1186 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   1187     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1188     _ = bin_op;
   1189     return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
   1190     // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1191 }
   1192 
   1193 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
   1194     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1195     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
   1196     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1197 }
   1198 
   1199 fn airClz(self: *Self, inst: Air.Inst.Index) !void {
   1200     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1201     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
   1202     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1203 }
   1204 
   1205 fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
   1206     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1207     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
   1208     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1209 }
   1210 
   1211 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
   1212     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1213     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
   1214     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1215 }
   1216 
   1217 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
   1218     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1219     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
   1220     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1221 }
   1222 
   1223 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
   1224     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1225     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
   1226     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1227 }
   1228 
   1229 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
   1230     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1231     const result: MCValue = if (self.liveness.isUnused(inst))
   1232         .dead
   1233     else
   1234         return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
   1235     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1236 }
   1237 
   1238 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
   1239     if (!self.liveness.operandDies(inst, op_index))
   1240         return false;
   1241 
   1242     switch (mcv) {
   1243         .register => |reg| {
   1244             // If it's in the registers table, need to associate the register with the
   1245             // new instruction.
   1246             if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
   1247                 if (!self.register_manager.isRegFree(reg)) {
   1248                     self.register_manager.registers[index] = inst;
   1249                 }
   1250             }
   1251             log.debug("%{d} => {} (reused)", .{ inst, reg });
   1252         },
   1253         .stack_offset => |off| {
   1254             log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
   1255         },
   1256         else => return false,
   1257     }
   1258 
   1259     // Prevent the operand deaths processing code from deallocating it.
   1260     self.liveness.clearOperandDeath(inst, op_index);
   1261 
   1262     // That makes us responsible for doing the rest of the stuff that processDeath would have done.
   1263     const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
   1264     branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
   1265 
   1266     return true;
   1267 }
   1268 
   1269 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
   1270     const elem_ty = ptr_ty.elemType();
   1271     switch (ptr) {
   1272         .none => unreachable,
   1273         .undef => unreachable,
   1274         .unreach => unreachable,
   1275         .dead => unreachable,
   1276         .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
   1277         .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
   1278         .ptr_embedded_in_code => |off| {
   1279             try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
   1280         },
   1281         .embedded_in_code => {
   1282             return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
   1283         },
   1284         .register => {
   1285             return self.fail("TODO implement loading from MCValue.register", .{});
   1286         },
   1287         .memory,
   1288         .stack_offset,
   1289         => {
   1290             const reg = try self.register_manager.allocReg(null);
   1291             self.register_manager.freezeRegs(&.{reg});
   1292             defer self.register_manager.unfreezeRegs(&.{reg});
   1293 
   1294             try self.genSetReg(ptr_ty, reg, ptr);
   1295             try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
   1296         },
   1297     }
   1298 }
   1299 
   1300 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
   1301     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1302     const elem_ty = self.air.typeOfIndex(inst);
   1303     const result: MCValue = result: {
   1304         if (!elem_ty.hasRuntimeBits())
   1305             break :result MCValue.none;
   1306 
   1307         const ptr = try self.resolveInst(ty_op.operand);
   1308         const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
   1309         if (self.liveness.isUnused(inst) and !is_volatile)
   1310             break :result MCValue.dead;
   1311 
   1312         const dst_mcv: MCValue = blk: {
   1313             if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
   1314                 // The MCValue that holds the pointer can be re-used as the value.
   1315                 break :blk ptr;
   1316             } else {
   1317                 break :blk try self.allocRegOrMem(inst, true);
   1318             }
   1319         };
   1320         try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
   1321         break :result dst_mcv;
   1322     };
   1323     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   1324 }
   1325 
   1326 fn airStore(self: *Self, inst: Air.Inst.Index) !void {
   1327     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1328     const ptr = try self.resolveInst(bin_op.lhs);
   1329     const value = try self.resolveInst(bin_op.rhs);
   1330     const elem_ty = self.air.typeOf(bin_op.rhs);
   1331     switch (ptr) {
   1332         .none => unreachable,
   1333         .undef => unreachable,
   1334         .unreach => unreachable,
   1335         .dead => unreachable,
   1336         .immediate => |imm| {
   1337             try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
   1338         },
   1339         .ptr_stack_offset => |off| {
   1340             try self.genSetStack(elem_ty, off, value);
   1341         },
   1342         .ptr_embedded_in_code => |off| {
   1343             try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value);
   1344         },
   1345         .embedded_in_code => {
   1346             return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
   1347         },
   1348         .register => {
   1349             return self.fail("TODO implement storing to MCValue.register", .{});
   1350         },
   1351         .memory => {
   1352             return self.fail("TODO implement storing to MCValue.memory", .{});
   1353         },
   1354         .stack_offset => {
   1355             return self.fail("TODO implement storing to MCValue.stack_offset", .{});
   1356         },
   1357     }
   1358     return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   1359 }
   1360 
   1361 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
   1362     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1363     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   1364     return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index);
   1365 }
   1366 
   1367 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
   1368     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1369     return self.structFieldPtr(ty_op.operand, ty_op.ty, index);
   1370 }
   1371 fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void {
   1372     _ = self;
   1373     _ = operand;
   1374     _ = ty;
   1375     _ = index;
   1376     return self.fail("TODO implement codegen struct_field_ptr", .{});
   1377     //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
   1378 }
   1379 
   1380 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
   1381     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1382     const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
   1383     _ = extra;
   1384     return self.fail("TODO implement codegen struct_field_val", .{});
   1385     //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
   1386 }
   1387 
   1388 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
   1389     _ = self;
   1390     _ = inst;
   1391     return self.fail("TODO implement codegen airFieldParentPtr", .{});
   1392 }
   1393 
   1394 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
   1395     const ty = self.air.instructions.items(.data)[inst].ty;
   1396     const name = self.mod_fn.getParamName(arg_index);
   1397     const name_with_null = name.ptr[0 .. name.len + 1];
   1398 
   1399     switch (mcv) {
   1400         .register => |reg| {
   1401             switch (self.debug_output) {
   1402                 .dwarf => |dbg_out| {
   1403                     try dbg_out.dbg_info.ensureUnusedCapacity(3);
   1404                     dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter);
   1405                     dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
   1406                         1, // ULEB128 dwarf expression length
   1407                         reg.dwarfLocOp(),
   1408                     });
   1409                     try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
   1410                     try self.addDbgInfoTypeReloc(ty); // DW.AT.type,  DW.FORM.ref4
   1411                     dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
   1412                 },
   1413                 .plan9 => {},
   1414                 .none => {},
   1415             }
   1416         },
   1417         .stack_offset => |offset| {
   1418             _ = offset;
   1419             switch (self.debug_output) {
   1420                 .dwarf => {},
   1421                 .plan9 => {},
   1422                 .none => {},
   1423             }
   1424         },
   1425         else => {},
   1426     }
   1427 }
   1428 
   1429 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
   1430     const arg_index = self.arg_index;
   1431     self.arg_index += 1;
   1432 
   1433     const ty = self.air.typeOfIndex(inst);
   1434     _ = ty;
   1435 
   1436     const result = self.args[arg_index];
   1437     // TODO support stack-only arguments
   1438     // TODO Copy registers to the stack
   1439     const mcv = result;
   1440     try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
   1441 
   1442     if (self.liveness.isUnused(inst))
   1443         return self.finishAirBookkeeping();
   1444 
   1445     switch (mcv) {
   1446         .register => |reg| {
   1447             self.register_manager.getRegAssumeFree(reg, inst);
   1448         },
   1449         else => {},
   1450     }
   1451 
   1452     return self.finishAir(inst, mcv, .{ .none, .none, .none });
   1453 }
   1454 
   1455 fn airBreakpoint(self: *Self) !void {
   1456     _ = try self.addInst(.{
   1457         .tag = .ebreak,
   1458         .data = .{ .nop = {} },
   1459     });
   1460     return self.finishAirBookkeeping();
   1461 }
   1462 
   1463 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
   1464     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{});
   1465     return self.finishAir(inst, result, .{ .none, .none, .none });
   1466 }
   1467 
   1468 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
   1469     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{});
   1470     return self.finishAir(inst, result, .{ .none, .none, .none });
   1471 }
   1472 
   1473 fn airFence(self: *Self) !void {
   1474     return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
   1475     //return self.finishAirBookkeeping();
   1476 }
   1477 
   1478 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void {
   1479     if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});
   1480     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   1481     const fn_ty = self.air.typeOf(pl_op.operand);
   1482     const callee = pl_op.operand;
   1483     const extra = self.air.extraData(Air.Call, pl_op.payload);
   1484     const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
   1485 
   1486     var info = try self.resolveCallingConventionValues(fn_ty);
   1487     defer info.deinit(self);
   1488 
   1489     // Due to incremental compilation, how function calls are generated depends
   1490     // on linking.
   1491     if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
   1492         for (info.args) |mc_arg, arg_i| {
   1493             const arg = args[arg_i];
   1494             const arg_ty = self.air.typeOf(arg);
   1495             const arg_mcv = try self.resolveInst(args[arg_i]);
   1496 
   1497             switch (mc_arg) {
   1498                 .none => continue,
   1499                 .undef => unreachable,
   1500                 .immediate => unreachable,
   1501                 .unreach => unreachable,
   1502                 .dead => unreachable,
   1503                 .embedded_in_code => unreachable,
   1504                 .memory => unreachable,
   1505                 .register => |reg| {
   1506                     try self.register_manager.getReg(reg, null);
   1507                     try self.genSetReg(arg_ty, reg, arg_mcv);
   1508                 },
   1509                 .stack_offset => {
   1510                     return self.fail("TODO implement calling with parameters in memory", .{});
   1511                 },
   1512                 .ptr_stack_offset => {
   1513                     return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
   1514                 },
   1515                 .ptr_embedded_in_code => {
   1516                     return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
   1517                 },
   1518             }
   1519         }
   1520 
   1521         if (self.air.value(callee)) |func_value| {
   1522             if (func_value.castTag(.function)) |func_payload| {
   1523                 const func = func_payload.data;
   1524 
   1525                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
   1526                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
   1527                 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
   1528                     const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
   1529                     break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
   1530                 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
   1531                     coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
   1532                 else
   1533                     unreachable;
   1534 
   1535                 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
   1536                 _ = try self.addInst(.{
   1537                     .tag = .jalr,
   1538                     .data = .{ .i_type = .{
   1539                         .rd = .ra,
   1540                         .rs1 = .ra,
   1541                         .imm12 = 0,
   1542                     } },
   1543                 });
   1544             } else if (func_value.castTag(.extern_fn)) |_| {
   1545                 return self.fail("TODO implement calling extern functions", .{});
   1546             } else {
   1547                 return self.fail("TODO implement calling bitcasted functions", .{});
   1548             }
   1549         } else {
   1550             return self.fail("TODO implement calling runtime known function pointer", .{});
   1551         }
   1552     } else if (self.bin_file.cast(link.File.MachO)) |_| {
   1553         unreachable; // unsupported architecture for MachO
   1554     } else if (self.bin_file.cast(link.File.Plan9)) |_| {
   1555         return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
   1556     } else unreachable;
   1557 
   1558     const result: MCValue = result: {
   1559         switch (info.return_value) {
   1560             .register => |reg| {
   1561                 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
   1562                     // Save function return value in a callee saved register
   1563                     break :result try self.copyToNewRegister(inst, info.return_value);
   1564                 }
   1565             },
   1566             else => {},
   1567         }
   1568         break :result info.return_value;
   1569     };
   1570 
   1571     if (args.len <= Liveness.bpi - 2) {
   1572         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   1573         buf[0] = callee;
   1574         std.mem.copy(Air.Inst.Ref, buf[1..], args);
   1575         return self.finishAir(inst, result, buf);
   1576     }
   1577     var bt = try self.iterateBigTomb(inst, 1 + args.len);
   1578     bt.feed(callee);
   1579     for (args) |arg| {
   1580         bt.feed(arg);
   1581     }
   1582     return bt.finishAir(result);
   1583 }
   1584 
   1585 fn ret(self: *Self, mcv: MCValue) !void {
   1586     const ret_ty = self.fn_type.fnReturnType();
   1587     try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
   1588     // Just add space for an instruction, patch this later
   1589     const index = try self.addInst(.{
   1590         .tag = .nop,
   1591         .data = .{ .nop = {} },
   1592     });
   1593     try self.exitlude_jump_relocs.append(self.gpa, index);
   1594 }
   1595 
   1596 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
   1597     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1598     const operand = try self.resolveInst(un_op);
   1599     try self.ret(operand);
   1600     return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   1601 }
   1602 
   1603 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
   1604     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1605     const ptr = try self.resolveInst(un_op);
   1606     _ = ptr;
   1607     return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
   1608     //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
   1609 }
   1610 
   1611 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
   1612     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1613     if (self.liveness.isUnused(inst))
   1614         return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
   1615     const ty = self.air.typeOf(bin_op.lhs);
   1616     assert(ty.eql(self.air.typeOf(bin_op.rhs)));
   1617     if (ty.zigTypeTag() == .ErrorSet)
   1618         return self.fail("TODO implement cmp for errors", .{});
   1619 
   1620     const lhs = try self.resolveInst(bin_op.lhs);
   1621     const rhs = try self.resolveInst(bin_op.rhs);
   1622     _ = op;
   1623     _ = lhs;
   1624     _ = rhs;
   1625 
   1626     return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch});
   1627     // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1628 }
   1629 
   1630 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
   1631     const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
   1632 
   1633     _ = try self.addInst(.{
   1634         .tag = .dbg_line,
   1635         .data = .{ .dbg_line_column = .{
   1636             .line = dbg_stmt.line,
   1637             .column = dbg_stmt.column,
   1638         } },
   1639     });
   1640 
   1641     return self.finishAirBookkeeping();
   1642 }
   1643 
   1644 fn airDbgFunc(self: *Self, inst: Air.Inst.Index) !void {
   1645     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1646     const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
   1647     // TODO emit debug info for function change
   1648     _ = function;
   1649     return self.finishAir(inst, .dead, .{ .none, .none, .none });
   1650 }
   1651 
   1652 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
   1653     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   1654     const name = self.air.nullTerminatedString(pl_op.payload);
   1655     const operand = pl_op.operand;
   1656     // TODO emit debug info for this variable
   1657     _ = name;
   1658     return self.finishAir(inst, .dead, .{ operand, .none, .none });
   1659 }
   1660 
   1661 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
   1662     _ = inst;
   1663 
   1664     return self.fail("TODO implement condbr {}", .{self.target.cpu.arch});
   1665     // return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
   1666 }
   1667 
   1668 fn isNull(self: *Self, operand: MCValue) !MCValue {
   1669     _ = operand;
   1670     // Here you can specialize this instruction if it makes sense to, otherwise the default
   1671     // will call isNonNull and invert the result.
   1672     return self.fail("TODO call isNonNull and invert the result", .{});
   1673 }
   1674 
   1675 fn isNonNull(self: *Self, operand: MCValue) !MCValue {
   1676     _ = operand;
   1677     // Here you can specialize this instruction if it makes sense to, otherwise the default
   1678     // will call isNull and invert the result.
   1679     return self.fail("TODO call isNull and invert the result", .{});
   1680 }
   1681 
   1682 fn isErr(self: *Self, operand: MCValue) !MCValue {
   1683     _ = operand;
   1684     // Here you can specialize this instruction if it makes sense to, otherwise the default
   1685     // will call isNonNull and invert the result.
   1686     return self.fail("TODO call isNonErr and invert the result", .{});
   1687 }
   1688 
   1689 fn isNonErr(self: *Self, operand: MCValue) !MCValue {
   1690     _ = operand;
   1691     // Here you can specialize this instruction if it makes sense to, otherwise the default
   1692     // will call isNull and invert the result.
   1693     return self.fail("TODO call isErr and invert the result", .{});
   1694 }
   1695 
   1696 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
   1697     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1698     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1699         const operand = try self.resolveInst(un_op);
   1700         break :result try self.isNull(operand);
   1701     };
   1702     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1703 }
   1704 
   1705 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   1706     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1707     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1708         const operand_ptr = try self.resolveInst(un_op);
   1709         const operand: MCValue = blk: {
   1710             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   1711                 // The MCValue that holds the pointer can be re-used as the value.
   1712                 break :blk operand_ptr;
   1713             } else {
   1714                 break :blk try self.allocRegOrMem(inst, true);
   1715             }
   1716         };
   1717         try self.load(operand, operand_ptr, self.air.typeOf(un_op));
   1718         break :result try self.isNull(operand);
   1719     };
   1720     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1721 }
   1722 
   1723 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
   1724     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1725     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1726         const operand = try self.resolveInst(un_op);
   1727         break :result try self.isNonNull(operand);
   1728     };
   1729     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1730 }
   1731 
   1732 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
   1733     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1734     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1735         const operand_ptr = try self.resolveInst(un_op);
   1736         const operand: MCValue = blk: {
   1737             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   1738                 // The MCValue that holds the pointer can be re-used as the value.
   1739                 break :blk operand_ptr;
   1740             } else {
   1741                 break :blk try self.allocRegOrMem(inst, true);
   1742             }
   1743         };
   1744         try self.load(operand, operand_ptr, self.air.typeOf(un_op));
   1745         break :result try self.isNonNull(operand);
   1746     };
   1747     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1748 }
   1749 
   1750 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
   1751     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1752     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1753         const operand = try self.resolveInst(un_op);
   1754         break :result try self.isErr(operand);
   1755     };
   1756     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1757 }
   1758 
   1759 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1760     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1761     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1762         const operand_ptr = try self.resolveInst(un_op);
   1763         const operand: MCValue = blk: {
   1764             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   1765                 // The MCValue that holds the pointer can be re-used as the value.
   1766                 break :blk operand_ptr;
   1767             } else {
   1768                 break :blk try self.allocRegOrMem(inst, true);
   1769             }
   1770         };
   1771         try self.load(operand, operand_ptr, self.air.typeOf(un_op));
   1772         break :result try self.isErr(operand);
   1773     };
   1774     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1775 }
   1776 
   1777 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
   1778     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1779     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1780         const operand = try self.resolveInst(un_op);
   1781         break :result try self.isNonErr(operand);
   1782     };
   1783     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1784 }
   1785 
   1786 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
   1787     const un_op = self.air.instructions.items(.data)[inst].un_op;
   1788     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
   1789         const operand_ptr = try self.resolveInst(un_op);
   1790         const operand: MCValue = blk: {
   1791             if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
   1792                 // The MCValue that holds the pointer can be re-used as the value.
   1793                 break :blk operand_ptr;
   1794             } else {
   1795                 break :blk try self.allocRegOrMem(inst, true);
   1796             }
   1797         };
   1798         try self.load(operand, operand_ptr, self.air.typeOf(un_op));
   1799         break :result try self.isNonErr(operand);
   1800     };
   1801     return self.finishAir(inst, result, .{ un_op, .none, .none });
   1802 }
   1803 
   1804 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
   1805     // A loop is a setup to be able to jump back to the beginning.
   1806     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1807     const loop = self.air.extraData(Air.Block, ty_pl.payload);
   1808     const body = self.air.extra[loop.end..][0..loop.data.body_len];
   1809     const start_index = self.code.items.len;
   1810     try self.genBody(body);
   1811     try self.jump(start_index);
   1812     return self.finishAirBookkeeping();
   1813 }
   1814 
   1815 /// Send control flow to the `index` of `self.code`.
   1816 fn jump(self: *Self, index: usize) !void {
   1817     _ = index;
   1818     return self.fail("TODO implement jump for {}", .{self.target.cpu.arch});
   1819 }
   1820 
   1821 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
   1822     try self.blocks.putNoClobber(self.gpa, inst, .{
   1823         // A block is a setup to be able to jump to the end.
   1824         .relocs = .{},
   1825         // It also acts as a receptacle for break operands.
   1826         // Here we use `MCValue.none` to represent a null value so that the first
   1827         // break instruction will choose a MCValue for the block result and overwrite
   1828         // this field. Following break instructions will use that MCValue to put their
   1829         // block results.
   1830         .mcv = MCValue{ .none = {} },
   1831     });
   1832     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
   1833 
   1834     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1835     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   1836     const body = self.air.extra[extra.end..][0..extra.data.body_len];
   1837     try self.genBody(body);
   1838 
   1839     for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc);
   1840 
   1841     const result = self.blocks.getPtr(inst).?.mcv;
   1842     return self.finishAir(inst, result, .{ .none, .none, .none });
   1843 }
   1844 
   1845 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
   1846     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   1847     const condition = pl_op.operand;
   1848     _ = condition;
   1849     return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
   1850     // return self.finishAir(inst, .dead, .{ condition, .none, .none });
   1851 }
   1852 
   1853 fn performReloc(self: *Self, reloc: Reloc) !void {
   1854     _ = self;
   1855     switch (reloc) {
   1856         .rel32 => unreachable,
   1857         .arm_branch => unreachable,
   1858     }
   1859 }
   1860 
   1861 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
   1862     const branch = self.air.instructions.items(.data)[inst].br;
   1863     try self.br(branch.block_inst, branch.operand);
   1864     return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
   1865 }
   1866 
   1867 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
   1868     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   1869     const air_tags = self.air.instructions.items(.tag);
   1870     _ = air_tags;
   1871     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
   1872     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
   1873 }
   1874 
   1875 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
   1876     const block_data = self.blocks.getPtr(block).?;
   1877 
   1878     if (self.air.typeOf(operand).hasRuntimeBits()) {
   1879         const operand_mcv = try self.resolveInst(operand);
   1880         const block_mcv = block_data.mcv;
   1881         if (block_mcv == .none) {
   1882             block_data.mcv = operand_mcv;
   1883         } else {
   1884             try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
   1885         }
   1886     }
   1887     return self.brVoid(block);
   1888 }
   1889 
   1890 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
   1891     const block_data = self.blocks.getPtr(block).?;
   1892 
   1893     // Emit a jump with a relocation. It will be patched up after the block ends.
   1894     try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
   1895 
   1896     return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch});
   1897 }
   1898 
   1899 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
   1900     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1901     const extra = self.air.extraData(Air.Asm, ty_pl.payload);
   1902     const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
   1903     const clobbers_len = @truncate(u31, extra.data.flags);
   1904     var extra_i: usize = extra.end;
   1905     const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
   1906     extra_i += outputs.len;
   1907     const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
   1908     extra_i += inputs.len;
   1909 
   1910     const dead = !is_volatile and self.liveness.isUnused(inst);
   1911     const result: MCValue = if (dead) .dead else result: {
   1912         if (outputs.len > 1) {
   1913             return self.fail("TODO implement codegen for asm with more than 1 output", .{});
   1914         }
   1915 
   1916         const output_constraint: ?[]const u8 = for (outputs) |output| {
   1917             if (output != .none) {
   1918                 return self.fail("TODO implement codegen for non-expr asm", .{});
   1919             }
   1920             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   1921             // This equation accounts for the fact that even if we have exactly 4 bytes
   1922             // for the string, we still use the next u32 for the null terminator.
   1923             extra_i += constraint.len / 4 + 1;
   1924 
   1925             break constraint;
   1926         } else null;
   1927 
   1928         for (inputs) |input| {
   1929             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   1930             // This equation accounts for the fact that even if we have exactly 4 bytes
   1931             // for the string, we still use the next u32 for the null terminator.
   1932             extra_i += constraint.len / 4 + 1;
   1933 
   1934             if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
   1935                 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
   1936             }
   1937             const reg_name = constraint[1 .. constraint.len - 1];
   1938             const reg = parseRegName(reg_name) orelse
   1939                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   1940 
   1941             const arg_mcv = try self.resolveInst(input);
   1942             try self.register_manager.getReg(reg, null);
   1943             try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
   1944         }
   1945 
   1946         {
   1947             var clobber_i: u32 = 0;
   1948             while (clobber_i < clobbers_len) : (clobber_i += 1) {
   1949                 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   1950                 // This equation accounts for the fact that even if we have exactly 4 bytes
   1951                 // for the string, we still use the next u32 for the null terminator.
   1952                 extra_i += clobber.len / 4 + 1;
   1953 
   1954                 // TODO honor these
   1955             }
   1956         }
   1957 
   1958         const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
   1959 
   1960         if (mem.eql(u8, asm_source, "ecall")) {
   1961             _ = try self.addInst(.{
   1962                 .tag = .ecall,
   1963                 .data = .{ .nop = {} },
   1964             });
   1965         } else {
   1966             return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
   1967         }
   1968 
   1969         if (output_constraint) |output| {
   1970             if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
   1971                 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
   1972             }
   1973             const reg_name = output[2 .. output.len - 1];
   1974             const reg = parseRegName(reg_name) orelse
   1975                 return self.fail("unrecognized register: '{s}'", .{reg_name});
   1976             break :result MCValue{ .register = reg };
   1977         } else {
   1978             break :result MCValue{ .none = {} };
   1979         }
   1980     };
   1981     simple: {
   1982         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   1983         var buf_index: usize = 0;
   1984         for (outputs) |output| {
   1985             if (output == .none) continue;
   1986 
   1987             if (buf_index >= buf.len) break :simple;
   1988             buf[buf_index] = output;
   1989             buf_index += 1;
   1990         }
   1991         if (buf_index + inputs.len > buf.len) break :simple;
   1992         std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
   1993         return self.finishAir(inst, result, buf);
   1994     }
   1995     var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
   1996     for (outputs) |output| {
   1997         if (output == .none) continue;
   1998 
   1999         bt.feed(output);
   2000     }
   2001     for (inputs) |input| {
   2002         bt.feed(input);
   2003     }
   2004     return bt.finishAir(result);
   2005 }
   2006 
   2007 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
   2008     try self.ensureProcessDeathCapacity(operand_count + 1);
   2009     return BigTomb{
   2010         .function = self,
   2011         .inst = inst,
   2012         .tomb_bits = self.liveness.getTombBits(inst),
   2013         .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
   2014         .bit_index = 0,
   2015     };
   2016 }
   2017 
   2018 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
   2019 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
   2020     switch (loc) {
   2021         .none => return,
   2022         .register => |reg| return self.genSetReg(ty, reg, val),
   2023         .stack_offset => |off| return self.genSetStack(ty, off, val),
   2024         .memory => {
   2025             return self.fail("TODO implement setRegOrMem for memory", .{});
   2026         },
   2027         else => unreachable,
   2028     }
   2029 }
   2030 
   2031 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
   2032     _ = ty;
   2033     _ = stack_offset;
   2034     _ = mcv;
   2035     return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch});
   2036 }
   2037 
   2038 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
   2039     switch (mcv) {
   2040         .dead => unreachable,
   2041         .ptr_stack_offset => unreachable,
   2042         .ptr_embedded_in_code => unreachable,
   2043         .unreach, .none => return, // Nothing to do.
   2044         .undef => {
   2045             if (!self.wantSafety())
   2046                 return; // The already existing value will do just fine.
   2047             // Write the debug undefined value.
   2048             return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
   2049         },
   2050         .immediate => |unsigned_x| {
   2051             const x = @bitCast(i64, unsigned_x);
   2052             if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
   2053                 _ = try self.addInst(.{
   2054                     .tag = .addi,
   2055                     .data = .{ .i_type = .{
   2056                         .rd = reg,
   2057                         .rs1 = .zero,
   2058                         .imm12 = @intCast(i12, x),
   2059                     } },
   2060                 });
   2061             } else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
   2062                 const lo12 = @truncate(i12, x);
   2063                 const carry: i32 = if (lo12 < 0) 1 else 0;
   2064                 const hi20 = @truncate(i20, (x >> 12) +% carry);
   2065 
   2066                 // TODO: add test case for 32-bit immediate
   2067                 _ = try self.addInst(.{
   2068                     .tag = .lui,
   2069                     .data = .{ .u_type = .{
   2070                         .rd = reg,
   2071                         .imm20 = hi20,
   2072                     } },
   2073                 });
   2074                 _ = try self.addInst(.{
   2075                     .tag = .addi,
   2076                     .data = .{ .i_type = .{
   2077                         .rd = reg,
   2078                         .rs1 = reg,
   2079                         .imm12 = lo12,
   2080                     } },
   2081                 });
   2082             } else {
   2083                 // li rd, immediate
   2084                 // "Myriad sequences"
   2085                 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
   2086             }
   2087         },
   2088         .memory => |addr| {
   2089             // The value is in memory at a hard-coded address.
   2090             // If the type is a pointer, it means the pointer address is at this memory location.
   2091             try self.genSetReg(ty, reg, .{ .immediate = addr });
   2092 
   2093             _ = try self.addInst(.{
   2094                 .tag = .ld,
   2095                 .data = .{ .i_type = .{
   2096                     .rd = reg,
   2097                     .rs1 = reg,
   2098                     .imm12 = 0,
   2099                 } },
   2100             });
   2101             // LOAD imm=[i12 offset = 0], rs1 =
   2102 
   2103             // return self.fail("TODO implement genSetReg memory for riscv64");
   2104         },
   2105         else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}),
   2106     }
   2107 }
   2108 
   2109 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
   2110     const un_op = self.air.instructions.items(.data)[inst].un_op;
   2111     const result = try self.resolveInst(un_op);
   2112     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2113 }
   2114 
   2115 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
   2116     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2117     const result = try self.resolveInst(ty_op.operand);
   2118     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2119 }
   2120 
   2121 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
   2122     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2123     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{
   2124         self.target.cpu.arch,
   2125     });
   2126     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2127 }
   2128 
   2129 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
   2130     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2131     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{
   2132         self.target.cpu.arch,
   2133     });
   2134     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2135 }
   2136 
   2137 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
   2138     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2139     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{
   2140         self.target.cpu.arch,
   2141     });
   2142     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2143 }
   2144 
   2145 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
   2146     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2147     const extra = self.air.extraData(Air.Block, ty_pl.payload);
   2148     _ = extra;
   2149     return self.fail("TODO implement airCmpxchg for {}", .{
   2150         self.target.cpu.arch,
   2151     });
   2152     // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
   2153 }
   2154 
   2155 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
   2156     _ = inst;
   2157     return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
   2158 }
   2159 
   2160 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
   2161     _ = inst;
   2162     return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
   2163 }
   2164 
   2165 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
   2166     _ = inst;
   2167     _ = order;
   2168     return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
   2169 }
   2170 
   2171 fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
   2172     _ = inst;
   2173     return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
   2174 }
   2175 
   2176 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
   2177     _ = inst;
   2178     return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
   2179 }
   2180 
   2181 fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
   2182     const un_op = self.air.instructions.items(.data)[inst].un_op;
   2183     const operand = try self.resolveInst(un_op);
   2184     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   2185         _ = operand;
   2186         return self.fail("TODO implement airTagName for riscv64", .{});
   2187     };
   2188     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2189 }
   2190 
   2191 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
   2192     const un_op = self.air.instructions.items(.data)[inst].un_op;
   2193     const operand = try self.resolveInst(un_op);
   2194     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   2195         _ = operand;
   2196         return self.fail("TODO implement airErrorName for riscv64", .{});
   2197     };
   2198     return self.finishAir(inst, result, .{ un_op, .none, .none });
   2199 }
   2200 
   2201 fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
   2202     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2203     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for riscv64", .{});
   2204     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2205 }
   2206 
   2207 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
   2208     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2209     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{});
   2210     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
   2211 }
   2212 
   2213 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
   2214     const vector_ty = self.air.typeOfIndex(inst);
   2215     const len = vector_ty.vectorLen();
   2216     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2217     const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
   2218     const result: MCValue = res: {
   2219         if (self.liveness.isUnused(inst)) break :res MCValue.dead;
   2220         return self.fail("TODO implement airAggregateInit for riscv64", .{});
   2221     };
   2222 
   2223     if (elements.len <= Liveness.bpi - 1) {
   2224         var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
   2225         std.mem.copy(Air.Inst.Ref, &buf, elements);
   2226         return self.finishAir(inst, result, buf);
   2227     }
   2228     var bt = try self.iterateBigTomb(inst, elements.len);
   2229     for (elements) |elem| {
   2230         bt.feed(elem);
   2231     }
   2232     return bt.finishAir(result);
   2233 }
   2234 
   2235 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
   2236     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2237     const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
   2238     _ = extra;
   2239     return self.fail("TODO implement airUnionInit for riscv64", .{});
   2240     // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
   2241 }
   2242 
   2243 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
   2244     const prefetch = self.air.instructions.items(.data)[inst].prefetch;
   2245     return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
   2246 }
   2247 
   2248 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
   2249     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   2250     const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
   2251     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
   2252         return self.fail("TODO implement airMulAdd for riscv64", .{});
   2253     };
   2254     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
   2255 }
   2256 
   2257 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
   2258     // First section of indexes correspond to a set number of constant values.
   2259     const ref_int = @enumToInt(inst);
   2260     if (ref_int < Air.Inst.Ref.typed_value_map.len) {
   2261         const tv = Air.Inst.Ref.typed_value_map[ref_int];
   2262         if (!tv.ty.hasRuntimeBits()) {
   2263             return MCValue{ .none = {} };
   2264         }
   2265         return self.genTypedValue(tv);
   2266     }
   2267 
   2268     // If the type has no codegen bits, no need to store it.
   2269     const inst_ty = self.air.typeOf(inst);
   2270     if (!inst_ty.hasRuntimeBits())
   2271         return MCValue{ .none = {} };
   2272 
   2273     const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
   2274     switch (self.air.instructions.items(.tag)[inst_index]) {
   2275         .constant => {
   2276             // Constants have static lifetimes, so they are always memoized in the outer most table.
   2277             const branch = &self.branch_stack.items[0];
   2278             const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
   2279             if (!gop.found_existing) {
   2280                 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
   2281                 gop.value_ptr.* = try self.genTypedValue(.{
   2282                     .ty = inst_ty,
   2283                     .val = self.air.values[ty_pl.payload],
   2284                 });
   2285             }
   2286             return gop.value_ptr.*;
   2287         },
   2288         .const_ty => unreachable,
   2289         else => return self.getResolvedInstValue(inst_index),
   2290     }
   2291 }
   2292 
   2293 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
   2294     // Treat each stack item as a "layer" on top of the previous one.
   2295     var i: usize = self.branch_stack.items.len;
   2296     while (true) {
   2297         i -= 1;
   2298         if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
   2299             assert(mcv != .dead);
   2300             return mcv;
   2301         }
   2302     }
   2303 }
   2304 
   2305 /// If the MCValue is an immediate, and it does not fit within this type,
   2306 /// we put it in a register.
   2307 /// A potential opportunity for future optimization here would be keeping track
   2308 /// of the fact that the instruction is available both as an immediate
   2309 /// and as a register.
   2310 fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
   2311     const mcv = try self.resolveInst(operand);
   2312     const ti = @typeInfo(T).Int;
   2313     switch (mcv) {
   2314         .immediate => |imm| {
   2315             // This immediate is unsigned.
   2316             const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
   2317             if (imm >= math.maxInt(U)) {
   2318                 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
   2319             }
   2320         },
   2321         else => {},
   2322     }
   2323     return mcv;
   2324 }
   2325 
   2326 fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
   2327     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
   2328     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
   2329     decl.alive = true;
   2330     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
   2331         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
   2332         const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
   2333         return MCValue{ .memory = got_addr };
   2334     } else if (self.bin_file.cast(link.File.MachO)) |_| {
   2335         // TODO I'm hacking my way through here by repurposing .memory for storing
   2336         // index to the GOT target symbol index.
   2337         return MCValue{ .memory = decl.link.macho.local_sym_index };
   2338     } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
   2339         const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
   2340         return MCValue{ .memory = got_addr };
   2341     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
   2342         try p9.seeDecl(decl);
   2343         const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
   2344         return MCValue{ .memory = got_addr };
   2345     } else {
   2346         return self.fail("TODO codegen non-ELF const Decl pointer", .{});
   2347     }
   2348     _ = tv;
   2349 }
   2350 
   2351 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
   2352     if (typed_value.val.isUndef())
   2353         return MCValue{ .undef = {} };
   2354 
   2355     if (typed_value.val.castTag(.decl_ref)) |payload| {
   2356         return self.lowerDeclRef(typed_value, payload.data);
   2357     }
   2358     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
   2359         return self.lowerDeclRef(typed_value, payload.data.decl);
   2360     }
   2361     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
   2362     switch (typed_value.ty.zigTypeTag()) {
   2363         .Pointer => switch (typed_value.ty.ptrSize()) {
   2364             .Slice => {
   2365                 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
   2366                 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
   2367                 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
   2368                 const slice_len = typed_value.val.sliceLen();
   2369                 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
   2370                 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
   2371                 const ptr_imm = ptr_mcv.memory;
   2372                 _ = slice_len;
   2373                 _ = ptr_imm;
   2374                 // We need more general support for const data being stored in memory to make this work.
   2375                 return self.fail("TODO codegen for const slices", .{});
   2376             },
   2377             else => {
   2378                 if (typed_value.val.tag() == .int_u64) {
   2379                     return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
   2380                 }
   2381                 return self.fail("TODO codegen more kinds of const pointers", .{});
   2382             },
   2383         },
   2384         .Int => {
   2385             const info = typed_value.ty.intInfo(self.target.*);
   2386             if (info.bits > ptr_bits or info.signedness == .signed) {
   2387                 return self.fail("TODO const int bigger than ptr and signed int", .{});
   2388             }
   2389             return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
   2390         },
   2391         .Bool => {
   2392             return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
   2393         },
   2394         .ComptimeInt => unreachable, // semantic analysis prevents this
   2395         .ComptimeFloat => unreachable, // semantic analysis prevents this
   2396         .Optional => {
   2397             if (typed_value.ty.isPtrLikeOptional()) {
   2398                 if (typed_value.val.isNull())
   2399                     return MCValue{ .immediate = 0 };
   2400 
   2401                 var buf: Type.Payload.ElemType = undefined;
   2402                 return self.genTypedValue(.{
   2403                     .ty = typed_value.ty.optionalChild(&buf),
   2404                     .val = typed_value.val,
   2405                 });
   2406             } else if (typed_value.ty.abiSize(self.target.*) == 1) {
   2407                 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
   2408             }
   2409             return self.fail("TODO non pointer optionals", .{});
   2410         },
   2411         .Enum => {
   2412             if (typed_value.val.castTag(.enum_field_index)) |field_index| {
   2413                 switch (typed_value.ty.tag()) {
   2414                     .enum_simple => {
   2415                         return MCValue{ .immediate = field_index.data };
   2416                     },
   2417                     .enum_full, .enum_nonexhaustive => {
   2418                         const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
   2419                         if (enum_full.values.count() != 0) {
   2420                             const tag_val = enum_full.values.keys()[field_index.data];
   2421                             return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
   2422                         } else {
   2423                             return MCValue{ .immediate = field_index.data };
   2424                         }
   2425                     },
   2426                     else => unreachable,
   2427                 }
   2428             } else {
   2429                 var int_tag_buffer: Type.Payload.Bits = undefined;
   2430                 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
   2431                 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
   2432             }
   2433         },
   2434         .ErrorSet => {
   2435             switch (typed_value.val.tag()) {
   2436                 .@"error" => {
   2437                     const err_name = typed_value.val.castTag(.@"error").?.data.name;
   2438                     const module = self.bin_file.options.module.?;
   2439                     const global_error_set = module.global_error_set;
   2440                     const error_index = global_error_set.get(err_name).?;
   2441                     return MCValue{ .immediate = error_index };
   2442                 },
   2443                 else => {
   2444                     // In this case we are rendering an error union which has a 0 bits payload.
   2445                     return MCValue{ .immediate = 0 };
   2446                 },
   2447             }
   2448         },
   2449         .ErrorUnion => {
   2450             const error_type = typed_value.ty.errorUnionSet();
   2451             const payload_type = typed_value.ty.errorUnionPayload();
   2452             const sub_val = typed_value.val.castTag(.eu_payload).?.data;
   2453 
   2454             if (!payload_type.hasRuntimeBits()) {
   2455                 // We use the error type directly as the type.
   2456                 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
   2457             }
   2458 
   2459             return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});
   2460         },
   2461         else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
   2462     }
   2463 }
   2464 
   2465 const CallMCValues = struct {
   2466     args: []MCValue,
   2467     return_value: MCValue,
   2468     stack_byte_count: u32,
   2469     stack_align: u32,
   2470 
   2471     fn deinit(self: *CallMCValues, func: *Self) void {
   2472         func.gpa.free(self.args);
   2473         self.* = undefined;
   2474     }
   2475 };
   2476 
   2477 /// Caller must call `CallMCValues.deinit`.
   2478 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
   2479     const cc = fn_ty.fnCallingConvention();
   2480     const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
   2481     defer self.gpa.free(param_types);
   2482     fn_ty.fnParamTypes(param_types);
   2483     var result: CallMCValues = .{
   2484         .args = try self.gpa.alloc(MCValue, param_types.len),
   2485         // These undefined values must be populated before returning from this function.
   2486         .return_value = undefined,
   2487         .stack_byte_count = undefined,
   2488         .stack_align = undefined,
   2489     };
   2490     errdefer self.gpa.free(result.args);
   2491 
   2492     const ret_ty = fn_ty.fnReturnType();
   2493 
   2494     switch (cc) {
   2495         .Naked => {
   2496             assert(result.args.len == 0);
   2497             result.return_value = .{ .unreach = {} };
   2498             result.stack_byte_count = 0;
   2499             result.stack_align = 1;
   2500             return result;
   2501         },
   2502         .Unspecified, .C => {
   2503             // LP64D ABI
   2504             //
   2505             // TODO make this generic with other ABIs, in particular
   2506             // with different hardware floating-point calling
   2507             // conventions
   2508             var next_register: usize = 0;
   2509             var next_stack_offset: u32 = 0;
   2510             const argument_registers = [_]Register{ .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7 };
   2511 
   2512             for (param_types) |ty, i| {
   2513                 const param_size = @intCast(u32, ty.abiSize(self.target.*));
   2514                 if (param_size <= 8) {
   2515                     if (next_register < argument_registers.len) {
   2516                         result.args[i] = .{ .register = argument_registers[next_register] };
   2517                         next_register += 1;
   2518                     } else {
   2519                         result.args[i] = .{ .stack_offset = next_stack_offset };
   2520                         next_register += next_stack_offset;
   2521                     }
   2522                 } else if (param_size <= 16) {
   2523                     if (next_register < argument_registers.len - 1) {
   2524                         return self.fail("TODO MCValues with 2 registers", .{});
   2525                     } else if (next_register < argument_registers.len) {
   2526                         return self.fail("TODO MCValues split register + stack", .{});
   2527                     } else {
   2528                         result.args[i] = .{ .stack_offset = next_stack_offset };
   2529                         next_register += next_stack_offset;
   2530                     }
   2531                 } else {
   2532                     result.args[i] = .{ .stack_offset = next_stack_offset };
   2533                     next_register += next_stack_offset;
   2534                 }
   2535             }
   2536 
   2537             result.stack_byte_count = next_stack_offset;
   2538             result.stack_align = 16;
   2539         },
   2540         else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}),
   2541     }
   2542 
   2543     if (ret_ty.zigTypeTag() == .NoReturn) {
   2544         result.return_value = .{ .unreach = {} };
   2545     } else if (!ret_ty.hasRuntimeBits()) {
   2546         result.return_value = .{ .none = {} };
   2547     } else switch (cc) {
   2548         .Naked => unreachable,
   2549         .Unspecified, .C => {
   2550             const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
   2551             if (ret_ty_size <= 8) {
   2552                 result.return_value = .{ .register = .a0 };
   2553             } else if (ret_ty_size <= 16) {
   2554                 return self.fail("TODO support MCValue 2 registers", .{});
   2555             } else {
   2556                 return self.fail("TODO support return by reference", .{});
   2557             }
   2558         },
   2559         else => return self.fail("TODO implement function return values for {}", .{cc}),
   2560     }
   2561     return result;
   2562 }
   2563 
   2564 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
   2565 fn wantSafety(self: *Self) bool {
   2566     return switch (self.bin_file.options.optimize_mode) {
   2567         .Debug => true,
   2568         .ReleaseSafe => true,
   2569         .ReleaseFast => false,
   2570         .ReleaseSmall => false,
   2571     };
   2572 }
   2573 
   2574 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   2575     @setCold(true);
   2576     assert(self.err_msg == null);
   2577     self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
   2578     return error.CodegenFail;
   2579 }
   2580 
   2581 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
   2582     @setCold(true);
   2583     assert(self.err_msg == null);
   2584     self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
   2585     return error.CodegenFail;
   2586 }
   2587 
   2588 fn parseRegName(name: []const u8) ?Register {
   2589     if (@hasDecl(Register, "parseRegName")) {
   2590         return Register.parseRegName(name);
   2591     }
   2592     return std.meta.stringToEnum(Register, name);
   2593 }