// AArch64 Instruction Encoding
// This module contains all AArch64 instruction encoders using an Instruction enum

///|
fn reg_num(reg : @abi.Reg) -> Int {
  match reg {
    Physical(preg) => preg.index
    Virtual(_) => abort("Virtual register in code emission")
  }
}

// Writable register encoding helper

///|
fn wreg_num(wreg : @abi.Writable) -> Int {
  reg_num(wreg.reg)
}

///|
/// AArch64 Instruction enum - covers all instruction types
enum Instruction {
  // Arithmetic - Register
  AddReg(Int, Int, Int)
  AddShifted(Int, Int, Int, @instr.ShiftType, Int)
  AddShifted32(Int, Int, Int, @instr.ShiftType, Int)
  // Add with extended register operand: ADD Xd, Xn, Wm, UXTW #shift
  AddExtUxtw(Int, Int, Int, Int) // rd, rn, rm, shift (0-4)
  AddExtSxtw(Int, Int, Int, Int) // rd, rn, rm, shift (0-4)
  SubExtUxtw(Int, Int, Int, Int) // rd, rn, rm, shift (0-4)
  SubExtSxtw(Int, Int, Int, Int) // rd, rn, rm, shift (0-4)
  AddImm(Int, Int, Int)
  AddImmShifted12(Int, Int, Int) // ADD with immediate shifted left by 12
  SubReg(Int, Int, Int)
  SubShifted(Int, Int, Int, @instr.ShiftType, Int)
  SubShifted32(Int, Int, Int, @instr.ShiftType, Int)
  SubImm(Int, Int, Int)
  SubImmShifted12(Int, Int, Int) // SUB with immediate shifted left by 12
  Mul(Int, Int, Int)
  Madd(Int, Int, Int, Int)
  Msub(Int, Int, Int, Int)
  Mneg(Int, Int, Int)
  Madd32(Int, Int, Int, Int)
  Msub32(Int, Int, Int, Int)
  Mneg32(Int, Int, Int)
  Umulh(Int, Int, Int) // Unsigned multiply high (64-bit)
  Smulh(Int, Int, Int) // Signed multiply high (64-bit)
  Umull(Int, Int, Int) // Unsigned 32x32->64 multiply
  Smull(Int, Int, Int) // Signed 32x32->64 multiply
  Sdiv(Int, Int, Int)
  Udiv(Int, Int, Int)
  Sdiv32(Int, Int, Int) // 32-bit signed divide
  Udiv32(Int, Int, Int) // 32-bit unsigned divide
  SubReg32(Int, Int, Int) // 32-bit subtract
  AddReg32(Int, Int, Int) // 32-bit add
  AddImm32(Int, Int, Int) // 32-bit add immediate
  AddImm32Shifted12(Int, Int, Int) // 32-bit add immediate shifted left by 12
  SubImm32(Int, Int, Int) // 32-bit sub immediate
  SubImm32Shifted12(Int, Int, Int) // 32-bit sub immediate shifted left by 12
  Mul32(Int, Int, Int) // 32-bit multiply
  // Bitwise - Register
  AndReg(Int, Int, Int)
  AndReg32(Int, Int, Int)
  BicReg(Int, Int, Int)
  BicReg32(Int, Int, Int)
  // Bitwise - Immediate (logical/bitmask immediate). imm_bits is N:immr:imms (13 bits).
  AndImm(Int, Int, Int)
  AndImm32(Int, Int, Int)
  AndShifted(Int, Int, Int, @instr.ShiftType, Int)
  AndShifted32(Int, Int, Int, @instr.ShiftType, Int)
  OrrReg(Int, Int, Int)
  OrrReg32(Int, Int, Int)
  OrnReg(Int, Int, Int)
  OrnReg32(Int, Int, Int)
  OrrImm(Int, Int, Int)
  OrrImm32(Int, Int, Int)
  OrrShifted(Int, Int, Int, @instr.ShiftType, Int)
  OrrShifted32(Int, Int, Int, @instr.ShiftType, Int)
  EorReg(Int, Int, Int)
  EorReg32(Int, Int, Int)
  EonReg(Int, Int, Int)
  EonReg32(Int, Int, Int)
  EorImm(Int, Int, Int)
  EorImm32(Int, Int, Int)
  EorShifted(Int, Int, Int, @instr.ShiftType, Int)
  EorShifted32(Int, Int, Int, @instr.ShiftType, Int)
  Mvn(Int, Int)
  Mvn32(Int, Int) // 32-bit bitwise NOT
  // Shift/Rotate - Register (64-bit)
  LslReg(Int, Int, Int)
  LsrReg(Int, Int, Int)
  AsrReg(Int, Int, Int)
  RorReg(Int, Int, Int)
  // Shift/Rotate - Register (32-bit)
  LslReg32(Int, Int, Int)
  LsrReg32(Int, Int, Int)
  AsrReg32(Int, Int, Int)
  RorReg32(Int, Int, Int)
  // Shift - Immediate (64-bit): LSR Xd, Xn, #shift
  LsrImm(Int, Int, Int)
  // Shift - Immediate (64-bit): LSL Xd, Xn, #shift
  LslImm(Int, Int, Int)
  // Shift - Immediate (64-bit): ASR Xd, Xn, #shift
  AsrImm(Int, Int, Int)
  // Rotate - Immediate (64-bit): ROR Xd, Xn, #shift (alias of EXTR with rm=rn)
  RorImm(Int, Int, Int)
  // Extract - Immediate (64-bit): EXTR Xd, Xn, Xm, #shift
  ExtrImm(Int, Int, Int, Int)
  // Shift - Immediate (32-bit): LSR Wd, Wn, #shift
  LsrImm32(Int, Int, Int)
  // Shift - Immediate (32-bit): LSL Wd, Wn, #shift
  LslImm32(Int, Int, Int)
  // Shift - Immediate (32-bit): ASR Wd, Wn, #shift
  AsrImm32(Int, Int, Int)
  // Rotate - Immediate (32-bit): ROR Wd, Wn, #shift (alias of EXTR with rm=rn)
  RorImm32(Int, Int, Int)
  // Extract - Immediate (32-bit): EXTR Wd, Wn, Wm, #shift
  ExtrImm32(Int, Int, Int, Int)
  // Unsigned Bitfield Extract: UBFX Xd, Xn, #0, #width
  // Extracts bits [0, width-1] from Xn to Xd (zero-extended)
  UbfxWidth(Int, Int, Int)
  // Bit manipulation (64-bit)
  Clz(Int, Int)
  Rbit(Int, Int)
  // Bit manipulation (32-bit)
  Clz32(Int, Int)
  Rbit32(Int, Int)
  // Move
  MovReg(Int, Int)
  MovReg32(Int, Int)
  Movz(Int, Int, Int)
  Movk(Int, Int, Int)
  Movn(Int, Int, Int)
  LoadImm64(Int, Int64)
  // Load/Store - GPR
  LdrImm(Int, Int, Int)
  LdrImmSigned(Int, Int, Int)
  LdrRegScaled(Int, Int, Int, Int)
  // Register-offset load/store with configurable extension/scaling.
  // option: 0b011 = LSL (X offset), 0b010 = UXTW, 0b110 = SXTW.
  // size_bits: 0=8b, 1=16b, 2=32b, 3=64b. shift must be 0 or the natural scale for size_bits.
  LdrRegOffset(Int, Int, Int, Int, Int, Int) // rt, rn, rm, option, size_bits, shift
  StrRegOffset(Int, Int, Int, Int, Int, Int) // rt, rn, rm, option, size_bits, shift
  // Register-offset load/store using X register offset (UXTX), optional scaling.
  // shift is either 0 (no scale) or the natural scale for the access size.
  LdrWRegScaled(Int, Int, Int, Int)
  StrWRegScaled(Int, Int, Int, Int)
  LdrbReg(Int, Int, Int)
  StrbReg(Int, Int, Int)
  LdrhReg(Int, Int, Int)
  StrhReg(Int, Int, Int)
  StrImm(Int, Int, Int)
  StrRegScaled(Int, Int, Int, Int)
  LdrbImm(Int, Int, Int)
  LdrhImm(Int, Int, Int)
  LdrWImm(Int, Int, Int)
  StrbImm(Int, Int, Int)
  StrhImm(Int, Int, Int)
  StrWImm(Int, Int, Int)
  // Load/Store - Sign extend
  LdrsbXImm(Int, Int, Int)
  LdrsbWImm(Int, Int, Int)
  LdrshXImm(Int, Int, Int)
  LdrshWImm(Int, Int, Int)
  LdrswImm(Int, Int, Int)
  // Load/Store Pair
  StpPre(Int, Int, Int, Int)
  LdpPost(Int, Int, Int, Int)
  StpOffset(Int, Int, Int, Int) // rt1, rt2, rn, offset
  LdpOffset(Int, Int, Int, Int) // rt1, rt2, rn, offset
  StpDOffset(Int, Int, Int, Int) // rt1(d), rt2(d), rn, offset (FPR)
  LdpDOffset(Int, Int, Int, Int) // rt1(d), rt2(d), rn, offset (FPR)
  // Pre-indexed stores (Standard prologue)
  StrPre(Int, Int, Int) // rt, rn, simm9 - STR Xt, [Xn, #simm9]!
  StpDPre(Int, Int, Int, Int) // rt1, rt2, rn, imm7 - STP Dt1, Dt2, [Xn, #imm7]!
  StrDPre(Int, Int, Int) // rt, rn, simm9 - STR Dt, [Xn, #simm9]!
  // Post-indexed loads (Standard epilogue)
  LdrPost(Int, Int, Int) // rt, rn, simm9 - LDR Xt, [Xn], #simm9
  LdpDPost(Int, Int, Int, Int) // rt1, rt2, rn, imm7 - LDP Dt1, Dt2, [Xn], #imm7
  LdrDPost(Int, Int, Int) // rt, rn, simm9 - LDR Dt, [Xn], #simm9
  // Sign/Zero extension
  SxtbX(Int, Int)
  SxthX(Int, Int)
  Sxtw(Int, Int)
  SxtbW(Int, Int)
  SxthW(Int, Int)
  UxtbX(Int, Int)
  UxthX(Int, Int)
  UxtbW(Int, Int)
  UxthW(Int, Int)
  // Float conversion
  Fcvtzs(Int, Int, Bool, Bool)
  Fcvtzu(Int, Int, Bool, Bool)
  Scvtf(Int, Int, Bool, Bool)
  Ucvtf(Int, Int, Bool, Bool)
  // Float arithmetic
  FaddD(Int, Int, Int)
  FaddS(Int, Int, Int)
  FsubD(Int, Int, Int)
  FsubS(Int, Int, Int)
  FmulD(Int, Int, Int)
  FmulS(Int, Int, Int)
  FdivD(Int, Int, Int)
  FdivS(Int, Int, Int)
  FmaxD(Int, Int, Int)
  FmaxS(Int, Int, Int)
  FminD(Int, Int, Int)
  FminS(Int, Int, Int)
  FmaxnmD(Int, Int, Int)
  FmaxnmS(Int, Int, Int)
  FminnmD(Int, Int, Int)
  FminnmS(Int, Int, Int)
  // Float unary
  FsqrtD(Int, Int)
  FsqrtS(Int, Int)
  FabsD(Int, Int)
  FabsS(Int, Int)
  FnegD(Int, Int)
  FnegS(Int, Int)
  FrintpD(Int, Int)
  FrintpS(Int, Int)
  FrintmD(Int, Int)
  FrintmS(Int, Int)
  FrintzD(Int, Int)
  FrintzS(Int, Int)
  FrintnD(Int, Int)
  FrintnS(Int, Int)
  // Float move
  FmovD(Int, Int)
  FmovS(Int, Int)
  FmovDToX(Int, Int)
  FmovSToW(Int, Int)
  FmovXToD(Int, Int)
  FmovWToS(Int, Int)
  // Float conversion
  FcvtDS(Int, Int)
  FcvtSD(Int, Int)
  // Float load/store
  LdrSImm(Int, Int, Int)
  StrSImm(Int, Int, Int)
  LdrDImm(Int, Int, Int)
  StrDImm(Int, Int, Int)
  // Float compare
  FcmpD(Int, Int)
  FcmpS(Int, Int)
  // Comparison
  CmpReg(Int, Int)
  CmpReg32(Int, Int) // 32-bit compare
  CmpImm(Int, Int)
  CmpImmShifted12(Int, Int) // rn, imm12 (encoded as imm12 << 12)
  CmpImm32(Int, Int) // 32-bit compare immediate
  CmpImm32Shifted12(Int, Int) // rn, imm12 (encoded as imm12 << 12)
  // ADDS XZR/WZR, Xn, #imm - add immediate and set flags (result discarded)
  AddsImmZr(Int, Int, Bool) // rn, imm12, is_64
  // CCMP Xn, #imm, #nzcv, cond - conditional compare immediate
  CCmpImm(Int, Int, Int, Int, Bool) // rn, imm5, nzcv, cond, is_64
  Cset(Int, Int)
  Csel(Int, Int, Int, Int)
  FcselD(Int, Int, Int, Int)
  FcselS(Int, Int, Int, Int)
  // Branch
  B(Int)
  BCond(Int, Int)
  Cbz(Int, Int) // 64-bit CBZ
  Cbnz(Int, Int) // 64-bit CBNZ
  Cbz32(Int, Int) // 32-bit CBZ
  Cbnz32(Int, Int) // 32-bit CBNZ
  BCondOffset(Int, Int)
  CbnzOffset(Int, Bool, Int) // rt, is_64, offset_bytes - branch if not zero with immediate offset
  Brk(Int)
  Ret(Int)
  Br(Int)
  Bl(Int)
  Blr(Int)
  Adr(Int, Int)
  // Memory barrier
  DmbIsh
  // NOP
  Nop
  // Alignment helper (emits multiple NOPs)
  AlignTo(Int)
  // SIMD for popcnt
  Cnt8B(Int, Int) // CNT Vd.8B, Vn.8B - count bits in each byte
  AddvB(Int, Int) // ADDV Bd, Vn.8B - sum all bytes
  // ============ NEON SIMD Instructions ============
  // V128 Load/Store
  LdrQ(Int, Int, Int) // LDR Qd, [Xn, #imm] (128-bit)
  StrQ(Int, Int, Int) // STR Qs, [Xn, #imm] (128-bit)
  // Splat (DUP from GPR)
  Dup16B(Int, Int) // DUP Vd.16B, Wn
  Dup8H(Int, Int) // DUP Vd.8H, Wn
  Dup4S(Int, Int) // DUP Vd.4S, Wn
  Dup2D(Int, Int) // DUP Vd.2D, Xn
  // Splat from element (DUP from scalar)
  DupElem4S(Int, Int, Int) // DUP Vd.4S, Vn.S[lane]
  DupElem2D(Int, Int, Int) // DUP Vd.2D, Vn.D[lane]
  // Extract lane (UMOV/SMOV)
  UmovB(Int, Int, Int) // UMOV Wd, Vn.B[lane]
  UmovH(Int, Int, Int) // UMOV Wd, Vn.H[lane]
  UmovS(Int, Int, Int) // UMOV Wd, Vn.S[lane]
  UmovD(Int, Int, Int) // UMOV Xd, Vn.D[lane]
  SmovB(Int, Int, Int) // SMOV Wd, Vn.B[lane]
  SmovH(Int, Int, Int) // SMOV Wd, Vn.H[lane]
  SmovS(Int, Int, Int) // SMOV Xd, Vn.S[lane] (sign extend to 64-bit)
  // Extract float lane (DUP to scalar)
  DupScalarS(Int, Int, Int) // DUP Sd, Vn.S[lane]
  DupScalarD(Int, Int, Int) // DUP Dd, Vn.D[lane]
  // Insert lane (INS from GPR)
  InsB(Int, Int, Int) // INS Vd.B[lane], Wn
  InsH(Int, Int, Int) // INS Vd.H[lane], Wn
  InsS(Int, Int, Int) // INS Vd.S[lane], Wn
  InsD(Int, Int, Int) // INS Vd.D[lane], Xn
  // Insert from element
  InsElemS(Int, Int, Int, Int) // INS Vd.S[lane], Vn.S[0]
  InsElemD(Int, Int, Int, Int) // INS Vd.D[lane], Vn.D[0]
  // Bitwise
  Not16B(Int, Int) // NOT Vd.16B, Vn.16B
  And16B(Int, Int, Int) // AND Vd.16B, Vn.16B, Vm.16B
  Orr16B(Int, Int, Int) // ORR Vd.16B, Vn.16B, Vm.16B
  Eor16B(Int, Int, Int) // EOR Vd.16B, Vn.16B, Vm.16B
  Bic16B(Int, Int, Int) // BIC Vd.16B, Vn.16B, Vm.16B
  Bsl16B(Int, Int, Int) // BSL Vd.16B, Vn.16B, Vm.16B
  // Swizzle/Shuffle
  Tbl1(Int, Int, Int) // TBL Vd.16B, {Vn.16B}, Vm.16B
  // Integer arithmetic (16 lanes, 8-bit)
  Add16B(Int, Int, Int) // ADD Vd.16B, Vn.16B, Vm.16B
  Sub16B(Int, Int, Int) // SUB Vd.16B, Vn.16B, Vm.16B
  // Integer arithmetic (8 lanes, 16-bit)
  Add8H(Int, Int, Int) // ADD Vd.8H, Vn.8H, Vm.8H
  Sub8H(Int, Int, Int) // SUB Vd.8H, Vn.8H, Vm.8H
  Mul8H(Int, Int, Int) // MUL Vd.8H, Vn.8H, Vm.8H
  // Integer arithmetic (4 lanes, 32-bit)
  Add4S(Int, Int, Int) // ADD Vd.4S, Vn.4S, Vm.4S
  Sub4S(Int, Int, Int) // SUB Vd.4S, Vn.4S, Vm.4S
  Mul4S(Int, Int, Int) // MUL Vd.4S, Vn.4S, Vm.4S
  // Integer arithmetic (2 lanes, 64-bit)
  Add2D(Int, Int, Int) // ADD Vd.2D, Vn.2D, Vm.2D
  Sub2D(Int, Int, Int) // SUB Vd.2D, Vn.2D, Vm.2D
  // No MUL for 64-bit lanes in NEON, need PMULL or separate approach
  // Saturating arithmetic
  Sqadd16B(Int, Int, Int) // SQADD Vd.16B, Vn.16B, Vm.16B
  Sqadd8H(Int, Int, Int) // SQADD Vd.8H, Vn.8H, Vm.8H
  Sqadd4S(Int, Int, Int) // SQADD Vd.4S, Vn.4S, Vm.4S
  Sqadd2D(Int, Int, Int) // SQADD Vd.2D, Vn.2D, Vm.2D
  Uqadd16B(Int, Int, Int) // UQADD Vd.16B, Vn.16B, Vm.16B
  Uqadd8H(Int, Int, Int) // UQADD Vd.8H, Vn.8H, Vm.8H
  Uqadd4S(Int, Int, Int) // UQADD Vd.4S, Vn.4S, Vm.4S
  Uqadd2D(Int, Int, Int) // UQADD Vd.2D, Vn.2D, Vm.2D
  Sqsub16B(Int, Int, Int) // SQSUB Vd.16B, Vn.16B, Vm.16B
  Sqsub8H(Int, Int, Int) // SQSUB Vd.8H, Vn.8H, Vm.8H
  Sqsub4S(Int, Int, Int) // SQSUB Vd.4S, Vn.4S, Vm.4S
  Sqsub2D(Int, Int, Int) // SQSUB Vd.2D, Vn.2D, Vm.2D
  Uqsub16B(Int, Int, Int) // UQSUB Vd.16B, Vn.16B, Vm.16B
  Uqsub8H(Int, Int, Int) // UQSUB Vd.8H, Vn.8H, Vm.8H
  Uqsub4S(Int, Int, Int) // UQSUB Vd.4S, Vn.4S, Vm.4S
  Uqsub2D(Int, Int, Int) // UQSUB Vd.2D, Vn.2D, Vm.2D
  // Min/Max
  Smin16B(Int, Int, Int) // SMIN Vd.16B, Vn.16B, Vm.16B
  Smin8H(Int, Int, Int) // SMIN Vd.8H, Vn.8H, Vm.8H
  Smin4S(Int, Int, Int) // SMIN Vd.4S, Vn.4S, Vm.4S
  Umin16B(Int, Int, Int) // UMIN Vd.16B, Vn.16B, Vm.16B
  Umin8H(Int, Int, Int) // UMIN Vd.8H, Vn.8H, Vm.8H
  Umin4S(Int, Int, Int) // UMIN Vd.4S, Vn.4S, Vm.4S
  Smax16B(Int, Int, Int) // SMAX Vd.16B, Vn.16B, Vm.16B
  Smax8H(Int, Int, Int) // SMAX Vd.8H, Vn.8H, Vm.8H
  Smax4S(Int, Int, Int) // SMAX Vd.4S, Vn.4S, Vm.4S
  Umax16B(Int, Int, Int) // UMAX Vd.16B, Vn.16B, Vm.16B
  Umax8H(Int, Int, Int) // UMAX Vd.8H, Vn.8H, Vm.8H
  Umax4S(Int, Int, Int) // UMAX Vd.4S, Vn.4S, Vm.4S
  // Urhadd
  Urhadd16B(Int, Int, Int) // URHADD Vd.16B, Vn.16B, Vm.16B
  Urhadd8H(Int, Int, Int) // URHADD Vd.8H, Vn.8H, Vm.8H
  Urhadd4S(Int, Int, Int) // URHADD Vd.4S, Vn.4S, Vm.4S
  // Abs/Neg
  Abs16B(Int, Int) // ABS Vd.16B, Vn.16B
  Abs8H(Int, Int) // ABS Vd.8H, Vn.8H
  Abs4S(Int, Int) // ABS Vd.4S, Vn.4S
  Abs2D(Int, Int) // ABS Vd.2D, Vn.2D
  Neg16B(Int, Int) // NEG Vd.16B, Vn.16B
  Neg8H(Int, Int) // NEG Vd.8H, Vn.8H
  Neg4S(Int, Int) // NEG Vd.4S, Vn.4S
  Neg2D(Int, Int) // NEG Vd.2D, Vn.2D
  // CNT for full 128-bit
  Cnt16B(Int, Int) // CNT Vd.16B, Vn.16B
  // Shifts (register-based with scalar in GPR)
  Sshl16B(Int, Int, Int) // SSHL Vd.16B, Vn.16B, Vm.16B (shift by element)
  Sshl8H(Int, Int, Int) // SSHL Vd.8H, Vn.8H, Vm.8H
  Sshl4S(Int, Int, Int) // SSHL Vd.4S, Vn.4S, Vm.4S
  Sshl2D(Int, Int, Int) // SSHL Vd.2D, Vn.2D, Vm.2D
  Ushl16B(Int, Int, Int) // USHL Vd.16B, Vn.16B, Vm.16B
  Ushl8H(Int, Int, Int) // USHL Vd.8H, Vn.8H, Vm.8H
  Ushl4S(Int, Int, Int) // USHL Vd.4S, Vn.4S, Vm.4S
  Ushl2D(Int, Int, Int) // USHL Vd.2D, Vn.2D, Vm.2D
  // Comparison
  Cmeq16B(Int, Int, Int) // CMEQ Vd.16B, Vn.16B, Vm.16B
  Cmeq8H(Int, Int, Int) // CMEQ Vd.8H, Vn.8H, Vm.8H
  Cmeq4S(Int, Int, Int) // CMEQ Vd.4S, Vn.4S, Vm.4S
  Cmeq2D(Int, Int, Int) // CMEQ Vd.2D, Vn.2D, Vm.2D
  Cmgt16B(Int, Int, Int) // CMGT Vd.16B, Vn.16B, Vm.16B
  Cmgt8H(Int, Int, Int) // CMGT Vd.8H, Vn.8H, Vm.8H
  Cmgt4S(Int, Int, Int) // CMGT Vd.4S, Vn.4S, Vm.4S
  Cmgt2D(Int, Int, Int) // CMGT Vd.2D, Vn.2D, Vm.2D
  Cmge16B(Int, Int, Int) // CMGE Vd.16B, Vn.16B, Vm.16B
  Cmge8H(Int, Int, Int) // CMGE Vd.8H, Vn.8H, Vm.8H
  Cmge4S(Int, Int, Int) // CMGE Vd.4S, Vn.4S, Vm.4S
  Cmge2D(Int, Int, Int) // CMGE Vd.2D, Vn.2D, Vm.2D
  Cmhi16B(Int, Int, Int) // CMHI Vd.16B, Vn.16B, Vm.16B (unsigned >)
  Cmhi8H(Int, Int, Int) // CMHI Vd.8H, Vn.8H, Vm.8H
  Cmhi4S(Int, Int, Int) // CMHI Vd.4S, Vn.4S, Vm.4S
  Cmhi2D(Int, Int, Int) // CMHI Vd.2D, Vn.2D, Vm.2D
  Cmhs16B(Int, Int, Int) // CMHS Vd.16B, Vn.16B, Vm.16B (unsigned >=)
  Cmhs8H(Int, Int, Int) // CMHS Vd.8H, Vn.8H, Vm.8H
  Cmhs4S(Int, Int, Int) // CMHS Vd.4S, Vn.4S, Vm.4S
  Cmhs2D(Int, Int, Int) // CMHS Vd.2D, Vn.2D, Vm.2D
  // Narrowing (non-saturating - used by i64x2.mul implementation)
  Xtn2S(Int, Int) // XTN Vd.2S, Vn.2D
  // Narrowing (saturating vector lane pack)
  Sqxtn8B(Int, Int) // SQXTN Vd.8B, Vn.8H
  Sqxtn4H(Int, Int) // SQXTN Vd.4H, Vn.4S
  Sqxtn2S(Int, Int) // SQXTN Vd.2S, Vn.2D
  Sqxtun8B(Int, Int) // SQXTUN Vd.8B, Vn.8H
  Sqxtun4H(Int, Int) // SQXTUN Vd.4H, Vn.4S
  // Note: Sqxtun2S not needed because there is no i64->i32 vector narrow op.
  // Note: Uqxtn8B/4H not needed because unsigned narrow uses a signed source.
  Uqxtn2S(Int, Int) // UQXTN Vd.2S, Vn.2D - used by i32x4.trunc_sat_f64x2_u_zero
  // Narrowing (second half - writes to upper portion of Vd)
  Sqxtn2_16B(Int, Int) // SQXTN2 Vd.16B, Vn.8H (writes upper 8 bytes)
  Sqxtn2_8H(Int, Int) // SQXTN2 Vd.8H, Vn.4S (writes upper 4 halfwords)
  Sqxtun2_16B(Int, Int) // SQXTUN2 Vd.16B, Vn.8H (writes upper 8 bytes)
  Sqxtun2_8H(Int, Int) // SQXTUN2 Vd.8H, Vn.4S (writes upper 4 halfwords)
  // Extending
  Sxtl8H(Int, Int) // SXTL Vd.8H, Vn.8B (aka SSHLL with shift 0)
  Sxtl4S(Int, Int) // SXTL Vd.4S, Vn.4H
  Sxtl2D(Int, Int) // SXTL Vd.2D, Vn.2S
  Sxtl2_8H(Int, Int) // SXTL2 Vd.8H, Vn.16B
  Sxtl2_4S(Int, Int) // SXTL2 Vd.4S, Vn.8H
  Sxtl2_2D(Int, Int) // SXTL2 Vd.2D, Vn.4S
  Uxtl8H(Int, Int) // UXTL Vd.8H, Vn.8B (aka USHLL with shift 0)
  Uxtl4S(Int, Int) // UXTL Vd.4S, Vn.4H
  Uxtl2D(Int, Int) // UXTL Vd.2D, Vn.2S
  Uxtl2_8H(Int, Int) // UXTL2 Vd.8H, Vn.16B
  Uxtl2_4S(Int, Int) // UXTL2 Vd.4S, Vn.8H
  Uxtl2_2D(Int, Int) // UXTL2 Vd.2D, Vn.4S
  // Extended multiply (low half)
  Smull8H(Int, Int, Int) // SMULL Vd.8H, Vn.8B, Vm.8B
  Smull4S(Int, Int, Int) // SMULL Vd.4S, Vn.4H, Vm.4H
  Smull2D(Int, Int, Int) // SMULL Vd.2D, Vn.2S, Vm.2S
  Umull8H(Int, Int, Int) // UMULL Vd.8H, Vn.8B, Vm.8B
  Umull4S(Int, Int, Int) // UMULL Vd.4S, Vn.4H, Vm.4H
  Umull2D(Int, Int, Int) // UMULL Vd.2D, Vn.2S, Vm.2S
  // Extended multiply (high half)
  Smull2_8H(Int, Int, Int) // SMULL2 Vd.8H, Vn.16B, Vm.16B
  Smull2_4S(Int, Int, Int) // SMULL2 Vd.4S, Vn.8H, Vm.8H
  Smull2_2D(Int, Int, Int) // SMULL2 Vd.2D, Vn.4S, Vm.4S
  Umull2_8H(Int, Int, Int) // UMULL2 Vd.8H, Vn.16B, Vm.16B
  Umull2_4S(Int, Int, Int) // UMULL2 Vd.4S, Vn.8H, Vm.8H
  Umull2_2D(Int, Int, Int) // UMULL2 Vd.2D, Vn.4S, Vm.4S
  // Pairwise add
  Saddlp8H(Int, Int) // SADDLP Vd.8H, Vn.16B
  Saddlp4S(Int, Int) // SADDLP Vd.4S, Vn.8H
  Uaddlp8H(Int, Int) // UADDLP Vd.8H, Vn.16B
  Uaddlp4S(Int, Int) // UADDLP Vd.4S, Vn.8H
  Uaddlp2D(Int, Int) // UADDLP Vd.2D, Vn.4S
  // Pairwise add (for dot product)
  Addp8H(Int, Int, Int) // ADDP Vd.8H, Vn.8H, Vm.8H
  Addp4S(Int, Int, Int) // ADDP Vd.4S, Vn.4S, Vm.4S
  // Q15 saturating rounding multiply high
  Sqrdmulh8H(Int, Int, Int) // SQRDMULH Vd.8H, Vn.8H, Vm.8H
  // Float arithmetic
  Fadd4S(Int, Int, Int) // FADD Vd.4S, Vn.4S, Vm.4S
  Fadd2D(Int, Int, Int) // FADD Vd.2D, Vn.2D, Vm.2D
  Fsub4S(Int, Int, Int) // FSUB Vd.4S, Vn.4S, Vm.4S
  Fsub2D(Int, Int, Int) // FSUB Vd.2D, Vn.2D, Vm.2D
  Fmul4S(Int, Int, Int) // FMUL Vd.4S, Vn.4S, Vm.4S
  Fmul2D(Int, Int, Int) // FMUL Vd.2D, Vn.2D, Vm.2D
  Fdiv4S(Int, Int, Int) // FDIV Vd.4S, Vn.4S, Vm.4S
  Fdiv2D(Int, Int, Int) // FDIV Vd.2D, Vn.2D, Vm.2D
  Fmin4S(Int, Int, Int) // FMIN Vd.4S, Vn.4S, Vm.4S
  Fmin2D(Int, Int, Int) // FMIN Vd.2D, Vn.2D, Vm.2D
  Fmax4S(Int, Int, Int) // FMAX Vd.4S, Vn.4S, Vm.4S
  Fmax2D(Int, Int, Int) // FMAX Vd.2D, Vn.2D, Vm.2D
  // Fused multiply-add (relaxed SIMD)
  Fmla4S(Int, Int, Int) // FMLA Vd.4S, Vn.4S, Vm.4S: Vd = Vd + Vn * Vm
  Fmla2D(Int, Int, Int) // FMLA Vd.2D, Vn.2D, Vm.2D
  Fmls4S(Int, Int, Int) // FMLS Vd.4S, Vn.4S, Vm.4S: Vd = Vd - Vn * Vm
  Fmls2D(Int, Int, Int) // FMLS Vd.2D, Vn.2D, Vm.2D
  // Float unary
  Fabs4S(Int, Int) // FABS Vd.4S, Vn.4S
  Fabs2D(Int, Int) // FABS Vd.2D, Vn.2D
  Fneg4S(Int, Int) // FNEG Vd.4S, Vn.4S
  Fneg2D(Int, Int) // FNEG Vd.2D, Vn.2D
  Fsqrt4S(Int, Int) // FSQRT Vd.4S, Vn.4S
  Fsqrt2D(Int, Int) // FSQRT Vd.2D, Vn.2D
  Frintp4S(Int, Int) // FRINTP Vd.4S, Vn.4S (ceil)
  Frintp2D(Int, Int) // FRINTP Vd.2D, Vn.2D
  Frintm4S(Int, Int) // FRINTM Vd.4S, Vn.4S (floor)
  Frintm2D(Int, Int) // FRINTM Vd.2D, Vn.2D
  Frintz4S(Int, Int) // FRINTZ Vd.4S, Vn.4S (trunc)
  Frintz2D(Int, Int) // FRINTZ Vd.2D, Vn.2D
  Frintn4S(Int, Int) // FRINTN Vd.4S, Vn.4S (nearest)
  Frintn2D(Int, Int) // FRINTN Vd.2D, Vn.2D
  // Float comparison
  Fcmeq4S(Int, Int, Int) // FCMEQ Vd.4S, Vn.4S, Vm.4S
  Fcmeq2D(Int, Int, Int) // FCMEQ Vd.2D, Vn.2D, Vm.2D
  Fcmgt4S(Int, Int, Int) // FCMGT Vd.4S, Vn.4S, Vm.4S
  Fcmgt2D(Int, Int, Int) // FCMGT Vd.2D, Vn.2D, Vm.2D
  Fcmge4S(Int, Int, Int) // FCMGE Vd.4S, Vn.4S, Vm.4S
  Fcmge2D(Int, Int, Int) // FCMGE Vd.2D, Vn.2D, Vm.2D
  // Float conversion
  Fcvtzs4S(Int, Int) // FCVTZS Vd.4S, Vn.4S (f32 -> i32)
  Fcvtzs2D(Int, Int) // FCVTZS Vd.2D, Vn.2D (f64 -> i64)
  Fcvtzu4S(Int, Int) // FCVTZU Vd.4S, Vn.4S
  Fcvtzu2D(Int, Int) // FCVTZU Vd.2D, Vn.2D
  Scvtf4S(Int, Int) // SCVTF Vd.4S, Vn.4S (i32 -> f32)
  Scvtf2D(Int, Int) // SCVTF Vd.2D, Vn.2D (i64 -> f64)
  Ucvtf4S(Int, Int) // UCVTF Vd.4S, Vn.4S
  Ucvtf2D(Int, Int) // UCVTF Vd.2D, Vn.2D
  // F64/F32 conversions
  Fcvtn2S(Int, Int) // FCVTN Vd.2S, Vn.2D (demote f64x2 -> f32x2, lower 64 bits)
  Fcvtl2D(Int, Int) // FCVTL Vd.2D, Vn.2S (promote f32x2 -> f64x2)
  // Load splat
  Ld1rB(Int, Int) // LD1R {Vd.16B}, [Xn]
  Ld1rH(Int, Int) // LD1R {Vd.8H}, [Xn]
  Ld1rS(Int, Int) // LD1R {Vd.4S}, [Xn]
  Ld1rD(Int, Int) // LD1R {Vd.2D}, [Xn]
  // Load single lane
  Ld1B(Int, Int, Int) // LD1 {Vd.B}[lane], [Xn]
  Ld1H(Int, Int, Int) // LD1 {Vd.H}[lane], [Xn]
  Ld1S(Int, Int, Int) // LD1 {Vd.S}[lane], [Xn]
  Ld1D(Int, Int, Int) // LD1 {Vd.D}[lane], [Xn]
  // Store single lane
  St1B(Int, Int, Int) // ST1 {Vn.B}[lane], [Xd]
  St1H(Int, Int, Int) // ST1 {Vn.H}[lane], [Xd]
  St1S(Int, Int, Int) // ST1 {Vn.S}[lane], [Xd]
  St1D(Int, Int, Int) // ST1 {Vn.D}[lane], [Xd]
  // UMAXV/UMINV for any_true/all_true
  Umaxv16B(Int, Int) // UMAXV Bd, Vn.16B
  Uminv16B(Int, Int) // UMINV Bd, Vn.16B
  Uminv8H(Int, Int) // UMINV Hd, Vn.8H
  Uminv4S(Int, Int) // UMINV Sd, Vn.4S
  // MOVI for zeroing
  MoviZero(Int) // MOVI Vd.2D, #0
  // Orr for move
  OrrVec(Int, Int) // ORR Vd.16B, Vn.16B, Vn.16B (move)
  // For i64x2.mul emulation
  Rev64_4S(Int, Int) // REV64 Vd.4S, Vn.4S (swap 32-bit halves within 64-bit lanes)
  ShlImm2D(Int, Int, Int) // SHL Vd.2D, Vn.2D, #imm
}

///|
fn Instruction::annotate(self : Instruction) -> String {
  match self {
    AddReg(rd, rn, rm) => "add x\{rd}, x\{rn}, x\{rm}"
    AddShifted(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "add x\{rd}, x\{rn}, x\{rm}, \{shift_name} #\{amount}"
    }
    AddShifted32(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "add w\{rd}, w\{rn}, w\{rm}, \{shift_name} #\{amount}"
    }
    AddExtUxtw(rd, rn, rm, shift) => {
      let suffix = if shift == 0 { "uxtw" } else { "uxtw #\{shift}" }
      "add x\{rd}, x\{rn}, w\{rm}, \{suffix}"
    }
    AddExtSxtw(rd, rn, rm, shift) => {
      let suffix = if shift == 0 { "sxtw" } else { "sxtw #\{shift}" }
      "add x\{rd}, x\{rn}, w\{rm}, \{suffix}"
    }
    SubExtUxtw(rd, rn, rm, shift) => {
      let suffix = if shift == 0 { "uxtw" } else { "uxtw #\{shift}" }
      "sub x\{rd}, x\{rn}, w\{rm}, \{suffix}"
    }
    SubExtSxtw(rd, rn, rm, shift) => {
      let suffix = if shift == 0 { "sxtw" } else { "sxtw #\{shift}" }
      "sub x\{rd}, x\{rn}, w\{rm}, \{suffix}"
    }
    AddImm(rd, rn, imm12) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      let rd_name = if rd == 31 { "sp" } else { "x\{rd}" }
      "add \{rd_name}, \{rn_name}, #\{imm12}"
    }
    AddImmShifted12(rd, rn, imm12) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      let rd_name = if rd == 31 { "sp" } else { "x\{rd}" }
      "add \{rd_name}, \{rn_name}, #\{imm12}, lsl #12"
    }
    SubReg(rd, rn, rm) => "sub x\{rd}, x\{rn}, x\{rm}"
    SubShifted(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "sub x\{rd}, x\{rn}, x\{rm}, \{shift_name} #\{amount}"
    }
    SubShifted32(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "sub w\{rd}, w\{rn}, w\{rm}, \{shift_name} #\{amount}"
    }
    SubImm(rd, rn, imm12) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      let rd_name = if rd == 31 { "sp" } else { "x\{rd}" }
      "sub \{rd_name}, \{rn_name}, #\{imm12}"
    }
    SubImmShifted12(rd, rn, imm12) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      let rd_name = if rd == 31 { "sp" } else { "x\{rd}" }
      "sub \{rd_name}, \{rn_name}, #\{imm12}, lsl #12"
    }
    Mul(rd, rn, rm) => "mul x\{rd}, x\{rn}, x\{rm}"
    Madd(rd, rn, rm, ra) => "madd x\{rd}, x\{rn}, x\{rm}, x\{ra}"
    Msub(rd, rn, rm, ra) => "msub x\{rd}, x\{rn}, x\{rm}, x\{ra}"
    Mneg(rd, rn, rm) => "mneg x\{rd}, x\{rn}, x\{rm}"
    Madd32(rd, rn, rm, ra) => "madd w\{rd}, w\{rn}, w\{rm}, w\{ra}"
    Msub32(rd, rn, rm, ra) => "msub w\{rd}, w\{rn}, w\{rm}, w\{ra}"
    Mneg32(rd, rn, rm) => "mneg w\{rd}, w\{rn}, w\{rm}"
    Umulh(rd, rn, rm) => "umulh x\{rd}, x\{rn}, x\{rm}"
    Smulh(rd, rn, rm) => "smulh x\{rd}, x\{rn}, x\{rm}"
    Umull(rd, rn, rm) => "umull x\{rd}, w\{rn}, w\{rm}"
    Smull(rd, rn, rm) => "smull x\{rd}, w\{rn}, w\{rm}"
    Sdiv(rd, rn, rm) => "sdiv x\{rd}, x\{rn}, x\{rm}"
    Udiv(rd, rn, rm) => "udiv x\{rd}, x\{rn}, x\{rm}"
    Sdiv32(rd, rn, rm) => "sdiv w\{rd}, w\{rn}, w\{rm}"
    Udiv32(rd, rn, rm) => "udiv w\{rd}, w\{rn}, w\{rm}"
    SubReg32(rd, rn, rm) => "sub w\{rd}, w\{rn}, w\{rm}"
    AddReg32(rd, rn, rm) => "add w\{rd}, w\{rn}, w\{rm}"
    AddImm32(rd, rn, imm) => "add w\{rd}, w\{rn}, #\{imm}"
    AddImm32Shifted12(rd, rn, imm) => "add w\{rd}, w\{rn}, #\{imm}, lsl #12"
    SubImm32(rd, rn, imm) => "sub w\{rd}, w\{rn}, #\{imm}"
    SubImm32Shifted12(rd, rn, imm) => "sub w\{rd}, w\{rn}, #\{imm}, lsl #12"
    Mul32(rd, rn, rm) => "mul w\{rd}, w\{rn}, w\{rm}"
    AndReg(rd, rn, rm) => "and x\{rd}, x\{rn}, x\{rm}"
    AndReg32(rd, rn, rm) => "and w\{rd}, w\{rn}, w\{rm}"
    BicReg(rd, rn, rm) => "bic x\{rd}, x\{rn}, x\{rm}"
    BicReg32(rd, rn, rm) => "bic w\{rd}, w\{rn}, w\{rm}"
    AndImm(rd, rn, imm_bits) => "and x\{rd}, x\{rn}, #imml(\{imm_bits})"
    AndImm32(rd, rn, imm_bits) => "and w\{rd}, w\{rn}, #imml(\{imm_bits})"
    AndShifted(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "and x\{rd}, x\{rn}, x\{rm}, \{shift_name} #\{amount}"
    }
    AndShifted32(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "and w\{rd}, w\{rn}, w\{rm}, \{shift_name} #\{amount}"
    }
    OrrReg(rd, rn, rm) => "orr x\{rd}, x\{rn}, x\{rm}"
    OrrReg32(rd, rn, rm) => "orr w\{rd}, w\{rn}, w\{rm}"
    OrnReg(rd, rn, rm) => "orn x\{rd}, x\{rn}, x\{rm}"
    OrnReg32(rd, rn, rm) => "orn w\{rd}, w\{rn}, w\{rm}"
    OrrImm(rd, rn, imm_bits) => "orr x\{rd}, x\{rn}, #imml(\{imm_bits})"
    OrrImm32(rd, rn, imm_bits) => "orr w\{rd}, w\{rn}, #imml(\{imm_bits})"
    OrrShifted(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "orr x\{rd}, x\{rn}, x\{rm}, \{shift_name} #\{amount}"
    }
    OrrShifted32(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "orr w\{rd}, w\{rn}, w\{rm}, \{shift_name} #\{amount}"
    }
    EorReg(rd, rn, rm) => "eor x\{rd}, x\{rn}, x\{rm}"
    EorReg32(rd, rn, rm) => "eor w\{rd}, w\{rn}, w\{rm}"
    EonReg(rd, rn, rm) => "eon x\{rd}, x\{rn}, x\{rm}"
    EonReg32(rd, rn, rm) => "eon w\{rd}, w\{rn}, w\{rm}"
    EorImm(rd, rn, imm_bits) => "eor x\{rd}, x\{rn}, #imml(\{imm_bits})"
    EorImm32(rd, rn, imm_bits) => "eor w\{rd}, w\{rn}, #imml(\{imm_bits})"
    EorShifted(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "eor x\{rd}, x\{rn}, x\{rm}, \{shift_name} #\{amount}"
    }
    EorShifted32(rd, rn, rm, shift, amount) => {
      let shift_name = match shift {
        Lsl => "lsl"
        Lsr => "lsr"
        Asr => "asr"
      }
      "eor w\{rd}, w\{rn}, w\{rm}, \{shift_name} #\{amount}"
    }
    Mvn(rd, rm) => "mvn x\{rd}, x\{rm}"
    Mvn32(rd, rm) => "mvn w\{rd}, w\{rm}"
    LslReg(rd, rn, rm) => "lsl x\{rd}, x\{rn}, x\{rm}"
    LsrReg(rd, rn, rm) => "lsr x\{rd}, x\{rn}, x\{rm}"
    AsrReg(rd, rn, rm) => "asr x\{rd}, x\{rn}, x\{rm}"
    RorReg(rd, rn, rm) => "ror x\{rd}, x\{rn}, x\{rm}"
    LslReg32(rd, rn, rm) => "lsl w\{rd}, w\{rn}, w\{rm}"
    LsrReg32(rd, rn, rm) => "lsr w\{rd}, w\{rn}, w\{rm}"
    AsrReg32(rd, rn, rm) => "asr w\{rd}, w\{rn}, w\{rm}"
    RorReg32(rd, rn, rm) => "ror w\{rd}, w\{rn}, w\{rm}"
    LsrImm(rd, rn, shift) => "lsr x\{rd}, x\{rn}, #\{shift}"
    LslImm(rd, rn, shift) => "lsl x\{rd}, x\{rn}, #\{shift}"
    AsrImm(rd, rn, shift) => "asr x\{rd}, x\{rn}, #\{shift}"
    RorImm(rd, rn, shift) => "ror x\{rd}, x\{rn}, #\{shift}"
    ExtrImm(rd, rn, rm, shift) => "extr x\{rd}, x\{rn}, x\{rm}, #\{shift}"
    LsrImm32(rd, rn, shift) => "lsr w\{rd}, w\{rn}, #\{shift}"
    LslImm32(rd, rn, shift) => "lsl w\{rd}, w\{rn}, #\{shift}"
    AsrImm32(rd, rn, shift) => "asr w\{rd}, w\{rn}, #\{shift}"
    RorImm32(rd, rn, shift) => "ror w\{rd}, w\{rn}, #\{shift}"
    ExtrImm32(rd, rn, rm, shift) => "extr w\{rd}, w\{rn}, w\{rm}, #\{shift}"
    UbfxWidth(rd, rn, width) => "ubfx x\{rd}, x\{rn}, #0, #\{width}"
    Clz(rd, rn) => "clz x\{rd}, x\{rn}"
    Rbit(rd, rn) => "rbit x\{rd}, x\{rn}"
    Clz32(rd, rn) => "clz w\{rd}, w\{rn}"
    Rbit32(rd, rn) => "rbit w\{rd}, w\{rn}"
    MovReg(rd, rm) => "mov x\{rd}, x\{rm}"
    MovReg32(rd, rm) => "mov w\{rd}, w\{rm}"
    Movz(rd, imm16, shift) => "movz x\{rd}, #\{imm16}, lsl #\{shift}"
    Movk(rd, imm16, shift) => "movk x\{rd}, #\{imm16}, lsl #\{shift}"
    Movn(rd, imm16, shift) => "movn x\{rd}, #\{imm16}, lsl #\{shift}"
    LoadImm64(_, _) => "load_imm64 (multi-instruction)"
    LdrImm(rt, rn, imm12) => "ldr x\{rt}, [x\{rn}, #\{imm12}]"
    LdrImmSigned(rt, rn, simm9) => "ldur x\{rt}, [x\{rn}, #\{simm9}]"
    LdrRegScaled(rt, rn, rm, shift) =>
      "ldr x\{rt}, [x\{rn}, x\{rm}, lsl #\{shift}]"
    LdrRegOffset(rt, rn, rm, option, size_bits, shift) => {
      let (mnem, rt_name) = match size_bits {
        0 => ("ldrb", "w\{rt}")
        1 => ("ldrh", "w\{rt}")
        2 => ("ldr", "w\{rt}")
        3 => ("ldr", "x\{rt}")
        _ => ("ldr", "x\{rt}")
      }
      let opt_str = match option {
        0b011 => "lsl"
        0b010 => "uxtw"
        0b110 => "sxtw"
        _ => "opt\{option}"
      }
      let rm_name = if option == 0b010 || option == 0b110 {
        "w\{rm}"
      } else {
        "x\{rm}"
      }
      if shift == 0 {
        "\{mnem} \{rt_name}, [x\{rn}, \{rm_name}, \{opt_str}]"
      } else {
        "\{mnem} \{rt_name}, [x\{rn}, \{rm_name}, \{opt_str} #\{shift}]"
      }
    }
    LdrWRegScaled(rt, rn, rm, shift) =>
      "ldr w\{rt}, [x\{rn}, x\{rm}, lsl #\{shift}]"
    StrWRegScaled(rt, rn, rm, shift) =>
      "str w\{rt}, [x\{rn}, x\{rm}, lsl #\{shift}]"
    LdrbReg(rt, rn, rm) => "ldrb w\{rt}, [x\{rn}, x\{rm}]"
    StrbReg(rt, rn, rm) => "strb w\{rt}, [x\{rn}, x\{rm}]"
    LdrhReg(rt, rn, rm) => "ldrh w\{rt}, [x\{rn}, x\{rm}]"
    StrhReg(rt, rn, rm) => "strh w\{rt}, [x\{rn}, x\{rm}]"
    StrImm(rt, rn, imm12) => "str x\{rt}, [x\{rn}, #\{imm12}]"
    StrRegScaled(rt, rn, rm, shift) =>
      "str x\{rt}, [x\{rn}, x\{rm}, lsl #\{shift}]"
    StrRegOffset(rt, rn, rm, option, size_bits, shift) => {
      let (mnem, rt_name) = match size_bits {
        0 => ("strb", "w\{rt}")
        1 => ("strh", "w\{rt}")
        2 => ("str", "w\{rt}")
        3 => ("str", "x\{rt}")
        _ => ("str", "x\{rt}")
      }
      let opt_str = match option {
        0b011 => "lsl"
        0b010 => "uxtw"
        0b110 => "sxtw"
        _ => "opt\{option}"
      }
      let rm_name = if option == 0b010 || option == 0b110 {
        "w\{rm}"
      } else {
        "x\{rm}"
      }
      if shift == 0 {
        "\{mnem} \{rt_name}, [x\{rn}, \{rm_name}, \{opt_str}]"
      } else {
        "\{mnem} \{rt_name}, [x\{rn}, \{rm_name}, \{opt_str} #\{shift}]"
      }
    }
    LdrbImm(rt, rn, imm12) => "ldrb w\{rt}, [x\{rn}, #\{imm12}]"
    LdrhImm(rt, rn, imm12) => "ldrh w\{rt}, [x\{rn}, #\{imm12}]"
    LdrWImm(rt, rn, imm12) => "ldr w\{rt}, [x\{rn}, #\{imm12}]"
    StrbImm(rt, rn, imm12) => "strb w\{rt}, [x\{rn}, #\{imm12}]"
    StrhImm(rt, rn, imm12) => "strh w\{rt}, [x\{rn}, #\{imm12}]"
    StrWImm(rt, rn, imm12) => "str w\{rt}, [x\{rn}, #\{imm12}]"
    LdrsbXImm(rt, rn, imm12) => "ldrsb x\{rt}, [x\{rn}, #\{imm12}]"
    LdrsbWImm(rt, rn, imm12) => "ldrsb w\{rt}, [x\{rn}, #\{imm12}]"
    LdrshXImm(rt, rn, imm12) => "ldrsh x\{rt}, [x\{rn}, #\{imm12}]"
    LdrshWImm(rt, rn, imm12) => "ldrsh w\{rt}, [x\{rn}, #\{imm12}]"
    LdrswImm(rt, rn, imm12) => "ldrsw x\{rt}, [x\{rn}, #\{imm12}]"
    StpPre(rt1, rt2, rn, imm) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "stp x\{rt1}, x\{rt2}, [\{rn_name}, #\{imm}]!"
    }
    LdpPost(rt1, rt2, rn, imm) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "ldp x\{rt1}, x\{rt2}, [\{rn_name}], #\{imm}"
    }
    StpOffset(rt1, rt2, rn, offset) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "stp x\{rt1}, x\{rt2}, [\{rn_name}, #\{offset}]"
    }
    LdpOffset(rt1, rt2, rn, offset) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "ldp x\{rt1}, x\{rt2}, [\{rn_name}, #\{offset}]"
    }
    StpDOffset(rt1, rt2, rn, offset) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "stp d\{rt1}, d\{rt2}, [\{rn_name}, #\{offset}]"
    }
    LdpDOffset(rt1, rt2, rn, offset) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "ldp d\{rt1}, d\{rt2}, [\{rn_name}, #\{offset}]"
    }
    StrPre(rt, rn, simm9) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "str x\{rt}, [\{rn_name}, #\{simm9}]!"
    }
    StpDPre(rt1, rt2, rn, imm) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "stp d\{rt1}, d\{rt2}, [\{rn_name}, #\{imm}]!"
    }
    StrDPre(rt, rn, simm9) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "str d\{rt}, [\{rn_name}, #\{simm9}]!"
    }
    LdrPost(rt, rn, simm9) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "ldr x\{rt}, [\{rn_name}], #\{simm9}"
    }
    LdpDPost(rt1, rt2, rn, imm) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "ldp d\{rt1}, d\{rt2}, [\{rn_name}], #\{imm}"
    }
    LdrDPost(rt, rn, simm9) => {
      let rn_name = if rn == 31 { "sp" } else { "x\{rn}" }
      "ldr d\{rt}, [\{rn_name}], #\{simm9}"
    }
    SxtbX(rd, rn) => "sxtb x\{rd}, w\{rn}"
    SxthX(rd, rn) => "sxth x\{rd}, w\{rn}"
    Sxtw(rd, rn) => "sxtw x\{rd}, w\{rn}"
    SxtbW(rd, rn) => "sxtb w\{rd}, w\{rn}"
    SxthW(rd, rn) => "sxth w\{rd}, w\{rn}"
    UxtbX(rd, rn) => "uxtb x\{rd}, w\{rn}"
    UxthX(rd, rn) => "uxth x\{rd}, w\{rn}"
    UxtbW(rd, rn) => "uxtb w\{rd}, w\{rn}"
    UxthW(rd, rn) => "uxth w\{rd}, w\{rn}"
    Fcvtzs(rd, rn, int64, double) => {
      let int_name = if int64 { "x" } else { "w" }
      let float_name = if double { "d" } else { "s" }
      "fcvtzs \{int_name}\{rd}, \{float_name}\{rn}"
    }
    Fcvtzu(rd, rn, int64, double) => {
      let int_name = if int64 { "x" } else { "w" }
      let float_name = if double { "d" } else { "s" }
      "fcvtzu \{int_name}\{rd}, \{float_name}\{rn}"
    }
    Scvtf(rd, rn, int64, double) => {
      let int_name = if int64 { "x" } else { "w" }
      let float_name = if double { "d" } else { "s" }
      "scvtf \{float_name}\{rd}, \{int_name}\{rn}"
    }
    Ucvtf(rd, rn, int64, double) => {
      let int_name = if int64 { "x" } else { "w" }
      let float_name = if double { "d" } else { "s" }
      "ucvtf \{float_name}\{rd}, \{int_name}\{rn}"
    }
    FaddD(rd, rn, rm) => "fadd d\{rd}, d\{rn}, d\{rm}"
    FaddS(rd, rn, rm) => "fadd s\{rd}, s\{rn}, s\{rm}"
    FsubD(rd, rn, rm) => "fsub d\{rd}, d\{rn}, d\{rm}"
    FsubS(rd, rn, rm) => "fsub s\{rd}, s\{rn}, s\{rm}"
    FmulD(rd, rn, rm) => "fmul d\{rd}, d\{rn}, d\{rm}"
    FmulS(rd, rn, rm) => "fmul s\{rd}, s\{rn}, s\{rm}"
    FdivD(rd, rn, rm) => "fdiv d\{rd}, d\{rn}, d\{rm}"
    FdivS(rd, rn, rm) => "fdiv s\{rd}, s\{rn}, s\{rm}"
    FmaxD(rd, rn, rm) => "fmax d\{rd}, d\{rn}, d\{rm}"
    FmaxS(rd, rn, rm) => "fmax s\{rd}, s\{rn}, s\{rm}"
    FminD(rd, rn, rm) => "fmin d\{rd}, d\{rn}, d\{rm}"
    FminS(rd, rn, rm) => "fmin s\{rd}, s\{rn}, s\{rm}"
    FmaxnmD(rd, rn, rm) => "fmaxnm d\{rd}, d\{rn}, d\{rm}"
    FmaxnmS(rd, rn, rm) => "fmaxnm s\{rd}, s\{rn}, s\{rm}"
    FminnmD(rd, rn, rm) => "fminnm d\{rd}, d\{rn}, d\{rm}"
    FminnmS(rd, rn, rm) => "fminnm s\{rd}, s\{rn}, s\{rm}"
    FsqrtD(rd, rn) => "fsqrt d\{rd}, d\{rn}"
    FsqrtS(rd, rn) => "fsqrt s\{rd}, s\{rn}"
    FabsD(rd, rn) => "fabs d\{rd}, d\{rn}"
    FabsS(rd, rn) => "fabs s\{rd}, s\{rn}"
    FnegD(rd, rn) => "fneg d\{rd}, d\{rn}"
    FnegS(rd, rn) => "fneg s\{rd}, s\{rn}"
    FrintpD(rd, rn) => "frintp d\{rd}, d\{rn}"
    FrintpS(rd, rn) => "frintp s\{rd}, s\{rn}"
    FrintmD(rd, rn) => "frintm d\{rd}, d\{rn}"
    FrintmS(rd, rn) => "frintm s\{rd}, s\{rn}"
    FrintzD(rd, rn) => "frintz d\{rd}, d\{rn}"
    FrintzS(rd, rn) => "frintz s\{rd}, s\{rn}"
    FrintnD(rd, rn) => "frintn d\{rd}, d\{rn}"
    FrintnS(rd, rn) => "frintn s\{rd}, s\{rn}"
    FmovD(rd, rm) => "fmov d\{rd}, d\{rm}"
    FmovS(rd, rm) => "fmov s\{rd}, s\{rm}"
    FmovDToX(rd, rn) => "fmov x\{rd}, d\{rn}"
    FmovSToW(rd, rn) => "fmov w\{rd}, s\{rn}"
    FmovXToD(rd, rn) => "fmov d\{rd}, x\{rn}"
    FmovWToS(rd, rn) => "fmov s\{rd}, w\{rn}"
    FcvtDS(rd, rn) => "fcvt d\{rd}, s\{rn}"
    FcvtSD(rd, rn) => "fcvt s\{rd}, d\{rn}"
    LdrSImm(rt, rn, imm12) => "ldr s\{rt}, [x\{rn}, #\{imm12}]"
    StrSImm(rt, rn, imm12) => "str s\{rt}, [x\{rn}, #\{imm12}]"
    LdrDImm(rt, rn, imm12) => "ldr d\{rt}, [x\{rn}, #\{imm12}]"
    StrDImm(rt, rn, imm12) => "str d\{rt}, [x\{rn}, #\{imm12}]"
    FcmpD(rn, rm) => "fcmp d\{rn}, d\{rm}"
    FcmpS(rn, rm) => "fcmp s\{rn}, s\{rm}"
    CmpReg(rn, rm) => "cmp x\{rn}, x\{rm}"
    CmpReg32(rn, rm) => "cmp w\{rn}, w\{rm}"
    CmpImm(rn, imm12) => "cmp x\{rn}, #\{imm12}"
    CmpImmShifted12(rn, imm12) => "cmp x\{rn}, #\{imm12}, lsl #12"
    CmpImm32(rn, imm12) => "cmp w\{rn}, #\{imm12}"
    CmpImm32Shifted12(rn, imm12) => "cmp w\{rn}, #\{imm12}, lsl #12"
    AddsImmZr(rn, imm12, is_64) => {
      let reg = if is_64 { "x" } else { "w" }
      let zr = if is_64 { "xzr" } else { "wzr" }
      "adds \{zr}, \{reg}\{rn}, #\{imm12}"
    }
    CCmpImm(rn, imm5, nzcv, cond, is_64) => {
      let reg = if is_64 { "x" } else { "w" }
      let cond_name = cond_name_str(cond)
      "ccmp \{reg}\{rn}, #\{imm5}, #\{nzcv}, \{cond_name}"
    }
    Cset(rd, cond) => {
      let cond_name = cond_name_str(cond)
      "cset x\{rd}, \{cond_name}"
    }
    Csel(rd, rn, rm, cond) => {
      let cond_name = cond_name_str(cond)
      "csel x\{rd}, x\{rn}, x\{rm}, \{cond_name}"
    }
    FcselD(rd, rn, rm, cond) => {
      let cond_name = cond_name_str(cond)
      "fcsel d\{rd}, d\{rn}, d\{rm}, \{cond_name}"
    }
    FcselS(rd, rn, rm, cond) => {
      let cond_name = cond_name_str(cond)
      "fcsel s\{rd}, s\{rn}, s\{rm}, \{cond_name}"
    }
    B(target_block) => "b block\{target_block}"
    BCond(cond, target_block) => {
      let cond_name = cond_name_str(cond)
      "b.\{cond_name} block\{target_block}"
    }
    Cbz(rt, target_block) => "cbz x\{rt}, block\{target_block}"
    Cbnz(rt, target_block) => "cbnz x\{rt}, block\{target_block}"
    Cbz32(rt, target_block) => "cbz w\{rt}, block\{target_block}"
    Cbnz32(rt, target_block) => "cbnz w\{rt}, block\{target_block}"
    BCondOffset(cond, offset_bytes) => {
      let cond_name = cond_name_str(cond)
      "b.\{cond_name} .+\{offset_bytes}"
    }
    CbnzOffset(rt, is_64, offset_bytes) => {
      let reg = if is_64 { "x" } else { "w" }
      "cbnz \{reg}\{rt}, .+\{offset_bytes}"
    }
    Brk(imm16) => "brk #\{imm16}"
    Ret(_) => "ret"
    Br(rn) => "br x\{rn}"
    Bl(target_block) => "bl block\{target_block}"
    Blr(rn) => "blr x\{rn}"
    Adr(rd, offset) => "adr x\{rd}, .+\{offset}"
    DmbIsh => "dmb ish"
    Nop => "nop"
    AlignTo(alignment) => "align to \{alignment} bytes"
    Cnt8B(rd, rn) => "cnt v\{rd}.8b, v\{rn}.8b"
    AddvB(rd, rn) => "addv b\{rd}, v\{rn}.8b"
    // NEON SIMD instructions
    LdrQ(rd, rn, imm) => "ldr q\{rd}, [x\{rn}, #\{imm}]"
    StrQ(rt, rn, imm) => "str q\{rt}, [x\{rn}, #\{imm}]"
    Dup16B(rd, rn) => "dup v\{rd}.16b, w\{rn}"
    Dup8H(rd, rn) => "dup v\{rd}.8h, w\{rn}"
    Dup4S(rd, rn) => "dup v\{rd}.4s, w\{rn}"
    Dup2D(rd, rn) => "dup v\{rd}.2d, x\{rn}"
    DupElem4S(rd, rn, lane) => "dup v\{rd}.4s, v\{rn}.s[\{lane}]"
    DupElem2D(rd, rn, lane) => "dup v\{rd}.2d, v\{rn}.d[\{lane}]"
    UmovB(rd, rn, lane) => "umov w\{rd}, v\{rn}.b[\{lane}]"
    UmovH(rd, rn, lane) => "umov w\{rd}, v\{rn}.h[\{lane}]"
    UmovS(rd, rn, lane) => "umov w\{rd}, v\{rn}.s[\{lane}]"
    UmovD(rd, rn, lane) => "umov x\{rd}, v\{rn}.d[\{lane}]"
    SmovB(rd, rn, lane) => "smov w\{rd}, v\{rn}.b[\{lane}]"
    SmovH(rd, rn, lane) => "smov w\{rd}, v\{rn}.h[\{lane}]"
    SmovS(rd, rn, lane) => "smov x\{rd}, v\{rn}.s[\{lane}]"
    DupScalarS(rd, rn, lane) => "dup s\{rd}, v\{rn}.s[\{lane}]"
    DupScalarD(rd, rn, lane) => "dup d\{rd}, v\{rn}.d[\{lane}]"
    InsB(rd, lane, rn) => "ins v\{rd}.b[\{lane}], w\{rn}"
    InsH(rd, lane, rn) => "ins v\{rd}.h[\{lane}], w\{rn}"
    InsS(rd, lane, rn) => "ins v\{rd}.s[\{lane}], w\{rn}"
    InsD(rd, lane, rn) => "ins v\{rd}.d[\{lane}], x\{rn}"
    InsElemS(rd, dlane, rn, slane) =>
      "ins v\{rd}.s[\{dlane}], v\{rn}.s[\{slane}]"
    InsElemD(rd, dlane, rn, slane) =>
      "ins v\{rd}.d[\{dlane}], v\{rn}.d[\{slane}]"
    Not16B(rd, rn) => "not v\{rd}.16b, v\{rn}.16b"
    And16B(rd, rn, rm) => "and v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Orr16B(rd, rn, rm) => "orr v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Eor16B(rd, rn, rm) => "eor v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Bic16B(rd, rn, rm) => "bic v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Bsl16B(rd, rn, rm) => "bsl v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Tbl1(rd, rn, rm) => "tbl v\{rd}.16b, {v\{rn}.16b}, v\{rm}.16b"
    Add16B(rd, rn, rm) => "add v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Sub16B(rd, rn, rm) => "sub v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Add8H(rd, rn, rm) => "add v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Sub8H(rd, rn, rm) => "sub v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Mul8H(rd, rn, rm) => "mul v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Add4S(rd, rn, rm) => "add v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Sub4S(rd, rn, rm) => "sub v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Mul4S(rd, rn, rm) => "mul v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Add2D(rd, rn, rm) => "add v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Sub2D(rd, rn, rm) => "sub v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Sqadd16B(rd, rn, rm) => "sqadd v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Sqadd8H(rd, rn, rm) => "sqadd v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Sqadd4S(rd, rn, rm) => "sqadd v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Sqadd2D(rd, rn, rm) => "sqadd v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Uqadd16B(rd, rn, rm) => "uqadd v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Uqadd8H(rd, rn, rm) => "uqadd v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Uqadd4S(rd, rn, rm) => "uqadd v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Uqadd2D(rd, rn, rm) => "uqadd v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Sqsub16B(rd, rn, rm) => "sqsub v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Sqsub8H(rd, rn, rm) => "sqsub v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Sqsub4S(rd, rn, rm) => "sqsub v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Sqsub2D(rd, rn, rm) => "sqsub v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Uqsub16B(rd, rn, rm) => "uqsub v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Uqsub8H(rd, rn, rm) => "uqsub v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Uqsub4S(rd, rn, rm) => "uqsub v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Uqsub2D(rd, rn, rm) => "uqsub v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Smin16B(rd, rn, rm) => "smin v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Smin8H(rd, rn, rm) => "smin v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Smin4S(rd, rn, rm) => "smin v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Umin16B(rd, rn, rm) => "umin v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Umin8H(rd, rn, rm) => "umin v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Umin4S(rd, rn, rm) => "umin v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Smax16B(rd, rn, rm) => "smax v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Smax8H(rd, rn, rm) => "smax v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Smax4S(rd, rn, rm) => "smax v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Umax16B(rd, rn, rm) => "umax v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Umax8H(rd, rn, rm) => "umax v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Umax4S(rd, rn, rm) => "umax v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Urhadd16B(rd, rn, rm) => "urhadd v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Urhadd8H(rd, rn, rm) => "urhadd v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Urhadd4S(rd, rn, rm) => "urhadd v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Abs16B(rd, rn) => "abs v\{rd}.16b, v\{rn}.16b"
    Abs8H(rd, rn) => "abs v\{rd}.8h, v\{rn}.8h"
    Abs4S(rd, rn) => "abs v\{rd}.4s, v\{rn}.4s"
    Abs2D(rd, rn) => "abs v\{rd}.2d, v\{rn}.2d"
    Neg16B(rd, rn) => "neg v\{rd}.16b, v\{rn}.16b"
    Neg8H(rd, rn) => "neg v\{rd}.8h, v\{rn}.8h"
    Neg4S(rd, rn) => "neg v\{rd}.4s, v\{rn}.4s"
    Neg2D(rd, rn) => "neg v\{rd}.2d, v\{rn}.2d"
    Cnt16B(rd, rn) => "cnt v\{rd}.16b, v\{rn}.16b"
    Sshl16B(rd, rn, rm) => "sshl v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Sshl8H(rd, rn, rm) => "sshl v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Sshl4S(rd, rn, rm) => "sshl v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Sshl2D(rd, rn, rm) => "sshl v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Ushl16B(rd, rn, rm) => "ushl v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Ushl8H(rd, rn, rm) => "ushl v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Ushl4S(rd, rn, rm) => "ushl v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Ushl2D(rd, rn, rm) => "ushl v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Cmeq16B(rd, rn, rm) => "cmeq v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Cmeq8H(rd, rn, rm) => "cmeq v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Cmeq4S(rd, rn, rm) => "cmeq v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Cmeq2D(rd, rn, rm) => "cmeq v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Cmgt16B(rd, rn, rm) => "cmgt v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Cmgt8H(rd, rn, rm) => "cmgt v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Cmgt4S(rd, rn, rm) => "cmgt v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Cmgt2D(rd, rn, rm) => "cmgt v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Cmge16B(rd, rn, rm) => "cmge v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Cmge8H(rd, rn, rm) => "cmge v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Cmge4S(rd, rn, rm) => "cmge v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Cmge2D(rd, rn, rm) => "cmge v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Cmhi16B(rd, rn, rm) => "cmhi v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Cmhi8H(rd, rn, rm) => "cmhi v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Cmhi4S(rd, rn, rm) => "cmhi v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Cmhi2D(rd, rn, rm) => "cmhi v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Cmhs16B(rd, rn, rm) => "cmhs v\{rd}.16b, v\{rn}.16b, v\{rm}.16b"
    Cmhs8H(rd, rn, rm) => "cmhs v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Cmhs4S(rd, rn, rm) => "cmhs v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Cmhs2D(rd, rn, rm) => "cmhs v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Xtn2S(rd, rn) => "xtn v\{rd}.2s, v\{rn}.2d"
    Sqxtn8B(rd, rn) => "sqxtn v\{rd}.8b, v\{rn}.8h"
    Sqxtn4H(rd, rn) => "sqxtn v\{rd}.4h, v\{rn}.4s"
    Sqxtn2S(rd, rn) => "sqxtn v\{rd}.2s, v\{rn}.2d"
    Sqxtun8B(rd, rn) => "sqxtun v\{rd}.8b, v\{rn}.8h"
    Sqxtun4H(rd, rn) => "sqxtun v\{rd}.4h, v\{rn}.4s"
    Uqxtn2S(rd, rn) => "uqxtn v\{rd}.2s, v\{rn}.2d"
    Sqxtn2_16B(rd, rn) => "sqxtn2 v\{rd}.16b, v\{rn}.8h"
    Sqxtn2_8H(rd, rn) => "sqxtn2 v\{rd}.8h, v\{rn}.4s"
    Sqxtun2_16B(rd, rn) => "sqxtun2 v\{rd}.16b, v\{rn}.8h"
    Sqxtun2_8H(rd, rn) => "sqxtun2 v\{rd}.8h, v\{rn}.4s"
    Sxtl8H(rd, rn) => "sxtl v\{rd}.8h, v\{rn}.8b"
    Sxtl4S(rd, rn) => "sxtl v\{rd}.4s, v\{rn}.4h"
    Sxtl2D(rd, rn) => "sxtl v\{rd}.2d, v\{rn}.2s"
    Sxtl2_8H(rd, rn) => "sxtl2 v\{rd}.8h, v\{rn}.16b"
    Sxtl2_4S(rd, rn) => "sxtl2 v\{rd}.4s, v\{rn}.8h"
    Sxtl2_2D(rd, rn) => "sxtl2 v\{rd}.2d, v\{rn}.4s"
    Uxtl8H(rd, rn) => "uxtl v\{rd}.8h, v\{rn}.8b"
    Uxtl4S(rd, rn) => "uxtl v\{rd}.4s, v\{rn}.4h"
    Uxtl2D(rd, rn) => "uxtl v\{rd}.2d, v\{rn}.2s"
    Uxtl2_8H(rd, rn) => "uxtl2 v\{rd}.8h, v\{rn}.16b"
    Uxtl2_4S(rd, rn) => "uxtl2 v\{rd}.4s, v\{rn}.8h"
    Uxtl2_2D(rd, rn) => "uxtl2 v\{rd}.2d, v\{rn}.4s"
    Smull8H(rd, rn, rm) => "smull v\{rd}.8h, v\{rn}.8b, v\{rm}.8b"
    Smull4S(rd, rn, rm) => "smull v\{rd}.4s, v\{rn}.4h, v\{rm}.4h"
    Smull2D(rd, rn, rm) => "smull v\{rd}.2d, v\{rn}.2s, v\{rm}.2s"
    Umull8H(rd, rn, rm) => "umull v\{rd}.8h, v\{rn}.8b, v\{rm}.8b"
    Umull4S(rd, rn, rm) => "umull v\{rd}.4s, v\{rn}.4h, v\{rm}.4h"
    Umull2D(rd, rn, rm) => "umull v\{rd}.2d, v\{rn}.2s, v\{rm}.2s"
    Smull2_8H(rd, rn, rm) => "smull2 v\{rd}.8h, v\{rn}.16b, v\{rm}.16b"
    Smull2_4S(rd, rn, rm) => "smull2 v\{rd}.4s, v\{rn}.8h, v\{rm}.8h"
    Smull2_2D(rd, rn, rm) => "smull2 v\{rd}.2d, v\{rn}.4s, v\{rm}.4s"
    Umull2_8H(rd, rn, rm) => "umull2 v\{rd}.8h, v\{rn}.16b, v\{rm}.16b"
    Umull2_4S(rd, rn, rm) => "umull2 v\{rd}.4s, v\{rn}.8h, v\{rm}.8h"
    Umull2_2D(rd, rn, rm) => "umull2 v\{rd}.2d, v\{rn}.4s, v\{rm}.4s"
    Saddlp8H(rd, rn) => "saddlp v\{rd}.8h, v\{rn}.16b"
    Saddlp4S(rd, rn) => "saddlp v\{rd}.4s, v\{rn}.8h"
    Uaddlp8H(rd, rn) => "uaddlp v\{rd}.8h, v\{rn}.16b"
    Uaddlp4S(rd, rn) => "uaddlp v\{rd}.4s, v\{rn}.8h"
    Uaddlp2D(rd, rn) => "uaddlp v\{rd}.2d, v\{rn}.4s"
    Addp8H(rd, rn, rm) => "addp v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Addp4S(rd, rn, rm) => "addp v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Sqrdmulh8H(rd, rn, rm) => "sqrdmulh v\{rd}.8h, v\{rn}.8h, v\{rm}.8h"
    Fadd4S(rd, rn, rm) => "fadd v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fadd2D(rd, rn, rm) => "fadd v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fsub4S(rd, rn, rm) => "fsub v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fsub2D(rd, rn, rm) => "fsub v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fmul4S(rd, rn, rm) => "fmul v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fmul2D(rd, rn, rm) => "fmul v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fdiv4S(rd, rn, rm) => "fdiv v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fdiv2D(rd, rn, rm) => "fdiv v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fmin4S(rd, rn, rm) => "fmin v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fmin2D(rd, rn, rm) => "fmin v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fmax4S(rd, rn, rm) => "fmax v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fmax2D(rd, rn, rm) => "fmax v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fmla4S(rd, rn, rm) => "fmla v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fmla2D(rd, rn, rm) => "fmla v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fmls4S(rd, rn, rm) => "fmls v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fmls2D(rd, rn, rm) => "fmls v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fabs4S(rd, rn) => "fabs v\{rd}.4s, v\{rn}.4s"
    Fabs2D(rd, rn) => "fabs v\{rd}.2d, v\{rn}.2d"
    Fneg4S(rd, rn) => "fneg v\{rd}.4s, v\{rn}.4s"
    Fneg2D(rd, rn) => "fneg v\{rd}.2d, v\{rn}.2d"
    Fsqrt4S(rd, rn) => "fsqrt v\{rd}.4s, v\{rn}.4s"
    Fsqrt2D(rd, rn) => "fsqrt v\{rd}.2d, v\{rn}.2d"
    Frintp4S(rd, rn) => "frintp v\{rd}.4s, v\{rn}.4s"
    Frintp2D(rd, rn) => "frintp v\{rd}.2d, v\{rn}.2d"
    Frintm4S(rd, rn) => "frintm v\{rd}.4s, v\{rn}.4s"
    Frintm2D(rd, rn) => "frintm v\{rd}.2d, v\{rn}.2d"
    Frintz4S(rd, rn) => "frintz v\{rd}.4s, v\{rn}.4s"
    Frintz2D(rd, rn) => "frintz v\{rd}.2d, v\{rn}.2d"
    Frintn4S(rd, rn) => "frintn v\{rd}.4s, v\{rn}.4s"
    Frintn2D(rd, rn) => "frintn v\{rd}.2d, v\{rn}.2d"
    Fcmeq4S(rd, rn, rm) => "fcmeq v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fcmeq2D(rd, rn, rm) => "fcmeq v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fcmgt4S(rd, rn, rm) => "fcmgt v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fcmgt2D(rd, rn, rm) => "fcmgt v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fcmge4S(rd, rn, rm) => "fcmge v\{rd}.4s, v\{rn}.4s, v\{rm}.4s"
    Fcmge2D(rd, rn, rm) => "fcmge v\{rd}.2d, v\{rn}.2d, v\{rm}.2d"
    Fcvtzs4S(rd, rn) => "fcvtzs v\{rd}.4s, v\{rn}.4s"
    Fcvtzs2D(rd, rn) => "fcvtzs v\{rd}.2d, v\{rn}.2d"
    Fcvtzu4S(rd, rn) => "fcvtzu v\{rd}.4s, v\{rn}.4s"
    Fcvtzu2D(rd, rn) => "fcvtzu v\{rd}.2d, v\{rn}.2d"
    Scvtf4S(rd, rn) => "scvtf v\{rd}.4s, v\{rn}.4s"
    Scvtf2D(rd, rn) => "scvtf v\{rd}.2d, v\{rn}.2d"
    Ucvtf4S(rd, rn) => "ucvtf v\{rd}.4s, v\{rn}.4s"
    Ucvtf2D(rd, rn) => "ucvtf v\{rd}.2d, v\{rn}.2d"
    Fcvtn2S(rd, rn) => "fcvtn v\{rd}.2s, v\{rn}.2d"
    Fcvtl2D(rd, rn) => "fcvtl v\{rd}.2d, v\{rn}.2s"
    Ld1rB(rd, rn) => "ld1r {v\{rd}.16b}, [x\{rn}]"
    Ld1rH(rd, rn) => "ld1r {v\{rd}.8h}, [x\{rn}]"
    Ld1rS(rd, rn) => "ld1r {v\{rd}.4s}, [x\{rn}]"
    Ld1rD(rd, rn) => "ld1r {v\{rd}.2d}, [x\{rn}]"
    Ld1B(rd, rn, lane) => "ld1 {v\{rd}.b}[\{lane}], [x\{rn}]"
    Ld1H(rd, rn, lane) => "ld1 {v\{rd}.h}[\{lane}], [x\{rn}]"
    Ld1S(rd, rn, lane) => "ld1 {v\{rd}.s}[\{lane}], [x\{rn}]"
    Ld1D(rd, rn, lane) => "ld1 {v\{rd}.d}[\{lane}], [x\{rn}]"
    St1B(rt, rn, lane) => "st1 {v\{rt}.b}[\{lane}], [x\{rn}]"
    St1H(rt, rn, lane) => "st1 {v\{rt}.h}[\{lane}], [x\{rn}]"
    St1S(rt, rn, lane) => "st1 {v\{rt}.s}[\{lane}], [x\{rn}]"
    St1D(rt, rn, lane) => "st1 {v\{rt}.d}[\{lane}], [x\{rn}]"
    Umaxv16B(rd, rn) => "umaxv b\{rd}, v\{rn}.16b"
    Uminv16B(rd, rn) => "uminv b\{rd}, v\{rn}.16b"
    Uminv8H(rd, rn) => "uminv h\{rd}, v\{rn}.8h"
    Uminv4S(rd, rn) => "uminv s\{rd}, v\{rn}.4s"
    MoviZero(rd) => "movi v\{rd}.2d, #0"
    OrrVec(rd, rn) => "orr v\{rd}.16b, v\{rn}.16b, v\{rn}.16b"
    Rev64_4S(rd, rn) => "rev64 v\{rd}.4s, v\{rn}.4s"
    ShlImm2D(rd, rn, imm) => "shl v\{rd}.2d, v\{rn}.2d, #\{imm}"
  }
}

///|
fn cond_name_str(cond : Int) -> String {
  match cond {
    0 => "eq"
    1 => "ne"
    2 => "hs"
    3 => "lo"
    4 => "mi"
    5 => "pl"
    6 => "vs"
    7 => "vc"
    8 => "hi"
    9 => "ls"
    10 => "ge"
    11 => "lt"
    12 => "gt"
    13 => "le"
    14 => "al"
    _ => "?\{cond}"
  }
}

///|