//! Machine Intermediate Representation.
//! This data is produced by x86_64 Codegen and consumed by x86_64 Isel.
//! These instructions have a 1:1 correspondence with machine code instructions
//! for the target. MIR can be lowered to source-annotated textual assembly code
//! instructions, or it can be lowered to machine code.
//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
//! so that, for example, the smaller encodings of jump instructions can be used.

instructions: std.MultiArrayList(Inst).Slice,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []const u32,
string_bytes: []const u8,
locals: []const Local,
table: []const Inst.Index,
/// Optional data which, when present, can be used to accelerate encoding speed.
memoized_encodings: []const u0 = &.{},
frame_locs: std.MultiArrayList(FrameLoc).Slice,

pub const Inst = struct {
    tag: Tag,
    ops: Ops,
    data: Data,

    pub const Index = u32;

    pub const Fixes = enum(u8) {
        /// ___
        @"_",

        /// ___ 0
        _0,
        /// ___ 1
        _1,
        /// ___ 2
        _2,
        /// ___ 3
        _3,
        /// ___ 4
        _4,

        /// ___ Demote
        _demote,
        /// ___ Flush
        _flush,
        /// ___ Flush Optimized
        _flushopt,
        /// ___ Instructions With T0 Hint
        _it0,
        /// ___ Instructions With T0 Hint
        _it1,
        /// ___ With NTA Hint
        _nta,
        /// System Call ___
        sys_,
        /// ___ With T0 Hint
        _t0,
        /// ___ With T1 Hint
        _t1,
        /// ___ With T2 Hint
        _t2,
        /// ___ Write Back
        _wb,
        /// ___ With Intent to Write and T1 Hint
        _wt1,

        /// ___ crement Shadow Stack Pointer Doubleword
        _csspd,
        /// ___ crement Shadow Stack Pointer Quadword
        _csspq,
        /// ___ FS Segment Base
        _fsbase,
        /// ___ GS
        _gs,
        /// ___ GS Segment Base
        _gsbase,
        /// ___ Model Specific Register
        _msr,
        /// ___ MXCSR
        _mxcsr,
        /// ___ Processor ID
        _pid,
        /// ___ Protection Key Rights For User Pages
        _pkru,
        /// ___ Performance-Monitoring Counters
        _pmc,
        /// ___ Random Number
        _rand,
        /// ___ r Busy Flag in a Supervisor Shadow Stack token
        _rssbsy,
        /// ___ Random Seed
        _seed,
        /// ___ Shadow Stack Doubleword
        _ssd,
        /// ___ Shadow Stack Quadword
        _ssq,
        /// ___ Shadow Stack Pointer Doubleword
        _sspd,
        /// ___ Shadow Stack Pointer Quadword
        _sspq,
        /// ___ Time-Stamp Counter
        _tsc,
        /// ___ Time-Stamp Counter And Processor ID
        _tscp,
        /// ___ User Shadow Stack Doubleword
        _ussd,
        /// ___ User Shadow Stack Quadword
        _ussq,
        /// VEX-Encoded ___ MXCSR
        v_mxcsr,

        /// Byte ___
        b_,
        /// Interrupt ___
        /// Integer ___
        i_,
        /// Interrupt ___ Word
        i_w,
        /// Interrupt ___ Doubleword
        i_d,
        /// Interrupt ___ Quadword
        i_q,
        /// User-Interrupt ___
        ui_,

        /// ___ mp
        _mp,
        /// ___ if CX register is 0
        _cxz,
        /// ___ if ECX register is 0
        _ecxz,
        /// ___ if RCX register is 0
        _rcxz,

        /// ___ Addition
        _a,
        /// ___ Subtraction
        _s,
        /// ___ Multiply
        _m,
        /// ___ Division
        _d,

        /// ___ Without Affecting Flags
        _x,
        /// ___ Left
        _l,
        /// ___ Left Double
        _ld,
        /// ___ Left Without Affecting Flags
        _lx,
        /// ___ Mask
        _msk,
        /// ___ Right
        /// ___ For Reading
        /// ___ Register
        _r,
        /// ___ Right Double
        _rd,
        /// ___ Right Without Affecting Flags
        _rx,

        /// ___ Forward
        _f,
        /// ___ Reverse
        //_r,

        /// ___ Above
        //_a,
        /// ___ Above Or Equal
        _ae,
        /// ___ Below
        _b,
        /// ___ Below Or Equal
        /// ___ Big Endian
        _be,
        /// ___ Carry
        /// ___ Carry Flag
        _c,
        /// ___ Equal
        _e,
        /// ___ Greater
        _g,
        /// ___ Greater Or Equal
        _ge,
        /// ___ Less
        //_l,
        /// ___ Less Or Equal
        _le,
        /// ___ Not Above
        _na,
        /// ___ Not Above Or Equal
        _nae,
        /// ___ Not Below
        _nb,
        /// ___ Not Below Or Equal
        _nbe,
        /// ___ Not Carry
        _nc,
        /// ___ Not Equal
        _ne,
        /// ___ Not Greater
        _ng,
        /// ___ Not Greater Or Equal
        _nge,
        /// ___ Not Less
        _nl,
        /// ___ Not Less Or Equal
        _nle,
        /// ___ Not Overflow
        _no,
        /// ___ Not Parity
        _np,
        /// ___ Not Sign
        _ns,
        /// ___ Not Zero
        _nz,
        /// ___ Overflow
        _o,
        /// ___ Parity
        _p,
        /// ___ Parity Even
        _pe,
        /// ___ Parity Odd
        _po,
        /// ___ Sign
        //_s,
        /// ___ Zero
        _z,
        /// ___ Alignment Check Flag
        _ac,
        /// ___ Direction Flag
        //_d,
        /// ___ Interrupt Flag
        _i,
        /// ___ Task-Switched Flag In CR0
        _ts,
        /// ___ User Interrupt Flag
        _ui,

        /// ___ Byte
        //_b,
        /// ___ Word
        /// ___ For Writing
        /// ___ With Intent to Write
        _w,
        /// ___ Doubleword
        //_d,
        /// ___ Double Quadword to Quadword
        _dq2q,
        /// ___ QuadWord
        _q,
        /// ___ Quadword to Double Quadword
        _q2dq,

        /// ___ String
        //_s,
        /// ___ String Byte
        _sb,
        /// ___ String Word
        _sw,
        /// ___ String Doubleword
        _sd,
        /// ___ String Quadword
        _sq,

        /// Repeat ___ String
        @"rep _s",
        /// Repeat ___ String Byte
        @"rep _sb",
        /// Repeat ___ String Word
        @"rep _sw",
        /// Repeat ___ String Doubleword
        @"rep _sd",
        /// Repeat ___ String Quadword
        @"rep _sq",

        /// Repeat Equal ___ String
        @"repe _s",
        /// Repeat Equal ___ String Byte
        @"repe _sb",
        /// Repeat Equal ___ String Word
        @"repe _sw",
        /// Repeat Equal ___ String Doubleword
        @"repe _sd",
        /// Repeat Equal ___ String Quadword
        @"repe _sq",

        /// Repeat Not Equal ___ String
        @"repne _s",
        /// Repeat Not Equal ___ String Byte
        @"repne _sb",
        /// Repeat Not Equal ___ String Word
        @"repne _sw",
        /// Repeat Not Equal ___ String Doubleword
        @"repne _sd",
        /// Repeat Not Equal ___ String Quadword
        @"repne _sq",

        /// Repeat Not Zero ___ String
        @"repnz _s",
        /// Repeat Not Zero ___ String Byte
        @"repnz _sb",
        /// Repeat Not Zero ___ String Word
        @"repnz _sw",
        /// Repeat Not Zero ___ String Doubleword
        @"repnz _sd",
        /// Repeat Not Zero ___ String Quadword
        @"repnz _sq",

        /// Repeat Zero ___ String
        @"repz _s",
        /// Repeat Zero ___ String Byte
        @"repz _sb",
        /// Repeat Zero ___ String Word
        @"repz _sw",
        /// Repeat Zero ___ String Doubleword
        @"repz _sd",
        /// Repeat Zero ___ String Quadword
        @"repz _sq",

        /// Locked ___
        @"lock _",
        /// ___ And Complement
        //_c,
        /// Locked ___ And Complement
        @"lock _c",
        /// ___ And Reset
        //_r,
        /// Locked ___ And Reset
        @"lock _r",
        /// ___ And Set
        //_s,
        /// Locked ___ And Set
        @"lock _s",
        /// ___ 8 Bytes
        _8b,
        /// Locked ___ 8 Bytes
        @"lock _8b",
        /// ___ 16 Bytes
        _16b,
        /// Locked ___ 16 Bytes
        @"lock _16b",

        /// Float ___
        f_,
        /// Float ___ +1.0
        /// Float ___ 1
        f_1,
        /// Float ___ Below
        f_b,
        /// Float ___ Below Or Equal
        f_be,
        /// Float ___ Control Word
        f_cw,
        /// Float ___ Equal
        f_e,
        /// Float ___ Environment
        f_env,
        /// Float ___ log_2(e)
        f_l2e,
        /// Float ___ log_2(10)
        f_l2t,
        /// Float ___ log_10(2)
        f_lg2,
        /// Float ___ log_e(2)
        f_ln2,
        /// Float ___ Not Below
        f_nb,
        /// Float ___ Not Below Or Equal
        f_nbe,
        /// Float ___ Not Equal
        f_ne,
        /// Float ___ Not Unordered
        f_nu,
        /// Float ___ Pop
        f_p,
        /// Float ___ +1
        f_p1,
        /// Float ___ π
        f_pi,
        /// Float ___ Pop Pop
        f_pp,
        /// Float ___ crement Stack-Top Pointer
        f_cstp,
        /// Float ___ Status Word
        f_sw,
        /// Float ___ Unordered
        f_u,
        /// Float ___ +0.0
        f_z,
        /// Float BCD ___
        fb_,
        /// Float BCD ___ Pop
        fb_p,
        /// Float And Integer ___
        fi_,
        /// Float And Integer ___ Pop
        fi_p,
        /// Float No Wait ___
        fn_,
        /// Float No Wait ___ Control Word
        fn_cw,
        /// Float No Wait ___ Environment
        fn_env,
        /// Float No Wait ___ status word
        fn_sw,
        /// Float Extended ___
        fx_,
        /// Float Extended ___ 64
        fx_64,

        /// ___ in 32-bit and Compatibility Mode
        _32,
        /// ___ in 64-bit Mode
        _64,

        /// Packed ___
        p_,
        /// Packed ___ Byte
        p_b,
        /// Packed ___ Word
        p_w,
        /// Packed ___ Doubleword
        p_d,
        /// Packed ___ Quadword
        p_q,
        /// Packed ___ Double Quadword
        /// Packed ___ Doubleword to Quadword
        p_dq,
        /// Packed ___ Unsigned Doubleword to Quadword
        p_udq,
        /// Packed Carry-Less ___ Quadword to Double Quadword
        pcl_qdq,
        /// Packed Half ___ Doubleword
        ph_d,
        /// Packed Half ___ Saturate Word
        ph_sw,
        /// Packed Half ___ Word
        ph_w,
        /// ___ Aligned Packed Integer Values
        _dqa,
        /// ___ Unaligned Packed Integer Values
        _dqu,

        /// ___ Scalar Single-Precision Values
        _ss,
        /// ___ Packed Single-Precision Values
        _ps,
        /// ___ Scalar Double-Precision Values
        //_sd,
        /// ___ Packed Double-Precision Values
        _pd,
        /// Half ___ Packed Single-Precision Values
        h_ps,
        /// Half ___ Packed Double-Precision Values
        h_pd,

        /// ___ Internal Caches
        //_d,
        /// ___ TLB Entries
        _lpg,
        /// ___ Process-Context Identifier
        _pcid,

        /// Load ___
        l_,
        /// Memory ___
        m_,
        /// Store ___
        s_,
        /// Timed ___
        t_,
        /// User Level Monitor ___
        um_,

        /// VEX-Encoded ___
        v_,
        /// VEX-Encoded ___ Byte
        v_b,
        /// VEX-Encoded ___ Word
        v_w,
        /// VEX-Encoded ___ Doubleword
        v_d,
        /// VEX-Encoded ___ Quadword
        v_q,
        /// VEX-Encoded ___ Aligned Packed Integer Values
        v_dqa,
        /// VEX-Encoded ___ Unaligned Packed Integer Values
        v_dqu,
        /// VEX-Encoded ___ Integer Data
        v_i128,
        /// VEX-Encoded Packed ___
        vp_,
        /// VEX-Encoded Packed ___ Byte
        vp_b,
        /// VEX-Encoded Packed ___ Word
        vp_w,
        /// VEX-Encoded Packed ___ Doubleword
        vp_d,
        /// VEX-Encoded Packed ___ Quadword
        vp_q,
        /// VEX-Encoded Packed ___ Double Quadword
        /// VEX-Encoded Packed ___ Doubleword to Quadword
        vp_dq,
        /// VEX-Encoded Packed ___ Unsigned Doubleword to Quadword
        vp_udq,
        /// VEx-Encoded Packed Carry-Less ___ Quadword to Double Quadword
        vpcl_qdq,
        /// VEX-Encoded Packed Half ___ Doubleword
        vph_d,
        /// VEX-Encoded Packed Half ___ Saturate Word
        vph_sw,
        /// VEX-Encoded Packed Half ___ Word
        vph_w,
        /// VEX-Encoded ___ Scalar Single-Precision Values
        v_ss,
        /// VEX-Encoded ___ Packed Single-Precision Values
        v_ps,
        /// VEX-Encoded ___ Scalar Double-Precision Values
        v_sd,
        /// VEX-Encoded ___ Packed Double-Precision Values
        v_pd,
        /// VEX-Encoded ___ 128-Bits Of Floating-Point Data
        v_f128,
        /// VEX-Encoded Half ___ Packed Single-Precision Values
        vh_ps,
        /// VEX-Encoded Half ___ Packed Double-Precision Values
        vh_pd,

        /// ___ 128-bit key with key locker
        _128,
        /// ___ 256-bit key with key locker
        _256,
        /// ___ with key locker using 128-bit key
        _128kl,
        /// ___ with key locker using 256-bit key
        _256kl,
        /// ___ with key locker on 8 blocks using 128-bit key
        _wide128kl,
        /// ___ with key locker on 8 blocks using 256-bit key
        _wide256kl,

        /// Mask ___ Byte
        k_b,
        /// Mask ___ Word
        k_w,
        /// Mask ___ Doubleword
        k_d,
        /// Mask ___ Quadword
        k_q,

        pub fn fromCond(cc: bits.Condition) Fixes {
            return switch (cc) {
                inline else => |cc_tag| @field(Fixes, "_" ++ @tagName(cc_tag)),
                .z_and_np, .nz_or_p => unreachable,
            };
        }
    };

    pub const Tag = enum(u8) {
        // General-purpose
        /// ASCII adjust al after addition
        /// ASCII adjust ax before division
        /// ASCII adjust ax after multiply
        /// ASCII adjust al after subtraction
        aa,
        /// Add with carry
        /// Unsigned integer addition of two operands with carry flag
        adc,
        /// Add
        /// Add packed integers
        /// Add packed single-precision floating-point values
        /// Add scalar single-precision floating-point values
        /// Add packed double-precision floating-point values
        /// Add scalar double-precision floating-point values
        /// Packed single-precision floating-point horizontal add
        /// Packed double-precision floating-point horizontal add
        /// Packed horizontal add
        /// Packed horizontal add and saturate
        add,
        /// Logical and
        /// Bitwise logical and of packed single-precision floating-point values
        /// Bitwise logical and of packed double-precision floating-point values
        @"and",
        /// Adjust RPL field of segment selector
        arpl,
        /// Bit scan forward
        /// Bit scan reverse
        bs,
        /// Byte swap
        /// Swap GS base register
        swap,
        /// Bit test
        /// Bit test and complement
        /// Bit test and reset
        /// Bit test and set
        bt,
        /// Check array index against bounds
        bound,
        /// Call
        /// Fast system call
        call,
        /// Convert byte to word
        cbw,
        /// Convert doubleword to quadword
        cdq,
        /// Convert doubleword to quadword
        cdqe,
        /// Clear AC flag in EFLAGS register
        /// Clear carry flag
        /// Clear direction flag
        /// Clear interrupt flag
        /// Clear task-switched flag in CR0
        /// Clear user interrupt flag
        /// Cache line demote
        /// Flush cache line
        /// Flush cache line optimized
        /// Clear busy flag in a supervisor shadow stack token
        /// Cache line write back
        cl,
        /// Complement carry flag
        cmc,
        /// Conditional move
        cmov,
        /// Logical compare
        /// Compare string
        /// Compare scalar single-precision floating-point values
        /// Compare scalar double-precision floating-point values
        cmp,
        /// Compare and exchange
        /// Compare and exchange bytes
        cmpxchg,
        /// CPU identification
        cpuid,
        /// Convert doubleword to quadword
        cqo,
        /// Convert word to doubleword
        cwd,
        /// Convert word to doubleword
        cwde,
        /// Decimal adjust AL after addition
        /// Decimal adjust AL after subtraction
        da,
        /// Decrement by 1
        /// Decrement stack-top pointer
        /// Decrement shadow stack pointer
        de,
        /// Unsigned division
        /// Signed division
        /// Divide
        /// Divide packed single-precision floating-point values
        /// Divide scalar single-precision floating-point values
        /// Divide packed double-precision floating-point values
        /// Divide scalar double-precision floating-point values
        div,
        /// Terminate and indirect branch in 32-bit and compatibility mode
        /// Terminate and indirect branch in 64-bit mode
        endbr,
        /// Enqueue command
        /// Enqueue command supervisor
        enqcmd,
        /// Make stack frame for procedure parameters
        /// Fast system call
        enter,
        /// Fast return from fast system call
        exit,
        /// Load fence
        /// Memory fence
        /// Store fence
        fence,
        /// Halt
        hlt,
        /// History reset
        hreset,
        /// Input from port
        /// Input from port to string
        /// Increment by 1
        /// Increment stack-top pointer
        /// Increment shadow stack pointer
        in,
        /// Call to interrupt procedure
        int,
        /// Invalidate internal caches
        /// Invalidate TLB entries
        /// Invalidate process-context identifier
        inv,
        /// Conditional jump
        /// Jump
        j,
        /// Load status flags into AH register
        lahf,
        /// Load access right byte
        lar,
        /// Load effective address
        lea,
        /// High level procedure exit
        leave,
        /// Load global descriptor table register
        lgdt,
        /// Load interrupt descriptor table register
        lidt,
        /// Load local descriptor table register
        lldt,
        /// Load machine status word
        lmsw,
        /// Load string
        lod,
        /// Loop according to ECX counter
        loop,
        /// Load segment limit
        lsl,
        /// Load task register
        ltr,
        /// Count the number of leading zero bits
        lzcnt,
        /// Move
        /// Move data from string to string
        /// Move data after swapping bytes
        /// Move scalar single-precision floating-point value
        /// Move scalar double-precision floating-point value
        /// Move doubleword
        /// Move quadword
        /// Move aligned packed integer values
        /// Move unaligned packed integer values
        /// Move quadword from XMM to MMX technology register
        /// Move quadword from MMX technology to XMM register
        mov,
        /// Move with sign extension
        movsx,
        /// Move with zero extension
        movzx,
        /// Multiply
        /// Signed multiplication
        /// Multiply packed single-precision floating-point values
        /// Multiply scalar single-precision floating-point values
        /// Multiply packed double-precision floating-point values
        /// Multiply scalar double-precision floating-point values
        /// Multiply packed unsigned doubleword integers
        /// Multiply packed doubleword integers
        /// Carry-less multiplication quadword
        mul,
        /// Two's complement negation
        neg,
        /// No-op
        /// No operation
        nop,
        /// One's complement negation
        not,
        /// Logical or
        /// Bitwise logical or of packed single-precision floating-point values
        /// Bitwise logical or of packed double-precision floating-point values
        @"or",
        /// Output to port
        /// Output string to port
        out,
        /// Spin loop hint
        /// Timed pause
        pause,
        /// Pop
        pop,
        /// Return the count of number of bits set to 1
        popcnt,
        /// Pop stack into EFLAGS register
        popf,
        /// Push
        push,
        /// Push EFLAGS register onto the stack
        pushf,
        /// Rotate left through carry
        /// Rotate right through carry
        rc,
        /// Read FS segment base
        /// Read GS segment base
        /// Read from model specific register
        /// Read processor ID
        /// Read protection key rights for user pages
        /// Read performance-monitoring counters
        /// Read random number
        /// Read random seed
        /// Read shadow stack pointer
        /// Read time-stamp counter
        /// Read time-stamp counter and processor ID
        rd,
        /// Return
        /// Return from fast system call
        /// Interrupt return
        /// User-interrupt return
        ret,
        /// Rotate left
        /// Rotate right
        /// Rotate right logical without affecting flags
        ro,
        /// Resume from system management mode
        rsm,
        /// Arithmetic shift left
        /// Arithmetic shift right
        /// Shift left arithmetic without affecting flags
        sa,
        /// Store AH into flags
        sahf,
        /// Integer subtraction with borrow
        sbb,
        /// Scan string
        sca,
        /// Send user interprocessor interrupt
        senduipi,
        /// Serialize instruction execution
        serialize,
        /// Set byte on condition
        set,
        /// Logical shift left
        /// Double precision shift left
        /// Logical shift right
        /// Double precision shift right
        /// Shift left logical without affecting flags
        /// Shift right logical without affecting flags
        sh,
        /// Store interrupt descriptor table register
        sidt,
        /// Store local descriptor table register
        sldt,
        /// Store machine status word
        smsw,
        /// Subtract
        /// Subtract packed integers
        /// Subtract packed single-precision floating-point values
        /// Subtract scalar single-precision floating-point values
        /// Subtract packed double-precision floating-point values
        /// Subtract scalar double-precision floating-point values
        /// Packed single-precision floating-point horizontal subtract
        /// Packed double-precision floating-point horizontal subtract
        /// Packed horizontal subtract
        /// Packed horizontal subtract and saturate
        sub,
        /// Set carry flag
        /// Set direction flag
        /// Set interrupt flag
        /// Store binary coded decimal integer and pop
        /// Store floating-point value
        /// Store integer
        /// Store x87 FPU control word
        /// Store x87 FPU environment
        /// Store x87 FPU status word
        /// Store MXCSR register state
        st,
        /// Store string
        sto,
        /// Test condition
        /// Logical compare
        /// Packed bit test
        @"test",
        /// Undefined instruction
        ud,
        /// User level set up monitor address
        umonitor,
        /// Verify a segment for reading
        /// Verify a segment for writing
        ver,
        /// Write to model specific register
        /// Write to model specific register
        /// Write to model specific register
        /// Write to shadow stack
        /// Write to user shadow stack
        wr,
        /// Exchange and add
        xadd,
        /// Exchange register/memory with register
        /// Exchange register contents
        xch,
        /// Get value of extended control register
        xgetbv,
        /// Table look-up translation
        xlat,
        /// Logical exclusive-or
        /// Bitwise logical xor of packed single-precision floating-point values
        /// Bitwise logical xor of packed double-precision floating-point values
        xor,

        // X87
        /// Compute 2^x-1
        @"2xm1",
        /// Absolute value
        abs,
        /// Change sign
        chs,
        /// Clear exceptions
        clex,
        /// Compare floating-point values
        com,
        /// Compare floating-point values and set EFLAGS
        /// Compare scalar ordered single-precision floating-point values
        /// Compare scalar ordered double-precision floating-point values
        comi,
        /// Cosine
        cos,
        /// Reverse divide
        divr,
        /// Free floating-point register
        free,
        /// Initialize floating-point unit
        init,
        /// Load binary coded decimal integer
        /// Load floating-point value
        /// Load integer
        /// Load constant
        /// Load x87 FPU control word
        /// Load x87 FPU environment
        /// Load MXCSR register state
        ld,
        /// Partial arctangent
        patan,
        /// Partial remainder
        prem,
        /// Partial tangent
        ptan,
        /// Round to integer
        rndint,
        /// Restore x87 FPU state
        /// Restore x87 FPU, MMX, XMM, and MXCSR state
        rstor,
        /// Store x87 FPU state
        /// Save x87 FPU, MMX technology, and MXCSR state
        save,
        /// Scale
        scale,
        /// Sine
        sin,
        /// Sine and cosine
        sincos,
        /// Square root
        /// Square root of packed single-precision floating-point values
        /// Square root of scalar single-precision floating-point value
        /// Square root of packed double-precision floating-point values
        /// Square root of scalar double-precision floating-point value
        sqrt,
        /// Store integer with truncation
        stt,
        /// Reverse subtract
        subr,
        /// Test
        tst,
        /// Unordered compare floating-point values
        ucom,
        /// Unordered compare floating-point values and set EFLAGS
        /// Unordered compare scalar single-precision floating-point values
        /// Unordered compare scalar double-precision floating-point values
        ucomi,
        /// Wait
        /// User level monitor wait
        wait,
        /// Examine floating-point
        xam,
        /// Extract exponent and significand
        xtract,
        /// Compute y * log2x
        /// Compute y * log2(x + 1)
        yl2x,

        // MMX
        /// Pack with signed saturation
        ackssw,
        /// Pack with signed saturation
        ackssd,
        /// Pack with unsigned saturation
        ackusw,
        /// Add packed signed integers with signed saturation
        adds,
        /// Add packed unsigned integers with unsigned saturation
        addus,
        /// Logical and not
        /// Bitwise logical and not of packed single-precision floating-point values
        /// Bitwise logical and not of packed double-precision floating-point values
        andn,
        /// Compare packed data for equal
        cmpeq,
        /// Compare packed data for greater than
        cmpgt,
        /// Empty MMX technology state
        emms,
        /// Multiply and add packed signed and unsigned bytes
        maddubs,
        /// Multiply and add packed integers
        maddw,
        /// Multiply packed signed integers and store low result
        mull,
        /// Multiply packed signed integers and store high result
        mulh,
        /// Shift packed data left logical
        sll,
        /// Shift packed data right arithmetic
        sra,
        /// Shift packed data right logical
        srl,
        /// Subtract packed signed integers with signed saturation
        subs,
        /// Subtract packed unsigned integers with unsigned saturation
        subus,
        /// Unpack high data
        unpckhbw,
        /// Unpack high data
        unpckhdq,
        /// Unpack high data
        unpckhwd,
        /// Unpack low data
        unpcklbw,
        /// Unpack low data
        unpckldq,
        /// Unpack low data
        unpcklwd,

        // SSE
        /// Average packed integers
        avg,
        /// Convert packed doubleword integers to packed single-precision floating-point values
        /// Convert packed doubleword integers to packed double-precision floating-point values
        cvtpi2,
        /// Convert packed single-precision floating-point values to packed doubleword integers
        cvtps2pi,
        /// Convert doubleword integer to scalar single-precision floating-point value
        /// Convert doubleword integer to scalar double-precision floating-point value
        cvtsi2,
        /// Convert scalar single-precision floating-point value to doubleword integer
        cvtss2si,
        /// Convert with truncation packed single-precision floating-point values to packed doubleword integers
        cvttps2pi,
        /// Convert with truncation scalar single-precision floating-point value to doubleword integer
        cvttss2si,
        /// Extract byte
        /// Extract word
        /// Extract doubleword
        /// Extract quadword
        extr,
        /// Insert byte
        /// Insert word
        /// Insert doubleword
        /// Insert quadword
        insr,
        /// Maximum of packed single-precision floating-point values
        /// Maximum of scalar single-precision floating-point values
        /// Maximum of packed double-precision floating-point values
        /// Maximum of scalar double-precision floating-point values
        max,
        /// Maximum of packed signed integers
        maxs,
        /// Maximum of packed unsigned integers
        maxu,
        /// Minimum of packed single-precision floating-point values
        /// Minimum of scalar single-precision floating-point values
        /// Minimum of packed double-precision floating-point values
        /// Minimum of scalar double-precision floating-point values
        min,
        /// Minimum of packed signed integers
        mins,
        /// Minimum of packed unsigned integers
        minu,
        /// Move aligned packed single-precision floating-point values
        /// Move aligned packed double-precision floating-point values
        mova,
        /// Move high packed single-precision floating-point values
        /// Move high packed double-precision floating-point values
        movh,
        /// Move packed single-precision floating-point values high to low
        movhl,
        /// Move low packed single-precision floating-point values
        /// Move low packed double-precision floating-point values
        movl,
        /// Move packed single-precision floating-point values low to high
        movlh,
        /// Move byte mask
        /// Extract packed single precision floating-point sign mask
        /// Extract packed double precision floating-point sign mask
        movmsk,
        /// Move unaligned packed single-precision floating-point values
        /// Move unaligned packed double-precision floating-point values
        movu,
        /// Multiply packed unsigned integers and store high result
        mulhu,
        /// Prefetch data into caches
        /// Prefetch data into caches with intent to write
        prefetch,
        /// Compute sum of absolute differences
        sadb,
        /// Packed interleave shuffle of quadruplets of single-precision floating-point values
        /// Packed interleave shuffle of pairs of double-precision floating-point values
        /// Shuffle packed doublewords
        /// Shuffle packed words
        shuf,
        /// Unpack and interleave high packed single-precision floating-point values
        /// Unpack and interleave high packed double-precision floating-point values
        unpckh,
        /// Unpack and interleave low packed single-precision floating-point values
        /// Unpack and interleave low packed double-precision floating-point values
        unpckl,

        // SSE2
        /// Convert packed doubleword integers to packed single-precision floating-point values
        /// Convert packed doubleword integers to packed double-precision floating-point values
        cvtdq2,
        /// Convert packed double-precision floating-point values to packed doubleword integers
        cvtpd2dq,
        /// Convert packed double-precision floating-point values to packed doubleword integers
        cvtpd2pi,
        /// Convert packed double-precision floating-point values to packed single-precision floating-point values
        cvtpd2,
        /// Convert packed single-precision floating-point values to packed doubleword integers
        cvtps2dq,
        /// Convert packed single-precision floating-point values to packed double-precision floating-point values
        cvtps2,
        /// Convert scalar double-precision floating-point value to doubleword integer
        cvtsd2si,
        /// Convert scalar double-precision floating-point value to scalar single-precision floating-point value
        cvtsd2,
        /// Convert scalar single-precision floating-point value to scalar double-precision floating-point value
        cvtss2,
        /// Convert with truncation packed double-precision floating-point values to packed doubleword integers
        cvttpd2dq,
        /// Convert with truncation packed double-precision floating-point values to packed doubleword integers
        cvttpd2pi,
        /// Convert with truncation packed single-precision floating-point values to packed doubleword integers
        cvttps2dq,
        /// Convert with truncation scalar double-precision floating-point value to doubleword integer
        cvttsd2si,
        /// Galois field affine transformation inverse
        gf2p8affineinvq,
        /// Galois field affine transformation
        gf2p8affineq,
        /// Galois field multiply bytes
        gf2p8mul,
        /// Shuffle packed high words
        shufh,
        /// Shuffle packed low words
        shufl,
        /// Unpack high data
        unpckhqdq,
        /// Unpack low data
        unpcklqdq,

        // SSE3
        /// Packed single-precision floating-point add/subtract
        /// Packed double-precision floating-point add/subtract
        addsub,
        /// Replicate double floating-point values
        movddup,
        /// Replicate single floating-point values
        movshdup,
        /// Replicate single floating-point values
        movsldup,

        // SSSE3
        /// Packed align right
        alignr,
        /// Packed multiply high with round and scale
        mulhrs,
        /// Packed sign
        sign,

        // SSE4.1
        /// Pack with unsigned saturation
        ackusd,
        /// Blend packed single-precision floating-point values
        /// Blend scalar single-precision floating-point values
        /// Blend packed double-precision floating-point values
        /// Blend scalar double-precision floating-point values
        /// Blend packed dwords
        blend,
        /// Variable blend packed single-precision floating-point values
        /// Variable blend scalar single-precision floating-point values
        /// Variable blend packed double-precision floating-point values
        /// Variable blend scalar double-precision floating-point values
        blendv,
        /// Dot product of packed single-precision floating-point values
        /// Dot product of packed double-precision floating-point values
        dp,
        /// Extract packed floating-point values
        /// Extract packed integer values
        extract,
        /// Insert scalar single-precision floating-point value
        /// Insert packed floating-point values
        insert,
        /// Packed horizontal word minimum
        minposu,
        /// Packed move with sign extend
        movsxb,
        movsxd,
        movsxw,
        /// Packed move with zero extend
        movzxb,
        movzxd,
        movzxw,
        /// Round packed single-precision floating-point values
        /// Round scalar single-precision floating-point value
        /// Round packed double-precision floating-point values
        /// Round scalar double-precision floating-point value
        round,

        // SSE4.2
        /// Accumulate CRC32 value
        crc32,

        // AES
        /// Perform one round of an AES decryption flow
        /// Perform ten rounds of AES decryption flow with key locker using 128-bit key
        /// Perform ten rounds of AES decryption flow with key locker using 256-bit key
        /// Perform ten rounds of AES decryption flow with key locker on 8 blocks using 128-bit key
        /// Perform ten rounds of AES decryption flow with key locker on 8 blocks using 256-bit key
        aesdec,
        /// Perform last round of an AES decryption flow
        aesdeclast,
        /// Perform one round of an AES encryption flow
        /// Perform ten rounds of AES encryption flow with key locker using 128-bit key
        /// Perform ten rounds of AES encryption flow with key locker using 256-bit key
        /// Perform ten rounds of AES encryption flow with key locker on 8 blocks using 128-bit key
        /// Perform ten rounds of AES encryption flow with key locker on 8 blocks using 256-bit key
        aesenc,
        /// Perform last round of an AES encryption flow
        aesenclast,
        /// Perform the AES InvMixColumn transformation
        aesimc,
        /// AES round key generation assist
        aeskeygenassist,

        // SHA
        /// Perform four rounds of SHA1 operation
        sha1rnds,
        /// Calculate SHA1 state variable E after four rounds
        sha1nexte,
        /// Perform an intermediate calculation for the next four SHA1 message dwords
        /// Perform a final calculation for the next four SHA1 message dwords
        sha1msg,
        /// Perform an intermediate calculation for the next four SHA256 message dwords
        /// Perform a final calculation for the next four SHA256 message dwords
        sha256msg,
        /// Perform two rounds of SHA256 operation
        sha256rnds,

        // AVX
        /// Load with broadcast floating-point data
        /// Load integer and broadcast
        broadcast,
        /// Conditional SIMD packed loads and stores
        /// Condition SIMD integer packed loads and stores
        maskmov,
        /// Permute floating-point values
        /// Permute integer values
        perm2,
        /// Permute in-lane pairs of double-precision floating-point values
        /// Permute in-lane quadruples of single-precision floating-point values
        permil,

        // BMI
        /// Bit field extract
        bextr,
        /// Extract lowest set isolated bit
        /// Get mask up to lowest set bit
        /// Reset lowest set bit
        bls,
        /// Count the number of trailing zero bits
        tzcnt,

        // BMI2
        /// Zero high bits starting with specified bit position
        bzhi,
        /// Parallel bits deposit
        pdep,
        /// Parallel bits extract
        pext,

        // F16C
        /// Convert 16-bit floating-point values to single-precision floating-point values
        cvtph2,
        /// Convert single-precision floating-point values to 16-bit floating-point values
        cvtps2ph,

        // FMA
        /// Fused multiply-add of packed single-precision floating-point values
        /// Fused multiply-add of scalar single-precision floating-point values
        /// Fused multiply-add of packed double-precision floating-point values
        /// Fused multiply-add of scalar double-precision floating-point values
        fmadd132,
        /// Fused multiply-add of packed single-precision floating-point values
        /// Fused multiply-add of scalar single-precision floating-point values
        /// Fused multiply-add of packed double-precision floating-point values
        /// Fused multiply-add of scalar double-precision floating-point values
        fmadd213,
        /// Fused multiply-add of packed single-precision floating-point values
        /// Fused multiply-add of scalar single-precision floating-point values
        /// Fused multiply-add of packed double-precision floating-point values
        /// Fused multiply-add of scalar double-precision floating-point values
        fmadd231,

        // AVX2
        /// Permute packed doubleword elements
        /// Permute packed qword elements
        /// Permute double-precision floating-point elements
        /// Permute single-precision floating-point elements
        perm,
        /// Variable bit shift left logical
        sllv,
        /// Variable bit shift right arithmetic
        srav,
        /// Variable bit shift right logical
        srlv,

        // ADX
        /// Unsigned integer addition of two operands with overflow flag
        ado,

        // AESKLE
        /// Encode 128-bit key with key locker
        /// Encode 256-bit key with key locker
        encodekey,
        /// Load internal wrapping key with key locker
        loadiwkey,

        /// A pseudo instruction that requires special lowering.
        /// This should be the only tag in this enum that doesn't
        /// directly correspond to one or more instruction mnemonics.
        pseudo,
    };

    pub const FixedTag = struct { Fixes, Tag };

    pub const Ops = enum(u8) {
        /// No data associated with this instruction (only mnemonic is used).
        none,
        /// Single register operand.
        /// Uses `r` payload.
        r,
        /// Register, register operands.
        /// Uses `rr` payload.
        rr,
        /// Register, register, register operands.
        /// Uses `rrr` payload.
        rrr,
        /// Register, register, register, register operands.
        /// Uses `rrrr` payload.
        rrrr,
        /// Register, register, register, immediate (byte) operands.
        /// Uses `rrri` payload.
        rrri,
        /// Register, register, immediate (sign-extended) operands.
        /// Uses `rri`  payload.
        rri_s,
        /// Register, register, immediate (unsigned) operands.
        /// Uses `rri`  payload.
        rri_u,
        /// Register, immediate (sign-extended) operands.
        /// Uses `ri` payload.
        ri_s,
        /// Register, immediate (unsigned) operands.
        /// Uses `ri` payload.
        ri_u,
        /// Register, 64-bit unsigned immediate operands.
        /// Uses `ri` payload with `i` index of extra data of type `Imm64`.
        ri_64,
        /// Immediate (sign-extended) operand.
        /// Uses `i` payload.
        i_s,
        /// Immediate (unsigned) operand.
        /// Uses `i` payload.
        i_u,
        /// Immediate (word), immediate (byte) operands.
        /// Uses `ii` payload.
        ii,
        /// Immediate (byte), register operands.
        /// Uses `ri` payload.
        ir,
        /// Register, memory operands.
        /// Uses `rx` payload with extra data of type `Memory`.
        rm,
        /// Register, memory, register operands.
        /// Uses `rrx` payload with extra data of type `Memory`.
        rmr,
        /// Register, memory, immediate (word) operands.
        /// Uses `rix` payload with extra data of type `Memory`.
        rmi,
        /// Register, memory, immediate (signed) operands.
        /// Uses `rx` payload with extra data of type `Imm32` followed by `Memory`.
        rmi_s,
        /// Register, memory, immediate (unsigned) operands.
        /// Uses `rx` payload with extra data of type `Imm32` followed by `Memory`.
        rmi_u,
        /// Register, register, memory.
        /// Uses `rrix` payload with extra data of type `Memory`.
        rrm,
        /// Register, register, memory, register.
        /// Uses `rrrx` payload with extra data of type `Memory`.
        rrmr,
        /// Register, register, memory, immediate (byte) operands.
        /// Uses `rrix` payload with extra data of type `Memory`.
        rrmi,
        /// Single memory operand.
        /// Uses `x` payload with extra data of type `Memory`.
        m,
        /// Memory, immediate (sign-extend) operands.
        /// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
        mi_s,
        /// Memory, immediate (unsigned) operands.
        /// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
        mi_u,
        /// Memory, register operands.
        /// Uses `rx` payload with extra data of type `Memory`.
        mr,
        /// Memory, register, register operands.
        /// Uses `rrx` payload with extra data of type `Memory`.
        mrr,
        /// Memory, register, immediate (word) operands.
        /// Uses `rix` payload with extra data of type `Memory`.
        mri,
        /// References another Mir instruction directly.
        /// Uses `inst` payload.
        inst,
        /// References a nav.
        /// Uses `nav` payload.
        nav,
        /// References an uav.
        /// Uses `uav` payload.
        uav,
        /// References a lazy symbol.
        /// Uses `lazy_sym` payload.
        lazy_sym,
        /// References an external symbol.
        /// Uses `extern_func` payload.
        extern_func,

        // Pseudo instructions:

        /// Conditional move if zero flag set and parity flag not set
        /// Clobbers the source operand!
        /// Uses `rr` payload.
        pseudo_cmov_z_and_np_rr,
        /// Conditional move if zero flag not set or parity flag set
        /// Uses `rr` payload.
        pseudo_cmov_nz_or_p_rr,
        /// Conditional move if zero flag not set or parity flag set
        /// Uses `rx` payload.
        pseudo_cmov_nz_or_p_rm,
        /// Set byte if zero flag set and parity flag not set
        /// Requires a scratch register!
        /// Uses `rr` payload.
        pseudo_set_z_and_np_r,
        /// Set byte if zero flag set and parity flag not set
        /// Requires a scratch register!
        /// Uses `rx` payload.
        pseudo_set_z_and_np_m,
        /// Set byte if zero flag not set or parity flag set
        /// Requires a scratch register!
        /// Uses `rr` payload.
        pseudo_set_nz_or_p_r,
        /// Set byte if zero flag not set or parity flag set
        /// Requires a scratch register!
        /// Uses `rx` payload.
        pseudo_set_nz_or_p_m,
        /// Jump if zero flag set and parity flag not set
        /// Uses `inst` payload.
        pseudo_j_z_and_np_inst,
        /// Jump if zero flag not set or parity flag set
        /// Uses `inst` payload.
        pseudo_j_nz_or_p_inst,

        /// Probe alignment
        /// Uses `ri` payload.
        pseudo_probe_align_ri_s,
        /// Probe adjust unrolled
        /// Uses `ri` payload.
        pseudo_probe_adjust_unrolled_ri_s,
        /// Probe adjust setup
        /// Uses `rri` payload.
        pseudo_probe_adjust_setup_rri_s,
        /// Probe adjust loop
        /// Uses `rr` payload.
        pseudo_probe_adjust_loop_rr,

        /// Push registers
        /// Uses `reg_list` payload.
        pseudo_push_reg_list,
        /// Pop registers
        /// Uses `reg_list` payload.
        pseudo_pop_reg_list,

        /// Define cfa rule as offset from register.
        /// Uses `ri` payload.
        pseudo_cfi_def_cfa_ri_s,
        /// Modify cfa rule register.
        /// Uses `r` payload.
        pseudo_cfi_def_cfa_register_r,
        /// Modify cfa rule offset.
        /// Uses `i` payload.
        pseudo_cfi_def_cfa_offset_i_s,
        /// Offset cfa rule offset.
        /// Uses `i` payload.
        pseudo_cfi_adjust_cfa_offset_i_s,
        /// Define register rule as stored at offset from cfa.
        /// Uses `ri` payload.
        pseudo_cfi_offset_ri_s,
        /// Define register rule as offset from cfa.
        /// Uses `ri` payload.
        pseudo_cfi_val_offset_ri_s,
        /// Define register rule as stored at offset from cfa rule register.
        /// Uses `ri` payload.
        pseudo_cfi_rel_offset_ri_s,
        /// Define register rule as register.
        /// Uses `rr` payload.
        pseudo_cfi_register_rr,
        /// Define register rule from initial.
        /// Uses `r` payload.
        pseudo_cfi_restore_r,
        /// Define register rule as undefined.
        /// Uses `r` payload.
        pseudo_cfi_undefined_r,
        /// Define register rule as itself.
        /// Uses `r` payload.
        pseudo_cfi_same_value_r,
        /// Push cfi state.
        pseudo_cfi_remember_state_none,
        /// Pop cfi state.
        pseudo_cfi_restore_state_none,
        /// Raw cfi bytes.
        /// Uses `bytes` payload.
        pseudo_cfi_escape_bytes,

        /// End of prologue
        /// Uses `none` payload.
        pseudo_dbg_prologue_end_none,
        /// Update debug line with is_stmt register set
        /// Uses `line_column` payload.
        pseudo_dbg_line_stmt_line_column,
        /// Update debug line with is_stmt register clear
        /// Uses `line_column` payload.
        pseudo_dbg_line_line_column,
        /// Start of epilogue
        /// Uses `none` payload.
        pseudo_dbg_epilogue_begin_none,
        /// Start of lexical block
        /// Uses `none` payload.
        pseudo_dbg_enter_block_none,
        /// End of lexical block
        /// Uses `none` payload.
        pseudo_dbg_leave_block_none,
        /// Start of inline function
        /// Uses `ip_index` payload.
        pseudo_dbg_enter_inline_func,
        /// End of inline function
        /// Uses `ip_index` payload.
        pseudo_dbg_leave_inline_func,
        /// Local argument.
        /// Uses `none` payload.
        pseudo_dbg_arg_none,
        /// Local argument.
        /// Uses `i` payload.
        pseudo_dbg_arg_i_s,
        /// Local argument.
        /// Uses `i` payload.
        pseudo_dbg_arg_i_u,
        /// Local argument.
        /// Uses `i64` payload.
        pseudo_dbg_arg_i_64,
        /// Local argument.
        /// Uses `ro` payload.
        pseudo_dbg_arg_ro,
        /// Local argument.
        /// Uses `fa` payload.
        pseudo_dbg_arg_fa,
        /// Local argument.
        /// Uses `x` payload with extra data of type `Memory`.
        pseudo_dbg_arg_m,
        /// Local argument.
        /// Uses `ip_index` payload.
        pseudo_dbg_arg_val,
        /// Remaining arguments are varargs.
        pseudo_dbg_var_args_none,
        /// Local variable.
        /// Uses `none` payload.
        pseudo_dbg_var_none,
        /// Local variable.
        /// Uses `i` payload.
        pseudo_dbg_var_i_s,
        /// Local variable.
        /// Uses `i` payload.
        pseudo_dbg_var_i_u,
        /// Local variable.
        /// Uses `i64` payload.
        pseudo_dbg_var_i_64,
        /// Local variable.
        /// Uses `ro` payload.
        pseudo_dbg_var_ro,
        /// Local variable.
        /// Uses `fa` payload.
        pseudo_dbg_var_fa,
        /// Local variable.
        /// Uses `x` payload with extra data of type `Memory`.
        pseudo_dbg_var_m,
        /// Local variable.
        /// Uses `ip_index` payload.
        pseudo_dbg_var_val,

        /// Tombstone
        /// Emitter should skip this instruction.
        pseudo_dead_none,
    };

    pub const Data = union {
        none: struct {
            fixes: Fixes = ._,
        },
        /// References another Mir instruction.
        inst: struct {
            fixes: Fixes = ._,
            inst: Index,
        },
        /// A 32-bit immediate value.
        i64: u64,
        i: struct {
            fixes: Fixes = ._,
            i: u32,
        },
        ii: struct {
            fixes: Fixes = ._,
            i1: u16,
            i2: u8,
        },
        r: struct {
            fixes: Fixes = ._,
            r1: Register,
        },
        rr: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
        },
        rrr: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            r3: Register,
        },
        rrrr: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            r3: Register,
            r4: Register,
        },
        rrri: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            r3: Register,
            i: u8,
        },
        rri: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            i: u32,
        },
        /// Register, immediate.
        ri: struct {
            fixes: Fixes = ._,
            r1: Register,
            i: u32,
        },
        /// Register, followed by custom payload found in extra.
        rx: struct {
            fixes: Fixes = ._,
            r1: Register,
            payload: u32,
        },
        /// Register, register, followed by Custom payload found in extra.
        rrx: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            payload: u32,
        },
        /// Register, register, register, followed by Custom payload found in extra.
        rrrx: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            r3: Register,
            payload: u32,
        },
        /// Register, byte immediate, followed by Custom payload found in extra.
        rix: struct {
            fixes: Fixes = ._,
            r1: Register,
            i: u16,
            payload: u32,
        },
        /// Register, register, byte immediate, followed by Custom payload found in extra.
        rrix: struct {
            fixes: Fixes = ._,
            r1: Register,
            r2: Register,
            i: u8,
            payload: u32,
        },
        /// Custom payload found in extra.
        x: struct {
            fixes: Fixes = ._,
            payload: u32,
        },
        bytes: struct {
            payload: u32,
            len: u32,

            pub fn get(bytes: @This(), mir: Mir) []const u8 {
                return std.mem.sliceAsBytes(mir.extra[bytes.payload..])[0..bytes.len];
            }
        },
        fa: bits.FrameAddr,
        ro: bits.RegisterOffset,
        nav: bits.NavOffset,
        uav: InternPool.Key.Ptr.BaseAddr.Uav,
        lazy_sym: link.File.LazySymbol,
        extern_func: Mir.NullTerminatedString,
        /// Debug line and column position
        line_column: struct {
            line: u32,
            column: u32,
        },
        ip_index: InternPool.Index,
        /// Register list
        reg_list: RegisterList,
    };

    comptime {
        if (!std.debug.runtime_safety) {
            // Make sure we don't accidentally make instructions bigger than expected.
            // Note that in safety builds, Zig is allowed to insert a secret field for safety checks.
            assert(@sizeOf(Data) == 8);
        }
        const Mnemonic = @import("Encoding.zig").Mnemonic;
        if (@typeInfo(Mnemonic).@"enum".fields.len != 977 or
            @typeInfo(Fixes).@"enum".fields.len != 231 or
            @typeInfo(Tag).@"enum".fields.len != 251)
        {
            const cond_src = (struct {
                fn src() std.builtin.SourceLocation {
                    return @src();
                }
            }).src();
            @setEvalBranchQuota(1_750_000);
            for (@typeInfo(Mnemonic).@"enum".fields) |mnemonic| {
                if (mnemonic.name[0] == '.') continue;
                for (@typeInfo(Fixes).@"enum".fields) |fixes| {
                    const pattern = fixes.name[if (std.mem.indexOfScalar(u8, fixes.name, ' ')) |index| index + " ".len else 0..];
                    const wildcard_index = std.mem.indexOfScalar(u8, pattern, '_').?;
                    const mnem_prefix = pattern[0..wildcard_index];
                    const mnem_suffix = pattern[wildcard_index + "_".len ..];
                    if (!std.mem.startsWith(u8, mnemonic.name, mnem_prefix)) continue;
                    if (!std.mem.endsWith(u8, mnemonic.name, mnem_suffix)) continue;
                    if (@hasField(
                        Tag,
                        mnemonic.name[mnem_prefix.len .. mnemonic.name.len - mnem_suffix.len],
                    )) break;
                } else @compileError("'" ++ mnemonic.name ++ "' is not encodable in Mir");
            }
            @compileError(std.fmt.comptimePrint(
                \\All mnemonics are encodable in Mir! You may now change the condition at {s}:{d} to:
                \\if (@typeInfo(Mnemonic).@"enum".fields.len != {d} or
                \\    @typeInfo(Fixes).@"enum".fields.len != {d} or
                \\    @typeInfo(Tag).@"enum".fields.len != {d})
            , .{
                cond_src.file,
                cond_src.line - 6,
                @typeInfo(Mnemonic).@"enum".fields.len,
                @typeInfo(Fixes).@"enum".fields.len,
                @typeInfo(Tag).@"enum".fields.len,
            }));
        }
    }
};

/// Used in conjunction with payload to transfer a list of used registers in a compact manner.
pub const RegisterList = struct {
    bitset: BitSet,

    const BitSet = std.bit_set.IntegerBitSet(32);
    const Self = @This();

    pub const empty: RegisterList = .{ .bitset = .initEmpty() };

    fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt {
        for (registers, 0..) |cpreg, i| {
            if (reg.id() == cpreg.id()) return @intCast(i);
        }
        unreachable; // register not in input register list!
    }

    pub fn push(self: *Self, registers: []const Register, reg: Register) void {
        const index = getIndexForReg(registers, reg);
        self.bitset.set(index);
    }

    pub fn isSet(self: Self, registers: []const Register, reg: Register) bool {
        const index = getIndexForReg(registers, reg);
        return self.bitset.isSet(index);
    }

    pub fn iterator(self: Self, comptime options: std.bit_set.IteratorOptions) BitSet.Iterator(options) {
        return self.bitset.iterator(options);
    }

    pub fn count(self: Self) i32 {
        return @intCast(self.bitset.count());
    }

    pub fn size(self: Self, target: *const std.Target) i32 {
        return @intCast(self.bitset.count() * @as(u4, switch (target.cpu.arch) {
            else => unreachable,
            .x86 => 4,
            .x86_64 => 8,
        }));
    }
};

pub const NullTerminatedString = enum(u32) {
    none = std.math.maxInt(u32),
    _,

    pub fn toSlice(nts: NullTerminatedString, mir: *const Mir) ?[:0]const u8 {
        if (nts == .none) return null;
        const string_bytes = mir.string_bytes[@intFromEnum(nts)..];
        return string_bytes[0..std.mem.indexOfScalar(u8, string_bytes, 0).? :0];
    }
};

pub const Local = struct {
    name: NullTerminatedString,
    type: InternPool.Index,
};

pub const Imm32 = struct {
    imm: u32,
};

pub const Imm64 = struct {
    msb: u32,
    lsb: u32,

    pub fn encode(v: u64) Imm64 {
        return .{
            .msb = @truncate(v >> 32),
            .lsb = @truncate(v),
        };
    }

    pub fn decode(imm: Imm64) u64 {
        var res: u64 = 0;
        res |= @as(u64, @intCast(imm.msb)) << 32;
        res |= @as(u64, @intCast(imm.lsb));
        return res;
    }
};

pub const Memory = struct {
    info: Info,
    base: u32,
    off: u32,
    extra: u32,

    pub const Info = packed struct(u32) {
        base: @typeInfo(bits.Memory.Base).@"union".tag_type.?,
        mod: @typeInfo(bits.Memory.Mod).@"union".tag_type.?,
        size: bits.Memory.Size,
        index: Register,
        scale: bits.Memory.Scale,
        _: u13 = undefined,
    };

    pub fn encode(mem: bits.Memory) Memory {
        return .{
            .info = .{
                .base = mem.base,
                .mod = mem.mod,
                .size = switch (mem.mod) {
                    .rm => |rm| rm.size,
                    .off => undefined,
                },
                .index = switch (mem.mod) {
                    .rm => |rm| rm.index,
                    .off => undefined,
                },
                .scale = switch (mem.mod) {
                    .rm => |rm| rm.scale,
                    .off => undefined,
                },
            },
            .base = switch (mem.base) {
                .none, .table => undefined,
                .reg => |reg| @intFromEnum(reg),
                .frame => |frame_index| @intFromEnum(frame_index),
                .rip_inst => |inst_index| inst_index,
                .nav => |nav| @intFromEnum(nav),
                .uav => |uav| @intFromEnum(uav.val),
                .lazy_sym => |lazy_sym| @intFromEnum(lazy_sym.ty),
                .extern_func => |extern_func| @intFromEnum(extern_func),
            },
            .off = switch (mem.mod) {
                .rm => |rm| @bitCast(rm.disp),
                .off => |off| @truncate(off),
            },
            .extra = switch (mem.mod) {
                .rm => switch (mem.base) {
                    else => undefined,
                    .uav => |uav| @intFromEnum(uav.orig_ty),
                    .lazy_sym => |lazy_sym| @intFromEnum(lazy_sym.kind),
                },
                .off => switch (mem.base) {
                    .reg => @intCast(mem.mod.off >> 32),
                    else => unreachable,
                },
            },
        };
    }

    pub fn decode(mem: Memory) encoder.Instruction.Memory {
        switch (mem.info.mod) {
            .rm => {
                if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) {
                    assert(mem.info.index == .none and mem.info.scale == .@"1");
                    return encoder.Instruction.Memory.initRip(mem.info.size, @bitCast(mem.off));
                }
                return encoder.Instruction.Memory.initSib(mem.info.size, .{
                    .disp = @bitCast(mem.off),
                    .base = switch (mem.info.base) {
                        .none => .none,
                        .reg => .{ .reg = @enumFromInt(mem.base) },
                        .frame => .{ .frame = @enumFromInt(mem.base) },
                        .table => .table,
                        .rip_inst => .{ .rip_inst = mem.base },
                        .nav => .{ .nav = @enumFromInt(mem.base) },
                        .uav => .{ .uav = .{ .val = @enumFromInt(mem.base), .orig_ty = @enumFromInt(mem.extra) } },
                        .lazy_sym => .{ .lazy_sym = .{ .kind = @enumFromInt(mem.extra), .ty = @enumFromInt(mem.base) } },
                        .extern_func => .{ .extern_func = @enumFromInt(mem.base) },
                    },
                    .scale_index = switch (mem.info.index) {
                        .none => null,
                        else => |index| .{ .scale = switch (mem.info.scale) {
                            inline else => |scale| comptime std.fmt.parseInt(
                                u4,
                                @tagName(scale),
                                10,
                            ) catch unreachable,
                        }, .index = index },
                    },
                });
            },
            .off => {
                assert(mem.info.base == .reg);
                return encoder.Instruction.Memory.initMoffs(
                    @enumFromInt(mem.base),
                    @as(u64, mem.extra) << 32 | mem.off,
                );
            },
        }
    }
};

pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
    mir.instructions.deinit(gpa);
    gpa.free(mir.extra);
    gpa.free(mir.string_bytes);
    gpa.free(mir.locals);
    gpa.free(mir.table);
    gpa.free(mir.memoized_encodings);
    mir.frame_locs.deinit(gpa);
    mir.* = undefined;
}

pub fn emit(
    mir: Mir,
    lf: *link.File,
    pt: Zcu.PerThread,
    src_loc: Zcu.LazySrcLoc,
    func_index: InternPool.Index,
    code: *std.ArrayListUnmanaged(u8),
    debug_output: link.File.DebugInfoOutput,
) codegen.CodeGenError!void {
    const zcu = pt.zcu;
    const comp = zcu.comp;
    const gpa = comp.gpa;
    const func = zcu.funcInfo(func_index);
    const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?;
    const nav = func.owner_nav;
    const mod = zcu.navFileScope(nav).mod.?;
    var e: Emit = .{
        .lower = .{
            .target = &mod.resolved_target.result,
            .allocator = gpa,
            .mir = mir,
            .cc = fn_info.cc,
            .src_loc = src_loc,
        },
        .bin_file = lf,
        .pt = pt,
        .pic = mod.pic,
        .atom_index = sym: {
            if (lf.cast(.elf)) |ef| break :sym try ef.zigObjectPtr().?.getOrCreateMetadataForNav(zcu, nav);
            if (lf.cast(.macho)) |mf| break :sym try mf.getZigObject().?.getOrCreateMetadataForNav(mf, nav);
            if (lf.cast(.coff)) |cf| {
                const atom = try cf.getOrCreateAtomForNav(nav);
                break :sym cf.getAtom(atom).getSymbolIndex().?;
            }
            unreachable;
        },
        .debug_output = debug_output,
        .code = code,

        .prev_di_loc = .{
            .line = func.lbrace_line,
            .column = func.lbrace_column,
            .is_stmt = switch (debug_output) {
                .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt,
                .none => undefined,
            },
        },
        .prev_di_pc = 0,

        .code_offset_mapping = .empty,
        .relocs = .empty,
        .table_relocs = .empty,
    };
    defer e.deinit();
    e.emitMir() catch |err| switch (err) {
        error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?),
        error.InvalidInstruction, error.CannotEncode => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}),
        else => return zcu.codegenFail(nav, "emit MIR failed: {s}", .{@errorName(err)}),
    };
}

pub fn emitLazy(
    mir: Mir,
    lf: *link.File,
    pt: Zcu.PerThread,
    src_loc: Zcu.LazySrcLoc,
    lazy_sym: link.File.LazySymbol,
    code: *std.ArrayListUnmanaged(u8),
    debug_output: link.File.DebugInfoOutput,
) codegen.CodeGenError!void {
    const zcu = pt.zcu;
    const comp = zcu.comp;
    const gpa = comp.gpa;
    const mod = comp.root_mod;
    var e: Emit = .{
        .lower = .{
            .target = &mod.resolved_target.result,
            .allocator = gpa,
            .mir = mir,
            .cc = .auto,
            .src_loc = src_loc,
        },
        .bin_file = lf,
        .pt = pt,
        .pic = mod.pic,
        .atom_index = sym: {
            if (lf.cast(.elf)) |ef| break :sym ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_sym) catch |err|
                return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
            if (lf.cast(.macho)) |mf| break :sym mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_sym) catch |err|
                return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
            if (lf.cast(.coff)) |cf| {
                const atom = cf.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
                    return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
                break :sym cf.getAtom(atom).getSymbolIndex().?;
            }
            unreachable;
        },
        .debug_output = debug_output,
        .code = code,

        .prev_di_loc = undefined,
        .prev_di_pc = undefined,

        .code_offset_mapping = .empty,
        .relocs = .empty,
        .table_relocs = .empty,
    };
    defer e.deinit();
    e.emitMir() catch |err| switch (err) {
        error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, e.lower.err_msg.?),
        error.InvalidInstruction, error.CannotEncode => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}),
        else => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s}", .{@errorName(err)}),
    };
}

pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } {
    const fields = std.meta.fields(T);
    var i: u32 = index;
    var result: T = undefined;
    inline for (fields) |field| {
        @field(result, field.name) = switch (field.type) {
            u32 => mir.extra[i],
            i32, Memory.Info => @bitCast(mir.extra[i]),
            bits.FrameIndex => @enumFromInt(mir.extra[i]),
            else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
        };
        i += 1;
    }
    return .{
        .data = result,
        .end = i,
    };
}

pub const FrameLoc = struct {
    base: Register,
    disp: i32,
};

pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffset {
    const frame_loc = mir.frame_locs.get(@intFromEnum(frame_addr.index));
    return .{ .reg = frame_loc.base, .off = frame_loc.disp + frame_addr.off };
}

pub fn resolveMemoryExtra(mir: Mir, payload: u32) Memory {
    const mem = mir.extraData(Mir.Memory, payload).data;
    return switch (mem.info.base) {
        .none, .reg, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => mem,
        .frame => if (mir.frame_locs.len > 0) .{
            .info = .{
                .base = .reg,
                .mod = mem.info.mod,
                .size = mem.info.size,
                .index = mem.info.index,
                .scale = mem.info.scale,
            },
            .base = @intFromEnum(mir.frame_locs.items(.base)[mem.base]),
            .off = @bitCast(mir.frame_locs.items(.disp)[mem.base] + @as(i32, @bitCast(mem.off))),
            .extra = mem.extra,
        } else mem,
    };
}

const assert = std.debug.assert;
const bits = @import("bits.zig");
const builtin = @import("builtin");
const encoder = @import("encoder.zig");
const std = @import("std");

const InternPool = @import("../../InternPool.zig");
const Mir = @This();
const Register = bits.Register;
const Emit = @import("Emit.zig");
const codegen = @import("../../codegen.zig");
const link = @import("../../link.zig");
const Zcu = @import("../../Zcu.zig");
