
/*---------------------------------------------------------------*/
/*--- begin                                  host_mips_defs.c ---*/
/*---------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2010-2013 RT-RK
      mips-valgrind@rt-rk.com

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

#include "libvex_basictypes.h"
#include "libvex.h"
#include "libvex_trc_values.h"

#include "main_util.h"
#include "host_generic_regs.h"
#include "host_mips_defs.h"

/* guest_COND offset. */
#define COND_OFFSET(__mode64) (__mode64 ? 612 : 448)

/* Register number for guest state pointer in host code. */
#define GuestSP 23


/*---------------- Registers ----------------*/

const RRegUniverse* getRRegUniverse_MIPS ( Bool mode64 )
{
   /* The real-register universe is a big constant, so we just want to
      initialise it once.  rRegUniverse_MIPS_initted values: 0=not initted,
      1=initted for 32-bit-mode, 2=initted for 64-bit-mode */
   static RRegUniverse rRegUniverse_MIPS;
   static UInt         rRegUniverse_MIPS_initted = 0;

   /* Handy shorthand, nothing more */
   RRegUniverse* ru = &rRegUniverse_MIPS;

   /* This isn't thread-safe.  Sigh. */
   UInt howNeeded = mode64 ? 2 : 1;
   if (LIKELY(rRegUniverse_MIPS_initted == howNeeded))
      return ru;

   RRegUniverse__init(ru);

   /* Add the registers.  The initial segment of this array must be
      those available for allocation by reg-alloc, and those that
      follow are not available for allocation. */
   ru->regs[ru->size++] = hregMIPS_GPR16(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR17(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR18(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR19(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR20(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR21(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR22(mode64);

   ru->regs[ru->size++] = hregMIPS_GPR12(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR13(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR14(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR15(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR24(mode64);
   /* s7  (=guest_state) */
   ru->regs[ru->size++] = hregMIPS_F16(mode64);
   ru->regs[ru->size++] = hregMIPS_F18(mode64);
   ru->regs[ru->size++] = hregMIPS_F20(mode64);
   ru->regs[ru->size++] = hregMIPS_F22(mode64);
   ru->regs[ru->size++] = hregMIPS_F24(mode64);
   ru->regs[ru->size++] = hregMIPS_F26(mode64);
   ru->regs[ru->size++] = hregMIPS_F28(mode64);
   ru->regs[ru->size++] = hregMIPS_F30(mode64);
   if (!mode64) {
      /* Fake double floating point */
      ru->regs[ru->size++] = hregMIPS_D0(mode64);
      ru->regs[ru->size++] = hregMIPS_D1(mode64);
      ru->regs[ru->size++] = hregMIPS_D2(mode64);
      ru->regs[ru->size++] = hregMIPS_D3(mode64);
      ru->regs[ru->size++] = hregMIPS_D4(mode64);
      ru->regs[ru->size++] = hregMIPS_D5(mode64);
      ru->regs[ru->size++] = hregMIPS_D6(mode64);
      ru->regs[ru->size++] = hregMIPS_D7(mode64);
   }

   ru->allocable = ru->size;
   /* And other regs, not available to the allocator. */

   ru->regs[ru->size++] = hregMIPS_HI(mode64);
   ru->regs[ru->size++] = hregMIPS_LO(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR0(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR1(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR2(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR3(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR4(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR5(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR6(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR7(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR8(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR9(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR10(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR11(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR23(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR25(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR29(mode64);
   ru->regs[ru->size++] = hregMIPS_GPR31(mode64);

   rRegUniverse_MIPS_initted = howNeeded;

   RRegUniverse__check_is_sane(ru);
   return ru;
}


void ppHRegMIPS(HReg reg, Bool mode64)
{
   Int r;
   static const HChar *ireg32_names[35]
       = { "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
      "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
      "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
      "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31",
      "%32", "%33", "%34",
   };

   static const HChar *freg32_names[32]
       = { "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
      "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
      "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
      "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "f30", "$f31"
   };

   static const HChar *freg64_names[32]
       = { "$d0", "$d1", "$d2", "$d3", "$d4", "$d5", "$d6", "$d7",
      "$d8", "$d9", "$d10", "$d11", "$d12", "$d13", "$d14", "$d15",
   };

   /* Be generic for all virtual regs. */
   if (hregIsVirtual(reg)) {
      ppHReg(reg);
      return;
   }

   /* But specific for real regs. */
   vassert(hregClass(reg) == HRcInt32 || hregClass(reg) == HRcInt64 ||
           hregClass(reg) == HRcFlt32 || hregClass(reg) == HRcFlt64);

   /* But specific for real regs. */
   switch (hregClass(reg)) {
      case HRcInt32:
         r = hregEncoding(reg);
         vassert(r >= 0 && r < 32);
         vex_printf("%s", ireg32_names[r]);
         return;
      case HRcInt64:
         r = hregEncoding (reg);
         vassert (r >= 0 && r < 32);
         vex_printf ("%s", ireg32_names[r]);
         return;
      case HRcFlt32:
         r = hregEncoding(reg);
         vassert(r >= 0 && r < 32);
         vex_printf("%s", freg32_names[r]);
         return;
      case HRcFlt64:
         r = hregEncoding(reg);
         vassert(r >= 0 && r < 32);
         vex_printf("%s", freg64_names[r]);
         return;
      default:
         vpanic("ppHRegMIPS");
         break;
   }

   return;
}


/*----------------- Condition Codes ----------------------*/

const HChar *showMIPSCondCode(MIPSCondCode cond)
{
   const HChar* ret;
   switch (cond) {
      case MIPScc_EQ:
         ret = "EQ";  /* equal */
         break;
      case MIPScc_NE:
         ret = "NEQ";  /* not equal */
         break;
      case MIPScc_HS:
         ret = "GE";  /* >=u (Greater Than or Equal) */
         break;
      case MIPScc_LO:
         ret = "LT";  /* <u  (lower) */
         break;
      case MIPScc_MI:
         ret = "MI";  /* minus (negative) */
         break;
      case MIPScc_PL:
         ret = "PL";  /* plus (zero or +ve) */
         break;
      case MIPScc_VS:
         ret = "VS";  /* overflow */
         break;
      case MIPScc_VC:
         ret = "VC";  /* no overflow */
         break;
      case MIPScc_HI:
         ret = "HI";  /* >u   (higher) */
         break;
      case MIPScc_LS:
         ret = "LS";  /* <=u  (lower or same) */
         break;
      case MIPScc_GE:
         ret = "GE";  /* >=s (signed greater or equal) */
         break;
      case MIPScc_LT:
         ret = "LT";  /* <s  (signed less than) */
         break;
      case MIPScc_GT:
         ret = "GT";  /* >s  (signed greater) */
         break;
      case MIPScc_LE:
         ret = "LE";  /* <=s (signed less or equal) */
         break;
      case MIPScc_AL:
         ret = "AL";  /* always (unconditional) */
         break;
      case MIPScc_NV:
         ret = "NV";  /* never (unconditional): */
         break;
      default:
         vpanic("showMIPSCondCode");
         break;
   }
   return ret;
}

const HChar *showMIPSFpOp(MIPSFpOp op)
{
   const HChar *ret;
   switch (op) {
      case Mfp_ADDD:
         ret = "add.d";
         break;
      case Mfp_SUBD:
         ret = "sub.d";
         break;
      case Mfp_MULD:
         ret = "mul.d";
         break;
      case Mfp_DIVD:
         ret = "div.d";
         break;
      case Mfp_MADDD:
         ret = "madd.d";
         break;
      case Mfp_MSUBD:
         ret = "msub.d";
         break;
      case Mfp_MADDS:
         ret = "madd.s";
         break;
      case Mfp_MSUBS:
         ret = "msub.s";
         break;
      case Mfp_ADDS:
         ret = "add.s";
         break;
      case Mfp_SUBS:
         ret = "sub.s";
         break;
      case Mfp_MULS:
         ret = "mul.s";
         break;
      case Mfp_DIVS:
         ret = "div.s";
         break;
      case Mfp_SQRTS:
         ret = "sqrt.s";
         break;
      case Mfp_SQRTD:
         ret = "sqrt.d";
         break;
      case Mfp_ABSS:
         ret = "abs.s";
         break;
      case Mfp_ABSD:
         ret = "abs.d";
         break;
      case Mfp_NEGS:
         ret = "neg.s";
         break;
      case Mfp_NEGD:
         ret = "neg.d";
         break;
      case Mfp_MOVS:
         ret = "mov.s";
         break;
      case Mfp_MOVD:
         ret = "mov.d";
         break;
      case Mfp_ROUNDWS:
         ret = "round.w.s";
         break;
      case Mfp_ROUNDWD:
         ret = "round.w.d";
         break;
      case Mfp_ROUNDLD:
         ret = "round.l.d";
         break;
      case Mfp_FLOORWS:
         ret = "floor.w.s";
         break;
      case Mfp_FLOORWD:
         ret = "floor.w.d";
         break;
      case Mfp_CVTDW:
         ret = "cvt.d.w";
         break;
      case Mfp_CVTDL:
         ret = "cvt.d.l";
         break;
      case Mfp_CVTDS:
         ret = "cvt.d.s";
         break;
      case Mfp_CVTSD:
         ret = "cvt.s.d";
         break;
      case Mfp_CVTSW:
         ret = "cvt.s.w";
         break;
      case Mfp_CVTWS:
         ret = "cvt.w.s";
         break;
      case Mfp_CVTWD:
         ret = "cvt.w.d";
         break;
      case Mfp_CVTLD:
         ret = "cvt.l.d";
         break;
      case Mfp_CVTLS:
         ret = "cvt.l.s";
         break;
      case Mfp_TRUWD:
         ret = "trunc.w.d";
         break;
      case Mfp_TRUWS:
         ret = "trunc.w.s";
         break;
      case Mfp_TRULD:
         ret = "trunc.l.d";
         break;
      case Mfp_TRULS:
         ret = "trunc.l.s";
         break;
      case Mfp_CEILWS:
         ret = "ceil.w.s";
         break;
      case Mfp_CEILWD:
         ret = "ceil.w.d";
         break;
      case Mfp_CEILLS:
         ret = "ceil.l.s";
         break;
      case Mfp_CEILLD:
         ret = "ceil.l.d";
         break;
      case Mfp_CMP_UN:
         ret = "c.un.d";
         break;
      case Mfp_CMP_EQ:
         ret = "c.eq.d";
         break;
      case Mfp_CMP_LT:
         ret = "c.lt.d";
         break;
      case Mfp_CMP_NGT:
         ret = "c.ngt.d";
         break;
      default:
         vex_printf("Unknown op: %d", op);
         vpanic("showMIPSFpOp");
         break;
   }
   return ret;
}

/* Show move from/to fpr to/from gpr */
const HChar* showMIPSFpGpMoveOp ( MIPSFpGpMoveOp op )
{
   const HChar *ret;
   switch (op) {
      case MFpGpMove_mfc1:
         ret = "mfc1";
         break;
      case MFpGpMove_dmfc1:
         ret = "dmfc1";
         break;
      case MFpGpMove_mtc1:
         ret = "mtc1";
         break;
      case MFpGpMove_dmtc1:
         ret = "dmtc1";
         break;
      default:
         vpanic("showMIPSFpGpMoveOp");
         break;
   }
   return ret;
}

/* Show floating point move conditional */
const HChar* showMIPSMoveCondOp ( MIPSMoveCondOp op )
{
   const HChar *ret;
   switch (op) {
      case MFpMoveCond_movns:
         ret = "movn.s";
         break;
      case MFpMoveCond_movnd:
         ret = "movn.d";
         break;
      case MMoveCond_movn:
         ret = "movn";
         break;
      default:
         vpanic("showMIPSFpMoveCondOp");
         break;
   }
   return ret;
}

/* --------- MIPSAMode: memory address expressions. --------- */

MIPSAMode *MIPSAMode_IR(Int idx, HReg base)
{
   MIPSAMode *am = LibVEX_Alloc_inline(sizeof(MIPSAMode));
   am->tag = Mam_IR;
   am->Mam.IR.base = base;
   am->Mam.IR.index = idx;

   return am;
}

MIPSAMode *MIPSAMode_RR(HReg idx, HReg base)
{
   MIPSAMode *am = LibVEX_Alloc_inline(sizeof(MIPSAMode));
   am->tag = Mam_RR;
   am->Mam.RR.base = base;
   am->Mam.RR.index = idx;

   return am;
}

MIPSAMode *dopyMIPSAMode(MIPSAMode * am)
{
   MIPSAMode* ret;
   switch (am->tag) {
      case Mam_IR:
         ret = MIPSAMode_IR(am->Mam.IR.index, am->Mam.IR.base);
         break;
      case Mam_RR:
         ret = MIPSAMode_RR(am->Mam.RR.index, am->Mam.RR.base);
         break;
      default:
         vpanic("dopyMIPSAMode");
         break;
   }
   return ret;
}

MIPSAMode *nextMIPSAModeFloat(MIPSAMode * am)
{
   MIPSAMode* ret;
   switch (am->tag) {
      case Mam_IR:
         ret = MIPSAMode_IR(am->Mam.IR.index + 4, am->Mam.IR.base);
         break;
      case Mam_RR:
         /* We can't do anything with the RR case, so if it appears
            we simply have to give up. */
         /* fallthrough */
      default:
         vpanic("nextMIPSAModeFloat");
         break;
   }
   return ret;
}

MIPSAMode *nextMIPSAModeInt(MIPSAMode * am)
{
   MIPSAMode* ret;
   switch (am->tag) {
      case Mam_IR:
         ret = MIPSAMode_IR(am->Mam.IR.index + 4, am->Mam.IR.base);
         break;
      case Mam_RR:
         /* We can't do anything with the RR case, so if it appears
            we simply have to give up. */
         /* fallthrough */
      default:
         vpanic("nextMIPSAModeInt");
         break;
   }
   return ret;
}

void ppMIPSAMode(MIPSAMode * am, Bool mode64)
{
   switch (am->tag) {
      case Mam_IR:
         if (am->Mam.IR.index == 0)
            vex_printf("0(");
         else
            vex_printf("%d(", (Int) am->Mam.IR.index);
         ppHRegMIPS(am->Mam.IR.base, mode64);
         vex_printf(")");
         return;
      case Mam_RR:
         ppHRegMIPS(am->Mam.RR.base, mode64);
         vex_printf(", ");
         ppHRegMIPS(am->Mam.RR.index, mode64);
         return;
      default:
         vpanic("ppMIPSAMode");
         break;
   }
}

static void addRegUsage_MIPSAMode(HRegUsage * u, MIPSAMode * am)
{
   switch (am->tag) {
      case Mam_IR:
         addHRegUse(u, HRmRead, am->Mam.IR.base);
         return;
      case Mam_RR:
         addHRegUse(u, HRmRead, am->Mam.RR.base);
         addHRegUse(u, HRmRead, am->Mam.RR.index);
         return;
      default:
         vpanic("addRegUsage_MIPSAMode");
         break;
   }
}

static void mapRegs_MIPSAMode(HRegRemap * m, MIPSAMode * am)
{
   switch (am->tag) {
      case Mam_IR:
         am->Mam.IR.base = lookupHRegRemap(m, am->Mam.IR.base);
         return;
      case Mam_RR:
         am->Mam.RR.base = lookupHRegRemap(m, am->Mam.RR.base);
         am->Mam.RR.index = lookupHRegRemap(m, am->Mam.RR.index);
         return;
      default:
         vpanic("mapRegs_MIPSAMode");
         break;
   }
}

/* --------- Operand, which can be a reg or a u16/s16. --------- */

MIPSRH *MIPSRH_Imm(Bool syned, UShort imm16)
{
   MIPSRH *op = LibVEX_Alloc_inline(sizeof(MIPSRH));
   op->tag = Mrh_Imm;
   op->Mrh.Imm.syned = syned;
   op->Mrh.Imm.imm16 = imm16;
   /* If this is a signed value, ensure it's not -32768, so that we
      are guaranteed always to be able to negate if needed. */
   if (syned)
      vassert(imm16 != 0x8000);
   vassert(syned == True || syned == False);
   return op;
}

MIPSRH *MIPSRH_Reg(HReg reg)
{
   MIPSRH *op = LibVEX_Alloc_inline(sizeof(MIPSRH));
   op->tag = Mrh_Reg;
   op->Mrh.Reg.reg = reg;
   return op;
}

void ppMIPSRH(MIPSRH * op, Bool mode64)
{
   MIPSRHTag tag = op->tag;
   switch (tag) {
      case Mrh_Imm:
         if (op->Mrh.Imm.syned)
            vex_printf("%d", (Int) (Short) op->Mrh.Imm.imm16);
         else
            vex_printf("%u", (UInt) (UShort) op->Mrh.Imm.imm16);
         return;
      case Mrh_Reg:
         ppHRegMIPS(op->Mrh.Reg.reg, mode64);
         return;
      default:
         vpanic("ppMIPSRH");
         break;
   }
}

/* An MIPSRH can only be used in a "read" context (what would it mean
   to write or modify a literal?) and so we enumerate its registers
   accordingly. */
static void addRegUsage_MIPSRH(HRegUsage * u, MIPSRH * op)
{
   switch (op->tag) {
      case Mrh_Imm:
         return;
      case Mrh_Reg:
         addHRegUse(u, HRmRead, op->Mrh.Reg.reg);
         return;
      default:
         vpanic("addRegUsage_MIPSRH");
         break;
   }
}

static void mapRegs_MIPSRH(HRegRemap * m, MIPSRH * op)
{
   switch (op->tag) {
      case Mrh_Imm:
         return;
      case Mrh_Reg:
         op->Mrh.Reg.reg = lookupHRegRemap(m, op->Mrh.Reg.reg);
         return;
      default:
         vpanic("mapRegs_MIPSRH");
         break;
   }
}

/* --------- Instructions. --------- */

const HChar *showMIPSUnaryOp(MIPSUnaryOp op)
{
   const HChar* ret;
   switch (op) {
      case Mun_CLO:
         ret = "clo";
         break;
      case Mun_CLZ:
         ret = "clz";
         break;
      case Mun_NOP:
         ret = "nop";
         break;
      case Mun_DCLO:
         ret = "dclo";
         break;
      case Mun_DCLZ:
         ret = "dclz";
         break;
      default:
         vpanic("showMIPSUnaryOp");
         break;
   }
   return ret;
}

const HChar *showMIPSAluOp(MIPSAluOp op, Bool immR)
{
   const HChar* ret;
   switch (op) {
      case Malu_ADD:
         ret = immR ? "addiu" : "addu";
         break;
      case Malu_SUB:
         ret = "subu";
         break;
      case Malu_AND:
         ret = immR ? "andi" : "and";
         break;
      case Malu_OR:
         ret = immR ? "ori" : "or";
         break;
      case Malu_NOR:
         vassert(immR == False); /*there's no nor with an immediate operand!? */
         ret = "nor";
         break;
      case Malu_XOR:
         ret = immR ? "xori" : "xor";
         break;
      case Malu_DADD:
         ret = immR ? "daddi" : "dadd";
         break;
      case Malu_DSUB:
         ret = immR ? "dsubi" : "dsub";
         break;
      case Malu_SLT:
         ret = immR ? "slti" : "slt";
         break;
      default:
         vpanic("showMIPSAluOp");
         break;
   }
   return ret;
}

const HChar *showMIPSShftOp(MIPSShftOp op, Bool immR, Bool sz32)
{
   const HChar *ret;
   switch (op) {
      case Mshft_SRA:
         ret = immR ? (sz32 ? "sra" : "dsra") : (sz32 ? "srav" : "dsrav");
         break;
      case Mshft_SLL:
         ret = immR ? (sz32 ? "sll" : "dsll") : (sz32 ? "sllv" : "dsllv");
         break;
      case Mshft_SRL:
         ret = immR ? (sz32 ? "srl" : "dsrl") : (sz32 ? "srlv" : "dsrlv");
         break;
      default:
         vpanic("showMIPSShftOp");
         break;
   }
   return ret;
}

const HChar *showMIPSMaccOp(MIPSMaccOp op, Bool variable)
{
   const HChar *ret;
   switch (op) {
      case Macc_ADD:
         ret = variable ? "madd" : "maddu";
         break;
      case Macc_SUB:
         ret = variable ? "msub" : "msubu";
         break;
      default:
         vpanic("showMIPSAccOp");
         break;
   }
   return ret;
}

MIPSInstr *MIPSInstr_LI(HReg dst, ULong imm)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_LI;
   i->Min.LI.dst = dst;
   i->Min.LI.imm = imm;
   return i;
}

MIPSInstr *MIPSInstr_Alu(MIPSAluOp op, HReg dst, HReg srcL, MIPSRH * srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Alu;
   i->Min.Alu.op = op;
   i->Min.Alu.dst = dst;
   i->Min.Alu.srcL = srcL;
   i->Min.Alu.srcR = srcR;
   return i;
}

MIPSInstr *MIPSInstr_Shft(MIPSShftOp op, Bool sz32, HReg dst, HReg srcL,
                          MIPSRH * srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Shft;
   i->Min.Shft.op = op;
   i->Min.Shft.sz32 = sz32;
   i->Min.Shft.dst = dst;
   i->Min.Shft.srcL = srcL;
   i->Min.Shft.srcR = srcR;
   return i;
}

MIPSInstr *MIPSInstr_Unary(MIPSUnaryOp op, HReg dst, HReg src)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Unary;
   i->Min.Unary.op = op;
   i->Min.Unary.dst = dst;
   i->Min.Unary.src = src;
   return i;
}

MIPSInstr *MIPSInstr_Cmp(Bool syned, Bool sz32, HReg dst, HReg srcL, HReg srcR,
                         MIPSCondCode cond)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Cmp;
   i->Min.Cmp.syned = syned;
   i->Min.Cmp.sz32 = sz32;
   i->Min.Cmp.dst = dst;
   i->Min.Cmp.srcL = srcL;
   i->Min.Cmp.srcR = srcR;
   i->Min.Cmp.cond = cond;
   return i;
}

/* multiply */
MIPSInstr *MIPSInstr_Mul(Bool syned, Bool wid, Bool sz32, HReg dst, HReg srcL,
                         HReg srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Mul;
   i->Min.Mul.syned = syned;
   i->Min.Mul.widening = wid; /* widen=True else False */
   i->Min.Mul.sz32 = sz32; /* True = 32 bits */
   i->Min.Mul.dst = dst;
   i->Min.Mul.srcL = srcL;
   i->Min.Mul.srcR = srcR;
   return i;
}

/* msub */
MIPSInstr *MIPSInstr_Msub(Bool syned, HReg srcL, HReg srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Macc;

   i->Min.Macc.op = Macc_SUB;
   i->Min.Macc.syned = syned;
   i->Min.Macc.srcL = srcL;
   i->Min.Macc.srcR = srcR;
   return i;
}

/* madd */
MIPSInstr *MIPSInstr_Madd(Bool syned, HReg srcL, HReg srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Macc;

   i->Min.Macc.op = Macc_ADD;
   i->Min.Macc.syned = syned;
   i->Min.Macc.srcL = srcL;
   i->Min.Macc.srcR = srcR;
   return i;
}

/* div */
MIPSInstr *MIPSInstr_Div(Bool syned, Bool sz32, HReg srcL, HReg srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Div;
   i->Min.Div.syned = syned;
   i->Min.Div.sz32 = sz32; /* True = 32 bits */
   i->Min.Div.srcL = srcL;
   i->Min.Div.srcR = srcR;
   return i;
}

MIPSInstr *MIPSInstr_Call ( MIPSCondCode cond, Addr64 target, UInt argiregs,
                            HReg src, RetLoc rloc )
{
   UInt mask;
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Call;
   i->Min.Call.cond = cond;
   i->Min.Call.target = target;
   i->Min.Call.argiregs = argiregs;
   i->Min.Call.src = src;
   i->Min.Call.rloc = rloc;
   /* Only $4 .. $7/$11 inclusive may be used as arg regs. */
   mask = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9)
          | (1 << 10) | (1 << 11);
   vassert(0 == (argiregs & ~mask));
   vassert(is_sane_RetLoc(rloc));
   return i;
}

MIPSInstr *MIPSInstr_CallAlways ( MIPSCondCode cond, Addr64 target,
                                  UInt argiregs, RetLoc rloc )
{
   UInt mask;
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Call;
   i->Min.Call.cond = cond;
   i->Min.Call.target = target;
   i->Min.Call.argiregs = argiregs;
   i->Min.Call.rloc = rloc;
   /* Only $4 .. $7/$11 inclusive may be used as arg regs. */
   mask = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9)
          | (1 << 10) | (1 << 11);
   vassert(0 == (argiregs & ~mask));
   vassert(is_sane_RetLoc(rloc));
   return i;
}

MIPSInstr *MIPSInstr_XDirect ( Addr64 dstGA, MIPSAMode* amPC,
                               MIPSCondCode cond, Bool toFastEP ) {
   MIPSInstr* i               = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag                     = Min_XDirect;
   i->Min.XDirect.dstGA       = dstGA;
   i->Min.XDirect.amPC        = amPC;
   i->Min.XDirect.cond        = cond;
   i->Min.XDirect.toFastEP    = toFastEP;
   return i;
}

MIPSInstr *MIPSInstr_XIndir ( HReg dstGA, MIPSAMode* amPC,
                              MIPSCondCode cond ) {
   MIPSInstr* i            = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag                  = Min_XIndir;
   i->Min.XIndir.dstGA     = dstGA;
   i->Min.XIndir.amPC      = amPC;
   i->Min.XIndir.cond      = cond;
   return i;
}

MIPSInstr *MIPSInstr_XAssisted ( HReg dstGA, MIPSAMode* amPC,
                                 MIPSCondCode cond, IRJumpKind jk ) {
   MIPSInstr* i               = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag                     = Min_XAssisted;
   i->Min.XAssisted.dstGA     = dstGA;
   i->Min.XAssisted.amPC      = amPC;
   i->Min.XAssisted.cond      = cond;
   i->Min.XAssisted.jk        = jk;
   return i;
}

MIPSInstr *MIPSInstr_Load(UChar sz, HReg dst, MIPSAMode * src, Bool mode64)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Load;
   i->Min.Load.sz = sz;
   i->Min.Load.src = src;
   i->Min.Load.dst = dst;
   vassert(sz == 1 || sz == 2 || sz == 4 || sz == 8);

   if (sz == 8)
      vassert(mode64);
   return i;
}

MIPSInstr *MIPSInstr_Store(UChar sz, MIPSAMode * dst, HReg src, Bool mode64)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Store;
   i->Min.Store.sz = sz;
   i->Min.Store.src = src;
   i->Min.Store.dst = dst;
   vassert(sz == 1 || sz == 2 || sz == 4 || sz == 8);

   if (sz == 8)
      vassert(mode64);
   return i;
}

MIPSInstr *MIPSInstr_LoadL(UChar sz, HReg dst, MIPSAMode * src, Bool mode64)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_LoadL;
   i->Min.LoadL.sz  = sz;
   i->Min.LoadL.src = src;
   i->Min.LoadL.dst = dst;
   vassert(sz == 4 || sz == 8);

   if (sz == 8)
      vassert(mode64);
   return i;
}

MIPSInstr *MIPSInstr_Cas(UChar sz, HReg old, HReg addr,
                         HReg expd, HReg data, Bool mode64)
{
   MIPSInstr *i    = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag          = Min_Cas;
   i->Min.Cas.sz   = sz;
   i->Min.Cas.old  = old;
   i->Min.Cas.addr = addr;
   i->Min.Cas.expd = expd;
   i->Min.Cas.data = data;
   vassert(sz == 1 || sz == 2 || sz == 4 || sz == 8);

   if (sz == 8)
      vassert(mode64);
   return i;
}

MIPSInstr *MIPSInstr_StoreC(UChar sz, MIPSAMode * dst, HReg src, Bool mode64)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_StoreC;
   i->Min.StoreC.sz  = sz;
   i->Min.StoreC.src = src;
   i->Min.StoreC.dst = dst;
   vassert(sz == 4 || sz == 8);

   if (sz == 8)
      vassert(mode64);
   return i;
}

MIPSInstr *MIPSInstr_Mthi(HReg src)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Mthi;
   i->Min.MtHL.src = src;
   return i;
}

MIPSInstr *MIPSInstr_Mtlo(HReg src)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Mtlo;
   i->Min.MtHL.src = src;
   return i;
}

MIPSInstr *MIPSInstr_Mfhi(HReg dst)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Mfhi;
   i->Min.MfHL.dst = dst;
   return i;
}

MIPSInstr *MIPSInstr_Mflo(HReg dst)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_Mflo;
   i->Min.MfHL.dst = dst;
   return i;
}

/* Read/Write Link Register */
MIPSInstr *MIPSInstr_RdWrLR(Bool wrLR, HReg gpr)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_RdWrLR;
   i->Min.RdWrLR.wrLR = wrLR;
   i->Min.RdWrLR.gpr = gpr;
   return i;
}

MIPSInstr *MIPSInstr_FpLdSt(Bool isLoad, UChar sz, HReg reg, MIPSAMode * addr)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_FpLdSt;
   i->Min.FpLdSt.isLoad = isLoad;
   i->Min.FpLdSt.sz = sz;
   i->Min.FpLdSt.reg = reg;
   i->Min.FpLdSt.addr = addr;
   vassert(sz == 4 || sz == 8);
   return i;
}

MIPSInstr *MIPSInstr_FpUnary(MIPSFpOp op, HReg dst, HReg src)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_FpUnary;
   i->Min.FpUnary.op = op;
   i->Min.FpUnary.dst = dst;
   i->Min.FpUnary.src = src;
   return i;
}

MIPSInstr *MIPSInstr_FpBinary(MIPSFpOp op, HReg dst, HReg srcL, HReg srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_FpBinary;
   i->Min.FpBinary.op = op;
   i->Min.FpBinary.dst = dst;
   i->Min.FpBinary.srcL = srcL;
   i->Min.FpBinary.srcR = srcR;
   return i;
}

MIPSInstr *MIPSInstr_FpTernary ( MIPSFpOp op, HReg dst, HReg src1, HReg src2,
                                 HReg src3 )
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_FpTernary;
   i->Min.FpTernary.op = op;
   i->Min.FpTernary.dst = dst;
   i->Min.FpTernary.src1 = src1;
   i->Min.FpTernary.src2 = src2;
   i->Min.FpTernary.src3 = src3;
   return i;
}

MIPSInstr *MIPSInstr_FpConvert(MIPSFpOp op, HReg dst, HReg src)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_FpConvert;
   i->Min.FpConvert.op = op;
   i->Min.FpConvert.dst = dst;
   i->Min.FpConvert.src = src;
   return i;

}

MIPSInstr *MIPSInstr_FpCompare(MIPSFpOp op, HReg dst, HReg srcL, HReg srcR)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_FpCompare;
   i->Min.FpCompare.op = op;
   i->Min.FpCompare.dst = dst;
   i->Min.FpCompare.srcL = srcL;
   i->Min.FpCompare.srcR = srcR;
   return i;
}

MIPSInstr *MIPSInstr_MtFCSR(HReg src)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_MtFCSR;
   i->Min.MtFCSR.src = src;
   return i;
}

MIPSInstr *MIPSInstr_MfFCSR(HReg dst)
{
   MIPSInstr *i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag = Min_MfFCSR;
   i->Min.MfFCSR.dst = dst;
   return i;
}

MIPSInstr *MIPSInstr_FpGpMove ( MIPSFpGpMoveOp op, HReg dst, HReg src )
{
   MIPSInstr *i        = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag              = Min_FpGpMove;
   i->Min.FpGpMove.op  = op;
   i->Min.FpGpMove.dst = dst;
   i->Min.FpGpMove.src = src;
   return i;
}

MIPSInstr *MIPSInstr_MoveCond ( MIPSMoveCondOp op, HReg dst, HReg src,
                                HReg cond )
{
   MIPSInstr *i        = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag              = Min_MoveCond;
   i->Min.MoveCond.op  = op;
   i->Min.MoveCond.dst = dst;
   i->Min.MoveCond.src = src;
   i->Min.MoveCond.cond = cond;
   return i;
}

MIPSInstr *MIPSInstr_EvCheck ( MIPSAMode* amCounter,
                            MIPSAMode* amFailAddr ) {
   MIPSInstr* i                 = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag                       = Min_EvCheck;
   i->Min.EvCheck.amCounter     = amCounter;
   i->Min.EvCheck.amFailAddr    = amFailAddr;
   return i;
}

MIPSInstr* MIPSInstr_ProfInc ( void ) {
   MIPSInstr* i = LibVEX_Alloc_inline(sizeof(MIPSInstr));
   i->tag       = Min_ProfInc;
   return i;
}

/* -------- Pretty Print instructions ------------- */
static void ppLoadImm(HReg dst, ULong imm, Bool mode64)
{
   vex_printf("li ");
   ppHRegMIPS(dst, mode64);
   vex_printf(",0x%016llx", imm);
}

void ppMIPSInstr(const MIPSInstr * i, Bool mode64)
{
   switch (i->tag) {
      case Min_LI:
         ppLoadImm(i->Min.LI.dst, i->Min.LI.imm, mode64);
         break;
      case Min_Alu: {
         HReg r_srcL = i->Min.Alu.srcL;
         MIPSRH *rh_srcR = i->Min.Alu.srcR;
         /* generic */
         vex_printf("%s ", showMIPSAluOp(i->Min.Alu.op,
                                         toBool(rh_srcR->tag == Mrh_Imm)));
         ppHRegMIPS(i->Min.Alu.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(r_srcL, mode64);
         vex_printf(",");
         ppMIPSRH(rh_srcR, mode64);
         return;
      }
      case Min_Shft: {
         HReg r_srcL = i->Min.Shft.srcL;
         MIPSRH *rh_srcR = i->Min.Shft.srcR;
         vex_printf("%s ", showMIPSShftOp(i->Min.Shft.op,
                                          toBool(rh_srcR->tag == Mrh_Imm),
                                          i->Min.Shft.sz32));
         ppHRegMIPS(i->Min.Shft.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(r_srcL, mode64);
         vex_printf(",");
         ppMIPSRH(rh_srcR, mode64);
         return;
      }
      case Min_Unary: {
         vex_printf("%s ", showMIPSUnaryOp(i->Min.Unary.op));
         ppHRegMIPS(i->Min.Unary.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.Unary.src, mode64);
         return;
      }
      case Min_Cmp: {
         vex_printf("word_compare ");
         ppHRegMIPS(i->Min.Cmp.dst, mode64);
         vex_printf(" = %s ( ", showMIPSCondCode(i->Min.Cmp.cond));
         ppHRegMIPS(i->Min.Cmp.srcL, mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Cmp.srcR, mode64);
         vex_printf(" )");

         return;
      }
      case Min_Mul: {
         switch (i->Min.Mul.widening) {
            case False:
               vex_printf("mul ");
               ppHRegMIPS(i->Min.Mul.dst, mode64);
               vex_printf(", ");
               ppHRegMIPS(i->Min.Mul.srcL, mode64);
               vex_printf(", ");
               ppHRegMIPS(i->Min.Mul.srcR, mode64);
               return;
            case True:
               vex_printf("%s%s ", i->Min.Mul.sz32 ? "mult" : "dmult",
                                   i->Min.Mul.syned ? "" : "u");
               ppHRegMIPS(i->Min.Mul.dst, mode64);
               vex_printf(", ");
               ppHRegMIPS(i->Min.Mul.srcL, mode64);
               vex_printf(", ");
               ppHRegMIPS(i->Min.Mul.srcR, mode64);
               return;
            }
         break;
      }
      case Min_Mthi: {
         vex_printf("mthi ");
         ppHRegMIPS(i->Min.MtHL.src, mode64);
         return;
      }
      case Min_Mtlo: {
         vex_printf("mtlo ");
         ppHRegMIPS(i->Min.MtHL.src, mode64);
         return;
      }
      case Min_Mfhi: {
         vex_printf("mfhi ");
         ppHRegMIPS(i->Min.MfHL.dst, mode64);
         return;
      }
      case Min_Mflo: {
         vex_printf("mflo ");
         ppHRegMIPS(i->Min.MfHL.dst, mode64);
         return;
      }
      case Min_Macc: {
         vex_printf("%s ", showMIPSMaccOp(i->Min.Macc.op, i->Min.Macc.syned));
         ppHRegMIPS(i->Min.Macc.srcL, mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Macc.srcR, mode64);
         return;
      }
      case Min_Div: {
         if (!i->Min.Div.sz32)
            vex_printf("d");
         vex_printf("div");
         vex_printf("%s ", i->Min.Div.syned ? "s" : "u");
         ppHRegMIPS(i->Min.Div.srcL, mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Div.srcR, mode64);
         return;
      }
      case Min_Call: {
         Int n;
         vex_printf("call: ");
         if (i->Min.Call.cond != MIPScc_AL) {
            vex_printf("if (%s) ", showMIPSCondCode(i->Min.Call.cond));
         }
         vex_printf(" {");
         if (!mode64)
            vex_printf(" addiu $29, $29, -16");

         ppLoadImm(hregMIPS_GPR25(mode64), i->Min.Call.target, mode64);

         vex_printf(" ; jarl $31, $25; # args [");
         for (n = 0; n < 32; n++) {
            if (i->Min.Call.argiregs & (1 << n)) {
               vex_printf("$%d", n);
               if ((i->Min.Call.argiregs >> n) > 1)
                  vex_printf(",");
            }
         }
         vex_printf("] nop; ");
         if (!mode64)
            vex_printf("addiu $29, $29, 16; ]");

         break;
      }
      case Min_XDirect:
         vex_printf("(xDirect) ");
         vex_printf("if (guest_COND.%s) { ",
                    showMIPSCondCode(i->Min.XDirect.cond));
         vex_printf("move $9, 0x%x,", (UInt)i->Min.XDirect.dstGA);
         vex_printf("; sw $9, ");
         ppMIPSAMode(i->Min.XDirect.amPC, mode64);
         vex_printf("; move $9, $disp_cp_chain_me_to_%sEP; jalr $9; nop}",
                    i->Min.XDirect.toFastEP ? "fast" : "slow");
         return;
      case Min_XIndir:
         vex_printf("(xIndir) ");
         vex_printf("if (guest_COND.%s) { sw ",
                    showMIPSCondCode(i->Min.XIndir.cond));
         ppHRegMIPS(i->Min.XIndir.dstGA, mode64);
         vex_printf(", ");
         ppMIPSAMode(i->Min.XIndir.amPC, mode64);
         vex_printf("; move $9, $disp_indir; jalr $9; nop}");
         return;
      case Min_XAssisted:
         vex_printf("(xAssisted) ");
         vex_printf("if (guest_COND.%s) { ",
                    showMIPSCondCode(i->Min.XAssisted.cond));
         vex_printf("sw ");
         ppHRegMIPS(i->Min.XAssisted.dstGA, mode64);
         vex_printf(", ");
         ppMIPSAMode(i->Min.XAssisted.amPC, mode64);
         vex_printf("; move $9, $IRJumpKind_to_TRCVAL(%d)",
                    (Int)i->Min.XAssisted.jk);
         vex_printf("; move $9, $disp_assisted; jalr $9; nop; }");
         return;
      case Min_Load: {
         Bool idxd = toBool(i->Min.Load.src->tag == Mam_RR);
         UChar sz = i->Min.Load.sz;
         HChar c_sz = sz == 1 ? 'b' : sz == 2 ? 'h' : sz == 4 ? 'w' : 'd';
         vex_printf("l%c%s ", c_sz, idxd ? "x" : "");
         ppHRegMIPS(i->Min.Load.dst, mode64);
         vex_printf(",");
         ppMIPSAMode(i->Min.Load.src, mode64);
         return;
      }
      case Min_Store: {
         UChar sz = i->Min.Store.sz;
         Bool idxd = toBool(i->Min.Store.dst->tag == Mam_RR);
         HChar c_sz = sz == 1 ? 'b' : sz == 2 ? 'h' : sz == 4 ? 'w' : 'd';
         vex_printf("s%c%s ", c_sz, idxd ? "x" : "");
         ppHRegMIPS(i->Min.Store.src, mode64);
         vex_printf(",");
         ppMIPSAMode(i->Min.Store.dst, mode64);
         return;
      }
      case Min_LoadL: {
         vex_printf("ll ");
         ppHRegMIPS(i->Min.LoadL.dst, mode64);
         vex_printf(",");
         ppMIPSAMode(i->Min.LoadL.src, mode64);
         return;
      }
      case Min_Cas: {
          Bool sz8  = toBool(i->Min.Cas.sz == 8);
          /*
           * ll(d)    old,  0(addr)
           * bne      old,  expd, end
           * nop
           * (d)addiu old,  old,  1
           * sc(d)    data, 0(addr)
           * movn     old,  expd, data
           * end:
           */
          // ll(d) old, 0(addr)
         vex_printf("cas: ");

         vex_printf("%s ", sz8 ? "lld" : "ll");
         ppHRegMIPS(i->Min.Cas.old , mode64);
         vex_printf(", 0(");
         ppHRegMIPS(i->Min.Cas.addr , mode64);
         vex_printf(")\n");

         vex_printf("bne ");
         ppHRegMIPS(i->Min.Cas.old , mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Cas.expd , mode64);
         vex_printf(", end\n");

         vex_printf("nop\n");

         vex_printf("%s ", sz8 ? "daddiu" : "addiu");
         ppHRegMIPS(i->Min.Cas.old , mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Cas.old , mode64);
         vex_printf(", 1\n");

         vex_printf("%s ", sz8 ? "scd" : "sc");
         ppHRegMIPS(i->Min.Cas.data , mode64);
         vex_printf(", 0(");
         ppHRegMIPS(i->Min.Cas.addr , mode64);
         vex_printf(")\n");

         vex_printf("movn ");
         ppHRegMIPS(i->Min.Cas.old , mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Cas.expd , mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.Cas.data , mode64);
         vex_printf("\nend:");
         return;
      }
      case Min_StoreC: {
         vex_printf("sc ");
         ppHRegMIPS(i->Min.StoreC.src, mode64);
         vex_printf(",");
         ppMIPSAMode(i->Min.StoreC.dst, mode64);
         return;
      }
      case Min_RdWrLR: {
         vex_printf("%s ", i->Min.RdWrLR.wrLR ? "mtlr" : "mflr");
         ppHRegMIPS(i->Min.RdWrLR.gpr, mode64);
         return;
      }
      case Min_FpUnary:
         vex_printf("%s ", showMIPSFpOp(i->Min.FpUnary.op));
         ppHRegMIPS(i->Min.FpUnary.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpUnary.src, mode64);
         return;
      case Min_FpBinary:
         vex_printf("%s", showMIPSFpOp(i->Min.FpBinary.op));
         ppHRegMIPS(i->Min.FpBinary.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpBinary.srcL, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpBinary.srcR, mode64);
         return;
      case Min_FpTernary:
         vex_printf("%s", showMIPSFpOp(i->Min.FpTernary.op));
         ppHRegMIPS(i->Min.FpTernary.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpTernary.src1, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpTernary.src2, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpTernary.src3, mode64);
         return;
      case Min_FpConvert:
         vex_printf("%s", showMIPSFpOp(i->Min.FpConvert.op));
         ppHRegMIPS(i->Min.FpConvert.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpConvert.src, mode64);
         return;
      case Min_FpCompare:
         vex_printf("%s ", showMIPSFpOp(i->Min.FpCompare.op));
         ppHRegMIPS(i->Min.FpCompare.srcL, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpCompare.srcR, mode64);
         return;
      case Min_FpMulAcc:
         vex_printf("%s ", showMIPSFpOp(i->Min.FpMulAcc.op));
         ppHRegMIPS(i->Min.FpMulAcc.dst, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpMulAcc.srcML, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpMulAcc.srcMR, mode64);
         vex_printf(",");
         ppHRegMIPS(i->Min.FpMulAcc.srcAcc, mode64);
         return;
      case Min_FpLdSt: {
         if (i->Min.FpLdSt.sz == 4) {
            if (i->Min.FpLdSt.isLoad) {
               vex_printf("lwc1 ");
               ppHRegMIPS(i->Min.FpLdSt.reg, mode64);
               vex_printf(",");
               ppMIPSAMode(i->Min.FpLdSt.addr, mode64);
            } else {
               vex_printf("swc1 ");
               ppHRegMIPS(i->Min.FpLdSt.reg, mode64);
               vex_printf(",");
               ppMIPSAMode(i->Min.FpLdSt.addr, mode64);
            }
         } else if (i->Min.FpLdSt.sz == 8) {
            if (i->Min.FpLdSt.isLoad) {
               vex_printf("ldc1 ");
               ppHRegMIPS(i->Min.FpLdSt.reg, mode64);
               vex_printf(",");
               ppMIPSAMode(i->Min.FpLdSt.addr, mode64);
            } else {
               vex_printf("sdc1 ");
               ppHRegMIPS(i->Min.FpLdSt.reg, mode64);
               vex_printf(",");
               ppMIPSAMode(i->Min.FpLdSt.addr, mode64);
            }
         }
         return;
      }
      case Min_MtFCSR: {
         vex_printf("ctc1 ");
         ppHRegMIPS(i->Min.MtFCSR.src, mode64);
         vex_printf(", $31");
         return;
      }
      case Min_MfFCSR: {
         vex_printf("ctc1 ");
         ppHRegMIPS(i->Min.MfFCSR.dst, mode64);
         vex_printf(", $31");
         return;
      }
      case Min_FpGpMove: {
         vex_printf("%s ", showMIPSFpGpMoveOp(i->Min.FpGpMove.op));
         ppHRegMIPS(i->Min.FpGpMove.dst, mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.FpGpMove.src, mode64);
         return;
      }
      case Min_MoveCond: {
         vex_printf("%s", showMIPSMoveCondOp(i->Min.MoveCond.op));
         ppHRegMIPS(i->Min.MoveCond.dst, mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.MoveCond.src, mode64);
         vex_printf(", ");
         ppHRegMIPS(i->Min.MoveCond.cond, mode64);
         return;
      }
      case Min_EvCheck:
         vex_printf("(evCheck) lw $9, ");
         ppMIPSAMode(i->Min.EvCheck.amCounter, mode64);
         vex_printf("; addiu $9, $9, -1");
         vex_printf("; sw $9, ");
         ppMIPSAMode(i->Min.EvCheck.amCounter, mode64);
         vex_printf("; bgez $t9, nofail; jalr *");
         ppMIPSAMode(i->Min.EvCheck.amFailAddr, mode64);
         vex_printf("; nofail:");
         return;
      case Min_ProfInc:
         if (mode64)
            vex_printf("(profInc) move $9, ($NotKnownYet); "
                       "ld $8, 0($9); "
                       "daddiu $8, $8, 1; "
                       "sd $8, 0($9); " );
         else
            vex_printf("(profInc) move $9, ($NotKnownYet); "
                       "lw $8, 0($9); "
                       "addiu $8, $8, 1; "
                       "sw $8, 0($9); "
                       "sltiu $1, $8, 1; "
                       "lw $8, 4($9); "
                       "addu $8, $8, $1; "
                       "sw $8, 4($9); " );
         return;
      default:
         vpanic("ppMIPSInstr");
         break;
   }
}

/* --------- Helpers for register allocation. --------- */

void getRegUsage_MIPSInstr(HRegUsage * u, const MIPSInstr * i, Bool mode64)
{
   initHRegUsage(u);
   switch (i->tag) {
      case Min_LI:
         addHRegUse(u, HRmWrite, i->Min.LI.dst);
         break;
      case Min_Alu:
         addHRegUse(u, HRmRead, i->Min.Alu.srcL);
         addRegUsage_MIPSRH(u, i->Min.Alu.srcR);
         addHRegUse(u, HRmWrite, i->Min.Alu.dst);
         return;
      case Min_Shft:
         addHRegUse(u, HRmRead, i->Min.Shft.srcL);
         addRegUsage_MIPSRH(u, i->Min.Shft.srcR);
         addHRegUse(u, HRmWrite, i->Min.Shft.dst);
         return;
      case Min_Cmp:
         addHRegUse(u, HRmRead, i->Min.Cmp.srcL);
         addHRegUse(u, HRmRead, i->Min.Cmp.srcR);
         addHRegUse(u, HRmWrite, i->Min.Cmp.dst);
         return;
      case Min_Unary:
         addHRegUse(u, HRmRead, i->Min.Unary.src);
         addHRegUse(u, HRmWrite, i->Min.Unary.dst);
         return;
      case Min_Mul:
         addHRegUse(u, HRmWrite, i->Min.Mul.dst);
         addHRegUse(u, HRmRead, i->Min.Mul.srcL);
         addHRegUse(u, HRmRead, i->Min.Mul.srcR);
         return;
      case Min_Mthi:
      case Min_Mtlo:
         addHRegUse(u, HRmWrite, hregMIPS_HI(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_LO(mode64));
         addHRegUse(u, HRmRead, i->Min.MtHL.src);
         return;
      case Min_Mfhi:
      case Min_Mflo:
         addHRegUse(u, HRmRead, hregMIPS_HI(mode64));
         addHRegUse(u, HRmRead, hregMIPS_LO(mode64));
         addHRegUse(u, HRmWrite, i->Min.MfHL.dst);
         return;
      case Min_MtFCSR:
         addHRegUse(u, HRmRead, i->Min.MtFCSR.src);
         return;
      case Min_MfFCSR:
         addHRegUse(u, HRmWrite, i->Min.MfFCSR.dst);
         return;
      case Min_Macc:
         addHRegUse(u, HRmModify, hregMIPS_HI(mode64));
         addHRegUse(u, HRmModify, hregMIPS_LO(mode64));
         addHRegUse(u, HRmRead, i->Min.Macc.srcL);
         addHRegUse(u, HRmRead, i->Min.Macc.srcR);
         return;
      case Min_Div:
         addHRegUse(u, HRmWrite, hregMIPS_HI(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_LO(mode64));
         addHRegUse(u, HRmRead, i->Min.Div.srcL);
         addHRegUse(u, HRmRead, i->Min.Div.srcR);
         return;
      case Min_Call: {
         /* Logic and comments copied/modified from x86, ppc and arm back end.
            First off, claim it trashes all the caller-saved regs
            which fall within the register allocator's jurisdiction. */
         if (i->Min.Call.cond != MIPScc_AL)
            addHRegUse(u, HRmRead, i->Min.Call.src);
         UInt argir;
         addHRegUse(u, HRmWrite, hregMIPS_GPR1(mode64));

         addHRegUse(u, HRmWrite, hregMIPS_GPR2(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR3(mode64));

         addHRegUse(u, HRmWrite, hregMIPS_GPR4(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR5(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR6(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR7(mode64));

         addHRegUse(u, HRmWrite, hregMIPS_GPR8(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR9(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR10(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR11(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR12(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR13(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR14(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR15(mode64));

         addHRegUse(u, HRmWrite, hregMIPS_GPR24(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR25(mode64));
         addHRegUse(u, HRmWrite, hregMIPS_GPR31(mode64));

         /* Now we have to state any parameter-carrying registers
            which might be read. This depends on the argiregs field. */
         argir = i->Min.Call.argiregs;
         if (argir & (1<<11)) addHRegUse(u, HRmRead, hregMIPS_GPR11(mode64));
         if (argir & (1<<10)) addHRegUse(u, HRmRead, hregMIPS_GPR10(mode64));
         if (argir & (1<<9)) addHRegUse(u, HRmRead, hregMIPS_GPR9(mode64));
         if (argir & (1<<8)) addHRegUse(u, HRmRead, hregMIPS_GPR8(mode64));
         if (argir & (1<<7)) addHRegUse(u, HRmRead, hregMIPS_GPR7(mode64));
         if (argir & (1<<6)) addHRegUse(u, HRmRead, hregMIPS_GPR6(mode64));
         if (argir & (1<<5)) addHRegUse(u, HRmRead, hregMIPS_GPR5(mode64));
         if (argir & (1<<4)) addHRegUse(u, HRmRead, hregMIPS_GPR4(mode64));

         vassert(0 == (argir & ~((1 << 4) | (1 << 5) | (1 << 6)
                                 | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)
                                 | (1 << 11))));

         return;
      }
      /* XDirect/XIndir/XAssisted are also a bit subtle.  They
         conditionally exit the block.  Hence we only need to list (1)
         the registers that they read, and (2) the registers that they
         write in the case where the block is not exited.  (2) is
         empty, hence only (1) is relevant here. */
      case Min_XDirect:
         addRegUsage_MIPSAMode(u, i->Min.XDirect.amPC);
         return;
      case Min_XIndir:
         addHRegUse(u, HRmRead, i->Min.XIndir.dstGA);
         addRegUsage_MIPSAMode(u, i->Min.XIndir.amPC);
         return;
      case Min_XAssisted:
         addHRegUse(u, HRmRead, i->Min.XAssisted.dstGA);
         addRegUsage_MIPSAMode(u, i->Min.XAssisted.amPC);
         return;
      case Min_Load:
         addRegUsage_MIPSAMode(u, i->Min.Load.src);
         addHRegUse(u, HRmWrite, i->Min.Load.dst);
         return;
      case Min_Store:
         addHRegUse(u, HRmRead, i->Min.Store.src);
         addRegUsage_MIPSAMode(u, i->Min.Store.dst);
         return;
      case Min_LoadL:
         addRegUsage_MIPSAMode(u, i->Min.LoadL.src);
         addHRegUse(u, HRmWrite, i->Min.LoadL.dst);
         return;
      case Min_Cas:
         addHRegUse(u, HRmWrite, i->Min.Cas.old);
         addHRegUse(u, HRmRead, i->Min.Cas.addr);
         addHRegUse(u, HRmRead, i->Min.Cas.expd);
         addHRegUse(u, HRmModify, i->Min.Cas.data);
         return;
      case Min_StoreC:
         addHRegUse(u, HRmWrite, i->Min.StoreC.src);
         addHRegUse(u, HRmRead, i->Min.StoreC.src);
         addRegUsage_MIPSAMode(u, i->Min.StoreC.dst);
         return;
      case Min_RdWrLR:
         addHRegUse(u, (i->Min.RdWrLR.wrLR ? HRmRead : HRmWrite),
                        i->Min.RdWrLR.gpr);
         return;
      case Min_FpLdSt:
         if (i->Min.FpLdSt.sz == 4) {
            addHRegUse(u, (i->Min.FpLdSt.isLoad ? HRmWrite : HRmRead),
                           i->Min.FpLdSt.reg);
            addRegUsage_MIPSAMode(u, i->Min.FpLdSt.addr);
            return;
         } else if (i->Min.FpLdSt.sz == 8) {
            addHRegUse(u, (i->Min.FpLdSt.isLoad ? HRmWrite : HRmRead),
                           i->Min.FpLdSt.reg);
            addRegUsage_MIPSAMode(u, i->Min.FpLdSt.addr);
            return;
         }
         break;
      case Min_FpUnary:
         addHRegUse(u, HRmWrite, i->Min.FpUnary.dst);
         addHRegUse(u, HRmRead, i->Min.FpUnary.src);
         return;
      case Min_FpBinary:
         addHRegUse(u, HRmWrite, i->Min.FpBinary.dst);
         addHRegUse(u, HRmRead, i->Min.FpBinary.srcL);
         addHRegUse(u, HRmRead, i->Min.FpBinary.srcR);
         return;
      case Min_FpTernary:
         addHRegUse(u, HRmWrite, i->Min.FpTernary.dst);
         addHRegUse(u, HRmRead, i->Min.FpTernary.src1);
         addHRegUse(u, HRmRead, i->Min.FpTernary.src2);
         addHRegUse(u, HRmRead, i->Min.FpTernary.src3);
         return;
      case Min_FpConvert:
         addHRegUse(u, HRmWrite, i->Min.FpConvert.dst);
         addHRegUse(u, HRmRead, i->Min.FpConvert.src);
         return;
      case Min_FpCompare:
         addHRegUse(u, HRmWrite, i->Min.FpCompare.dst);
         addHRegUse(u, HRmRead, i->Min.FpCompare.srcL);
         addHRegUse(u, HRmRead, i->Min.FpCompare.srcR);
         return;
      case Min_FpGpMove:
         addHRegUse(u, HRmWrite, i->Min.FpGpMove.dst);
         addHRegUse(u, HRmRead, i->Min.FpGpMove.src);
         return;
      case Min_MoveCond:
         addHRegUse(u, HRmModify, i->Min.MoveCond.dst);
         addHRegUse(u, HRmRead, i->Min.MoveCond.src);
         addHRegUse(u, HRmRead, i->Min.MoveCond.cond);
         return;
      case Min_EvCheck:
         /* We expect both amodes only to mention %ebp, so this is in
            fact pointless, since %ebp isn't allocatable, but anyway.. */
         addRegUsage_MIPSAMode(u, i->Min.EvCheck.amCounter);
         addRegUsage_MIPSAMode(u, i->Min.EvCheck.amFailAddr);
         return;
      case Min_ProfInc:
         /* does not use any registers. */
         return;
      default:
         ppMIPSInstr(i, mode64);
         vpanic("getRegUsage_MIPSInstr");
         break;
   }
}

/* local helper */
static void mapReg(HRegRemap * m, HReg * r)
{
   *r = lookupHRegRemap(m, *r);
}

void mapRegs_MIPSInstr(HRegRemap * m, MIPSInstr * i, Bool mode64)
{
   switch (i->tag) {
      case Min_LI:
         mapReg(m, &i->Min.LI.dst);
         break;
      case Min_Alu:
         mapReg(m, &i->Min.Alu.srcL);
         mapRegs_MIPSRH(m, i->Min.Alu.srcR);
         mapReg(m, &i->Min.Alu.dst);
         return;
      case Min_Shft:
         mapReg(m, &i->Min.Shft.srcL);
         mapRegs_MIPSRH(m, i->Min.Shft.srcR);
         mapReg(m, &i->Min.Shft.dst);
         return;
      case Min_Cmp:
         mapReg(m, &i->Min.Cmp.srcL);
         mapReg(m, &i->Min.Cmp.srcR);
         mapReg(m, &i->Min.Cmp.dst);
         return;
      case Min_Unary:
         mapReg(m, &i->Min.Unary.src);
         mapReg(m, &i->Min.Unary.dst);
         return;
      case Min_Mul:
         mapReg(m, &i->Min.Mul.dst);
         mapReg(m, &i->Min.Mul.srcL);
         mapReg(m, &i->Min.Mul.srcR);
         return;
      case Min_Mthi:
      case Min_Mtlo:
         mapReg(m, &i->Min.MtHL.src);
         return;
      case Min_Mfhi:
      case Min_Mflo:
         mapReg(m, &i->Min.MfHL.dst);
         return;
      case Min_Macc:
         mapReg(m, &i->Min.Macc.srcL);
         mapReg(m, &i->Min.Macc.srcR);
         return;
      case Min_Div:
         mapReg(m, &i->Min.Div.srcL);
         mapReg(m, &i->Min.Div.srcR);
         return;
      case Min_Call:
         {
            if (i->Min.Call.cond != MIPScc_AL)
               mapReg(m, &i->Min.Call.src);
            return;
         }
      case Min_XDirect:
         mapRegs_MIPSAMode(m, i->Min.XDirect.amPC);
         return;
      case Min_XIndir:
         mapReg(m, &i->Min.XIndir.dstGA);
         mapRegs_MIPSAMode(m, i->Min.XIndir.amPC);
         return;
      case Min_XAssisted:
         mapReg(m, &i->Min.XAssisted.dstGA);
         mapRegs_MIPSAMode(m, i->Min.XAssisted.amPC);
         return;
      case Min_Load:
         mapRegs_MIPSAMode(m, i->Min.Load.src);
         mapReg(m, &i->Min.Load.dst);
         return;
      case Min_Store:
         mapReg(m, &i->Min.Store.src);
         mapRegs_MIPSAMode(m, i->Min.Store.dst);
         return;
      case Min_LoadL:
         mapRegs_MIPSAMode(m, i->Min.LoadL.src);
         mapReg(m, &i->Min.LoadL.dst);
         return;
      case Min_Cas:
         mapReg(m, &i->Min.Cas.old);
         mapReg(m, &i->Min.Cas.addr);
         mapReg(m, &i->Min.Cas.expd);
         mapReg(m, &i->Min.Cas.data);
         return;
      case Min_StoreC:
         mapReg(m, &i->Min.StoreC.src);
         mapRegs_MIPSAMode(m, i->Min.StoreC.dst);
         return;
      case Min_RdWrLR:
         mapReg(m, &i->Min.RdWrLR.gpr);
         return;
      case Min_FpLdSt:
         if (i->Min.FpLdSt.sz == 4) {
            mapReg(m, &i->Min.FpLdSt.reg);
            mapRegs_MIPSAMode(m, i->Min.FpLdSt.addr);
            return;
         } else if (i->Min.FpLdSt.sz == 8) {
            mapReg(m, &i->Min.FpLdSt.reg);
            mapRegs_MIPSAMode(m, i->Min.FpLdSt.addr);
            return;
         }
         break;
      case Min_FpUnary:
         mapReg(m, &i->Min.FpUnary.dst);
         mapReg(m, &i->Min.FpUnary.src);
         return;
      case Min_FpBinary:
         mapReg(m, &i->Min.FpBinary.dst);
         mapReg(m, &i->Min.FpBinary.srcL);
         mapReg(m, &i->Min.FpBinary.srcR);
         return;
      case Min_FpTernary:
         mapReg(m, &i->Min.FpTernary.dst);
         mapReg(m, &i->Min.FpTernary.src1);
         mapReg(m, &i->Min.FpTernary.src2);
         mapReg(m, &i->Min.FpTernary.src3);
         return;
      case Min_FpConvert:
         mapReg(m, &i->Min.FpConvert.dst);
         mapReg(m, &i->Min.FpConvert.src);
         return;
      case Min_FpCompare:
         mapReg(m, &i->Min.FpCompare.dst);
         mapReg(m, &i->Min.FpCompare.srcL);
         mapReg(m, &i->Min.FpCompare.srcR);
         return;
      case Min_MtFCSR:
         mapReg(m, &i->Min.MtFCSR.src);
         return;
      case Min_MfFCSR:
         mapReg(m, &i->Min.MfFCSR.dst);
         return;
      case Min_FpGpMove:
         mapReg(m, &i->Min.FpGpMove.dst);
         mapReg(m, &i->Min.FpGpMove.src);
         return;
      case Min_MoveCond:
         mapReg(m, &i->Min.MoveCond.dst);
         mapReg(m, &i->Min.MoveCond.src);
         mapReg(m, &i->Min.MoveCond.cond);
         return;
      case Min_EvCheck:
         /* We expect both amodes only to mention %ebp, so this is in
            fact pointless, since %ebp isn't allocatable, but anyway.. */
         mapRegs_MIPSAMode(m, i->Min.EvCheck.amCounter);
         mapRegs_MIPSAMode(m, i->Min.EvCheck.amFailAddr);
         return;
      case Min_ProfInc:
         /* does not use any registers. */
         return;
      default:
         ppMIPSInstr(i, mode64);
         vpanic("mapRegs_MIPSInstr");
         break;
   }

}

/* Figure out if i represents a reg-reg move, and if so assign the
   source and destination to *src and *dst.  If in doubt say No.  Used
   by the register allocator to do move coalescing.
*/
Bool isMove_MIPSInstr(const MIPSInstr * i, HReg * src, HReg * dst)
{
   /* Moves between integer regs */
   if (i->tag == Min_Alu) {
      /* or Rd,Rs,Rs == mr Rd,Rs */
      if (i->Min.Alu.op != Malu_OR)
         return False;
      if (i->Min.Alu.srcR->tag != Mrh_Reg)
         return False;
      if (!sameHReg(i->Min.Alu.srcR->Mrh.Reg.reg, i->Min.Alu.srcL))
         return False;
      *src = i->Min.Alu.srcL;
      *dst = i->Min.Alu.dst;
      return True;
   }
   return False;
}

/* Generate mips spill/reload instructions under the direction of the
   register allocator. */
void genSpill_MIPS( /*OUT*/ HInstr ** i1, /*OUT*/ HInstr ** i2, HReg rreg,
                    Int offsetB, Bool mode64)
{
   MIPSAMode *am;
   vassert(offsetB >= 0);
   vassert(!hregIsVirtual(rreg));
   *i1 = *i2 = NULL;
   am = MIPSAMode_IR(offsetB, GuestStatePointer(mode64));

   switch (hregClass(rreg)) {
      case HRcInt64:
         vassert(mode64);
         *i1 = MIPSInstr_Store(8, am, rreg, mode64);
         break;
      case HRcInt32:
         vassert(!mode64);
         *i1 = MIPSInstr_Store(4, am, rreg, mode64);
         break;
      case HRcFlt32:
         vassert(!mode64);
         *i1 = MIPSInstr_FpLdSt(False /*Store */ , 4, rreg, am);
         break;
      case HRcFlt64:
         *i1 = MIPSInstr_FpLdSt(False /*Store */ , 8, rreg, am);
         break;
      default:
         ppHRegClass(hregClass(rreg));
         vpanic("genSpill_MIPS: unimplemented regclass");
         break;
   }
}

void genReload_MIPS( /*OUT*/ HInstr ** i1, /*OUT*/ HInstr ** i2, HReg rreg,
                     Int offsetB, Bool mode64)
{
   MIPSAMode *am;
   vassert(!hregIsVirtual(rreg));
   am = MIPSAMode_IR(offsetB, GuestStatePointer(mode64));

   switch (hregClass(rreg)) {
      case HRcInt64:
         vassert(mode64);
         *i1 = MIPSInstr_Load(8, rreg, am, mode64);
         break;
      case HRcInt32:
         vassert(!mode64);
         *i1 = MIPSInstr_Load(4, rreg, am, mode64);
         break;
      case HRcFlt32:
         if (mode64)
            *i1 = MIPSInstr_FpLdSt(True /*Load */ , 8, rreg, am);
         else
            *i1 = MIPSInstr_FpLdSt(True /*Load */ , 4, rreg, am);
         break;
      case HRcFlt64:
         *i1 = MIPSInstr_FpLdSt(True /*Load */ , 8, rreg, am);
         break;
      default:
         ppHRegClass(hregClass(rreg));
         vpanic("genReload_MIPS: unimplemented regclass");
         break;
   }
}

/* --------- The mips assembler --------- */

inline static UInt iregNo(HReg r, Bool mode64)
{
   UInt n;
   vassert(hregClass(r) == (mode64 ? HRcInt64 : HRcInt32));
   vassert(!hregIsVirtual(r));
   n = hregEncoding(r);
   vassert(n <= 32);
   return n;
}

inline static UInt fregNo(HReg r, Bool mode64)
{
   UInt n;
   vassert(!hregIsVirtual(r));
   n = hregEncoding(r);
   vassert(n <= 31);
   return n;
}

inline static UInt dregNo(HReg r)
{
   UInt n;
   vassert(!hregIsVirtual(r));
   n = hregEncoding(r);
   vassert(n <= 31);
   return n;
}

/* Emit 32bit instruction */
static UChar *emit32(UChar * p, UInt w32)
{
#if defined (_MIPSEL)
   *p++ = toUChar(w32 & 0x000000FF);
   *p++ = toUChar((w32 >> 8) & 0x000000FF);
   *p++ = toUChar((w32 >> 16) & 0x000000FF);
   *p++ = toUChar((w32 >> 24) & 0x000000FF);
/* HACK !!!!
   MIPS endianess is decided at compile time using gcc defined
   symbols _MIPSEL or _MIPSEB. When compiling libvex in a cross-arch
   setup, then none of these is defined. We just choose here by default
   mips Big Endian to allow libvexmultiarch_test to work when using
   a mips host architecture.
   A cleaner way would be to either have mips using 'dynamic endness'
   (like ppc64be or le, decided at runtime) or at least defining
   by default _MIPSEB when compiling on a non mips system.
#elif defined (_MIPSEB).
*/
#else
   *p++ = toUChar((w32 >> 24) & 0x000000FF);
   *p++ = toUChar((w32 >> 16) & 0x000000FF);
   *p++ = toUChar((w32 >> 8) & 0x000000FF);
   *p++ = toUChar(w32 & 0x000000FF);
#endif
   return p;
}
/* Fetch an instruction */
static UInt fetch32 ( UChar* p )
{
   UInt w32 = 0;
#if defined (_MIPSEL)
   w32 |= ((0xFF & (UInt)p[0]) << 0);
   w32 |= ((0xFF & (UInt)p[1]) << 8);
   w32 |= ((0xFF & (UInt)p[2]) << 16);
   w32 |= ((0xFF & (UInt)p[3]) << 24);
#elif defined (_MIPSEB)
   w32 |= ((0xFF & (UInt)p[0]) << 24);
   w32 |= ((0xFF & (UInt)p[1]) << 16);
   w32 |= ((0xFF & (UInt)p[2]) <<  8);
   w32 |= ((0xFF & (UInt)p[3]) <<  0);
#endif
   return w32;
}

/* physical structure of mips instructions */
/* type I : opcode    - 6 bits
         rs         - 5 bits
         rt         - 5 bits
         immediate - 16 bits
*/
static UChar *mkFormI(UChar * p, UInt opc, UInt rs, UInt rt, UInt imm)
{
   UInt theInstr;
   vassert(opc < 0x40);
   vassert(rs < 0x20);
   vassert(rt < 0x20);
   imm = imm & 0xFFFF;
   theInstr = ((opc << 26) | (rs << 21) | (rt << 16) | (imm));
   return emit32(p, theInstr);
}

/* type R: opcode    - 6 bits
         rs    - 5 bits
         rt    - 5 bits
         rd    - 5 bits
         sa    - 5 bits
         func  - 6 bits
*/
static UChar *mkFormR(UChar * p, UInt opc, UInt rs, UInt rt, UInt rd, UInt sa,
            UInt func)
{
   if (rs >= 0x20)
      vex_printf("rs = %d\n", rs);
   UInt theInstr;
   vassert(opc < 0x40);
   vassert(rs < 0x20);
   vassert(rt < 0x20);
   vassert(rd < 0x20);
   vassert(sa < 0x20);
   func = func & 0xFFFF;
   theInstr = ((opc << 26) | (rs << 21) | (rt << 16) | (rd << 11) | (sa << 6) |
               (func));

   return emit32(p, theInstr);
}

static UChar *mkFormS(UChar * p, UInt opc1, UInt rRD, UInt rRS, UInt rRT,
                      UInt sa, UInt opc2)
{
   UInt theInstr;
   vassert(opc1 <= 0x3F);
   vassert(rRD < 0x20);
   vassert(rRS < 0x20);
   vassert(rRT < 0x20);
   vassert(opc2 <= 0x3F);
   vassert(sa >= 0 && sa <= 0x3F);

   theInstr = ((opc1 << 26) | (rRS << 21) | (rRT << 16) | (rRD << 11) |
              ((sa & 0x1F) << 6) | (opc2));

   return emit32(p, theInstr);
}

static UChar *doAMode_IR(UChar * p, UInt opc1, UInt rSD, MIPSAMode * am,
                         Bool mode64)
{
   UInt rA, idx, r_dst;
   vassert(am->tag == Mam_IR);
   vassert(am->Mam.IR.index < 0x10000);

   rA = iregNo(am->Mam.IR.base, mode64);
   idx = am->Mam.IR.index;

   if (rSD == 33 || rSD == 34)
      r_dst = 24;
   else
      r_dst = rSD;

   if (opc1 < 40) {
      /* load */
      if (rSD == 33)
         /* mfhi */
         p = mkFormR(p, 0, 0, 0, r_dst, 0, 16);
      else if (rSD == 34)
         /* mflo */
         p = mkFormR(p, 0, 0, 0, r_dst, 0, 18);
   }

   p = mkFormI(p, opc1, rA, r_dst, idx);

   if (opc1 >= 40) {
      /* store */
      if (rSD == 33)
         /* mthi */
         p = mkFormR(p, 0, r_dst, 0, 0, 0, 17);
      else if (rSD == 34)
         /* mtlo */
         p = mkFormR(p, 0, r_dst, 0, 0, 0, 19);
   }

   return p;
}

static UChar *doAMode_RR(UChar * p, UInt opc1, UInt rSD, MIPSAMode * am,
                         Bool mode64)
{
   UInt rA, rB, r_dst;
   vassert(am->tag == Mam_RR);

   rA = iregNo(am->Mam.RR.base, mode64);
   rB = iregNo(am->Mam.RR.index, mode64);

   if (rSD == 33 || rSD == 34)
      r_dst = 24;
   else
      r_dst = rSD;

   if (opc1 < 40) {
      /* load */
      if (rSD == 33)
         /* mfhi */
         p = mkFormR(p, 0, 0, 0, r_dst, 0, 16);
      else if (rSD == 34)
         /* mflo */
         p = mkFormR(p, 0, 0, 0, r_dst, 0, 18);
   }

   if (mode64) {
      /* daddu rA, rA, rB$
         sd/ld r_dst, 0(rA)$
         dsubu rA, rA, rB */
      p = mkFormR(p, 0, rA, rB, rA, 0, 45);
      p = mkFormI(p, opc1, rA, r_dst, 0);
      p = mkFormR(p, 0, rA, rB, rA, 0, 47);
   } else {
      /* addu rA, rA, rB
         sw/lw r_dst, 0(rA)
         subu rA, rA, rB */
      p = mkFormR(p, 0, rA, rB, rA, 0, 33);
      p = mkFormI(p, opc1, rA, r_dst, 0);
      p = mkFormR(p, 0, rA, rB, rA, 0, 35);
   }
   if (opc1 >= 40) {
      /* store */
      if (rSD == 33)
         /* mthi */
         p = mkFormR(p, 0, r_dst, 0, 0, 0, 17);
      else if (rSD == 34)
         /* mtlo */
         p = mkFormR(p, 0, r_dst, 0, 0, 0, 19);
   }

   return p;
}

/* Load imm to r_dst */
static UChar *mkLoadImm(UChar * p, UInt r_dst, ULong imm, Bool mode64)
{
   if (!mode64) {
      vassert(r_dst < 0x20);
      UInt u32 = (UInt) imm;
      Int s32 = (Int) u32;
      Long s64 = (Long) s32;
      imm = (ULong) s64;
   }

   if (imm >= 0xFFFFFFFFFFFF8000ULL || imm < 0x8000) {
      /* sign-extendable from 16 bits
         addiu r_dst, 0, imm  => li r_dst, imm */
      p = mkFormI(p, 9, 0, r_dst, imm & 0xFFFF);
   } else {
      if (imm >= 0xFFFFFFFF80000000ULL || imm < 0x80000000ULL) {
         /* sign-extendable from 32 bits
            addiu r_dst, r0, (imm >> 16) => lis r_dst, (imm >> 16)
            lui r_dst, (imm >> 16) */
         p = mkFormI(p, 15, 0, r_dst, (imm >> 16) & 0xFFFF);
         /* ori r_dst, r_dst, (imm & 0xFFFF) */
         p = mkFormI(p, 13, r_dst, r_dst, imm & 0xFFFF);
      } else {
         vassert(mode64);
         /* lui load in upper half of low word */
         p = mkFormI(p, 15, 0, r_dst, (imm >> 48) & 0xFFFF);
         /* ori */
         p = mkFormI(p, 13, r_dst, r_dst, (imm >> 32) & 0xFFFF);
         /* shift */
         p = mkFormS(p, 0, r_dst, 0, r_dst, 16, 56);
         /* ori */
         p = mkFormI(p, 13, r_dst, r_dst, (imm >> 16) & 0xFFFF);
         /* shift */
         p = mkFormS(p, 0, r_dst, 0, r_dst, 16, 56);
         /* ori */
         p = mkFormI(p, 13, r_dst, r_dst, imm & 0xFFFF);
      }
   }
   return p;
}

/* A simplified version of mkLoadImm that always generates 2 or 6
   instructions (32 or 64 bits respectively) even if it could generate
   fewer.  This is needed for generating fixed sized patchable
   sequences. */
static UChar* mkLoadImm_EXACTLY2or6 ( UChar* p,
                                      UInt r_dst, ULong imm, Bool mode64)
{
   vassert(r_dst < 0x20);

   if (!mode64) {
      /* In 32-bit mode, make sure the top 32 bits of imm are a sign
         extension of the bottom 32 bits. (Probably unnecessary.) */
      UInt u32 = (UInt)imm;
      Int  s32 = (Int)u32;
      Long s64 = (Long)s32;
      imm = (ULong)s64;
   }

   if (!mode64) {
      /* sign-extendable from 32 bits
         addiu r_dst, r0, (imm >> 16) => lis r_dst, (imm >> 16)
         lui r_dst, (imm >> 16) */
      p = mkFormI(p, 15, 0, r_dst, (imm >> 16) & 0xFFFF);
      /* ori r_dst, r_dst, (imm & 0xFFFF) */
      p = mkFormI(p, 13, r_dst, r_dst, imm & 0xFFFF);
   } else {
      /* full 64bit immediate load: 6 (six!) insns. */
      vassert(mode64);
      /* lui load in upper half of low word */
      p = mkFormI(p, 15, 0, r_dst, (imm >> 48) & 0xFFFF);
      /* ori */
      p = mkFormI(p, 13, r_dst, r_dst, (imm >> 32) & 0xFFFF);
      /* shift */
      p = mkFormS(p, 0, r_dst, 0, r_dst, 16, 56);
      /* ori */
      p = mkFormI(p, 13, r_dst, r_dst, (imm >> 16) & 0xFFFF);
      /* shift */
      p = mkFormS(p, 0, r_dst, 0, r_dst, 16, 56);
      /* ori */
      p = mkFormI(p, 13, r_dst, r_dst, imm & 0xFFFF);
   }
   return p;
}

/* Checks whether the sequence of bytes at p was indeed created
   by mkLoadImm_EXACTLY2or6 with the given parameters. */
static Bool isLoadImm_EXACTLY2or6 ( UChar* p_to_check,
                                    UInt r_dst, ULong imm, Bool mode64 )
{
   vassert(r_dst < 0x20);
   Bool ret;
   if (!mode64) {
      /* In 32-bit mode, make sure the top 32 bits of imm are a sign
         extension of the bottom 32 bits.  (Probably unnecessary.) */
      UInt u32 = (UInt)imm;
      Int  s32 = (Int)u32;
      Long s64 = (Long)s32;
      imm = (ULong)s64;
   }

   if (!mode64) {
      UInt   expect[2] = { 0, 0 };
      UChar* p         = (UChar*)&expect[0];
      /* lui r_dst, (immi >> 16) */
      p = mkFormI(p, 15, 0, r_dst, (imm >> 16) & 0xFFFF);
      /* ori r_dst, r_dst, (imm & 0xFFFF) */
      p = mkFormI(p, 13, r_dst, r_dst, imm & 0xFFFF);
      vassert(p == (UChar*)&expect[2]);

      ret = fetch32(p_to_check + 0) == expect[0]
            && fetch32(p_to_check + 4) == expect[1];
   } else {
      UInt   expect[6] = { 0, 0, 0, 0, 0, 0};
      UChar* p         = (UChar*)&expect[0];
      /* lui load in upper half of low word */
      p = mkFormI(p, 15, 0, r_dst, (imm >> 48) & 0xFFFF);
      /* ori */
      p = mkFormI(p, 13, r_dst, r_dst, (imm >> 32) & 0xFFFF);
      /* shift */
      p = mkFormS(p, 0, r_dst, 0, r_dst, 16, 56);
      /* ori */
      p = mkFormI(p, 13, r_dst, r_dst, (imm >> 16) & 0xFFFF);
      /* shift */
      p = mkFormS(p, 0, r_dst, 0, r_dst, 16, 56);
      /* ori */
      p = mkFormI(p, 13, r_dst, r_dst, imm & 0xFFFF);
      vassert(p == (UChar*)&expect[6]);

      ret = fetch32(p_to_check + 0) == expect[0]
            && fetch32(p_to_check + 4) == expect[1]
            && fetch32(p_to_check + 8) == expect[2]
            && fetch32(p_to_check + 12) == expect[3]
            && fetch32(p_to_check + 16) == expect[4]
            && fetch32(p_to_check + 20) == expect[5];
   }
   return ret;
}

/* Generate a machine-word sized load or store. Simplified version of
   the Min_Load and Min_Store cases below.
   This will generate 32-bit load/store on MIPS32, and 64-bit load/store on
   MIPS64 platforms.
*/
static UChar* do_load_or_store_machine_word ( UChar* p, Bool isLoad, UInt reg,
                                              MIPSAMode* am, Bool mode64 )
{
   if (isLoad) { /* load */
      switch (am->tag) {
         case Mam_IR:
            if (mode64) {
               vassert(0 == (am->Mam.IR.index & 3));
            }
            p = doAMode_IR(p, mode64 ? 55 : 35, reg, am, mode64);
            break;
         case Mam_RR:
            /* we could handle this case, but we don't expect to ever
               need to. */
            vassert(0);
            break;
         default:
            vassert(0);
            break;
      }
   } else /* store */ {
      switch (am->tag) {
         case Mam_IR:
            if (mode64) {
               vassert(0 == (am->Mam.IR.index & 3));
            }
            p = doAMode_IR(p, mode64 ? 63 : 43, reg, am, mode64);
            break;
         case Mam_RR:
            /* we could handle this case, but we don't expect to ever
               need to. */
            vassert(0);
            break;
         default:
            vassert(0);
            break;
      }
   }
   return p;
}

/* Generate a 32-bit sized load or store. Simplified version of
   do_load_or_store_machine_word above. */
static UChar* do_load_or_store_word32 ( UChar* p, Bool isLoad, UInt reg,
                                        MIPSAMode* am, Bool mode64 )
{
   if (isLoad) { /* load */
      switch (am->tag) {
         case Mam_IR:
            if (mode64) {
               vassert(0 == (am->Mam.IR.index & 3));
            }
            p = doAMode_IR(p, 35, reg, am, mode64);
            break;
         case Mam_RR:
            /* we could handle this case, but we don't expect to ever
               need to. */
            vassert(0);
            break;
         default:
            vassert(0);
            break;
      }
   } else /* store */ {
      switch (am->tag) {
         case Mam_IR:
            if (mode64) {
               vassert(0 == (am->Mam.IR.index & 3));
            }
            p = doAMode_IR(p, 43, reg, am, mode64);
            break;
         case Mam_RR:
            /* we could handle this case, but we don't expect to ever
               need to. */
            vassert(0);
            break;
         default:
            vassert(0);
            break;
      }
   }
   return p;
}

/* Move r_dst to r_src */
static UChar *mkMoveReg(UChar * p, UInt r_dst, UInt r_src)
{
   vassert(r_dst < 0x20);
   vassert(r_src < 0x20);

   if (r_dst != r_src) {
      /* or r_dst, r_src, r_src */
      p = mkFormR(p, 0, r_src, r_src, r_dst, 0, 37);
   }
   return p;
}

/* Emit an instruction into buf and return the number of bytes used.
   Note that buf is not the insn's final place, and therefore it is
   imperative to emit position-independent code.  If the emitted
   instruction was a profiler inc, set *is_profInc to True, else
   leave it unchanged. */
Int emit_MIPSInstr ( /*MB_MOD*/Bool* is_profInc,
                     UChar* buf, Int nbuf, const MIPSInstr* i,
                     Bool mode64,
                     VexEndness endness_host,
                     const void* disp_cp_chain_me_to_slowEP,
                     const void* disp_cp_chain_me_to_fastEP,
                     const void* disp_cp_xindir,
                     const void* disp_cp_xassisted )
{
   UChar *p = &buf[0];
   UChar *ptmp = p;
   vassert(nbuf >= 32);

   switch (i->tag) {
      case Min_LI:
         p = mkLoadImm(p, iregNo(i->Min.LI.dst, mode64), i->Min.LI.imm, mode64);
         goto done;

      case Min_Alu: {
         MIPSRH *srcR = i->Min.Alu.srcR;
         Bool immR = toBool(srcR->tag == Mrh_Imm);
         UInt r_dst = iregNo(i->Min.Alu.dst, mode64);
         UInt r_srcL = iregNo(i->Min.Alu.srcL, mode64);
         UInt r_srcR = immR ? (-1) /*bogus */ : iregNo(srcR->Mrh.Reg.reg,
                                                       mode64);
         switch (i->Min.Alu.op) {
            /* Malu_ADD, Malu_SUB, Malu_AND, Malu_OR, Malu_NOR, Malu_XOR, Malu_SLT */
            case Malu_ADD:
               if (immR) {
                  vassert(srcR->Mrh.Imm.syned);
                  /* addiu */
                  p = mkFormI(p, 9, r_srcL, r_dst, srcR->Mrh.Imm.imm16);
               } else {
                  /* addu */
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 33);
               }
               break;
            case Malu_SUB:
               if (immR) {
                  /* addiu , but with negated imm */
                  vassert(srcR->Mrh.Imm.syned);
                  vassert(srcR->Mrh.Imm.imm16 != 0x8000);
                  p = mkFormI(p, 9, r_srcL, r_dst, (-srcR->Mrh.Imm.imm16));
               } else {
                  /* subu */
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 35);
               }
               break;
            case Malu_AND:
               if (immR) {
                  /* andi */
                  vassert(!srcR->Mrh.Imm.syned);
                  p = mkFormI(p, 12, r_srcL, r_dst, srcR->Mrh.Imm.imm16);
               } else {
                  /* and */
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 36);
               }
               break;
            case Malu_OR:
               if (immR) {
                  /* ori */
                  vassert(!srcR->Mrh.Imm.syned);
                  p = mkFormI(p, 13, r_srcL, r_dst, srcR->Mrh.Imm.imm16);
               } else {
                  /* or */
                  if (r_srcL == 33)
                     /* MFHI */
                     p = mkFormR(p, 0, 0, 0, r_dst, 0, 16);
                  else if (r_srcL == 34)
                     /* MFLO */
                     p = mkFormR(p, 0, 0, 0, r_dst, 0, 18);
                  else if (r_dst == 33)
                     /* MTHI */
                     p = mkFormR(p, 0, r_srcL, 0, 0, 0, 17);
                  else if (r_dst == 34)
                     /* MTLO */
                     p = mkFormR(p, 0, r_srcL, 0, 0, 0, 19);
                  else
                     p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 37);
               }
               break;
            case Malu_NOR:
               /* nor */
               vassert(!immR);
               p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 39);
               break;
            case Malu_XOR:
               if (immR) {
                  /* xori */
                  vassert(!srcR->Mrh.Imm.syned);
                  p = mkFormI(p, 14, r_srcL, r_dst, srcR->Mrh.Imm.imm16);
               } else {
                  /* xor */
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 38);
               }
               break;
            case Malu_DADD:
               if (immR) {
                  vassert(srcR->Mrh.Imm.syned);
                  vassert(srcR->Mrh.Imm.imm16 != 0x8000);
                  p = mkFormI(p, 25, r_srcL, r_dst, srcR->Mrh.Imm.imm16);
               } else {
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 45);
               }
               break;
            case Malu_DSUB:
               if (immR) {
                  p = mkFormI(p, 25, r_srcL, r_dst, (-srcR->Mrh.Imm.imm16));
               } else {
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 47);
               }
               break;
            case Malu_SLT:
               if (immR) {
                  goto bad;
               } else {
                  p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 42);
               }
               break;

            default:
               goto bad;
         }
         goto done;
      }

      case Min_Shft: {
         MIPSRH *srcR = i->Min.Shft.srcR;
         Bool sz32 = i->Min.Shft.sz32;
         Bool immR = toBool(srcR->tag == Mrh_Imm);
         UInt r_dst = iregNo(i->Min.Shft.dst, mode64);
         UInt r_srcL = iregNo(i->Min.Shft.srcL, mode64);
         UInt r_srcR = immR ? (-1) /*bogus */ : iregNo(srcR->Mrh.Reg.reg,
                                                       mode64);
         if (!mode64)
            vassert(sz32);
         switch (i->Min.Shft.op) {
            case Mshft_SLL:
               if (sz32) {
                  if (immR) {
                     UInt n = srcR->Mrh.Imm.imm16;
                     vassert(n >= 0 && n <= 32);
                     p = mkFormS(p, 0, r_dst, 0, r_srcL, n, 0);
                  } else {
                     /* shift variable */
                     p = mkFormS(p, 0, r_dst, r_srcR, r_srcL, 0, 4);
                  }
               } else {
                  if (immR) {
                     UInt n = srcR->Mrh.Imm.imm16;
                     vassert((n >= 0 && n < 32) || (n > 31 && n < 64));
                     if (n >= 0 && n < 32) {
                        p = mkFormS(p, 0, r_dst, 0, r_srcL, n, 56);
                     } else {
                        p = mkFormS(p, 0, r_dst, 0, r_srcL, n - 32, 60);
                     }
                  } else {
                     p = mkFormS(p, 0, r_dst, r_srcR, r_srcL, 0, 20);
                  }
               }
               break;

            case Mshft_SRL:
               if (sz32) {
                  /* SRL, SRLV */
                  if (immR) {
                     UInt n = srcR->Mrh.Imm.imm16;
                     vassert(n >= 0 && n < 32);
                     p = mkFormS(p, 0, r_dst, 0, r_srcL, n, 2);
                  } else {
                     /* shift variable */
                     p = mkFormS(p, 0, r_dst, r_srcR, r_srcL, 0, 6);
                  }
               } else {
                  /* DSRL, DSRL32, DSRLV */
                  if (immR) {
                     UInt n = srcR->Mrh.Imm.imm16;
                     vassert((n >= 0 && n < 32) || (n > 31 && n < 64));
                     if (n >= 0 && n < 32) {
                        p = mkFormS(p, 0, r_dst, 0, r_srcL, n, 58);
                     } else {
                        p = mkFormS(p, 0, r_dst, 0, r_srcL, n - 32, 62);
                     }
                  } else {
                     p = mkFormS(p, 0, r_dst, r_srcR, r_srcL, 0, 22);
                  }
               }
               break;

            case Mshft_SRA:
               if (sz32) {
                  /* SRA, SRAV */
                  if (immR) {
                     UInt n = srcR->Mrh.Imm.imm16;
                     vassert(n >= 0 && n < 32);
                     p = mkFormS(p, 0, r_dst, 0, r_srcL, n, 3);
                  } else {
                     /* shift variable */
                     p = mkFormS(p, 0, r_dst, r_srcR, r_srcL, 0, 7);
                  }
               } else {
                  /* DSRA, DSRA32, DSRAV */
                  if (immR) {
                     UInt n = srcR->Mrh.Imm.imm16;
                     vassert((n >= 0 && n < 32) || (n > 31 && n < 64));
                     if (n >= 0 && n < 32) {
                        p = mkFormS(p, 0, r_dst, 0, r_srcL, n, 59);
                     } else {
                        p = mkFormS(p, 0, r_dst, 0, r_srcL, n - 32, 63);
                     }
                  } else {
                     p = mkFormS(p, 0, r_dst, r_srcR, r_srcL, 0, 23);
                  }
               }
               break;

            default:
               goto bad;
         }

         goto done;
      }

      case Min_Unary: {
         UInt r_dst = iregNo(i->Min.Unary.dst, mode64);
         UInt r_src = iregNo(i->Min.Unary.src, mode64);

         switch (i->Min.Unary.op) {
            /* Mun_CLO, Mun_CLZ, Mun_NOP, Mun_DCLO, Mun_DCLZ */
            case Mun_CLO:  /* clo */
               p = mkFormR(p, 28, r_src, r_dst , r_dst, 0, 33);
               break;
            case Mun_CLZ:  /* clz */
               p = mkFormR(p, 28, r_src, r_dst , r_dst, 0, 32);
               break;
            case Mun_NOP:  /* nop (sll r0,r0,0) */
               p = mkFormR(p, 0, 0, 0, 0, 0, 0);
               break;
            case Mun_DCLO:  /* clo */
               p = mkFormR(p, 28, r_src, r_dst , r_dst, 0, 37);
               break;
            case Mun_DCLZ:  /* clz */
               p = mkFormR(p, 28, r_src, r_dst , r_dst, 0, 36);
               break;
         }
         goto done;
      }

      case Min_Cmp: {
         UInt r_srcL = iregNo(i->Min.Cmp.srcL, mode64);
         UInt r_srcR = iregNo(i->Min.Cmp.srcR, mode64);
         UInt r_dst = iregNo(i->Min.Cmp.dst, mode64);

         switch (i->Min.Cmp.cond) {
            case MIPScc_EQ:
               /* xor r_dst, r_srcL, r_srcR
                  sltiu r_dst, r_dst, 1 */
               p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 38);
               p = mkFormI(p, 11, r_dst, r_dst, 1);
               break;
            case MIPScc_NE:
               /* xor r_dst, r_srcL, r_srcR
                  sltu r_dst, zero, r_dst */
               p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 38);
               p = mkFormR(p, 0, 0, r_dst, r_dst, 0, 43);
               break;
            case MIPScc_LT:
               /* slt r_dst, r_srcL, r_srcR */
               p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 42);
               break;
            case MIPScc_LO:
               /* sltu r_dst, r_srcL, r_srcR */
               p = mkFormR(p, 0, r_srcL, r_srcR, r_dst, 0, 43);
               break;
            case MIPScc_LE:
               /* slt r_dst, r_srcR, r_srcL
                  xori r_dst, r_dst, 1 */
               p = mkFormR(p, 0, r_srcR, r_srcL, r_dst, 0, 42);
               p = mkFormI(p, 14, r_dst, r_dst, 1);
               break;
            case MIPScc_LS:
               /* sltu r_dst, rsrcR, r_srcL
                  xori r_dsr, r_dst, 1 */
               p = mkFormR(p, 0, r_srcR, r_srcL, r_dst, 0, 43);
               p = mkFormI(p, 14, r_dst, r_dst, 1);
               break;
            default:
               goto bad;
         }
         goto done;
      }

      case Min_Mul: {
         Bool syned = i->Min.Mul.syned;
         Bool widening = i->Min.Mul.widening;
         Bool sz32 = i->Min.Mul.sz32;
         UInt r_srcL = iregNo(i->Min.Mul.srcL, mode64);
         UInt r_srcR = iregNo(i->Min.Mul.srcR, mode64);
         UInt r_dst = iregNo(i->Min.Mul.dst, mode64);
         if (widening) {
            if (sz32) {
               if (syned)
                  /* mult */
                  p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 24);
               else
                  /* multu */
                  p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 25);
            } else {
               if (syned)  /* DMULT  r_dst,r_srcL,r_srcR */
                  p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 28);
               else  /* DMULTU r_dst,r_srcL,r_srcR */
                  p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 29);
            }
         } else {
            if (sz32)
               /* mul */
               p = mkFormR(p, 28, r_srcL, r_srcR, r_dst, 0, 2);
            else if (mode64 && !sz32)
               p = mkFormR(p, 28, r_srcL, r_srcR, r_dst, 0, 2);
            else
               goto bad;
         }
         goto done;
      }

      case Min_Macc: {
         Bool syned = i->Min.Macc.syned;
         UInt r_srcL = iregNo(i->Min.Macc.srcL, mode64);
         UInt r_srcR = iregNo(i->Min.Macc.srcR, mode64);

         if (syned) {
            switch (i->Min.Macc.op) {
               case Macc_ADD:
                  /* madd */
                  p = mkFormR(p, 28, r_srcL, r_srcR, 0, 0, 0);
                  break;
               case Macc_SUB:
                  /* msub */
                  p = mkFormR(p, 28, r_srcL, r_srcR, 0, 0,
                         4);
                  break;
               default:
                  goto bad;
            }
         } else {
            switch (i->Min.Macc.op) {
               case Macc_ADD:
                  /* maddu */
                  p = mkFormR(p, 28, r_srcL, r_srcR, 0, 0,
                         1);
                  break;
               case Macc_SUB:
                  /* msubu */
                  p = mkFormR(p, 28, r_srcL, r_srcR, 0, 0,
                         5);
                  break;
               default:
                  goto bad;
            }
         }

         goto done;
      }

      case Min_Div: {
         Bool syned = i->Min.Div.syned;
         Bool sz32 = i->Min.Div.sz32;
         UInt r_srcL = iregNo(i->Min.Div.srcL, mode64);
         UInt r_srcR = iregNo(i->Min.Div.srcR, mode64);
         if (sz32) {
            if (syned) {
               /* div */
               p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 26);
            } else
               /* divu */
               p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 27);
            goto done;
         } else {
            if (syned) {
               /* ddiv */
               p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 30);
            } else
               /* ddivu */
               p = mkFormR(p, 0, r_srcL, r_srcR, 0, 0, 31);
            goto done;
         }
      }

      case Min_Mthi: {
         UInt r_src = iregNo(i->Min.MtHL.src, mode64);
         p = mkFormR(p, 0, r_src, 0, 0, 0, 17);
         goto done;
      }

      case Min_Mtlo: {
         UInt r_src = iregNo(i->Min.MtHL.src, mode64);
         p = mkFormR(p, 0, r_src, 0, 0, 0, 19);
         goto done;
      }

      case Min_Mfhi: {
         UInt r_dst = iregNo(i->Min.MfHL.dst, mode64);
         p = mkFormR(p, 0, 0, 0, r_dst, 0, 16);
         goto done;
      }

      case Min_Mflo: {
         UInt r_dst = iregNo(i->Min.MfHL.dst, mode64);
         p = mkFormR(p, 0, 0, 0, r_dst, 0, 18);
         goto done;
      }

      case Min_MtFCSR: {
         UInt r_src = iregNo(i->Min.MtFCSR.src, mode64);
         /* ctc1 */
         p = mkFormR(p, 17, 6, r_src, 31, 0, 0);
         goto done;
      }

      case Min_MfFCSR: {
         UInt r_dst = iregNo(i->Min.MfFCSR.dst, mode64);
         /* cfc1 */
         p = mkFormR(p, 17, 2, r_dst, 31, 0, 0);
         goto done;
      }

      case Min_Call: {
         if (i->Min.Call.cond != MIPScc_AL
             && i->Min.Call.rloc.pri != RLPri_None) {
            /* The call might not happen (it isn't unconditional) and
               it returns a result.  In this case we will need to
               generate a control flow diamond to put 0x555..555 in
               the return register(s) in the case where the call
               doesn't happen.  If this ever becomes necessary, maybe
               copy code from the ARM equivalent.  Until that day,
               just give up. */
            goto bad;
         }
         MIPSCondCode cond = i->Min.Call.cond;
         UInt r_dst = 25;  /* using %r25 as address temporary -
                              see getRegUsage_MIPSInstr */

         /* jump over the following insns if condition does not hold */
         if (cond != MIPScc_AL) {
            /* jmp fwds if !condition */
            /* don't know how many bytes to jump over yet...
               make space for a jump instruction + nop!!! and fill in later. */
            ptmp = p;  /* fill in this bit later */
            p += 8;    /* p += 8 */
         }

         if (!mode64) {
            /* addiu $29, $29, -16 */
            p = mkFormI(p, 9, 29, 29, 0xFFF0);
         }

         /* load target to r_dst; p += 4|8 */
         p = mkLoadImm(p, r_dst, i->Min.Call.target, mode64);

         /* jalr r_dst */
         p = mkFormR(p, 0, r_dst, 0, 31, 0, 9);  /* p += 4 */
         p = mkFormR(p, 0, 0, 0, 0, 0, 0);       /* p += 4 */

         if (!mode64) {
            /* addiu $29, $29, 16 */
            p = mkFormI(p, 9, 29, 29, 0x0010);
         }

         /* Fix up the conditional jump, if there was one. */
         if (cond != MIPScc_AL) {
            UInt r_src = iregNo(i->Min.Call.src, mode64);
            Int delta = p - ptmp;

            vassert(delta >= 20 && delta <= 32);
            /* blez r_src, delta/4-1
               nop */
            ptmp = mkFormI(ptmp, 6, r_src, 0, delta / 4 - 1);
            mkFormR(ptmp, 0, 0, 0, 0, 0, 0);
         }
         goto done;
      }

      case Min_XDirect: {
         /* NB: what goes on here has to be very closely coordinated
            with the chainXDirect_MIPS and unchainXDirect_MIPS below. */
         /* We're generating chain-me requests here, so we need to be
            sure this is actually allowed -- no-redir translations
            can't use chain-me's.  Hence: */
         vassert(disp_cp_chain_me_to_slowEP != NULL);
         vassert(disp_cp_chain_me_to_fastEP != NULL);

         /* Use ptmp for backpatching conditional jumps. */
         ptmp = NULL;

         /* First off, if this is conditional, create a conditional
            jump over the rest of it.  Or at least, leave a space for
            it that we will shortly fill in. */
         if (i->Min.XDirect.cond != MIPScc_AL) {
            vassert(i->Min.XDirect.cond != MIPScc_NV);
            ptmp = p;
            p += 12;
         }

         /* Update the guest PC. */
         /* move r9, dstGA */
         /* sw/sd r9, amPC */
         p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9, (ULong)i->Min.XDirect.dstGA,
                                   mode64);
         p = do_load_or_store_machine_word(p, False /*!isLoad*/ , /*r*/ 9,
                                           i->Min.XDirect.amPC, mode64);

         /* --- FIRST PATCHABLE BYTE follows --- */
         /* VG_(disp_cp_chain_me_to_{slowEP,fastEP}) (where we're
            calling to) backs up the return address, so as to find the
            address of the first patchable byte.  So: don't change the
            number of instructions (3) below. */
         /* move r9, VG_(disp_cp_chain_me_to_{slowEP,fastEP}) */
         /* jr  r9  */
         const void* disp_cp_chain_me
                  = i->Min.XDirect.toFastEP ? disp_cp_chain_me_to_fastEP
                                              : disp_cp_chain_me_to_slowEP;
         p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9,
                                   (Addr)disp_cp_chain_me, mode64);
         /* jalr $9 */
         /* nop */
         p = mkFormR(p, 0, 9, 0, 31, 0, 9);  /* p += 4 */
         p = mkFormR(p, 0, 0, 0, 0, 0, 0);   /* p += 4 */
         /* --- END of PATCHABLE BYTES --- */

         /* Fix up the conditional jump, if there was one. */
         if (i->Min.XDirect.cond != MIPScc_AL) {
            Int delta = p - ptmp;
            delta = delta / 4 - 3;
            vassert(delta > 0 && delta < 40);

            /* lw $9, COND_OFFSET(GuestSP)
               beq $9, $0, 2
               nop */
            ptmp = mkFormI(ptmp, 35, GuestSP, 9, COND_OFFSET(mode64));
            ptmp = mkFormI(ptmp, 4, 0, 9, (delta));
            mkFormR(ptmp, 0, 0, 0, 0, 0, 0);
         }
         goto done;
      }

      case Min_XIndir: {
         /* We're generating transfers that could lead indirectly to a
            chain-me, so we need to be sure this is actually allowed --
            no-redir translations are not allowed to reach normal
            translations without going through the scheduler.  That means
            no XDirects or XIndirs out from no-redir translations.
            Hence: */
         vassert(disp_cp_xindir != NULL);

         /* Use ptmp for backpatching conditional jumps. */
         ptmp = NULL;

         /* First off, if this is conditional, create a conditional
            jump over the rest of it. */
         if (i->Min.XIndir.cond != MIPScc_AL) {
            vassert(i->Min.XIndir.cond != MIPScc_NV);
            ptmp = p;
            p += 12;
         }

         /* Update the guest PC. */
         /* sw/sd r-dstGA, amPC */
         p = do_load_or_store_machine_word(p, False /*!isLoad*/ ,
                                           iregNo(i->Min.XIndir.dstGA, mode64),
                                           i->Min.XIndir.amPC, mode64);

         /* move r9, VG_(disp_cp_xindir) */
         /* jalr   r9 */
         /* nop */
         p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9,
                                   (Addr)disp_cp_xindir, mode64);
         p = mkFormR(p, 0, 9, 0, 31, 0, 9);  /* p += 4 */
         p = mkFormR(p, 0, 0, 0, 0, 0, 0);   /* p += 4 */

         /* Fix up the conditional jump, if there was one. */
         if (i->Min.XIndir.cond != MIPScc_AL) {
            Int delta = p - ptmp;
            delta = delta / 4 - 3;
            vassert(delta > 0 && delta < 40);

            /* lw $9, COND_OFFSET($GuestSP)
               beq $9, $0, 2
               nop */
            ptmp = mkFormI(ptmp, 35, GuestSP, 9, COND_OFFSET(mode64));
            ptmp = mkFormI(ptmp, 4, 0, 9, (delta));
            mkFormR(ptmp, 0, 0, 0, 0, 0, 0);
         }
         goto done;
      }

      case Min_XAssisted: {
         /* First off, if this is conditional, create a conditional jump
            over the rest of it.  Or at least, leave a space for it that
            we will shortly fill in. */
         ptmp = NULL;
         if (i->Min.XAssisted.cond != MIPScc_AL) {
            vassert(i->Min.XAssisted.cond != MIPScc_NV);
            ptmp = p;
            p += 12;
         }

         /* Update the guest PC. */
         /* sw/sd r-dstGA, amPC */
         p = do_load_or_store_machine_word(p, False /*!isLoad*/ ,
                                           iregNo(i->Min.XIndir.dstGA, mode64),
                                           i->Min.XIndir.amPC, mode64);

         /* imm32/64 r31, $magic_number */
         UInt trcval = 0;
         switch (i->Min.XAssisted.jk) {
            case Ijk_ClientReq:     trcval = VEX_TRC_JMP_CLIENTREQ;     break;
            case Ijk_Sys_syscall:   trcval = VEX_TRC_JMP_SYS_SYSCALL;   break;
            /* case Ijk_Sys_int128: trcval = VEX_TRC_JMP_SYS_INT128;   break; */
            case Ijk_Yield:         trcval = VEX_TRC_JMP_YIELD;       break;
            case Ijk_EmWarn:        trcval = VEX_TRC_JMP_EMWARN;        break;
            case Ijk_EmFail:        trcval = VEX_TRC_JMP_EMFAIL;        break;
            /* case Ijk_MapFail:   trcval = VEX_TRC_JMP_MAPFAIL;       break; */
            case Ijk_NoDecode:      trcval = VEX_TRC_JMP_NODECODE;      break;
            case Ijk_InvalICache:   trcval = VEX_TRC_JMP_INVALICACHE;   break;
            case Ijk_NoRedir:       trcval = VEX_TRC_JMP_NOREDIR;       break;
            case Ijk_SigILL:        trcval = VEX_TRC_JMP_SIGILL;        break;
            case Ijk_SigTRAP:       trcval = VEX_TRC_JMP_SIGTRAP;       break;
            /* case Ijk_SigSEGV:   trcval = VEX_TRC_JMP_SIGSEGV;       break; */
            case Ijk_SigBUS:        trcval = VEX_TRC_JMP_SIGBUS;        break;
            case Ijk_SigFPE_IntDiv: trcval = VEX_TRC_JMP_SIGFPE_INTDIV; break;
            case Ijk_SigFPE_IntOvf: trcval = VEX_TRC_JMP_SIGFPE_INTOVF; break;
            case Ijk_Boring:        trcval = VEX_TRC_JMP_BORING;        break;
            /* We don't expect to see the following being assisted.
               case Ijk_Ret:
               case Ijk_Call:
               fallthrough */
            default:
               ppIRJumpKind(i->Min.XAssisted.jk);
               vpanic("emit_MIPSInstr.Min_XAssisted: unexpected jump kind");
         }
         vassert(trcval != 0);
         p = mkLoadImm_EXACTLY2or6(p, /*r*/ GuestSP, trcval, mode64);

         /* move r9, VG_(disp_cp_xassisted) */
         p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9,
                                   (ULong)(Addr)disp_cp_xassisted, mode64);
         /* jalr $9
             nop */
         p = mkFormR(p, 0, 9, 0, 31, 0, 9);  /* p += 4 */
         p = mkFormR(p, 0, 0, 0, 0, 0, 0);   /* p += 4 */

         /* Fix up the conditional jump, if there was one. */
         if (i->Min.XAssisted.cond != MIPScc_AL) {
            Int delta = p - ptmp;
            delta = delta / 4 - 3;
            vassert(delta > 0 && delta < 40);

            /* lw $9, COND_OFFSET($GuestSP)
               beq $9, $0, 2
               nop */
            ptmp = mkFormI(ptmp, 35, GuestSP, 9, COND_OFFSET(mode64));
            ptmp = mkFormI(ptmp, 4, 0, 9, (delta));
            mkFormR(ptmp, 0, 0, 0, 0, 0, 0);
         }
         goto done;
      }

      case Min_Load: {
         MIPSAMode *am_addr = i->Min.Load.src;
         if (am_addr->tag == Mam_IR) {
            UInt r_dst = iregNo(i->Min.Load.dst, mode64);
            UInt opc, sz = i->Min.Load.sz;
            if (mode64 && (sz == 4 || sz == 8)) {
               /* should be guaranteed to us by iselWordExpr_AMode */
               vassert(0 == (am_addr->Mam.IR.index & 3));
            }
            switch (sz) {
               case 1:
                  opc = 32;
                  break;
               case 2:
                  opc = 33;
                  break;
               case 4:
                  opc = 35;
                  break;
               case 8:
                  opc = 55;
                  vassert(mode64);
                  break;
               default:
                  goto bad;
            }

            p = doAMode_IR(p, opc, r_dst, am_addr, mode64);
            goto done;
         } else if (am_addr->tag == Mam_RR) {
            UInt r_dst = iregNo(i->Min.Load.dst, mode64);
            UInt opc, sz = i->Min.Load.sz;

            switch (sz) {
               case 1:
                  opc = 32;
                  break;
               case 2:
                  opc = 33;
                  break;
               case 4:
                  opc = 35;
                  break;
               case 8:
                  opc = 55;
                  vassert(mode64);
                  break;
               default:
                  goto bad;
            }

            p = doAMode_RR(p, opc, r_dst, am_addr, mode64);
            goto done;
         }
         break;
      }

      case Min_Store: {
         MIPSAMode *am_addr = i->Min.Store.dst;
         if (am_addr->tag == Mam_IR) {
            UInt r_src = iregNo(i->Min.Store.src, mode64);
            UInt opc, sz = i->Min.Store.sz;
            if (mode64 && (sz == 4 || sz == 8)) {
               /* should be guaranteed to us by iselWordExpr_AMode */
               vassert(0 == (am_addr->Mam.IR.index & 3));
            }
            switch (sz) {
               case 1:
                  opc = 40;
                  break;
               case 2:
                  opc = 41;
                  break;
               case 4:
                  opc = 43;
                  break;
               case 8:
                  vassert(mode64);
                  opc = 63;
                  break;
               default:
                  goto bad;
            }

            p = doAMode_IR(p, opc, r_src, am_addr, mode64);
            goto done;
         } else if (am_addr->tag == Mam_RR) {
            UInt r_src = iregNo(i->Min.Store.src, mode64);
            UInt opc, sz = i->Min.Store.sz;

            switch (sz) {
               case 1:
                  opc = 40;
                  break;
               case 2:
                  opc = 41;
                  break;
               case 4:
                  opc = 43;
                  break;
               case 8:
                  vassert(mode64);
                  opc = 63;
                  break;
               default:
                  goto bad;
            }

            p = doAMode_RR(p, opc, r_src, am_addr, mode64);
            goto done;
         }
         break;
      }
      case Min_LoadL: {
         MIPSAMode *am_addr = i->Min.LoadL.src;
         UInt r_src = iregNo(am_addr->Mam.IR.base, mode64);
         UInt idx = am_addr->Mam.IR.index;
         UInt r_dst = iregNo(i->Min.LoadL.dst, mode64);

         if (i->Min.LoadL.sz == 4)
            p = mkFormI(p, 0x30, r_src, r_dst, idx);
         else
            p = mkFormI(p, 0x34, r_src, r_dst, idx);
         goto done;
      }
      case Min_StoreC: {
         MIPSAMode *am_addr = i->Min.StoreC.dst;
         UInt r_src = iregNo(i->Min.StoreC.src, mode64);
         UInt idx = am_addr->Mam.IR.index;
         UInt r_dst = iregNo(am_addr->Mam.IR.base, mode64);

         if (i->Min.StoreC.sz == 4)
            p = mkFormI(p, 0x38, r_dst, r_src, idx);
         else
            p = mkFormI(p, 0x3C, r_dst, r_src, idx);
         goto done;
      }
      case Min_Cas: {
         if (i->Min.Cas.sz != 8 && i->Min.Cas.sz != 4)
            goto bad;
         UInt old  = iregNo(i->Min.Cas.old, mode64);
         UInt addr = iregNo(i->Min.Cas.addr, mode64);
         UInt expd = iregNo(i->Min.Cas.expd, mode64);
         UInt data = iregNo(i->Min.Cas.data, mode64);
         Bool sz8  = toBool(i->Min.Cas.sz == 8);

         /*
          * ll(d)    old,  0(addr)
          * bne      old,  expd, end
          * nop
          * (d)addiu old,  old,  1
          * sc(d)    data, 0(addr)
          * movn     old,  expd, data
          * end:
          */
         // ll(d) old, 0(addr)
         p = mkFormI(p, sz8 ? 0x34 : 0x30, addr, old, 0);
         // bne  old,  expd, end
         p = mkFormI(p, 5, old, expd, 4);
         // nop
         p = mkFormR(p, 0, 0, 0, 0, 0, 0);
         // (d)addiu old,  old,  1
         p = mkFormI(p, sz8 ? 25 : 9, old, old, 1);
         // sc(d)  data, 0(addr)
         p = mkFormI(p, sz8 ? 0x3C : 0x38, addr, data, 0);
         // movn old,  expd, data
         p = mkFormR(p, 0, expd, data, old, 0, 0xb);

         goto done;
      }
      case Min_RdWrLR: {
         UInt reg = iregNo(i->Min.RdWrLR.gpr, mode64);
         Bool wrLR = i->Min.RdWrLR.wrLR;
         if (wrLR)
            p = mkMoveReg(p, 31, reg);
         else
            p = mkMoveReg(p, reg, 31);
         goto done;
      }

      /* Floating point */
      case Min_FpLdSt: {
         MIPSAMode *am_addr = i->Min.FpLdSt.addr;
         UChar sz = i->Min.FpLdSt.sz;
         vassert(sz == 4 || sz == 8);
         if (sz == 4) {
            UInt f_reg = fregNo(i->Min.FpLdSt.reg, mode64);
            if (i->Min.FpLdSt.isLoad) {
               if (am_addr->tag == Mam_IR)
                  p = doAMode_IR(p, 0x31, f_reg, am_addr, mode64);
               else if (am_addr->tag == Mam_RR)
                  p = doAMode_RR(p, 0x31, f_reg, am_addr, mode64);
            } else {
               if (am_addr->tag == Mam_IR)
                  p = doAMode_IR(p, 0x39, f_reg, am_addr, mode64);
               else if (am_addr->tag == Mam_RR)
                  p = doAMode_RR(p, 0x39, f_reg, am_addr, mode64);
            }
         } else if (sz == 8) {
            UInt f_reg = dregNo(i->Min.FpLdSt.reg);
            if (i->Min.FpLdSt.isLoad) {
               if (am_addr->tag == Mam_IR) {
                  p = doAMode_IR(p, 0x35, f_reg, am_addr, mode64);
               } else if (am_addr->tag == Mam_RR) {
                  p = doAMode_RR(p, 0x35, f_reg, am_addr, mode64);
               }
            } else {
               if (am_addr->tag == Mam_IR) {
                  p = doAMode_IR(p, 0x3d, f_reg, am_addr, mode64);
               } else if (am_addr->tag == Mam_RR) {
                  p = doAMode_RR(p, 0x3d, f_reg, am_addr, mode64);
               }
            }
         }
         goto done;
      }

      case Min_FpUnary: {
         switch (i->Min.FpUnary.op) {
            case Mfp_MOVS: {  /* FP move */
               UInt fr_dst = fregNo(i->Min.FpUnary.dst, mode64);
               UInt fr_src = fregNo(i->Min.FpUnary.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x6);
               break;
            }
            case Mfp_MOVD: {  /* FP move */
                UInt fr_dst = dregNo(i->Min.FpUnary.dst);
                UInt fr_src = dregNo(i->Min.FpUnary.src);
                p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x6);
                break;
             }
            case Mfp_ABSS: {  /* ABS.S */
               UInt fr_dst = fregNo(i->Min.FpUnary.dst, mode64);
               UInt fr_src = fregNo(i->Min.FpUnary.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x5);
               break;
            }
            case Mfp_ABSD: {  /* ABS.D */
               UInt fr_dst = dregNo(i->Min.FpUnary.dst);
               UInt fr_src = dregNo(i->Min.FpUnary.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x5);
               break;
            }
            case Mfp_NEGS: {  /* NEG.S */
               UInt fr_dst = fregNo(i->Min.FpUnary.dst, mode64);
               UInt fr_src = fregNo(i->Min.FpUnary.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x7);
               break;
            }
            case Mfp_NEGD: {  /* NEG.D */
               UInt fr_dst = dregNo(i->Min.FpUnary.dst);
               UInt fr_src = dregNo(i->Min.FpUnary.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x7);
               break;
            }
            case Mfp_SQRTS: {  /* SQRT.S */
               UInt fr_dst = fregNo(i->Min.FpUnary.dst, mode64);
               UInt fr_src = fregNo(i->Min.FpUnary.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x04);
               break;
            }
            case Mfp_SQRTD: {  /* SQRT.D */
               UInt fr_dst = dregNo(i->Min.FpUnary.dst);
               UInt fr_src = dregNo(i->Min.FpUnary.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x04);
               break;
            }
            default:
               goto bad;
         }
         goto done;
      }

      case Min_FpBinary: {
         switch (i->Min.FpBinary.op) {
            case Mfp_ADDS: {
               UInt fr_dst = fregNo(i->Min.FpBinary.dst, mode64);
               UInt fr_srcL = fregNo(i->Min.FpBinary.srcL, mode64);
               UInt fr_srcR = fregNo(i->Min.FpBinary.srcR, mode64);
               p = mkFormR(p, 0x11, 0x10, fr_srcR, fr_srcL, fr_dst, 0);
               break;
            }
            case Mfp_SUBS: {
               UInt fr_dst = fregNo(i->Min.FpBinary.dst, mode64);
               UInt fr_srcL = fregNo(i->Min.FpBinary.srcL, mode64);
               UInt fr_srcR = fregNo(i->Min.FpBinary.srcR, mode64);
               p = mkFormR(p, 0x11, 0x10, fr_srcR, fr_srcL, fr_dst, 1);
               break;
            }
            case Mfp_MULS: {
               UInt fr_dst = fregNo(i->Min.FpBinary.dst, mode64);
               UInt fr_srcL = fregNo(i->Min.FpBinary.srcL, mode64);
               UInt fr_srcR = fregNo(i->Min.FpBinary.srcR, mode64);
               p = mkFormR(p, 0x11, 0x10, fr_srcR, fr_srcL, fr_dst, 2);
               break;
            }
            case Mfp_DIVS: {
               UInt fr_dst = fregNo(i->Min.FpBinary.dst, mode64);
               UInt fr_srcL = fregNo(i->Min.FpBinary.srcL, mode64);
               UInt fr_srcR = fregNo(i->Min.FpBinary.srcR, mode64);
               p = mkFormR(p, 0x11, 0x10, fr_srcR, fr_srcL, fr_dst, 3);
               break;
            }
            case Mfp_ADDD: {
               UInt fr_dst = dregNo(i->Min.FpBinary.dst);
               UInt fr_srcL = dregNo(i->Min.FpBinary.srcL);
               UInt fr_srcR = dregNo(i->Min.FpBinary.srcR);
               p = mkFormR(p, 0x11, 0x11, fr_srcR, fr_srcL, fr_dst, 0);
               break;
            }
            case Mfp_SUBD: {
               UInt fr_dst = dregNo(i->Min.FpBinary.dst);
               UInt fr_srcL = dregNo(i->Min.FpBinary.srcL);
               UInt fr_srcR = dregNo(i->Min.FpBinary.srcR);
               p = mkFormR(p, 0x11, 0x11, fr_srcR, fr_srcL, fr_dst, 1);
               break;
            }
            case Mfp_MULD: {
               UInt fr_dst = dregNo(i->Min.FpBinary.dst);
               UInt fr_srcL = dregNo(i->Min.FpBinary.srcL);
               UInt fr_srcR = dregNo(i->Min.FpBinary.srcR);
               p = mkFormR(p, 0x11, 0x11, fr_srcR, fr_srcL, fr_dst, 2);
               break;
            }
            case Mfp_DIVD: {
               UInt fr_dst = dregNo(i->Min.FpBinary.dst);
               UInt fr_srcL = dregNo(i->Min.FpBinary.srcL);
               UInt fr_srcR = dregNo(i->Min.FpBinary.srcR);
               p = mkFormR(p, 0x11, 0x11, fr_srcR, fr_srcL, fr_dst, 3);
               break;
            }
            default:
               goto bad;
         }
         goto done;
      }

      case Min_FpTernary: {
         switch (i->Min.FpTernary.op) {
            case Mfp_MADDS: {
               UInt fr_dst = fregNo(i->Min.FpTernary.dst, mode64);
               UInt fr_src1 = fregNo(i->Min.FpTernary.src1, mode64);
               UInt fr_src2 = fregNo(i->Min.FpTernary.src2, mode64);
               UInt fr_src3 = fregNo(i->Min.FpTernary.src3, mode64);
               p = mkFormR(p, 0x13, fr_src1, fr_src2, fr_src3, fr_dst, 0x20);
               break;
            }
            case Mfp_MADDD: {
               UInt fr_dst = dregNo(i->Min.FpTernary.dst);
               UInt fr_src1 = dregNo(i->Min.FpTernary.src1);
               UInt fr_src2 = dregNo(i->Min.FpTernary.src2);
               UInt fr_src3 = dregNo(i->Min.FpTernary.src3);
               p = mkFormR(p, 0x13, fr_src1, fr_src2, fr_src3, fr_dst, 0x21);
               break;
            }
            case Mfp_MSUBS: {
               UInt fr_dst = fregNo(i->Min.FpTernary.dst, mode64);
               UInt fr_src1 = fregNo(i->Min.FpTernary.src1, mode64);
               UInt fr_src2 = fregNo(i->Min.FpTernary.src2, mode64);
               UInt fr_src3 = fregNo(i->Min.FpTernary.src3, mode64);
               p = mkFormR(p, 0x13, fr_src1, fr_src2, fr_src3, fr_dst, 0x28);
               break;
            }
            case Mfp_MSUBD: {
               UInt fr_dst = dregNo(i->Min.FpTernary.dst);
               UInt fr_src1 = dregNo(i->Min.FpTernary.src1);
               UInt fr_src2 = dregNo(i->Min.FpTernary.src2);
               UInt fr_src3 = dregNo(i->Min.FpTernary.src3);
               p = mkFormR(p, 0x13, fr_src1, fr_src2, fr_src3, fr_dst, 0x29);
               break;
            }
            default:
               goto bad;
         }
         goto done;
      }

      case Min_FpConvert: {
         switch (i->Min.FpConvert.op) {
            UInt fr_dst, fr_src;
            case Mfp_CVTSD:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x20);
               break;
            case Mfp_CVTSW:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x14, 0, fr_src, fr_dst, 0x20);
               break;
            case Mfp_CVTWD:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x24);
               break;
            case Mfp_CVTWS:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x24);
               break;
            case Mfp_CVTDW:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x14, 0, fr_src, fr_dst, 0x21);
               break;
            case Mfp_CVTDL:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x15, 0, fr_src, fr_dst, 0x21);
               break;
            case Mfp_CVTDS:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x21);
               break;
            case Mfp_CVTSL:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x15, 0, fr_src, fr_dst, 0x20);
               break;
            case Mfp_CVTLS:
               if (mode64) {
                  fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
                  fr_src = dregNo(i->Min.FpConvert.src);
               } else {
                  fr_dst = dregNo(i->Min.FpConvert.dst);
                  fr_src = fregNo(i->Min.FpConvert.src, mode64);
               }
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x25);
               break;
            case Mfp_CVTLD:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x25);
               break;
            case Mfp_TRUWS:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x0D);
               break;
            case Mfp_TRUWD:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x0D);
               break;
            case Mfp_TRULS:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x09);
               break;
            case Mfp_TRULD:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x09);
               break;
            case Mfp_CEILWS:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x0E);
               break;
            case Mfp_CEILWD:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x0E);
               break;
            case Mfp_CEILLS:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x0A);
               break;
            case Mfp_CEILLD:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x0A);
               break;
            case Mfp_ROUNDWS:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x0C);
               break;
            case Mfp_ROUNDWD:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x0C);
               break;
            case Mfp_ROUNDLD:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x08);
               break;
            case Mfp_FLOORWS:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = fregNo(i->Min.FpConvert.src, mode64);
               p = mkFormR(p, 0x11, 0x10, 0, fr_src, fr_dst, 0x0F);
               break;
            case Mfp_FLOORWD:
               fr_dst = fregNo(i->Min.FpConvert.dst, mode64);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x0F);
               break;
            case Mfp_FLOORLD:
               fr_dst = dregNo(i->Min.FpConvert.dst);
               fr_src = dregNo(i->Min.FpConvert.src);
               p = mkFormR(p, 0x11, 0x11, 0, fr_src, fr_dst, 0x0B);
               break;

            default:
               goto bad;
         }
         goto done;
      }

      case Min_FpCompare: {
         UInt r_dst   = iregNo(i->Min.FpCompare.dst, mode64);
         UInt fr_srcL = dregNo(i->Min.FpCompare.srcL);
         UInt fr_srcR = dregNo(i->Min.FpCompare.srcR);

         UInt op;
         switch (i->Min.FpConvert.op) {
            case Mfp_CMP_UN:
               op = 1;
               break;
            case Mfp_CMP_EQ:
               op = 2;
               break;
            case Mfp_CMP_LT:
               op = 12;
               break;
            case Mfp_CMP_NGT:
               op = 15;
               break;               
            default:
               goto bad;
         }
         /* c.cond.d fr_srcL, fr_srcR
            cfc1     r_dst,   $31
            srl      r_dst,   r_dst, 23
            andi     r_dst,   r_dst, 1 */
         p = mkFormR(p, 0x11, 0x11, fr_srcL, fr_srcR, 0, op + 48);
         p = mkFormR(p, 0x11, 0x2, r_dst, 31, 0, 0);
         p = mkFormS(p, 0, r_dst, 0, r_dst, 23, 2);
         p = mkFormI(p, 12, r_dst, r_dst, 1);
         goto done;
      }

      case Min_FpGpMove: {
         switch (i->Min.FpGpMove.op) {
            UInt rt, fs;
            case MFpGpMove_mfc1: {
               rt = iregNo(i->Min.FpGpMove.dst, mode64);
               fs = fregNo(i->Min.FpGpMove.src, mode64);
               p = mkFormR(p, 0x11, 0x0, rt, fs, 0x0, 0x0);
               break;
            }
            case MFpGpMove_dmfc1: {
               vassert(mode64);
               rt = iregNo(i->Min.FpGpMove.dst, mode64);
               fs = fregNo(i->Min.FpGpMove.src, mode64);
               p = mkFormR(p, 0x11, 0x1, rt, fs, 0x0, 0x0);
               break;
            }
            case MFpGpMove_mtc1: {
               rt = iregNo(i->Min.FpGpMove.src, mode64);
               fs = fregNo(i->Min.FpGpMove.dst, mode64);
               p = mkFormR(p, 0x11, 0x4, rt, fs, 0x0, 0x0);
               break;
            }
            case MFpGpMove_dmtc1: {
               vassert(mode64);
               rt = iregNo(i->Min.FpGpMove.src, mode64);
               fs = fregNo(i->Min.FpGpMove.dst, mode64);
               p = mkFormR(p, 0x11, 0x5, rt, fs, 0x0, 0x0);
               break;
            }
            default:
               goto bad;
         }
         goto done;
      }

      case Min_MoveCond: {
         switch (i->Min.MoveCond.op) {
            UInt d, s, t;
            case MFpMoveCond_movns: {
               d = fregNo(i->Min.MoveCond.dst, mode64);
               s = fregNo(i->Min.MoveCond.src, mode64);
               t = iregNo(i->Min.MoveCond.cond, mode64);
               p = mkFormR(p, 0x11, 0x10, t, s, d, 0x13);
               break;
            }
            case MFpMoveCond_movnd: {
               d = dregNo(i->Min.MoveCond.dst);
               s = dregNo(i->Min.MoveCond.src);
               t = iregNo(i->Min.MoveCond.cond, mode64);
               p = mkFormR(p, 0x11, 0x11, t, s, d, 0x13);
               break;
            }
            case MMoveCond_movn: {
               d = iregNo(i->Min.MoveCond.dst, mode64);
               s = iregNo(i->Min.MoveCond.src, mode64);
               t = iregNo(i->Min.MoveCond.cond, mode64);
               p = mkFormR(p, 0, s, t, d, 0, 0xb);
               break;
            }
            default:
               goto bad;
         }
         goto done;
      }

      case Min_EvCheck: {
         /* This requires a 32-bit dec/test in 32 mode. */
         /* We generate:
               lw      r9, amCounter
               addiu   r9, r9, -1
               sw      r9, amCounter
               bgez    r9, nofail
               lw      r9, amFailAddr
               jalr    r9
               nop
              nofail:
         */
         UChar* p0 = p;
         /* lw  r9, amCounter */
         p = do_load_or_store_word32(p, True /*isLoad*/ , /*r*/ 9,
                                     i->Min.EvCheck.amCounter, mode64);
         /* addiu r9,r9,-1 */
         p = mkFormI(p, 9, 9, 9, 0xFFFF);
         /* sw r30, amCounter */
         p = do_load_or_store_word32(p, False /*!isLoad*/ , /*r*/ 9,
                                     i->Min.EvCheck.amCounter, mode64);
         /* bgez t9, nofail */
         p = mkFormI(p, 1, 9, 1, 3);
         /* lw/ld r9, amFailAddr */
         p = do_load_or_store_machine_word(p, True /*isLoad*/ , /*r*/ 9,
                                           i->Min.EvCheck.amFailAddr, mode64);
         /* jalr $9 */
         p = mkFormR(p, 0, 9, 0, 31, 0, 9);  /* p += 4 */
         p = mkFormR(p, 0, 0, 0, 0, 0, 0);   /* p += 4 */
         /* nofail: */

         /* Crosscheck */
         vassert(evCheckSzB_MIPS() == (UChar*)p - (UChar*)p0);
         goto done;
      }

      case Min_ProfInc: {
         /* Generate a code template to increment a memory location whose
            address will be known later as an immediate value. This code
            template will be patched once the memory location is known.
            For now we do this with address == 0x65556555. */
         if (mode64) {
            /* 64-bit:
               move r9, 0x6555655565556555ULL
               ld r8, 0(r9)
               daddiu r8, r8, 1
               sd r8, 0(r9) */

            /* move r9, 0x6555655565556555ULL */
            p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9, 0x6555655565556555ULL,
                                      True /*mode64*/);
            /* ld r8, 0(r9) */
            p = mkFormI(p, 55, 9, 8, 0);

            /* daddiu r8, r8, 1 */
            p = mkFormI(p, 25, 8, 8, 1);

            /* sd r8, 0(r9) */
            p = mkFormI(p, 63, 9, 8, 0);
         } else {
            /* 32-bit:
               move r9, 0x65556555
               lw r8, 0(r9)
               addiu r8, r8, 1         # add least significant word
               sw r8, 0(r9)
               sltiu r1, r8, 1         # set carry-in bit
               lw r8, 4(r9)
               addu r8, r8, r1
               sw r8, 4(r9) */

            /* move r9, 0x65556555 */
            p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9, 0x65556555ULL,
                                      False /*!mode64*/);
            /* lw r8, 0(r9) */
            p = mkFormI(p, 35, 9, 8, 0);

            /* addiu r8, r8, 1         # add least significant word */
            p = mkFormI(p, 9, 8, 8, 1);

            /* sw r8, 0(r9) */
            p = mkFormI(p, 43, 9, 8, 0);

            /* sltiu r1, r8, 1         # set carry-in bit */
            p = mkFormI(p, 11, 8, 1, 1);

            /* lw r8, 4(r9) */
            p = mkFormI(p, 35, 9, 8, 4);

            /* addu r8, r8, r1 */
            p = mkFormR(p, 0, 8, 1, 8, 0, 33);

            /*  sw r8, 4(r9) */
            p = mkFormI(p, 43, 9, 8, 4);

         }
         /* Tell the caller .. */
         vassert(!(*is_profInc));
         *is_profInc = True;
         goto done;
      }

      default:
         goto bad;

   }

   bad:
      vex_printf("\n=> ");
      ppMIPSInstr(i, mode64);
      vpanic("emit_MIPSInstr");
      /* NOTREACHED */ done:
      vassert(p - &buf[0] <= 128);
      return p - &buf[0];
}

/* How big is an event check?  See case for Min_EvCheck in
   emit_MIPSInstr just above.  That crosschecks what this returns, so
   we can tell if we're inconsistent. */
Int evCheckSzB_MIPS (void)
{
  UInt kInstrSize = 4;
  return 7*kInstrSize;
}

/* NB: what goes on here has to be very closely coordinated with the
   emitInstr case for XDirect, above. */
VexInvalRange chainXDirect_MIPS ( VexEndness endness_host,
                                  void* place_to_chain,
                                  const void* disp_cp_chain_me_EXPECTED,
                                  const void* place_to_jump_to,
                                  Bool  mode64 )
{
   vassert(endness_host == VexEndnessLE || endness_host == VexEndnessBE);
   /* What we're expecting to see is:
        move r9, disp_cp_chain_me_to_EXPECTED
        jalr r9
        nop
      viz
        <8 or 24 bytes generated by mkLoadImm_EXACTLY2or6>
        0x120F809   # jalr r9
        0x00000000  # nop
   */
   UChar* p = (UChar*)place_to_chain;
   vassert(0 == (3 & (HWord)p));
   vassert(isLoadImm_EXACTLY2or6(p, /*r*/9,
                                 (UInt)(Addr)disp_cp_chain_me_EXPECTED,
                                 mode64));
   vassert(fetch32(p + (mode64 ? 24 : 8) + 0) == 0x120F809);
   vassert(fetch32(p + (mode64 ? 24 : 8) + 4) == 0x00000000);
   /* And what we want to change it to is either:
          move r9, place_to_jump_to
          jalr r9
          nop
        viz
          <8 bytes generated by mkLoadImm_EXACTLY2or6>
          0x120F809   # jalr r9
          0x00000000  # nop

      The replacement has the same length as the original.
   */

   p = mkLoadImm_EXACTLY2or6(p, /*r*/9,
                             (Addr)place_to_jump_to, mode64);
   p = emit32(p, 0x120F809);
   p = emit32(p, 0x00000000);

   Int len = p - (UChar*)place_to_chain;
   vassert(len == (mode64 ? 32 : 16)); /* stay sane */
   VexInvalRange vir = {(HWord)place_to_chain, len};
   return vir;
}

/* NB: what goes on here has to be very closely coordinated with the
   emitInstr case for XDirect, above. */
VexInvalRange unchainXDirect_MIPS ( VexEndness endness_host,
                                    void* place_to_unchain,
                                    const void* place_to_jump_to_EXPECTED,
                                    const void* disp_cp_chain_me,
                                    Bool  mode64 )
{
   vassert(endness_host == VexEndnessLE || endness_host == VexEndnessBE);
   /* What we're expecting to see is:
        move r9, place_to_jump_to_EXPECTED
        jalr r9
        nop
      viz
        <8 or 24 bytes generated by mkLoadImm_EXACTLY2or6>
        0x120F809   # jalr r9
        0x00000000  # nop
   */
   UChar* p = (UChar*)place_to_unchain;
   vassert(0 == (3 & (HWord)p));
   vassert(isLoadImm_EXACTLY2or6(p, /*r*/ 9,
                                 (Addr)place_to_jump_to_EXPECTED,
                                 mode64));
   vassert(fetch32(p + (mode64 ? 24 : 8) + 0) == 0x120F809);
   vassert(fetch32(p + (mode64 ? 24 : 8) + 4) == 0x00000000);
   /* And what we want to change it to is:
        move r9, disp_cp_chain_me
        jalr r9
        nop
      viz
        <8 or 24 bytes generated by mkLoadImm_EXACTLY2or6>
        0x120F809   # jalr r9
        0x00000000  # nop
      The replacement has the same length as the original.
   */
   p = mkLoadImm_EXACTLY2or6(p, /*r*/ 9,
                             (Addr)disp_cp_chain_me, mode64);
   p = emit32(p, 0x120F809);
   p = emit32(p, 0x00000000);

   Int len = p - (UChar*)place_to_unchain;
   vassert(len == (mode64 ? 32 : 16)); /* stay sane */
   VexInvalRange vir = {(HWord)place_to_unchain, len};
   return vir;
}

/* Patch the counter address into a profile inc point, as previously
   created by the Min_ProfInc case for emit_MIPSInstr. */
VexInvalRange patchProfInc_MIPS ( VexEndness endness_host,
                                  void*  place_to_patch,
                                  const ULong* location_of_counter,
                                  Bool mode64 )
{
   vassert(endness_host == VexEndnessLE || endness_host == VexEndnessBE);
   if (mode64) {
      vassert(sizeof(ULong*) == 8);
   } else {
      vassert(sizeof(ULong*) == 4);
   }
   UChar* p = (UChar*)place_to_patch;
   vassert(0 == (3 & (HWord)p));
   vassert(isLoadImm_EXACTLY2or6((UChar *)p, /*r*/9,
                                 mode64 ? 0x6555655565556555ULL : 0x65556555,
                                 mode64));

   if (mode64) {
      vassert(fetch32(p + 24 + 0) == 0xDD280000);
      vassert(fetch32(p + 24 + 4) == 0x65080001);
      vassert(fetch32(p + 24 + 8) == 0xFD280000);
   } else {
      vassert(fetch32(p + 8 + 0) == 0x8D280000);
      vassert(fetch32(p + 8 + 4) == 0x25080001);
      vassert(fetch32(p + 8 + 8) == 0xAD280000);
      vassert(fetch32(p + 8 + 12) == 0x2d010001);
      vassert(fetch32(p + 8 + 16) == 0x8d280004);
      vassert(fetch32(p + 8 + 20) == 0x01014021);
      vassert(fetch32(p + 8 + 24) == 0xad280004);
   }

   p = mkLoadImm_EXACTLY2or6(p, /*r*/9,
                             (Addr)location_of_counter, mode64);

   VexInvalRange vir = {(HWord)p, 8};
   return vir;
}


/*---------------------------------------------------------------*/
/*--- end                                    host_mips_defs.c ---*/
/*---------------------------------------------------------------*/
