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