/*--------------------------------------------------------------------*/
/*--- The core dispatch loop, for jumping to a code address.       ---*/
/*---                                    dispatch-nanomips-linux.S ---*/
/*--------------------------------------------------------------------*/

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

#   Copyright (C) 2017-2018 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 "pub_core_basics_asm.h"

#if defined(VGP_nanomips_linux)

#include "pub_core_dispatch_asm.h"
#include "pub_core_transtab_asm.h"
#include "libvex_guest_offsets.h"   /* for OFFSET_mips_PC */

# Signature:
# void VG_(disp_run_translations)( UWord* two_words,
#                                  void*  guest_state,
#                                  Addr   host_addr);

# The dispatch loop.  VG_(disp_run_translations) is used to run all
# translations, including no-redir ones.

.text
.globl VG_(disp_run_translations)
VG_(disp_run_translations):
# a0 holds two_words
# a1 holds guest_state
# a2 holds host_addr
   save 32, $s0-$s7
   save 32, $fp-$ra
   sw $gp, 20($sp)
   sw $a0, 16($sp)

# Load address of guest_state into guest state register ($s7)
   move $s7, $a1

# And jump into the code cache.  Chained translations in
# the code cache run, until for whatever reason, they can't
# continue.  When that happens, the translation in question
# will jump (or call) to one of the continuation points
# VG_(cp_...) below.

   jrc $a2

# * Postamble and exit:
   postamble:
# At this point, $t4 and $t5 contain two
# words to be returned to the caller.  $t4
# holds a TRC value, and $t5 optionally may
# hold another word (for CHAIN_ME exits, the
# address of the place to patch.)

#  Restore $a0 from stack; holds address of two_words
   lw $a0, 16($sp)
   sw $t4, 0($a0)      # Store $t4 to two_words[0]
   sw $t5, 4($a0)      # Store $t5 to two_words[1]

   lw $gp, 20($sp)
   restore 32, $fp-$ra
   restore 32, $s0-$s7

   jrc $ra

# * Continuation points:

.global VG_(disp_cp_chain_me_to_slowEP)
VG_(disp_cp_chain_me_to_slowEP):
# We got called.  The return address indicates
# where the patching needs to happen. Collect
# the return address and, exit back to C land,
# handing the caller the pair (Chain_me_S, RA) */
   li $t4, VG_TRC_CHAIN_ME_TO_SLOW_EP
#  8 = mkLoadImm32_EXACTLY2
#  4 = jalrc $9
   addiu  $t5, $ra, -12
   bc postamble

.global VG_(disp_cp_chain_me_to_fastEP)
VG_(disp_cp_chain_me_to_fastEP):
# We got called. The return address indicates
# where the patching needs to happen. Collect
# the return address and, exit back to C land,
# handing the caller the pair (Chain_me_S, RA) */
   li $t4, VG_TRC_CHAIN_ME_TO_FAST_EP
#  8 = mkLoadImm32_EXACTLY2
#  4 = jalrc $9
   addiu  $t5, $ra, -12
   bc postamble

.global VG_(disp_cp_xindir)
VG_(disp_cp_xindir):
# /* Where are we going? */
   lw $a7, OFFSET_mips32_PC($s7)
   lw $t1, VG_(stats__n_xIndirs_32)
   addiu $t1, $t1, 1
   sw $t1, VG_(stats__n_xIndirs_32)

# try a fast lookup in the translation cache
# t2 = VG_TT_FAST_HASH(addr) * sizeof(ULong*)
#    = (t2 >> 2 & VG_TT_FAST_MASK)  << 3
   move $t2, $a7
   li $t0, VG_TT_FAST_MASK
   srl $t2, $t2, 2
   and $t2, $t2, $t0
   sll $t2, $t2, 3

# t1 = (addr of VG_(tt_fast)) + t2
   la $t1, VG_(tt_fast)
   addu $t1, $t1, $t2

# t9 = VG_(tt_fast)[hash] :: ULong*
   lw $t0, 0($t1)
   addiu $t1, $t1, 4
   lw $t9, 0($t1)

# little-endian, so comparing 1st 32bit word
   bnec $t0, $a7, fast_lookup_failed
   jrc $t9

   fast_lookup_failed:
#  %PC is up to date */
#  back out decrement of the dispatch counter */
#  hold dispatch_ctr in t0 (r8) */
   lw $t1, VG_(stats__n_xIndirs_32)
   addiu $t1, $t1, 0x1
   sw $t1, VG_(stats__n_xIndirs_32)
   li $t4, VG_TRC_INNER_FASTMISS
   move $t5, $zero
   bc postamble

.global VG_(disp_cp_xassisted)
VG_(disp_cp_xassisted):
# guest-state-pointer contains the TRC. Put the value into the
# return register
   move $t4, $s7
   move $t5, $zero
   bc postamble

.global VG_(disp_cp_evcheck_fail)
VG_(disp_cp_evcheck_fail):
   li $t4, VG_TRC_INNER_COUNTERZERO
   move $t5, $zero
   bc postamble

.size VG_(disp_run_translations), . - VG_(disp_run_translations)

#endif

# Let the linker know we don't need an executable stack
MARK_STACK_NO_EXEC

/*--------------------------------------------------------------------*/
/*--- end                               dispatch-nanomips-linux.S  ---*/
/*--------------------------------------------------------------------*/
