
/*---------------------------------------------------------------*/
/*--- begin                              guest_mips_helpers.c ---*/
/*---------------------------------------------------------------*/

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

   Copyright (C) 2010-2017 RT-RK

   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 3 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, see <http://www.gnu.org/licenses/>.

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

#include "libvex_basictypes.h"
#include "libvex_emnote.h"
#include "libvex_guest_mips32.h"
#include "libvex_guest_mips64.h"
#include "libvex_ir.h"
#include "libvex.h"

#include "main_util.h"
#include "main_globals.h"
#include "guest_generic_bb_to_IR.h"
#include "guest_mips_defs.h"

#if defined (__GNUC__)
#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
#define GCC_VERSION 0
#endif

/* This file contains helper functions for mips guest code.  Calls to
   these functions are generated by the back end.
*/

#define ALWAYSDEFD32(field)                            \
    { offsetof(VexGuestMIPS32State, field),            \
      (sizeof ((VexGuestMIPS32State*)0)->field) }

#define ALWAYSDEFD64(field)                            \
    { offsetof(VexGuestMIPS64State, field),            \
      (sizeof ((VexGuestMIPS64State*)0)->field) }

IRExpr *guest_mips32_spechelper(const HChar * function_name, IRExpr ** args,
                                IRStmt ** precedingStmts, Int n_precedingStmts)
{
   return NULL;
}

IRExpr *guest_mips64_spechelper ( const HChar * function_name, IRExpr ** args,
                                  IRStmt ** precedingStmts,
                                  Int n_precedingStmts )
{
   return NULL;
}

/* VISIBLE TO LIBVEX CLIENT */
void LibVEX_GuestMIPS32_initialise( /*OUT*/ VexGuestMIPS32State * vex_state)
{
   vex_state->guest_r0 = 0;   /* Hardwired to 0 */
   vex_state->guest_r1 = 0;   /* Assembler temporary */
   vex_state->guest_r2 = 0;   /* Values for function returns ... */
   vex_state->guest_r3 = 0;   /* ...and expression evaluation */
   vex_state->guest_r4 = 0;   /* Function arguments */
   vex_state->guest_r5 = 0;
   vex_state->guest_r6 = 0;
   vex_state->guest_r7 = 0;
   vex_state->guest_r8 = 0;   /* Temporaries */
   vex_state->guest_r9 = 0;
   vex_state->guest_r10 = 0;
   vex_state->guest_r11 = 0;
   vex_state->guest_r12 = 0;
   vex_state->guest_r13 = 0;
   vex_state->guest_r14 = 0;
   vex_state->guest_r15 = 0;
   vex_state->guest_r16 = 0;  /* Saved temporaries */
   vex_state->guest_r17 = 0;
   vex_state->guest_r18 = 0;
   vex_state->guest_r19 = 0;
   vex_state->guest_r20 = 0;
   vex_state->guest_r21 = 0;
   vex_state->guest_r22 = 0;
   vex_state->guest_r23 = 0;
   vex_state->guest_r24 = 0;  /* Temporaries */
   vex_state->guest_r25 = 0;
   vex_state->guest_r26 = 0;  /* Reserved for OS kernel */
   vex_state->guest_r27 = 0;
   vex_state->guest_r28 = 0;  /* Global pointer */
   vex_state->guest_r29 = 0;  /* Stack pointer */
   vex_state->guest_r30 = 0;  /* Frame pointer */
   vex_state->guest_r31 = 0;  /* Return address */
   vex_state->guest_PC = 0;   /* Program counter */
   vex_state->guest_HI = 0;   /* Multiply and divide register higher result */
   vex_state->guest_LO = 0;   /* Multiply and divide register lower result */

   /* FPU Registers */
   vex_state->guest_f0 = 0x7ff800007ff80000ULL; /* Floting point GP registers */
   vex_state->guest_f1 = 0x7ff800007ff80000ULL;
   vex_state->guest_f2 = 0x7ff800007ff80000ULL;
   vex_state->guest_f3 = 0x7ff800007ff80000ULL;
   vex_state->guest_f4 = 0x7ff800007ff80000ULL;
   vex_state->guest_f5 = 0x7ff800007ff80000ULL;
   vex_state->guest_f6 = 0x7ff800007ff80000ULL;
   vex_state->guest_f7 = 0x7ff800007ff80000ULL;
   vex_state->guest_f8 = 0x7ff800007ff80000ULL;
   vex_state->guest_f9 = 0x7ff800007ff80000ULL;
   vex_state->guest_f10 = 0x7ff800007ff80000ULL;
   vex_state->guest_f11 = 0x7ff800007ff80000ULL;
   vex_state->guest_f12 = 0x7ff800007ff80000ULL;
   vex_state->guest_f13 = 0x7ff800007ff80000ULL;
   vex_state->guest_f14 = 0x7ff800007ff80000ULL;
   vex_state->guest_f15 = 0x7ff800007ff80000ULL;
   vex_state->guest_f16 = 0x7ff800007ff80000ULL;
   vex_state->guest_f17 = 0x7ff800007ff80000ULL;
   vex_state->guest_f18 = 0x7ff800007ff80000ULL;
   vex_state->guest_f19 = 0x7ff800007ff80000ULL;
   vex_state->guest_f20 = 0x7ff800007ff80000ULL;
   vex_state->guest_f21 = 0x7ff800007ff80000ULL;
   vex_state->guest_f22 = 0x7ff800007ff80000ULL;
   vex_state->guest_f23 = 0x7ff800007ff80000ULL;
   vex_state->guest_f24 = 0x7ff800007ff80000ULL;
   vex_state->guest_f25 = 0x7ff800007ff80000ULL;
   vex_state->guest_f26 = 0x7ff800007ff80000ULL;
   vex_state->guest_f27 = 0x7ff800007ff80000ULL;
   vex_state->guest_f28 = 0x7ff800007ff80000ULL;
   vex_state->guest_f29 = 0x7ff800007ff80000ULL;
   vex_state->guest_f30 = 0x7ff800007ff80000ULL;
   vex_state->guest_f31 = 0x7ff800007ff80000ULL;

   vex_state->guest_FIR = 0;  /* FP implementation and revision register */
   vex_state->guest_FCCR = 0; /* FP condition codes register */
   vex_state->guest_FEXR = 0; /* FP exceptions register */
   vex_state->guest_FENR = 0; /* FP enables register */
   vex_state->guest_FCSR = 0; /* FP control/status register */
   vex_state->guest_ULR = 0; /* TLS */

   /* Various pseudo-regs mandated by Vex or Valgrind. */
   /* Emulation notes */
   vex_state->guest_EMNOTE = 0;

   /* For clflush: record start and length of area to invalidate */
   vex_state->guest_CMSTART = 0;
   vex_state->guest_CMLEN = 0;
   vex_state->host_EvC_COUNTER = 0;
   vex_state->host_EvC_FAILADDR = 0;

   /* Used to record the unredirected guest address at the start of
      a translation whose start has been redirected. By reading
      this pseudo-register shortly afterwards, the translation can
      find out what the corresponding no-redirection address was.
      Note, this is only set for wrap-style redirects, not for
      replace-style ones. */
   vex_state->guest_NRADDR = 0;

   vex_state->guest_COND = 0;

   vex_state->guest_CP0_status = 0;
   vex_state->guest_CP0_Config5 = 0;

   vex_state->guest_LLaddr = 0xFFFFFFFF;
   vex_state->guest_LLdata = 0;

   /* MIPS32 DSP ASE(r2) specific registers */
   vex_state->guest_DSPControl = 0;   /* DSPControl register */
   vex_state->guest_ac0 = 0;          /* Accumulator 0 */
   vex_state->guest_ac1 = 0;          /* Accumulator 1 */
   vex_state->guest_ac2 = 0;          /* Accumulator 2 */
   vex_state->guest_ac3 = 0;          /* Accumulator 3 */

   vex_state->guest_w0.w64[0] = 0;
   vex_state->guest_w0.w64[1] = 0;
   vex_state->guest_w1.w64[0] = 0;
   vex_state->guest_w1.w64[1] = 0;
   vex_state->guest_w2.w64[0] = 0;
   vex_state->guest_w2.w64[1] = 0;

   vex_state->guest_IP_AT_SYSCALL = 0;
}

void LibVEX_GuestMIPS64_initialise ( /*OUT*/ VexGuestMIPS64State * vex_state )
{
   vex_state->guest_r0 = 0;  /* Hardwired to 0 */
   vex_state->guest_r1 = 0;  /* Assembler temporary */
   vex_state->guest_r2 = 0;  /* Values for function returns ... */
   vex_state->guest_r3 = 0;
   vex_state->guest_r4 = 0;  /* Function arguments */
   vex_state->guest_r5 = 0;
   vex_state->guest_r6 = 0;
   vex_state->guest_r7 = 0;
   vex_state->guest_r8 = 0;
   vex_state->guest_r9 = 0;
   vex_state->guest_r10 = 0;
   vex_state->guest_r11 = 0;
   vex_state->guest_r12 = 0;  /* Temporaries */
   vex_state->guest_r13 = 0;
   vex_state->guest_r14 = 0;
   vex_state->guest_r15 = 0;
   vex_state->guest_r16 = 0;  /* Saved temporaries */
   vex_state->guest_r17 = 0;
   vex_state->guest_r18 = 0;
   vex_state->guest_r19 = 0;
   vex_state->guest_r20 = 0;
   vex_state->guest_r21 = 0;
   vex_state->guest_r22 = 0;
   vex_state->guest_r23 = 0;
   vex_state->guest_r24 = 0;  /* Temporaries */
   vex_state->guest_r25 = 0;
   vex_state->guest_r26 = 0;  /* Reserved for OS kernel */
   vex_state->guest_r27 = 0;
   vex_state->guest_r28 = 0;  /* Global pointer */
   vex_state->guest_r29 = 0;  /* Stack pointer */
   vex_state->guest_r30 = 0;  /* Frame pointer */
   vex_state->guest_r31 = 0;  /* Return address */
   vex_state->guest_PC = 0;   /* Program counter */
   vex_state->guest_HI = 0;   /* Multiply and divide register higher result */
   vex_state->guest_LO = 0;   /* Multiply and divide register lower result */

   /* FPU Registers */
   vex_state->guest_f0 =  0x7ff800007ff80000ULL;  /* Floting point registers */
   vex_state->guest_f1 =  0x7ff800007ff80000ULL;
   vex_state->guest_f2 =  0x7ff800007ff80000ULL;
   vex_state->guest_f3 =  0x7ff800007ff80000ULL;
   vex_state->guest_f4 =  0x7ff800007ff80000ULL;
   vex_state->guest_f5 =  0x7ff800007ff80000ULL;
   vex_state->guest_f6 =  0x7ff800007ff80000ULL;
   vex_state->guest_f7 =  0x7ff800007ff80000ULL;
   vex_state->guest_f8 =  0x7ff800007ff80000ULL;
   vex_state->guest_f9 =  0x7ff800007ff80000ULL;
   vex_state->guest_f10 = 0x7ff800007ff80000ULL;
   vex_state->guest_f11 = 0x7ff800007ff80000ULL;
   vex_state->guest_f12 = 0x7ff800007ff80000ULL;
   vex_state->guest_f13 = 0x7ff800007ff80000ULL;
   vex_state->guest_f14 = 0x7ff800007ff80000ULL;
   vex_state->guest_f15 = 0x7ff800007ff80000ULL;
   vex_state->guest_f16 = 0x7ff800007ff80000ULL;
   vex_state->guest_f17 = 0x7ff800007ff80000ULL;
   vex_state->guest_f18 = 0x7ff800007ff80000ULL;
   vex_state->guest_f19 = 0x7ff800007ff80000ULL;
   vex_state->guest_f20 = 0x7ff800007ff80000ULL;
   vex_state->guest_f21 = 0x7ff800007ff80000ULL;
   vex_state->guest_f22 = 0x7ff800007ff80000ULL;
   vex_state->guest_f23 = 0x7ff800007ff80000ULL;
   vex_state->guest_f24 = 0x7ff800007ff80000ULL;
   vex_state->guest_f25 = 0x7ff800007ff80000ULL;
   vex_state->guest_f26 = 0x7ff800007ff80000ULL;
   vex_state->guest_f27 = 0x7ff800007ff80000ULL;
   vex_state->guest_f28 = 0x7ff800007ff80000ULL;
   vex_state->guest_f29 = 0x7ff800007ff80000ULL;
   vex_state->guest_f30 = 0x7ff800007ff80000ULL;
   vex_state->guest_f31 = 0x7ff800007ff80000ULL;

   vex_state->guest_FIR = 0;   /* FP implementation and revision register */
   vex_state->guest_FCCR = 0;  /* FP condition codes register */
   vex_state->guest_FEXR = 0;  /* FP exceptions register */
   vex_state->guest_FENR = 0;  /* FP enables register */
   vex_state->guest_FCSR = 0;  /* FP control/status register */

   vex_state->guest_ULR = 0;

   /* Various pseudo-regs mandated by Vex or Valgrind. */
   /* Emulation notes */
   vex_state->guest_EMNOTE = 0;

   /* For clflush: record start and length of area to invalidate */
   vex_state->guest_CMSTART = 0;
   vex_state->guest_CMLEN = 0;
   vex_state->host_EvC_COUNTER = 0;
   vex_state->host_EvC_FAILADDR = 0;

   /* Used to record the unredirected guest address at the start of
      a translation whose start has been redirected. By reading
      this pseudo-register shortly afterwards, the translation can
      find out what the corresponding no-redirection address was.
      Note, this is only set for wrap-style redirects, not for
      replace-style ones. */
   vex_state->guest_NRADDR = 0;

   vex_state->guest_COND = 0;

   vex_state->guest_CP0_status = MIPS_CP0_STATUS_FR;

   vex_state->guest_LLaddr = 0xFFFFFFFFFFFFFFFFULL;
   vex_state->guest_LLdata = 0;

   vex_state->guest_IP_AT_SYSCALL = 0;

   vex_state->guest_MSACSR = 0;
}

/*-----------------------------------------------------------*/
/*--- Describing the mips guest state, for the benefit    ---*/
/*--- of iropt and instrumenters.                         ---*/
/*-----------------------------------------------------------*/

/* Figure out if any part of the guest state contained in minoff
   .. maxoff requires precise memory exceptions.  If in doubt return
   True (but this generates significantly slower code).

   We enforce precise exns for guest SP, PC.

   Only SP is needed in mode VexRegUpdSpAtMemAccess.
*/
Bool guest_mips32_state_requires_precise_mem_exns (
        Int minoff, Int maxoff, VexRegisterUpdates pxControl
     )
{
   Int sp_min = offsetof(VexGuestMIPS32State, guest_r29);
   Int sp_max = sp_min + 4 - 1;
   Int pc_min = offsetof(VexGuestMIPS32State, guest_PC);
   Int pc_max = pc_min + 4 - 1;

   if (maxoff < sp_min || minoff > sp_max) {
      /* no overlap with sp */
      if (pxControl == VexRegUpdSpAtMemAccess)
         return False;  /* We only need to check stack pointer. */
   } else {
      return True;
   }

   if (maxoff < pc_min || minoff > pc_max) {
      /* no overlap with pc */
   } else {
      return True;
   }

   /* We appear to need precise updates of R11 in order to get proper
      stacktraces from non-optimised code. */
   Int fp_min = offsetof(VexGuestMIPS32State, guest_r30);
   Int fp_max = fp_min + 4 - 1;

   if (maxoff < fp_min || minoff > fp_max) {
      /* no overlap with fp */
   } else {
      return True;
   }

   return False;
}

Bool guest_mips64_state_requires_precise_mem_exns (
        Int minoff, Int maxoff, VexRegisterUpdates pxControl
     )
{
   Int sp_min = offsetof(VexGuestMIPS64State, guest_r29);
   Int sp_max = sp_min + 8 - 1;
   Int pc_min = offsetof(VexGuestMIPS64State, guest_PC);
   Int pc_max = pc_min + 8 - 1;

   if ( maxoff < sp_min || minoff > sp_max ) {
      /* no overlap with sp */
      if (pxControl == VexRegUpdSpAtMemAccess)
         return False;  /* We only need to check stack pointer. */
   } else {
      return True;
   }

   if ( maxoff < pc_min || minoff > pc_max ) {
      /* no overlap with pc */
   } else {
      return True;
   }

   Int fp_min = offsetof(VexGuestMIPS64State, guest_r30);
   Int fp_max = fp_min + 8 - 1;

   if ( maxoff < fp_min || minoff > fp_max ) {
      /* no overlap with fp */
   } else {
      return True;
   }

   return False;
}

VexGuestLayout mips32Guest_layout = {
   /* Total size of the guest state, in bytes. */
   .total_sizeB = sizeof(VexGuestMIPS32State),
   /* Describe the stack pointer. */
   .offset_SP = offsetof(VexGuestMIPS32State, guest_r29),
   .sizeof_SP = 4,
   /* Describe the frame pointer. */
   .offset_FP = offsetof(VexGuestMIPS32State, guest_r30),
   .sizeof_FP = 4,
   /* Describe the instruction pointer. */
   .offset_IP = offsetof(VexGuestMIPS32State, guest_PC),
   .sizeof_IP = 4,
   /* Describe any sections to be regarded by Memcheck as
      'always-defined'. */
   .n_alwaysDefd = 8,
   /* ? :(  */
   .alwaysDefd = {
             /* 0 */ ALWAYSDEFD32(guest_r0),
             /* 1 */ ALWAYSDEFD32(guest_r1),
             /* 2 */ ALWAYSDEFD32(guest_EMNOTE),
             /* 3 */ ALWAYSDEFD32(guest_CMSTART),
             /* 4 */ ALWAYSDEFD32(guest_CMLEN),
             /* 5 */ ALWAYSDEFD32(guest_r29),
             /* 6 */ ALWAYSDEFD32(guest_r31),
             /* 7 */ ALWAYSDEFD32(guest_ULR)
             }
};

VexGuestLayout mips64Guest_layout = {
   /* Total size of the guest state, in bytes. */
   .total_sizeB = sizeof(VexGuestMIPS64State),
   /* Describe the stack pointer. */
   .offset_SP = offsetof(VexGuestMIPS64State, guest_r29),
   .sizeof_SP = 8,
   /* Describe the frame pointer. */
   .offset_FP = offsetof(VexGuestMIPS64State, guest_r30),
   .sizeof_FP = 8,
   /* Describe the instruction pointer. */
   .offset_IP = offsetof(VexGuestMIPS64State, guest_PC),
   .sizeof_IP = 8,
   /* Describe any sections to be regarded by Memcheck as
      'always-defined'. */
   .n_alwaysDefd = 7,
   /* ? :(  */
   .alwaysDefd = {
                  /* 0 */ ALWAYSDEFD64 (guest_r0),
                  /* 1 */ ALWAYSDEFD64 (guest_EMNOTE),
                  /* 2 */ ALWAYSDEFD64 (guest_CMSTART),
                  /* 3 */ ALWAYSDEFD64 (guest_CMLEN),
                  /* 4 */ ALWAYSDEFD64 (guest_r29),
                  /* 5 */ ALWAYSDEFD64 (guest_r31),
                  /* 6 */ ALWAYSDEFD64 (guest_ULR)
                  }
};

#define ASM_VOLATILE_RDHWR(opcode)                                 \
   __asm__ __volatile__(".word 0x7C02003B | "#opcode" << 11  \n\t" \
                        : "+r" (x) : :                             \
                       )

HWord mips_dirtyhelper_rdhwr ( UInt rd )
{
#if defined(__mips__)
   register HWord x __asm__("v0") = 0;

   switch (rd) {
      case 0:  /* x = CPUNum() */
         ASM_VOLATILE_RDHWR(0); /* rdhwr v0, $0 */
         break;

      case 1:  /* x = SYNCI_Step() */
         ASM_VOLATILE_RDHWR(1); /* rdhwr v0, $1 */
         break;

      case 2:  /* x = CC() */
         ASM_VOLATILE_RDHWR(2); /* rdhwr v0, $2 */
         break;

      case 3:  /* x = CCRes() */
         ASM_VOLATILE_RDHWR(3); /* rdhwr v0, $3 */
         break;

      case 31:  /* x = CVMX_get_cycles() */
         ASM_VOLATILE_RDHWR(31); /* rdhwr v0, $31 */
         break;

      default:
         vassert(0);
         break;
   }
   return x;
#else
   return 0;
#endif
}

#define ASM_VOLATILE_UNARY32(inst)                                  \
   __asm__ volatile(".set  push"        "\n\t"                      \
                    ".set  hardfloat"   "\n\t"                      \
                    "cfc1  $8,   $31"   "\n\t"                      \
                    "ctc1  %2,   $31"   "\n\t"                      \
                    "mtc1  %1,   $f20"  "\n\t"                      \
                    #inst" $f20, $f20"  "\n\t"                      \
                    "cfc1  %0,   $31"   "\n\t"                      \
                    "ctc1  $8,   $31"   "\n\t"                      \
                    ".set  pop"         "\n\t"                      \
                    : "=r" (ret)                                    \
                    : "r" (loFsVal), "r" (fcsr)                     \
                    : "$8", "$f20"                                  \
                   );

#define ASM_VOLATILE_UNARY32_DOUBLE(inst)                           \
   __asm__ volatile(".set  push"        "\n\t"                      \
                    ".set  hardfloat"   "\n\t"                      \
                    "cfc1  $8,   $31"   "\n\t"                      \
                    "ctc1  %2,   $31"   "\n\t"                      \
                    "ldc1  $f20, 0(%1)" "\n\t"                      \
                    #inst" $f20, $f20"  "\n\t"                      \
                    "cfc1  %0,   $31"   "\n\t"                      \
                    "ctc1  $8,   $31"   "\n\t"                      \
                    ".set  pop"         "\n\t"                      \
                    : "=r" (ret)                                    \
                    : "r" (&fsVal), "r" (fcsr)                      \
                    : "$8", "$f20", "$f21"                          \
                   );

#define ASM_VOLATILE_UNARY64(inst)                                  \
   __asm__ volatile(".set  push"         "\n\t"                     \
                    ".set  hardfloat"    "\n\t"                     \
                    ".set  fp=64"        "\n\t"                     \
                    "cfc1  $8,   $31"    "\n\t"                     \
                    "ctc1  %2,   $31"    "\n\t"                     \
                    "ldc1  $f24, 0(%1)"  "\n\t"                     \
                    #inst" $f24, $f24"   "\n\t"                     \
                    "cfc1  %0,   $31"    "\n\t"                     \
                    "ctc1  $8,   $31"    "\n\t"                     \
                    ".set  pop"          "\n\t"                     \
                    : "=r" (ret)                                    \
                    : "r" (&(addr[fs])), "r" (fcsr)                 \
                    : "$8", "$f24"                                  \
                   );

#define ASM_VOLATILE_MSA_UNARY(inst)                                \
   __asm__ volatile(".set  push"              "\n\t"                \
                    ".set  mips32r2"          "\n\t"                \
                    ".set  hardfloat"         "\n\t"                \
                    ".set  fp=64"             "\n\t"                \
                    ".set  msa"               "\n\t"                \
                    ".set  noreorder"         "\n\t"                \
                    "cfcmsa  $t0,  $1"        "\n\t"                \
                    "ctcmsa  $1,   %2"        "\n\t"                \
                    "ld.b  $w24, 0(%1)"       "\n\t"                \
                    #inst" $w24, $w24"        "\n\t"                \
                    "cfcmsa  %0,   $1"        "\n\t"                \
                    "ctcmsa  $1,  $t0"        "\n\t"                \
                    ".set  pop"               "\n\t"                \
                    : "=r" (ret)                                    \
                    : "r" (&(addr[ws])), "r" (msacsr)               \
                    : "t0"                                          \
                   );

#define ASM_VOLATILE_BINARY32(inst)                                 \
   __asm__ volatile(".set  push"              "\n\t"                \
                    ".set  hardfloat"         "\n\t"                \
                    "cfc1  $8,   $31"         "\n\t"                \
                    "ctc1  %3,   $31"         "\n\t"                \
                    "mtc1  %1,   $f20"        "\n\t"                \
                    "mtc1  %2,   $f22"        "\n\t"                \
                    #inst" $f20, $f20, $f22"  "\n\t"                \
                    "cfc1  %0,   $31"         "\n\t"                \
                    "ctc1  $8,   $31"         "\n\t"                \
                    ".set  pop"               "\n\t"                \
                    : "=r" (ret)                                    \
                    : "r" (loFsVal), "r" (loFtVal), "r" (fcsr)      \
                    : "$8", "$f20", "$f22"                          \
                   );

#define ASM_VOLATILE_BINARY32_DOUBLE(inst)                          \
   __asm__ volatile(".set  push"              "\n\t"                \
                    ".set  hardfloat"         "\n\t"                \
                    "cfc1  $8,   $31"         "\n\t"                \
                    "ctc1  %3,   $31"         "\n\t"                \
                    "ldc1  $f20, 0(%1)"       "\n\t"                \
                    "ldc1  $f22, 0(%2)"       "\n\t"                \
                    #inst" $f20, $f20, $f22"  "\n\t"                \
                    "cfc1  %0,   $31"         "\n\t"                \
                    "ctc1  $8,   $31"         "\n\t"                \
                    ".set  pop"               "\n\t"                \
                    : "=r" (ret)                                    \
                    : "r" (&fsVal), "r" (&ftVal), "r" (fcsr)        \
                    : "$8", "$f20", "$f21", "$f22", "$f23"          \
                   );

#define ASM_VOLATILE_BINARY64(inst)                                     \
   __asm__ volatile(".set  push"              "\n\t"                    \
                    ".set  hardfloat"         "\n\t"                    \
                    "cfc1  $8,   $31"         "\n\t"                    \
                    "ctc1  %3,   $31"         "\n\t"                    \
                    "ldc1  $f24, 0(%1)"       "\n\t"                    \
                    "ldc1  $f26, 0(%2)"       "\n\t"                    \
                    #inst" $f24, $f24, $f26"  "\n\t"                    \
                    "cfc1  %0,   $31"         "\n\t"                    \
                    "ctc1  $8,   $31"         "\n\t"                    \
                    ".set  pop"               "\n\t"                    \
                    : "=r" (ret)                                        \
                    : "r" (&(addr[fs])), "r" (&(addr[ft])), "r" (fcsr)  \
                    : "$8", "$f24", "$f26"                              \
                   );

#define ASM_VOLATILE_MSA_BINARY(inst)                                   \
   __asm__ volatile(".set  push"              "\n\t"                    \
                    ".set  mips32r2"          "\n\t"                    \
                    ".set  hardfloat"         "\n\t"                    \
                    ".set  fp=64"             "\n\t"                    \
                    ".set  msa"               "\n\t"                    \
                    "cfcmsa $t0,  $1"         "\n\t"                    \
                    "ctcmsa  $1,  %3"         "\n\t"                    \
                    "ld.b  $w24, 0(%1)"       "\n\t"                    \
                    "ld.b  $w26, 0(%2)"       "\n\t"                    \
                    #inst" $w24, $w24, $w26"  "\n\t"                    \
                    "cfcmsa %0,   $1"         "\n\t"                    \
                    "ctcmsa $1,  $t0"         "\n\t"                    \
                    ".set  pop"               "\n\t"                    \
                    : "=r" (ret)                                        \
                    : "r" (&(addr[ws])), "r" (&(addr[wt])), "r" (msacsr)\
                    : "t0"                                              \
                   );

/* TODO: Add cases for all fpu instructions because all fpu instructions are
         change the value of FCSR register. */
extern UInt mips_dirtyhelper_calculate_FCSR_fp32 ( void* gs, UInt fs, UInt ft,
                                                   flt_op inst )
{
   UInt ret = 0;
#if defined(__mips__)
   VexGuestMIPS32State* guest_state = (VexGuestMIPS32State*)gs;
   UInt loFsVal, hiFsVal, loFtVal, hiFtVal;
#if defined (_MIPSEL)
   ULong *addr = (ULong *)&guest_state->guest_f0;
   loFsVal     = (UInt)addr[fs];
   hiFsVal     = (UInt)addr[fs+1];
   loFtVal     = (UInt)addr[ft];
   hiFtVal     = (UInt)addr[ft+1];
#elif defined (_MIPSEB)
   UInt *addr = (UInt *)&guest_state->guest_f0;
   loFsVal    = (UInt)addr[fs*2];
   hiFsVal    = (UInt)addr[fs*2+2];
   loFtVal    = (UInt)addr[ft*2];
   hiFtVal    = (UInt)addr[ft*2+2];
#endif
   ULong fsVal   = ((ULong) hiFsVal) << 32 | loFsVal;
   ULong ftVal   = ((ULong) hiFtVal) << 32 | loFtVal;
   UInt fcsr     = guest_state->guest_FCSR;
   switch (inst) {
      case ROUNDWD:
         ASM_VOLATILE_UNARY32_DOUBLE(round.w.d)
         break;
      case FLOORWS:
         ASM_VOLATILE_UNARY32(floor.w.s)
         break;
      case FLOORWD:
         ASM_VOLATILE_UNARY32_DOUBLE(floor.w.d)
         break;
      case TRUNCWS:
         ASM_VOLATILE_UNARY32(trunc.w.s)
         break;
      case TRUNCWD:
         ASM_VOLATILE_UNARY32_DOUBLE(trunc.w.d)
         break;
      case CEILWS:
         ASM_VOLATILE_UNARY32(ceil.w.s)
         break;
      case CEILWD:
         ASM_VOLATILE_UNARY32_DOUBLE(ceil.w.d)
         break;
      case CVTDS:
         ASM_VOLATILE_UNARY32(cvt.d.s)
         break;
      case CVTDW:
         ASM_VOLATILE_UNARY32(cvt.d.w)
         break;
      case CVTSW:
         ASM_VOLATILE_UNARY32(cvt.s.w)
         break;
      case CVTSD:
         ASM_VOLATILE_UNARY32_DOUBLE(cvt.s.d)
         break;
      case CVTWS:
         ASM_VOLATILE_UNARY32(cvt.w.s)
         break;
      case CVTWD:
         ASM_VOLATILE_UNARY32_DOUBLE(cvt.w.d)
         break;
      case ROUNDWS:
         ASM_VOLATILE_UNARY32(round.w.s)
         break;
      case ADDS:
          ASM_VOLATILE_BINARY32(add.s)
          break;
      case ADDD:
          ASM_VOLATILE_BINARY32_DOUBLE(add.d)
          break;
      case SUBS:
          ASM_VOLATILE_BINARY32(sub.s)
          break;
      case SUBD:
          ASM_VOLATILE_BINARY32_DOUBLE(sub.d)
          break;
      case DIVS:
          ASM_VOLATILE_BINARY32(div.s)
          break;
      default:
         vassert(0);
         break;
   }
#endif
   return ret;
}

/* TODO: Add cases for all fpu instructions because all fpu instructions are
         change the value of FCSR register. */
extern UInt mips_dirtyhelper_calculate_FCSR_fp64 ( void* gs, UInt fs, UInt ft,
                                                   flt_op inst )
{
   UInt ret = 0;
#if defined(__mips__) && ((__mips == 64) ||                                  \
                          (defined(__mips_isa_rev) && (__mips_isa_rev >= 2)))
#if defined(VGA_mips32)
   VexGuestMIPS32State* guest_state = (VexGuestMIPS32State*)gs;
#else
   VexGuestMIPS64State* guest_state = (VexGuestMIPS64State*)gs;
#endif
   ULong *addr = (ULong *)&guest_state->guest_f0;
   UInt fcsr   = guest_state->guest_FCSR;
   switch (inst) {
      case ROUNDWD:
         ASM_VOLATILE_UNARY64(round.w.d)
         break;
      case FLOORWS:
         ASM_VOLATILE_UNARY64(floor.w.s)
         break;
      case FLOORWD:
         ASM_VOLATILE_UNARY64(floor.w.d)
         break;
      case TRUNCWS:
         ASM_VOLATILE_UNARY64(trunc.w.s)
         break;
      case TRUNCWD:
         ASM_VOLATILE_UNARY64(trunc.w.d)
         break;
      case CEILWS:
         ASM_VOLATILE_UNARY64(ceil.w.s)
         break;
      case CEILWD:
         ASM_VOLATILE_UNARY64(ceil.w.d)
         break;
      case CVTDS:
         ASM_VOLATILE_UNARY64(cvt.d.s)
         break;
      case CVTDW:
         ASM_VOLATILE_UNARY64(cvt.d.w)
         break;
      case CVTSW:
         ASM_VOLATILE_UNARY64(cvt.s.w)
         break;
      case CVTSD:
         ASM_VOLATILE_UNARY64(cvt.s.d)
         break;
      case CVTWS:
         ASM_VOLATILE_UNARY64(cvt.w.s)
         break;
      case CVTWD:
         ASM_VOLATILE_UNARY64(cvt.w.d)
         break;
      case ROUNDWS:
         ASM_VOLATILE_UNARY64(round.w.s)
         break;
      case CEILLS:
         ASM_VOLATILE_UNARY64(ceil.l.s)
         break;
      case CEILLD:
         ASM_VOLATILE_UNARY64(ceil.l.d)
         break;
      case CVTDL:
         ASM_VOLATILE_UNARY64(cvt.d.l)
         break;
      case CVTLS:
         ASM_VOLATILE_UNARY64(cvt.l.s)
         break;
      case CVTLD:
         ASM_VOLATILE_UNARY64(cvt.l.d)
         break;
      case CVTSL:
         ASM_VOLATILE_UNARY64(cvt.s.l)
         break;
      case FLOORLS:
         ASM_VOLATILE_UNARY64(floor.l.s)
         break;
      case FLOORLD:
         ASM_VOLATILE_UNARY64(floor.l.d)
         break;
      case ROUNDLS:
         ASM_VOLATILE_UNARY64(round.l.s)
         break;
      case ROUNDLD:
         ASM_VOLATILE_UNARY64(round.l.d)
         break;
      case TRUNCLS:
         ASM_VOLATILE_UNARY64(trunc.l.s)
         break;
      case TRUNCLD:
         ASM_VOLATILE_UNARY64(trunc.l.d)
         break;
      case ADDS:
          ASM_VOLATILE_BINARY64(add.s)
          break;
      case ADDD:
          ASM_VOLATILE_BINARY64(add.d)
          break;
      case SUBS:
          ASM_VOLATILE_BINARY64(sub.s)
          break;
      case SUBD:
          ASM_VOLATILE_BINARY64(sub.d)
          break;
      case DIVS:
          ASM_VOLATILE_BINARY64(div.s)
          break;
#if defined(__mips_isa_rev) && (__mips_isa_rev >= 6)
      case RINTS:
         ASM_VOLATILE_UNARY64(rint.s)
         break;
      case RINTD:
         ASM_VOLATILE_UNARY64(rint.d)
         break;
      case MAXS:
          ASM_VOLATILE_BINARY64(max.s)
          break;
      case MAXD:
          ASM_VOLATILE_BINARY64(max.d)
          break;
      case MINS:
          ASM_VOLATILE_BINARY64(min.s)
          break;
      case MIND:
          ASM_VOLATILE_BINARY64(min.d)
          break;
      case MAXAS:
          ASM_VOLATILE_BINARY64(maxa.s)
          break;
      case MAXAD:
          ASM_VOLATILE_BINARY64(maxa.d)
          break;
      case MINAS:
          ASM_VOLATILE_BINARY64(mina.s)
          break;
      case MINAD:
          ASM_VOLATILE_BINARY64(mina.d)
          break;
      case CMPAFS:
          ASM_VOLATILE_BINARY64(cmp.af.s)
          break;
      case CMPAFD:
          ASM_VOLATILE_BINARY64(cmp.af.d)
          break;
      case CMPSAFS:
          ASM_VOLATILE_BINARY64(cmp.saf.s)
          break;
      case CMPSAFD:
          ASM_VOLATILE_BINARY64(cmp.saf.d)
          break;
#endif
      default:
         vassert(0);
         break;
   }
#endif
   return ret;
}


extern UInt mips_dirtyhelper_calculate_MSACSR ( void* gs, UInt ws, UInt wt,
                                                msa_flt_op inst ) {
   UInt ret = 0;
/* GCC 4.8 and later support MIPS MSA. */
#if defined(__mips__) && (defined(__clang__) || (GCC_VERSION >= 408))
#if defined(VGA_mips32)
   VexGuestMIPS32State* guest_state = (VexGuestMIPS32State*)gs;
#else
   VexGuestMIPS64State* guest_state = (VexGuestMIPS64State*)gs;
#endif
   V128 *addr = (V128 *)&guest_state->guest_w0;
   UInt msacsr   = guest_state->guest_MSACSR;

   switch (inst) {
      case FADDW:
         ASM_VOLATILE_MSA_BINARY(fadd.w)
         break;

      case FADDD:
         ASM_VOLATILE_MSA_BINARY(fadd.d)
         break;

      case FSUBW:
         ASM_VOLATILE_MSA_BINARY(fsub.w);
         break;

      case FSUBD:
         ASM_VOLATILE_MSA_BINARY(fsub.d);
         break;

      case FMULW:
         ASM_VOLATILE_MSA_BINARY(fmul.w);
         break;

      case FMULD:
         ASM_VOLATILE_MSA_BINARY(fmul.d);
         break;

      case FDIVW:
         ASM_VOLATILE_MSA_BINARY(fdiv.w);
         break;

      case FDIVD:
         ASM_VOLATILE_MSA_BINARY(fdiv.d);
         break;

      case FMADDW:
         ASM_VOLATILE_MSA_BINARY(fmadd.w);
         break;

      case FMADDD:
         ASM_VOLATILE_MSA_BINARY(fmadd.d);
         break;

      case FCAFW:
         ASM_VOLATILE_MSA_BINARY(fcaf.w);
         break;

      case FCAFD:
         ASM_VOLATILE_MSA_BINARY(fcaf.d);
         break;

      case FSAFW:
         ASM_VOLATILE_MSA_BINARY(fsaf.w);
         break;

      case FSAFD:
         ASM_VOLATILE_MSA_BINARY(fsaf.d);
         break;

      case FCEQW:
         ASM_VOLATILE_MSA_BINARY(fceq.w);
         break;

      case FCEQD:
         ASM_VOLATILE_MSA_BINARY(fceq.d);
         break;

      case FSEQW:
         ASM_VOLATILE_MSA_BINARY(fseq.w);
         break;

      case FSEQD:
         ASM_VOLATILE_MSA_BINARY(fseq.d);
         break;

      case FCLTW:
         ASM_VOLATILE_MSA_BINARY(fclt.w);
         break;

      case FCLTD:
         ASM_VOLATILE_MSA_BINARY(fclt.d);
         break;

      case FSLTW:
         ASM_VOLATILE_MSA_BINARY(fslt.w);
         break;

      case FSLTD:
         ASM_VOLATILE_MSA_BINARY(fslt.d);
         break;

      case FCLEW:
         ASM_VOLATILE_MSA_BINARY(fcle.w);
         break;

      case FCLED:
         ASM_VOLATILE_MSA_BINARY(fcle.d);
         break;

      case FSLEW:
         ASM_VOLATILE_MSA_BINARY(fsle.w);
         break;

      case FSLED:
         ASM_VOLATILE_MSA_BINARY(fsle.d);
         break;

      case FCNEW:
         ASM_VOLATILE_MSA_BINARY(fcne.w);
         break;

      case FCNED:
         ASM_VOLATILE_MSA_BINARY(fcne.d);
         break;

      case FSNEW:
         ASM_VOLATILE_MSA_BINARY(fsne.w);
         break;

      case FSNED:
         ASM_VOLATILE_MSA_BINARY(fsne.d);
         break;

      case FEXP2W:
         ASM_VOLATILE_MSA_BINARY(fexp2.w);
         break;

      case FEXP2D:
         ASM_VOLATILE_MSA_BINARY(fexp2.d);
         break;

      case FMINW:
         ASM_VOLATILE_MSA_BINARY(fmin.w);
         break;

      case FMIND:
         ASM_VOLATILE_MSA_BINARY(fmin.d);
         break;

      case FMINAW:
         ASM_VOLATILE_MSA_BINARY(fmin_a.w);
         break;

      case FMINAD:
         ASM_VOLATILE_MSA_BINARY(fmin_a.d);
         break;

      case FCUNW:
         ASM_VOLATILE_MSA_BINARY(fcun.w);
         break;

      case FCUND:
         ASM_VOLATILE_MSA_BINARY(fcun.d);
         break;

      case FSUNW:
         ASM_VOLATILE_MSA_BINARY(fsun.w);
         break;

      case FSUND:
         ASM_VOLATILE_MSA_BINARY(fsun.d);
         break;

      case FCORW:
         ASM_VOLATILE_MSA_BINARY(fcor.w);
         break;

      case FCORD:
         ASM_VOLATILE_MSA_BINARY(fcor.d);
         break;

      case FSORW:
         ASM_VOLATILE_MSA_BINARY(fsor.w);
         break;

      case FSORD:
         ASM_VOLATILE_MSA_BINARY(fsor.d);
         break;

      case FCUEQW:
         ASM_VOLATILE_MSA_BINARY(fcueq.w);
         break;

      case FCUEQD:
         ASM_VOLATILE_MSA_BINARY(fcueq.d);
         break;

      case FSUEQW:
         ASM_VOLATILE_MSA_BINARY(fsueq.w);
         break;

      case FSUEQD:
         ASM_VOLATILE_MSA_BINARY(fsueq.d);
         break;

      case FCUNEW:
         ASM_VOLATILE_MSA_BINARY(fcune.w);
         break;

      case FCUNED:
         ASM_VOLATILE_MSA_BINARY(fcune.d);
         break;

      case FSUNEW:
         ASM_VOLATILE_MSA_BINARY(fsune.w);
         break;

      case FSUNED:
         ASM_VOLATILE_MSA_BINARY(fsune.d);
         break;

      case FCULEW:
         ASM_VOLATILE_MSA_BINARY(fcule.w);
         break;

      case FCULED:
         ASM_VOLATILE_MSA_BINARY(fcule.d);
         break;

      case FSULEW:
         ASM_VOLATILE_MSA_BINARY(fsule.w);
         break;

      case FSULED:
         ASM_VOLATILE_MSA_BINARY(fsule.d);
         break;

      case FCULTW:
         ASM_VOLATILE_MSA_BINARY(fcult.w);
         break;

      case FCULTD:
         ASM_VOLATILE_MSA_BINARY(fcult.d);
         break;

      case FSULTW:
         ASM_VOLATILE_MSA_BINARY(fsult.w);
         break;

      case FSULTD:
         ASM_VOLATILE_MSA_BINARY(fsult.d);
         break;

      case FMAXW:
         ASM_VOLATILE_MSA_BINARY(fmax.w);
         break;

      case FMAXD:
         ASM_VOLATILE_MSA_BINARY(fmax.d);
         break;

      case FMAXAW:
         ASM_VOLATILE_MSA_BINARY(fmax_a.w);
         break;

      case FMAXAD:
         ASM_VOLATILE_MSA_BINARY(fmax_a.d);
         break;

      case FFINTSW:
         ASM_VOLATILE_MSA_UNARY(ffint_s.w);
         break;

      case FFINTSD:
         ASM_VOLATILE_MSA_UNARY(ffint_s.d);
         break;

      case FRCPW:
         ASM_VOLATILE_MSA_UNARY(frcp.w);
         break;

      case FRCPD:
         ASM_VOLATILE_MSA_UNARY(frcp.d);
         break;

      case FRSQRTW:
         ASM_VOLATILE_MSA_UNARY(frsqrt.w);
         break;

      case FRSQRTD:
         ASM_VOLATILE_MSA_UNARY(frsqrt.d);
         break;

      case FSQRTW:
         ASM_VOLATILE_MSA_UNARY(fsqrt.w);
         break;

      case FSQRTD:
         ASM_VOLATILE_MSA_UNARY(fsqrt.d);
         break;

      case FRINTW:
         ASM_VOLATILE_MSA_UNARY(frint.w);
         break;

      case FRINTD:
         ASM_VOLATILE_MSA_UNARY(frint.d);
         break;

      case FTRUNCUW:
         ASM_VOLATILE_MSA_UNARY(ftrunc_u.w);
         break;

      case FTRUNCUD:
         ASM_VOLATILE_MSA_UNARY(ftrunc_u.d);
         break;

      case FTRUNCSW:
         ASM_VOLATILE_MSA_UNARY(ftrunc_s.w);
         break;

      case FTRUNCSD:
         ASM_VOLATILE_MSA_UNARY(ftrunc_s.d);
         break;

      case FEXDOH:
         ASM_VOLATILE_MSA_BINARY(fexdo.h);
         break;

      case FEXDOW:
         ASM_VOLATILE_MSA_BINARY(fexdo.w);
         break;

      case FEXUPRW:
         ASM_VOLATILE_MSA_UNARY(fexupr.w);
         break;

      case FEXUPRD:
         ASM_VOLATILE_MSA_UNARY(fexupr.d);
         break;

      case FEXUPLW:
         ASM_VOLATILE_MSA_UNARY(fexupl.w);
         break;

      case FEXUPLD:
         ASM_VOLATILE_MSA_UNARY(fexupl.d);
         break;

      case FTQH:
         ASM_VOLATILE_MSA_BINARY(ftq.h);
         break;

      case FTQW:
         ASM_VOLATILE_MSA_BINARY(ftq.w);
         break;

      case FFQRD:
         ASM_VOLATILE_MSA_UNARY(ffqr.d);
         break;

      case FFQRW:
         ASM_VOLATILE_MSA_UNARY(ffqr.w);
         break;

      case FFQLD:
         ASM_VOLATILE_MSA_UNARY(ffql.d);
         break;

      case FFQLW:
         ASM_VOLATILE_MSA_UNARY(ffql.w);
         break;

      case FTINT_SD:
         ASM_VOLATILE_MSA_UNARY(ftint_s.d);
         break;

      case FTINT_SW:
         ASM_VOLATILE_MSA_UNARY(ftint_s.w);
         break;

      case FTINT_UD:
         ASM_VOLATILE_MSA_UNARY(ftint_u.d);
         break;

      case FTINT_UW:
         ASM_VOLATILE_MSA_UNARY(ftint_u.w);
         break;

      case FLOG2D:
         ASM_VOLATILE_MSA_UNARY(flog2.d);
         break;

      case FLOG2W:
         ASM_VOLATILE_MSA_UNARY(flog2.w);
         break;

      case FFINT_UD:
         ASM_VOLATILE_MSA_UNARY(ffint_u.d);
         break;

      case FFINT_UW:
         ASM_VOLATILE_MSA_UNARY(ffint_u.w);
         break;
   }

#endif
   return ret;
}

extern UInt mips_dirtyhelper_get_MSAIR(void) {
   UInt ret = 0;
/* GCC 4.8 and later support MIPS MSA. */
#if defined(__mips__) && (defined(__clang__) || (GCC_VERSION >= 408))
   __asm__ volatile(".set push        \n\t"
                    ".set mips32r2    \n\t"
                    ".set hardfloat   \n\t"
                    ".set fp=64       \n\t"
                    ".set msa         \n\t"
                    ".set noreorder   \n\t"
                    "cfcmsa  %0, $0   \n\t"
                    ".set pop         \n\t"
                    : "=r" (ret) : : );
#endif
   return ret;
}




/*---------------------------------------------------------------*/
/*--- end                                guest_mips_helpers.c ---*/
/*---------------------------------------------------------------*/
