Upgrade V8 to version 4.9.385.28

https://chromium.googlesource.com/v8/v8/+/4.9.385.28

FPIIM-449

Change-Id: I4b2e74289d4bf3667f2f3dc8aa2e541f63e26eb4
diff --git a/src/mips/simulator-mips.cc b/src/mips/simulator-mips.cc
index fabca67..aa4224a 100644
--- a/src/mips/simulator-mips.cc
+++ b/src/mips/simulator-mips.cc
@@ -7,12 +7,11 @@
 #include <stdlib.h>
 #include <cmath>
 
-#include "src/v8.h"
-
 #if V8_TARGET_ARCH_MIPS
 
 #include "src/assembler.h"
 #include "src/base/bits.h"
+#include "src/codegen.h"
 #include "src/disasm.h"
 #include "src/mips/constants-mips.h"
 #include "src/mips/simulator-mips.h"
@@ -130,7 +129,7 @@
 
 #else  // GENERATED_CODE_COVERAGE
 
-#define UNSUPPORTED() printf("Unsupported instruction.\n");
+#define UNSUPPORTED() printf("Sim: Unsupported instruction.\n");
 
 static void InitializeCoverage() {}
 
@@ -590,7 +589,7 @@
                  reinterpret_cast<intptr_t>(cur), *cur, *cur);
           HeapObject* obj = reinterpret_cast<HeapObject*>(*cur);
           int value = *cur;
-          Heap* current_heap = v8::internal::Isolate::Current()->heap();
+          Heap* current_heap = sim_->isolate_->heap();
           if (((value & 1) == 0) || current_heap->Contains(obj)) {
             PrintF(" (");
             if ((value & 1) == 0) {
@@ -888,9 +887,8 @@
 
 
 CachePage* Simulator::GetCachePage(v8::internal::HashMap* i_cache, void* page) {
-  v8::internal::HashMap::Entry* entry = i_cache->Lookup(page,
-                                                        ICacheHash(page),
-                                                        true);
+  v8::internal::HashMap::Entry* entry =
+      i_cache->LookupOrInsert(page, ICacheHash(page));
   if (entry->value == NULL) {
     CachePage* new_page = new CachePage();
     entry->value = new_page;
@@ -970,7 +968,12 @@
   for (int i = 0; i < kNumFPURegisters; i++) {
     FPUregisters_[i] = 0;
   }
-  FCSR_ = 0;
+  if (IsMipsArchVariant(kMips32r6)) {
+    FCSR_ = kFCSRNaN2008FlagMask;
+  } else {
+    DCHECK(IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kMips32r2));
+    FCSR_ = 0;
+  }
 
   // The sp is initialized to point to the bottom (high address) of the
   // allocated stack area. To be safe in potential stack underflows we leave
@@ -981,16 +984,11 @@
   registers_[pc] = bad_ra;
   registers_[ra] = bad_ra;
   InitializeCoverage();
-  for (int i = 0; i < kNumExceptions; i++) {
-    exceptions[i] = 0;
-  }
-
   last_debugger_input_ = NULL;
 }
 
 
-Simulator::~Simulator() {
-}
+Simulator::~Simulator() { free(stack_); }
 
 
 // When the generated code calls an external reference we need to catch that in
@@ -1002,12 +1000,12 @@
 // offset from the swi instruction so the simulator knows what to call.
 class Redirection {
  public:
-  Redirection(void* external_function, ExternalReference::Type type)
+  Redirection(Isolate* isolate, void* external_function,
+              ExternalReference::Type type)
       : external_function_(external_function),
         swi_instruction_(rtCallRedirInstr),
         type_(type),
         next_(NULL) {
-    Isolate* isolate = Isolate::Current();
     next_ = isolate->simulator_redirection();
     Simulator::current(isolate)->
         FlushICache(isolate->simulator_i_cache(),
@@ -1023,20 +1021,19 @@
   void* external_function() { return external_function_; }
   ExternalReference::Type type() { return type_; }
 
-  static Redirection* Get(void* external_function,
+  static Redirection* Get(Isolate* isolate, void* external_function,
                           ExternalReference::Type type) {
-    Isolate* isolate = Isolate::Current();
     Redirection* current = isolate->simulator_redirection();
     for (; current != NULL; current = current->next_) {
       if (current->external_function_ == external_function) return current;
     }
-    return new Redirection(external_function, type);
+    return new Redirection(isolate, external_function, type);
   }
 
   static Redirection* FromSwiInstruction(Instruction* swi_instruction) {
     char* addr_of_swi = reinterpret_cast<char*>(swi_instruction);
     char* addr_of_redirection =
-        addr_of_swi - OFFSET_OF(Redirection, swi_instruction_);
+        addr_of_swi - offsetof(Redirection, swi_instruction_);
     return reinterpret_cast<Redirection*>(addr_of_redirection);
   }
 
@@ -1046,6 +1043,14 @@
     return redirection->external_function();
   }
 
+  static void DeleteChain(Redirection* redirection) {
+    while (redirection != nullptr) {
+      Redirection* next = redirection->next_;
+      delete redirection;
+      redirection = next;
+    }
+  }
+
  private:
   void* external_function_;
   uint32_t swi_instruction_;
@@ -1054,9 +1059,23 @@
 };
 
 
-void* Simulator::RedirectExternalReference(void* external_function,
+// static
+void Simulator::TearDown(HashMap* i_cache, Redirection* first) {
+  Redirection::DeleteChain(first);
+  if (i_cache != nullptr) {
+    for (HashMap::Entry* entry = i_cache->Start(); entry != nullptr;
+         entry = i_cache->Next(entry)) {
+      delete static_cast<CachePage*>(entry->value);
+    }
+    delete i_cache;
+  }
+}
+
+
+void* Simulator::RedirectExternalReference(Isolate* isolate,
+                                           void* external_function,
                                            ExternalReference::Type type) {
-  Redirection* redirection = Redirection::Get(external_function, type);
+  Redirection* redirection = Redirection::Get(isolate, external_function, type);
   return redirection->address_of_swi_instruction();
 }
 
@@ -1272,6 +1291,139 @@
 }
 
 
+void Simulator::set_fcsr_rounding_mode(FPURoundingMode mode) {
+  FCSR_ |= mode & kFPURoundingModeMask;
+}
+
+
+unsigned int Simulator::get_fcsr_rounding_mode() {
+  return FCSR_ & kFPURoundingModeMask;
+}
+
+
+void Simulator::set_fpu_register_word_invalid_result(float original,
+                                                     float rounded) {
+  if (FCSR_ & kFCSRNaN2008FlagMask) {
+    double max_int32 = std::numeric_limits<int32_t>::max();
+    double min_int32 = std::numeric_limits<int32_t>::min();
+    if (std::isnan(original)) {
+      set_fpu_register_word(fd_reg(), 0);
+    } else if (rounded > max_int32) {
+      set_fpu_register_word(fd_reg(), kFPUInvalidResult);
+    } else if (rounded < min_int32) {
+      set_fpu_register_word(fd_reg(), kFPUInvalidResultNegative);
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    set_fpu_register_word(fd_reg(), kFPUInvalidResult);
+  }
+}
+
+
+void Simulator::set_fpu_register_invalid_result(float original, float rounded) {
+  if (FCSR_ & kFCSRNaN2008FlagMask) {
+    double max_int32 = std::numeric_limits<int32_t>::max();
+    double min_int32 = std::numeric_limits<int32_t>::min();
+    if (std::isnan(original)) {
+      set_fpu_register(fd_reg(), 0);
+    } else if (rounded > max_int32) {
+      set_fpu_register(fd_reg(), kFPUInvalidResult);
+    } else if (rounded < min_int32) {
+      set_fpu_register(fd_reg(), kFPUInvalidResultNegative);
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    set_fpu_register(fd_reg(), kFPUInvalidResult);
+  }
+}
+
+
+void Simulator::set_fpu_register_invalid_result64(float original,
+                                                  float rounded) {
+  if (FCSR_ & kFCSRNaN2008FlagMask) {
+    // The value of INT64_MAX (2^63-1) can't be represented as double exactly,
+    // loading the most accurate representation into max_int64, which is 2^63.
+    double max_int64 = std::numeric_limits<int64_t>::max();
+    double min_int64 = std::numeric_limits<int64_t>::min();
+    if (std::isnan(original)) {
+      set_fpu_register(fd_reg(), 0);
+    } else if (rounded >= max_int64) {
+      set_fpu_register(fd_reg(), kFPU64InvalidResult);
+    } else if (rounded < min_int64) {
+      set_fpu_register(fd_reg(), kFPU64InvalidResultNegative);
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    set_fpu_register(fd_reg(), kFPU64InvalidResult);
+  }
+}
+
+
+void Simulator::set_fpu_register_word_invalid_result(double original,
+                                                     double rounded) {
+  if (FCSR_ & kFCSRNaN2008FlagMask) {
+    double max_int32 = std::numeric_limits<int32_t>::max();
+    double min_int32 = std::numeric_limits<int32_t>::min();
+    if (std::isnan(original)) {
+      set_fpu_register_word(fd_reg(), 0);
+    } else if (rounded > max_int32) {
+      set_fpu_register_word(fd_reg(), kFPUInvalidResult);
+    } else if (rounded < min_int32) {
+      set_fpu_register_word(fd_reg(), kFPUInvalidResultNegative);
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    set_fpu_register_word(fd_reg(), kFPUInvalidResult);
+  }
+}
+
+
+void Simulator::set_fpu_register_invalid_result(double original,
+                                                double rounded) {
+  if (FCSR_ & kFCSRNaN2008FlagMask) {
+    double max_int32 = std::numeric_limits<int32_t>::max();
+    double min_int32 = std::numeric_limits<int32_t>::min();
+    if (std::isnan(original)) {
+      set_fpu_register(fd_reg(), 0);
+    } else if (rounded > max_int32) {
+      set_fpu_register(fd_reg(), kFPUInvalidResult);
+    } else if (rounded < min_int32) {
+      set_fpu_register(fd_reg(), kFPUInvalidResultNegative);
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    set_fpu_register(fd_reg(), kFPUInvalidResult);
+  }
+}
+
+
+void Simulator::set_fpu_register_invalid_result64(double original,
+                                                  double rounded) {
+  if (FCSR_ & kFCSRNaN2008FlagMask) {
+    // The value of INT64_MAX (2^63-1) can't be represented as double exactly,
+    // loading the most accurate representation into max_int64, which is 2^63.
+    double max_int64 = std::numeric_limits<int64_t>::max();
+    double min_int64 = std::numeric_limits<int64_t>::min();
+    if (std::isnan(original)) {
+      set_fpu_register(fd_reg(), 0);
+    } else if (rounded >= max_int64) {
+      set_fpu_register(fd_reg(), kFPU64InvalidResult);
+    } else if (rounded < min_int64) {
+      set_fpu_register(fd_reg(), kFPU64InvalidResultNegative);
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    set_fpu_register(fd_reg(), kFPU64InvalidResult);
+  }
+}
+
+
 // Sets the rounding error codes in FCSR based on the result of the rounding.
 // Returns true if the operation was invalid.
 bool Simulator::set_fcsr_round_error(double original, double rounded) {
@@ -1304,6 +1456,270 @@
 }
 
 
+// Sets the rounding error codes in FCSR based on the result of the rounding.
+// Returns true if the operation was invalid.
+bool Simulator::set_fcsr_round64_error(double original, double rounded) {
+  bool ret = false;
+  // The value of INT64_MAX (2^63-1) can't be represented as double exactly,
+  // loading the most accurate representation into max_int64, which is 2^63.
+  double max_int64 = std::numeric_limits<int64_t>::max();
+  double min_int64 = std::numeric_limits<int64_t>::min();
+
+  if (!std::isfinite(original) || !std::isfinite(rounded)) {
+    set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+    ret = true;
+  }
+
+  if (original != rounded) {
+    set_fcsr_bit(kFCSRInexactFlagBit, true);
+  }
+
+  if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
+    set_fcsr_bit(kFCSRUnderflowFlagBit, true);
+    ret = true;
+  }
+
+  if (rounded >= max_int64 || rounded < min_int64) {
+    set_fcsr_bit(kFCSROverflowFlagBit, true);
+    // The reference is not really clear but it seems this is required:
+    set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+    ret = true;
+  }
+
+  return ret;
+}
+
+
+// Sets the rounding error codes in FCSR based on the result of the rounding.
+// Returns true if the operation was invalid.
+bool Simulator::set_fcsr_round_error(float original, float rounded) {
+  bool ret = false;
+  double max_int32 = std::numeric_limits<int32_t>::max();
+  double min_int32 = std::numeric_limits<int32_t>::min();
+
+  if (!std::isfinite(original) || !std::isfinite(rounded)) {
+    set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+    ret = true;
+  }
+
+  if (original != rounded) {
+    set_fcsr_bit(kFCSRInexactFlagBit, true);
+  }
+
+  if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) {
+    set_fcsr_bit(kFCSRUnderflowFlagBit, true);
+    ret = true;
+  }
+
+  if (rounded > max_int32 || rounded < min_int32) {
+    set_fcsr_bit(kFCSROverflowFlagBit, true);
+    // The reference is not really clear but it seems this is required:
+    set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+    ret = true;
+  }
+
+  return ret;
+}
+
+
+// Sets the rounding error codes in FCSR based on the result of the rounding.
+// Returns true if the operation was invalid.
+bool Simulator::set_fcsr_round64_error(float original, float rounded) {
+  bool ret = false;
+  // The value of INT64_MAX (2^63-1) can't be represented as double exactly,
+  // loading the most accurate representation into max_int64, which is 2^63.
+  double max_int64 = std::numeric_limits<int64_t>::max();
+  double min_int64 = std::numeric_limits<int64_t>::min();
+
+  if (!std::isfinite(original) || !std::isfinite(rounded)) {
+    set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+    ret = true;
+  }
+
+  if (original != rounded) {
+    set_fcsr_bit(kFCSRInexactFlagBit, true);
+  }
+
+  if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) {
+    set_fcsr_bit(kFCSRUnderflowFlagBit, true);
+    ret = true;
+  }
+
+  if (rounded >= max_int64 || rounded < min_int64) {
+    set_fcsr_bit(kFCSROverflowFlagBit, true);
+    // The reference is not really clear but it seems this is required:
+    set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+    ret = true;
+  }
+
+  return ret;
+}
+
+
+void Simulator::round_according_to_fcsr(double toRound, double& rounded,
+                                        int32_t& rounded_int, double fs) {
+  // 0 RN (round to nearest): Round a result to the nearest
+  // representable value; if the result is exactly halfway between
+  // two representable values, round to zero. Behave like round_w_d.
+
+  // 1 RZ (round toward zero): Round a result to the closest
+  // representable value whose absolute value is less than or
+  // equal to the infinitely accurate result. Behave like trunc_w_d.
+
+  // 2 RP (round up, or toward  infinity): Round a result to the
+  // next representable value up. Behave like ceil_w_d.
+
+  // 3 RD (round down, or toward −infinity): Round a result to
+  // the next representable value down. Behave like floor_w_d.
+  switch (get_fcsr_rounding_mode()) {
+    case kRoundToNearest:
+      rounded = std::floor(fs + 0.5);
+      rounded_int = static_cast<int32_t>(rounded);
+      if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        rounded_int--;
+      }
+      break;
+    case kRoundToZero:
+      rounded = trunc(fs);
+      rounded_int = static_cast<int32_t>(rounded);
+      break;
+    case kRoundToPlusInf:
+      rounded = std::ceil(fs);
+      rounded_int = static_cast<int32_t>(rounded);
+      break;
+    case kRoundToMinusInf:
+      rounded = std::floor(fs);
+      rounded_int = static_cast<int32_t>(rounded);
+      break;
+  }
+}
+
+
+void Simulator::round_according_to_fcsr(float toRound, float& rounded,
+                                        int32_t& rounded_int, float fs) {
+  // 0 RN (round to nearest): Round a result to the nearest
+  // representable value; if the result is exactly halfway between
+  // two representable values, round to zero. Behave like round_w_d.
+
+  // 1 RZ (round toward zero): Round a result to the closest
+  // representable value whose absolute value is less than or
+  // equal to the infinitely accurate result. Behave like trunc_w_d.
+
+  // 2 RP (round up, or toward  infinity): Round a result to the
+  // next representable value up. Behave like ceil_w_d.
+
+  // 3 RD (round down, or toward −infinity): Round a result to
+  // the next representable value down. Behave like floor_w_d.
+  switch (get_fcsr_rounding_mode()) {
+    case kRoundToNearest:
+      rounded = std::floor(fs + 0.5);
+      rounded_int = static_cast<int32_t>(rounded);
+      if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        rounded_int--;
+      }
+      break;
+    case kRoundToZero:
+      rounded = trunc(fs);
+      rounded_int = static_cast<int32_t>(rounded);
+      break;
+    case kRoundToPlusInf:
+      rounded = std::ceil(fs);
+      rounded_int = static_cast<int32_t>(rounded);
+      break;
+    case kRoundToMinusInf:
+      rounded = std::floor(fs);
+      rounded_int = static_cast<int32_t>(rounded);
+      break;
+  }
+}
+
+
+void Simulator::round64_according_to_fcsr(double toRound, double& rounded,
+                                          int64_t& rounded_int, double fs) {
+  // 0 RN (round to nearest): Round a result to the nearest
+  // representable value; if the result is exactly halfway between
+  // two representable values, round to zero. Behave like round_w_d.
+
+  // 1 RZ (round toward zero): Round a result to the closest
+  // representable value whose absolute value is less than or.
+  // equal to the infinitely accurate result. Behave like trunc_w_d.
+
+  // 2 RP (round up, or toward +infinity): Round a result to the
+  // next representable value up. Behave like ceil_w_d.
+
+  // 3 RN (round down, or toward −infinity): Round a result to
+  // the next representable value down. Behave like floor_w_d.
+  switch (FCSR_ & 3) {
+    case kRoundToNearest:
+      rounded = std::floor(fs + 0.5);
+      rounded_int = static_cast<int64_t>(rounded);
+      if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        rounded_int--;
+      }
+      break;
+    case kRoundToZero:
+      rounded = trunc(fs);
+      rounded_int = static_cast<int64_t>(rounded);
+      break;
+    case kRoundToPlusInf:
+      rounded = std::ceil(fs);
+      rounded_int = static_cast<int64_t>(rounded);
+      break;
+    case kRoundToMinusInf:
+      rounded = std::floor(fs);
+      rounded_int = static_cast<int64_t>(rounded);
+      break;
+  }
+}
+
+
+void Simulator::round64_according_to_fcsr(float toRound, float& rounded,
+                                          int64_t& rounded_int, float fs) {
+  // 0 RN (round to nearest): Round a result to the nearest
+  // representable value; if the result is exactly halfway between
+  // two representable values, round to zero. Behave like round_w_d.
+
+  // 1 RZ (round toward zero): Round a result to the closest
+  // representable value whose absolute value is less than or.
+  // equal to the infinitely accurate result. Behave like trunc_w_d.
+
+  // 2 RP (round up, or toward +infinity): Round a result to the
+  // next representable value up. Behave like ceil_w_d.
+
+  // 3 RN (round down, or toward −infinity): Round a result to
+  // the next representable value down. Behave like floor_w_d.
+  switch (FCSR_ & 3) {
+    case kRoundToNearest:
+      rounded = std::floor(fs + 0.5);
+      rounded_int = static_cast<int64_t>(rounded);
+      if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        rounded_int--;
+      }
+      break;
+    case kRoundToZero:
+      rounded = trunc(fs);
+      rounded_int = static_cast<int64_t>(rounded);
+      break;
+    case kRoundToPlusInf:
+      rounded = std::ceil(fs);
+      rounded_int = static_cast<int64_t>(rounded);
+      break;
+    case kRoundToMinusInf:
+      rounded = std::floor(fs);
+      rounded_int = static_cast<int64_t>(rounded);
+      break;
+  }
+}
+
+
 // Raw access to the PC register.
 void Simulator::set_pc(int32_t value) {
   pc_modified_ = true;
@@ -1330,6 +1746,41 @@
 // executed in the simulator.  Since the host is typically IA32 we will not
 // get the correct MIPS-like behaviour on unaligned accesses.
 
+void Simulator::TraceRegWr(int32_t value) {
+  if (::v8::internal::FLAG_trace_sim) {
+    SNPrintF(trace_buf_, "%08x", value);
+  }
+}
+
+
+// TODO(plind): consider making icount_ printing a flag option.
+void Simulator::TraceMemRd(int32_t addr, int32_t value) {
+  if (::v8::internal::FLAG_trace_sim) {
+    SNPrintF(trace_buf_, "%08x <-- [%08x]    (%" PRIu64 ")", value, addr,
+             icount_);
+  }
+}
+
+
+void Simulator::TraceMemWr(int32_t addr, int32_t value, TraceType t) {
+  if (::v8::internal::FLAG_trace_sim) {
+    switch (t) {
+      case BYTE:
+        SNPrintF(trace_buf_, "      %02x --> [%08x]",
+                 static_cast<int8_t>(value), addr);
+        break;
+      case HALF:
+        SNPrintF(trace_buf_, "    %04x --> [%08x]", static_cast<int16_t>(value),
+                 addr);
+        break;
+      case WORD:
+        SNPrintF(trace_buf_, "%08x --> [%08x]", value, addr);
+        break;
+    }
+  }
+}
+
+
 int Simulator::ReadW(int32_t addr, Instruction* instr) {
   if (addr >=0 && addr < 0x400) {
     // This has to be a NULL-dereference, drop into debugger.
@@ -1340,6 +1791,7 @@
   }
   if ((addr & kPointerAlignmentMask) == 0) {
     intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+    TraceMemRd(addr, static_cast<int32_t>(*ptr));
     return *ptr;
   }
   PrintF("Unaligned read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
@@ -1361,6 +1813,7 @@
   }
   if ((addr & kPointerAlignmentMask) == 0) {
     intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+    TraceMemWr(addr, value, WORD);
     *ptr = value;
     return;
   }
@@ -1401,6 +1854,7 @@
 uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
   if ((addr & 1) == 0) {
     uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+    TraceMemRd(addr, static_cast<int32_t>(*ptr));
     return *ptr;
   }
   PrintF("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
@@ -1414,6 +1868,7 @@
 int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
   if ((addr & 1) == 0) {
     int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+    TraceMemRd(addr, static_cast<int32_t>(*ptr));
     return *ptr;
   }
   PrintF("Unaligned signed halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
@@ -1427,6 +1882,7 @@
 void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
   if ((addr & 1) == 0) {
     uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+    TraceMemWr(addr, value, HALF);
     *ptr = value;
     return;
   }
@@ -1440,6 +1896,7 @@
 void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
   if ((addr & 1) == 0) {
     int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+    TraceMemWr(addr, value, HALF);
     *ptr = value;
     return;
   }
@@ -1452,32 +1909,42 @@
 
 uint32_t Simulator::ReadBU(int32_t addr) {
   uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+  TraceMemRd(addr, static_cast<int32_t>(*ptr));
   return *ptr & 0xff;
 }
 
 
 int32_t Simulator::ReadB(int32_t addr) {
   int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+  TraceMemRd(addr, static_cast<int32_t>(*ptr));
   return *ptr;
 }
 
 
 void Simulator::WriteB(int32_t addr, uint8_t value) {
   uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+  TraceMemWr(addr, value, BYTE);
   *ptr = value;
 }
 
 
 void Simulator::WriteB(int32_t addr, int8_t value) {
   int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+  TraceMemWr(addr, value, BYTE);
   *ptr = value;
 }
 
 
 // Returns the limit of the stack area to enable checking for stack overflows.
-uintptr_t Simulator::StackLimit() const {
-  // Leave a safety margin of 1024 bytes to prevent overrunning the stack when
-  // pushing values.
+uintptr_t Simulator::StackLimit(uintptr_t c_limit) const {
+  // The simulator uses a separate JS stack. If we have exhausted the C stack,
+  // we also drop down the JS limit to reflect the exhaustion on the JS stack.
+  if (GetCurrentStackPosition() < c_limit) {
+    return reinterpret_cast<uintptr_t>(get_sp());
+  }
+
+  // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes
+  // to prevent overrunning the stack when pushing values.
   return reinterpret_cast<uintptr_t>(stack_) + 1024;
 }
 
@@ -1763,7 +2230,8 @@
 void Simulator::PrintWatchpoint(uint32_t code) {
   MipsDebugger dbg(this);
   ++break_count_;
-  PrintF("\n---- break %d marker: %3d  (instr count: %8d) ----------"
+  PrintF("\n---- break %d marker: %3d  (instr count: %" PRIu64
+         ") ----------"
          "----------------------------------",
          code, break_count_, icount_);
   dbg.PrintAllRegs();  // Print registers and continue running.
@@ -1847,280 +2315,521 @@
 }
 
 
-void Simulator::SignalExceptions() {
-  for (int i = 1; i < kNumExceptions; i++) {
-    if (exceptions[i] != 0) {
-      V8_Fatal(__FILE__, __LINE__, "Error: Exception %i raised.", i);
+void Simulator::SignalException(Exception e) {
+  V8_Fatal(__FILE__, __LINE__, "Error: Exception %i raised.",
+           static_cast<int>(e));
+}
+
+
+void Simulator::DecodeTypeRegisterDRsType() {
+  double ft, fs, fd;
+  uint32_t cc, fcsr_cc;
+  int64_t i64;
+  fs = get_fpu_register_double(fs_reg());
+  ft = (get_instr()->FunctionFieldRaw() != MOVF)
+           ? get_fpu_register_double(ft_reg())
+           : 0.0;
+  fd = get_fpu_register_double(fd_reg());
+  int64_t ft_int = bit_cast<int64_t>(ft);
+  int64_t fd_int = bit_cast<int64_t>(fd);
+  cc = get_instr()->FCccValue();
+  fcsr_cc = get_fcsr_condition_bit(cc);
+  switch (get_instr()->FunctionFieldRaw()) {
+    case RINT: {
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      double result, temp, temp_result;
+      double upper = std::ceil(fs);
+      double lower = std::floor(fs);
+      switch (get_fcsr_rounding_mode()) {
+        case kRoundToNearest:
+          if (upper - fs < fs - lower) {
+            result = upper;
+          } else if (upper - fs > fs - lower) {
+            result = lower;
+          } else {
+            temp_result = upper / 2;
+            double reminder = modf(temp_result, &temp);
+            if (reminder == 0) {
+              result = upper;
+            } else {
+              result = lower;
+            }
+          }
+          break;
+        case kRoundToZero:
+          result = (fs > 0 ? lower : upper);
+          break;
+        case kRoundToPlusInf:
+          result = upper;
+          break;
+        case kRoundToMinusInf:
+          result = lower;
+          break;
+      }
+      set_fpu_register_double(fd_reg(), result);
+      if (result != fs) {
+        set_fcsr_bit(kFCSRInexactFlagBit, true);
+      }
+      break;
     }
+    case SEL:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_fpu_register_double(fd_reg(), (fd_int & 0x1) == 0 ? fs : ft);
+      break;
+    case SELEQZ_C:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_fpu_register_double(fd_reg(), (ft_int & 0x1) == 0 ? fs : 0.0);
+      break;
+    case SELNEZ_C:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_fpu_register_double(fd_reg(), (ft_int & 0x1) != 0 ? fs : 0.0);
+      break;
+    case MOVZ_C: {
+      DCHECK(IsMipsArchVariant(kMips32r2));
+      if (rt() == 0) {
+        set_fpu_register_double(fd_reg(), fs);
+      }
+      break;
+    }
+    case MOVN_C: {
+      DCHECK(IsMipsArchVariant(kMips32r2));
+      int32_t rt_reg = get_instr()->RtValue();
+      int32_t rt = get_register(rt_reg);
+      if (rt != 0) {
+        set_fpu_register_double(fd_reg(), fs);
+      }
+      break;
+    }
+    case MOVF: {
+      // Same function field for MOVT.D and MOVF.D
+      uint32_t ft_cc = (ft_reg() >> 2) & 0x7;
+      ft_cc = get_fcsr_condition_bit(ft_cc);
+      if (get_instr()->Bit(16)) {  // Read Tf bit.
+        // MOVT.D
+        if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg(), fs);
+      } else {
+        // MOVF.D
+        if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg(), fs);
+      }
+      break;
+    }
+    case MIN:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_double(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else {
+        set_fpu_register_double(fd_reg(), (fs >= ft) ? ft : fs);
+      }
+      break;
+    case MINA:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_double(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else {
+        double result;
+        if (fabs(fs) > fabs(ft)) {
+          result = ft;
+        } else if (fabs(fs) < fabs(ft)) {
+          result = fs;
+        } else {
+          result = (fs < ft ? fs : ft);
+        }
+        set_fpu_register_double(fd_reg(), result);
+      }
+      break;
+    case MAXA:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_double(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else {
+        double result;
+        if (fabs(fs) < fabs(ft)) {
+          result = ft;
+        } else if (fabs(fs) > fabs(ft)) {
+          result = fs;
+        } else {
+          result = (fs > ft ? fs : ft);
+        }
+        set_fpu_register_double(fd_reg(), result);
+      }
+      break;
+    case MAX:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_double(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_double(fd_reg(), fs);
+      } else {
+        set_fpu_register_double(fd_reg(), (fs <= ft) ? ft : fs);
+      }
+      break;
+      break;
+    case ADD_D:
+      set_fpu_register_double(fd_reg(), fs + ft);
+      break;
+    case SUB_D:
+      set_fpu_register_double(fd_reg(), fs - ft);
+      break;
+    case MUL_D:
+      set_fpu_register_double(fd_reg(), fs * ft);
+      break;
+    case DIV_D:
+      set_fpu_register_double(fd_reg(), fs / ft);
+      break;
+    case ABS_D:
+      set_fpu_register_double(fd_reg(), fabs(fs));
+      break;
+    case MOV_D:
+      set_fpu_register_double(fd_reg(), fs);
+      break;
+    case NEG_D:
+      set_fpu_register_double(fd_reg(), -fs);
+      break;
+    case SQRT_D:
+      lazily_initialize_fast_sqrt(isolate_);
+      set_fpu_register_double(fd_reg(), fast_sqrt(fs, isolate_));
+      break;
+    case RSQRT_D: {
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      lazily_initialize_fast_sqrt(isolate_);
+      double result = 1.0 / fast_sqrt(fs, isolate_);
+      set_fpu_register_double(fd_reg(), result);
+      break;
+    }
+    case RECIP_D: {
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      double result = 1.0 / fs;
+      set_fpu_register_double(fd_reg(), result);
+      break;
+    }
+    case C_UN_D:
+      set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
+      break;
+    case C_EQ_D:
+      set_fcsr_bit(fcsr_cc, (fs == ft));
+      break;
+    case C_UEQ_D:
+      set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
+      break;
+    case C_OLT_D:
+      set_fcsr_bit(fcsr_cc, (fs < ft));
+      break;
+    case C_ULT_D:
+      set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
+      break;
+    case C_OLE_D:
+      set_fcsr_bit(fcsr_cc, (fs <= ft));
+      break;
+    case C_ULE_D:
+      set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
+      break;
+    case CVT_W_D: {  // Convert double to word.
+      double rounded;
+      int32_t result;
+      round_according_to_fcsr(fs, rounded, result, fs);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case ROUND_W_D:  // Round double to word (round half to even).
+    {
+      double rounded = std::floor(fs + 0.5);
+      int32_t result = static_cast<int32_t>(rounded);
+      if ((result & 1) != 0 && result - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        result--;
+      }
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case TRUNC_W_D:  // Truncate double to word (round towards 0).
+    {
+      double rounded = trunc(fs);
+      int32_t result = static_cast<int32_t>(rounded);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case FLOOR_W_D:  // Round double to word towards negative infinity.
+    {
+      double rounded = std::floor(fs);
+      int32_t result = static_cast<int32_t>(rounded);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case CEIL_W_D:  // Round double to word towards positive infinity.
+    {
+      double rounded = std::ceil(fs);
+      int32_t result = static_cast<int32_t>(rounded);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case CVT_S_D:  // Convert double to float (single).
+      set_fpu_register_float(fd_reg(), static_cast<float>(fs));
+      break;
+    case CVT_L_D: {  // Mips32r2: Truncate double to 64-bit long-word.
+      if (IsFp64Mode()) {
+        int64_t result;
+        double rounded;
+        round64_according_to_fcsr(fs, rounded, result, fs);
+        set_fpu_register(fd_reg(), result);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+      break;
+    }
+    case TRUNC_L_D: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      double rounded = trunc(fs);
+      i64 = static_cast<int64_t>(rounded);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case ROUND_L_D: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      double rounded = std::floor(fs + 0.5);
+      int64_t result = static_cast<int64_t>(rounded);
+      if ((result & 1) != 0 && result - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        result--;
+      }
+      int64_t i64 = static_cast<int64_t>(result);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case FLOOR_L_D: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      double rounded = std::floor(fs);
+      int64_t i64 = static_cast<int64_t>(rounded);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case CEIL_L_D: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      double rounded = std::ceil(fs);
+      int64_t i64 = static_cast<int64_t>(rounded);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case CLASS_D: {  // Mips32r6 instruction
+      // Convert double input to uint64_t for easier bit manipulation
+      uint64_t classed = bit_cast<uint64_t>(fs);
+
+      // Extracting sign, exponent and mantissa from the input double
+      uint32_t sign = (classed >> 63) & 1;
+      uint32_t exponent = (classed >> 52) & 0x00000000000007ff;
+      uint64_t mantissa = classed & 0x000fffffffffffff;
+      uint64_t result;
+      double dResult;
+
+      // Setting flags if input double is negative infinity,
+      // positive infinity, negative zero or positive zero
+      bool negInf = (classed == 0xFFF0000000000000);
+      bool posInf = (classed == 0x7FF0000000000000);
+      bool negZero = (classed == 0x8000000000000000);
+      bool posZero = (classed == 0x0000000000000000);
+
+      bool signalingNan;
+      bool quietNan;
+      bool negSubnorm;
+      bool posSubnorm;
+      bool negNorm;
+      bool posNorm;
+
+      // Setting flags if double is NaN
+      signalingNan = false;
+      quietNan = false;
+      if (!negInf && !posInf && exponent == 0x7ff) {
+        quietNan = ((mantissa & 0x0008000000000000) != 0) &&
+                   ((mantissa & (0x0008000000000000 - 1)) == 0);
+        signalingNan = !quietNan;
+      }
+
+      // Setting flags if double is subnormal number
+      posSubnorm = false;
+      negSubnorm = false;
+      if ((exponent == 0) && (mantissa != 0)) {
+        DCHECK(sign == 0 || sign == 1);
+        posSubnorm = (sign == 0);
+        negSubnorm = (sign == 1);
+      }
+
+      // Setting flags if double is normal number
+      posNorm = false;
+      negNorm = false;
+      if (!posSubnorm && !negSubnorm && !posInf && !negInf && !signalingNan &&
+          !quietNan && !negZero && !posZero) {
+        DCHECK(sign == 0 || sign == 1);
+        posNorm = (sign == 0);
+        negNorm = (sign == 1);
+      }
+
+      // Calculating result according to description of CLASS.D instruction
+      result = (posZero << 9) | (posSubnorm << 8) | (posNorm << 7) |
+               (posInf << 6) | (negZero << 5) | (negSubnorm << 4) |
+               (negNorm << 3) | (negInf << 2) | (quietNan << 1) | signalingNan;
+
+      DCHECK(result != 0);
+
+      dResult = bit_cast<double>(result);
+      set_fpu_register_double(fd_reg(), dResult);
+
+      break;
+    }
+    case C_F_D: {
+      set_fcsr_bit(fcsr_cc, false);
+      break;
+    }
+    default:
+      UNREACHABLE();
   }
 }
 
 
-// Handle execution based on instruction types.
-
-void Simulator::ConfigureTypeRegister(Instruction* instr,
-                                      int32_t* alu_out,
-                                      int64_t* i64hilo,
-                                      uint64_t* u64hilo,
-                                      int32_t* next_pc,
-                                      int32_t* return_addr_reg,
-                                      bool* do_interrupt) {
-  // Every local variable declared here needs to be const.
-  // This is to make sure that changed values are sent back to
-  // DecodeTypeRegister correctly.
-
-  // Instruction fields.
-  const Opcode   op     = instr->OpcodeFieldRaw();
-  const int32_t  rs_reg = instr->RsValue();
-  const int32_t  rs     = get_register(rs_reg);
-  const uint32_t rs_u   = static_cast<uint32_t>(rs);
-  const int32_t  rt_reg = instr->RtValue();
-  const int32_t  rt     = get_register(rt_reg);
-  const uint32_t rt_u   = static_cast<uint32_t>(rt);
-  const int32_t  rd_reg = instr->RdValue();
-  const uint32_t sa     = instr->SaValue();
-
-  const int32_t  fs_reg = instr->FsValue();
-
-
-  // ---------- Configuration.
-  switch (op) {
-    case COP1:    // Coprocessor instructions.
-      switch (instr->RsFieldRaw()) {
-        case CFC1:
-          // At the moment only FCSR is supported.
-          DCHECK(fs_reg == kFCSRRegister);
-          *alu_out = FCSR_;
-          break;
-        case MFC1:
-          *alu_out = get_fpu_register_word(fs_reg);
-          break;
-        case MFHC1:
-          *alu_out = get_fpu_register_hi_word(fs_reg);
-          break;
-        case CTC1:
-        case MTC1:
-        case MTHC1:
-        case S:
-        case D:
-        case W:
-        case L:
-        case PS:
-          // Do everything in the execution step.
-          break;
-        default:
-        // BC1 BC1EQZ BC1NEZ handled in DecodeTypeImmed, should never come here.
-          UNREACHABLE();
+void Simulator::DecodeTypeRegisterWRsType() {
+  float fs = get_fpu_register_float(fs_reg());
+  float ft = get_fpu_register_float(ft_reg());
+  int32_t alu_out = 0x12345678;
+  switch (get_instr()->FunctionFieldRaw()) {
+    case CVT_S_W:  // Convert word to float (single).
+      alu_out = get_fpu_register_signed_word(fs_reg());
+      set_fpu_register_float(fd_reg(), static_cast<float>(alu_out));
+      break;
+    case CVT_D_W:  // Convert word to double.
+      alu_out = get_fpu_register_signed_word(fs_reg());
+      set_fpu_register_double(fd_reg(), static_cast<double>(alu_out));
+      break;
+    case CMP_AF:
+      set_fpu_register_word(fd_reg(), 0);
+      break;
+    case CMP_UN:
+      if (std::isnan(fs) || std::isnan(ft)) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
       }
       break;
-    case COP1X:
-      break;
-    case SPECIAL:
-      switch (instr->FunctionFieldRaw()) {
-        case JR:
-        case JALR:
-          *next_pc = get_register(instr->RsValue());
-          *return_addr_reg = instr->RdValue();
-          break;
-        case SLL:
-          *alu_out = rt << sa;
-          break;
-        case SRL:
-          if (rs_reg == 0) {
-            // Regular logical right shift of a word by a fixed number of
-            // bits instruction. RS field is always equal to 0.
-            *alu_out = rt_u >> sa;
-          } else {
-            // Logical right-rotate of a word by a fixed number of bits. This
-            // is special case of SRL instruction, added in MIPS32 Release 2.
-            // RS field is equal to 00001.
-            *alu_out = (rt_u >> sa) | (rt_u << (32 - sa));
-          }
-          break;
-        case SRA:
-          *alu_out = rt >> sa;
-          break;
-        case SLLV:
-          *alu_out = rt << rs;
-          break;
-        case SRLV:
-          if (sa == 0) {
-            // Regular logical right-shift of a word by a variable number of
-            // bits instruction. SA field is always equal to 0.
-            *alu_out = rt_u >> rs;
-          } else {
-            // Logical right-rotate of a word by a variable number of bits.
-            // This is special case od SRLV instruction, added in MIPS32
-            // Release 2. SA field is equal to 00001.
-            *alu_out = (rt_u >> rs_u) | (rt_u << (32 - rs_u));
-          }
-          break;
-        case SRAV:
-          *alu_out = rt >> rs;
-          break;
-        case MFHI:  // MFHI == CLZ on R6.
-          if (!IsMipsArchVariant(kMips32r6)) {
-            DCHECK(instr->SaValue() == 0);
-            *alu_out = get_register(HI);
-          } else {
-            // MIPS spec: If no bits were set in GPR rs, the result written to
-            // GPR rd is 32.
-            DCHECK(instr->SaValue() == 1);
-            *alu_out = base::bits::CountLeadingZeros32(rs_u);
-          }
-          break;
-        case MFLO:
-          *alu_out = get_register(LO);
-          break;
-        case MULT:  // MULT == MUL_MUH.
-          if (!IsMipsArchVariant(kMips32r6)) {
-            *i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
-          } else {
-            switch (instr->SaValue()) {
-              case MUL_OP:
-              case MUH_OP:
-                *i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
-                break;
-              default:
-                UNIMPLEMENTED_MIPS();
-                break;
-            }
-          }
-          break;
-        case MULTU:  // MULTU == MUL_MUH_U.
-          if (!IsMipsArchVariant(kMips32r6)) {
-            *u64hilo = static_cast<uint64_t>(rs_u) *
-                static_cast<uint64_t>(rt_u);
-          } else {
-            switch (instr->SaValue()) {
-              case MUL_OP:
-              case MUH_OP:
-                *u64hilo = static_cast<uint64_t>(rs_u) *
-                    static_cast<uint64_t>(rt_u);
-                break;
-              default:
-                UNIMPLEMENTED_MIPS();
-                break;
-            }
-          }
-          break;
-        case ADD:
-          if (HaveSameSign(rs, rt)) {
-            if (rs > 0) {
-              exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue - rt);
-            } else if (rs < 0) {
-              exceptions[kIntegerUnderflow] = rs < (Registers::kMinValue - rt);
-            }
-          }
-          *alu_out = rs + rt;
-          break;
-        case ADDU:
-          *alu_out = rs + rt;
-          break;
-        case SUB:
-          if (!HaveSameSign(rs, rt)) {
-            if (rs > 0) {
-              exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue + rt);
-            } else if (rs < 0) {
-              exceptions[kIntegerUnderflow] = rs < (Registers::kMinValue + rt);
-            }
-          }
-          *alu_out = rs - rt;
-          break;
-        case SUBU:
-          *alu_out = rs - rt;
-          break;
-        case AND:
-          *alu_out = rs & rt;
-          break;
-        case OR:
-          *alu_out = rs | rt;
-          break;
-        case XOR:
-          *alu_out = rs ^ rt;
-          break;
-        case NOR:
-          *alu_out = ~(rs | rt);
-          break;
-        case SLT:
-          *alu_out = rs < rt ? 1 : 0;
-          break;
-        case SLTU:
-          *alu_out = rs_u < rt_u ? 1 : 0;
-          break;
-        // Break and trap instructions.
-        case BREAK:
-          *do_interrupt = true;
-          break;
-        case TGE:
-          *do_interrupt = rs >= rt;
-          break;
-        case TGEU:
-          *do_interrupt = rs_u >= rt_u;
-          break;
-        case TLT:
-          *do_interrupt = rs < rt;
-          break;
-        case TLTU:
-          *do_interrupt = rs_u < rt_u;
-          break;
-        case TEQ:
-          *do_interrupt = rs == rt;
-          break;
-        case TNE:
-          *do_interrupt = rs != rt;
-          break;
-        case MOVN:
-        case MOVZ:
-        case MOVCI:
-          // No action taken on decode.
-          break;
-        case DIV:
-        case DIVU:
-          // div and divu never raise exceptions.
-          break;
-        default:
-          UNREACHABLE();
+    case CMP_EQ:
+      if (fs == ft) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
       }
       break;
-    case SPECIAL2:
-      switch (instr->FunctionFieldRaw()) {
-        case MUL:
-          *alu_out = rs_u * rt_u;  // Only the lower 32 bits are kept.
-          break;
-        case CLZ:
-          // MIPS32 spec: If no bits were set in GPR rs, the result written to
-          // GPR rd is 32.
-          *alu_out = base::bits::CountLeadingZeros32(rs_u);
-          break;
-        default:
-          UNREACHABLE();
+    case CMP_UEQ:
+      if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
       }
       break;
-    case SPECIAL3:
-      switch (instr->FunctionFieldRaw()) {
-        case INS: {   // Mips32r2 instruction.
-          // Interpret rd field as 5-bit msb of insert.
-          uint16_t msb = rd_reg;
-          // Interpret sa field as 5-bit lsb of insert.
-          uint16_t lsb = sa;
-          uint16_t size = msb - lsb + 1;
-          uint32_t mask = (1 << size) - 1;
-          *alu_out = (rt_u & ~(mask << lsb)) | ((rs_u & mask) << lsb);
-          break;
-        }
-        case EXT: {   // Mips32r2 instruction.
-          // Interpret rd field as 5-bit msb of extract.
-          uint16_t msb = rd_reg;
-          // Interpret sa field as 5-bit lsb of extract.
-          uint16_t lsb = sa;
-          uint16_t size = msb + 1;
-          uint32_t mask = (1 << size) - 1;
-          *alu_out = (rs_u & (mask << lsb)) >> lsb;
-          break;
-        }
-        default:
-          UNREACHABLE();
+    case CMP_LT:
+      if (fs < ft) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
+      }
+      break;
+    case CMP_ULT:
+      if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
+      }
+      break;
+    case CMP_LE:
+      if (fs <= ft) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
+      }
+      break;
+    case CMP_ULE:
+      if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
+      }
+      break;
+    case CMP_OR:
+      if (!std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
+      }
+      break;
+    case CMP_UNE:
+      if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
+      }
+      break;
+    case CMP_NE:
+      if (fs != ft) {
+        set_fpu_register_word(fd_reg(), -1);
+      } else {
+        set_fpu_register_word(fd_reg(), 0);
       }
       break;
     default:
@@ -2129,627 +2838,1157 @@
 }
 
 
-void Simulator::DecodeTypeRegister(Instruction* instr) {
-  // Instruction fields.
-  const Opcode   op     = instr->OpcodeFieldRaw();
-  const int32_t  rs_reg = instr->RsValue();
-  const int32_t  rs     = get_register(rs_reg);
-  const uint32_t rs_u   = static_cast<uint32_t>(rs);
-  const int32_t  rt_reg = instr->RtValue();
-  const int32_t  rt     = get_register(rt_reg);
-  const uint32_t rt_u   = static_cast<uint32_t>(rt);
-  const int32_t  rd_reg = instr->RdValue();
-
-  const int32_t  fr_reg = instr->FrValue();
-  const int32_t  fs_reg = instr->FsValue();
-  const int32_t  ft_reg = instr->FtValue();
-  const int32_t  fd_reg = instr->FdValue();
-  int64_t  i64hilo = 0;
-  uint64_t u64hilo = 0;
-
-  // ALU output.
-  // It should not be used as is. Instructions using it should always
-  // initialize it first.
-  int32_t alu_out = 0x12345678;
-
-  // For break and trap instructions.
-  bool do_interrupt = false;
-
-  // For jr and jalr.
-  // Get current pc.
-  int32_t current_pc = get_pc();
-  // Next pc
-  int32_t next_pc = 0;
-  int32_t return_addr_reg = 31;
-
-  // Set up the variables if needed before executing the instruction.
-  ConfigureTypeRegister(instr,
-                        &alu_out,
-                        &i64hilo,
-                        &u64hilo,
-                        &next_pc,
-                        &return_addr_reg,
-                        &do_interrupt);
-
-  // ---------- Raise exceptions triggered.
-  SignalExceptions();
-
-  // ---------- Execution.
-  switch (op) {
-    case COP1:
-      switch (instr->RsFieldRaw()) {
-        case CFC1:
-          set_register(rt_reg, alu_out);
-          break;
-        case MFC1:
-          set_register(rt_reg, alu_out);
-          break;
-        case MFHC1:
-          set_register(rt_reg, alu_out);
-          break;
-        case CTC1:
-          // At the moment only FCSR is supported.
-          DCHECK(fs_reg == kFCSRRegister);
-          FCSR_ = registers_[rt_reg];
-          break;
-        case MTC1:
-          // Hardware writes upper 32-bits to zero on mtc1.
-          set_fpu_register_hi_word(fs_reg, 0);
-          set_fpu_register_word(fs_reg, registers_[rt_reg]);
-          break;
-        case MTHC1:
-          set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
-          break;
-        case S:
-          float f;
-          switch (instr->FunctionFieldRaw()) {
-            case CVT_D_S:
-              f = get_fpu_register_float(fs_reg);
-              set_fpu_register_double(fd_reg, static_cast<double>(f));
-              break;
-            default:
-            // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
-            // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
-              UNREACHABLE();
-          }
-          break;
-        case D:
-          double ft, fs;
-          uint32_t cc, fcsr_cc;
-          int64_t  i64;
-          fs = get_fpu_register_double(fs_reg);
-          ft = get_fpu_register_double(ft_reg);
-          cc = instr->FCccValue();
-          fcsr_cc = get_fcsr_condition_bit(cc);
-          switch (instr->FunctionFieldRaw()) {
-            case ADD_D:
-              set_fpu_register_double(fd_reg, fs + ft);
-              break;
-            case SUB_D:
-              set_fpu_register_double(fd_reg, fs - ft);
-              break;
-            case MUL_D:
-              set_fpu_register_double(fd_reg, fs * ft);
-              break;
-            case DIV_D:
-              set_fpu_register_double(fd_reg, fs / ft);
-              break;
-            case ABS_D:
-              set_fpu_register_double(fd_reg, fabs(fs));
-              break;
-            case MOV_D:
-              set_fpu_register_double(fd_reg, fs);
-              break;
-            case NEG_D:
-              set_fpu_register_double(fd_reg, -fs);
-              break;
-            case SQRT_D:
-              set_fpu_register_double(fd_reg, sqrt(fs));
-              break;
-            case C_UN_D:
-              set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
-              break;
-            case C_EQ_D:
-              set_fcsr_bit(fcsr_cc, (fs == ft));
-              break;
-            case C_UEQ_D:
-              set_fcsr_bit(fcsr_cc,
-                           (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
-              break;
-            case C_OLT_D:
-              set_fcsr_bit(fcsr_cc, (fs < ft));
-              break;
-            case C_ULT_D:
-              set_fcsr_bit(fcsr_cc,
-                           (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
-              break;
-            case C_OLE_D:
-              set_fcsr_bit(fcsr_cc, (fs <= ft));
-              break;
-            case C_ULE_D:
-              set_fcsr_bit(fcsr_cc,
-                           (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
-              break;
-            case CVT_W_D:   // Convert double to word.
-              // Rounding modes are not yet supported.
-              DCHECK((FCSR_ & 3) == 0);
-              // In rounding mode 0 it should behave like ROUND.
-            case ROUND_W_D:  // Round double to word (round half to even).
-              {
-                double rounded = std::floor(fs + 0.5);
-                int32_t result = static_cast<int32_t>(rounded);
-                if ((result & 1) != 0 && result - fs == 0.5) {
-                  // If the number is halfway between two integers,
-                  // round to the even one.
-                  result--;
-                }
-                set_fpu_register_word(fd_reg, result);
-                if (set_fcsr_round_error(fs, rounded)) {
-                  set_fpu_register_word(fd_reg, kFPUInvalidResult);
-                }
-              }
-              break;
-            case TRUNC_W_D:  // Truncate double to word (round towards 0).
-              {
-                double rounded = trunc(fs);
-                int32_t result = static_cast<int32_t>(rounded);
-                set_fpu_register_word(fd_reg, result);
-                if (set_fcsr_round_error(fs, rounded)) {
-                  set_fpu_register_word(fd_reg, kFPUInvalidResult);
-                }
-              }
-              break;
-            case FLOOR_W_D:  // Round double to word towards negative infinity.
-              {
-                double rounded = std::floor(fs);
-                int32_t result = static_cast<int32_t>(rounded);
-                set_fpu_register_word(fd_reg, result);
-                if (set_fcsr_round_error(fs, rounded)) {
-                  set_fpu_register_word(fd_reg, kFPUInvalidResult);
-                }
-              }
-              break;
-            case CEIL_W_D:  // Round double to word towards positive infinity.
-              {
-                double rounded = std::ceil(fs);
-                int32_t result = static_cast<int32_t>(rounded);
-                set_fpu_register_word(fd_reg, result);
-                if (set_fcsr_round_error(fs, rounded)) {
-                  set_fpu_register_word(fd_reg, kFPUInvalidResult);
-                }
-              }
-              break;
-            case CVT_S_D:  // Convert double to float (single).
-              set_fpu_register_float(fd_reg, static_cast<float>(fs));
-              break;
-            case CVT_L_D: {  // Mips32r2: Truncate double to 64-bit long-word.
-              double rounded = trunc(fs);
-              i64 = static_cast<int64_t>(rounded);
-              if (IsFp64Mode()) {
-                set_fpu_register(fd_reg, i64);
-              } else {
-                set_fpu_register_word(fd_reg, i64 & 0xffffffff);
-                set_fpu_register_word(fd_reg + 1, i64 >> 32);
-              }
-              break;
-            }
-            case TRUNC_L_D: {  // Mips32r2 instruction.
-              double rounded = trunc(fs);
-              i64 = static_cast<int64_t>(rounded);
-              if (IsFp64Mode()) {
-                set_fpu_register(fd_reg, i64);
-              } else {
-                set_fpu_register_word(fd_reg, i64 & 0xffffffff);
-                set_fpu_register_word(fd_reg + 1, i64 >> 32);
-              }
-              break;
-            }
-            case ROUND_L_D: {  // Mips32r2 instruction.
-              double rounded =
-                  fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
-              i64 = static_cast<int64_t>(rounded);
-              if (IsFp64Mode()) {
-                set_fpu_register(fd_reg, i64);
-              } else {
-                set_fpu_register_word(fd_reg, i64 & 0xffffffff);
-                set_fpu_register_word(fd_reg + 1, i64 >> 32);
-              }
-              break;
-            }
-            case FLOOR_L_D:  // Mips32r2 instruction.
-              i64 = static_cast<int64_t>(std::floor(fs));
-              if (IsFp64Mode()) {
-                set_fpu_register(fd_reg, i64);
-              } else {
-                set_fpu_register_word(fd_reg, i64 & 0xffffffff);
-                set_fpu_register_word(fd_reg + 1, i64 >> 32);
-              }
-              break;
-            case CEIL_L_D:  // Mips32r2 instruction.
-              i64 = static_cast<int64_t>(std::ceil(fs));
-              if (IsFp64Mode()) {
-                set_fpu_register(fd_reg, i64);
-              } else {
-                set_fpu_register_word(fd_reg, i64 & 0xffffffff);
-                set_fpu_register_word(fd_reg + 1, i64 >> 32);
-              }
-              break;
-            case C_F_D:
-              UNIMPLEMENTED_MIPS();
-              break;
-            default:
-              UNREACHABLE();
-          }
-          break;
-        case W:
-          switch (instr->FunctionFieldRaw()) {
-            case CVT_S_W:   // Convert word to float (single).
-              alu_out = get_fpu_register_signed_word(fs_reg);
-              set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
-              break;
-            case CVT_D_W:   // Convert word to double.
-              alu_out = get_fpu_register_signed_word(fs_reg);
-              set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
-              break;
-            default:  // Mips64r6 CMP.S instructions unimplemented.
-              UNREACHABLE();
-          }
-          break;
-        case L:
-          fs = get_fpu_register_double(fs_reg);
-          ft = get_fpu_register_double(ft_reg);
-          switch (instr->FunctionFieldRaw()) {
-          case CVT_D_L:  // Mips32r2 instruction.
-            // Watch the signs here, we want 2 32-bit vals
-            // to make a sign-64.
-            if (IsFp64Mode()) {
-              i64 = get_fpu_register(fs_reg);
+void Simulator::DecodeTypeRegisterSRsType() {
+  float fs, ft, fd;
+  fs = get_fpu_register_float(fs_reg());
+  ft = get_fpu_register_float(ft_reg());
+  fd = get_fpu_register_float(fd_reg());
+  int32_t ft_int = bit_cast<int32_t>(ft);
+  int32_t fd_int = bit_cast<int32_t>(fd);
+  uint32_t cc, fcsr_cc;
+  cc = get_instr()->FCccValue();
+  fcsr_cc = get_fcsr_condition_bit(cc);
+  switch (get_instr()->FunctionFieldRaw()) {
+    case RINT: {
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      float result, temp_result;
+      double temp;
+      float upper = std::ceil(fs);
+      float lower = std::floor(fs);
+      switch (get_fcsr_rounding_mode()) {
+        case kRoundToNearest:
+          if (upper - fs < fs - lower) {
+            result = upper;
+          } else if (upper - fs > fs - lower) {
+            result = lower;
+          } else {
+            temp_result = upper / 2;
+            float reminder = modf(temp_result, &temp);
+            if (reminder == 0) {
+              result = upper;
             } else {
-              i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg));
-              i64 |= static_cast<int64_t>(
-                  get_fpu_register_word(fs_reg + 1)) << 32;
+              result = lower;
             }
-            set_fpu_register_double(fd_reg, static_cast<double>(i64));
-            break;
-            case CVT_S_L:
-              UNIMPLEMENTED_MIPS();
-              break;
-            case CMP_AF:  // Mips64r6 CMP.D instructions.
-              UNIMPLEMENTED_MIPS();
-              break;
-            case CMP_UN:
-              if (std::isnan(fs) || std::isnan(ft)) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            case CMP_EQ:
-              if (fs == ft) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            case CMP_UEQ:
-              if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            case CMP_LT:
-              if (fs < ft) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            case CMP_ULT:
-              if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            case CMP_LE:
-              if (fs <= ft) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            case CMP_ULE:
-              if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
-                set_fpu_register(fd_reg, -1);
-              } else {
-                set_fpu_register(fd_reg, 0);
-              }
-              break;
-            default:  // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED.
-              UNREACHABLE();
           }
           break;
-        default:
-          UNREACHABLE();
+        case kRoundToZero:
+          result = (fs > 0 ? lower : upper);
+          break;
+        case kRoundToPlusInf:
+          result = upper;
+          break;
+        case kRoundToMinusInf:
+          result = lower;
+          break;
+      }
+      set_fpu_register_float(fd_reg(), result);
+      if (result != fs) {
+        set_fcsr_bit(kFCSRInexactFlagBit, true);
       }
       break;
-    case COP1X:
-      switch (instr->FunctionFieldRaw()) {
-        case MADD_D:
-          double fr, ft, fs;
-          fr = get_fpu_register_double(fr_reg);
-          fs = get_fpu_register_double(fs_reg);
-          ft = get_fpu_register_double(ft_reg);
-          set_fpu_register_double(fd_reg, fs * ft + fr);
-          break;
-        default:
-          UNREACHABLE();
+    }
+    case ADD_S:
+      set_fpu_register_float(fd_reg(), fs + ft);
+      break;
+    case SUB_S:
+      set_fpu_register_float(fd_reg(), fs - ft);
+      break;
+    case MUL_S:
+      set_fpu_register_float(fd_reg(), fs * ft);
+      break;
+    case DIV_S:
+      set_fpu_register_float(fd_reg(), fs / ft);
+      break;
+    case ABS_S:
+      set_fpu_register_float(fd_reg(), fabs(fs));
+      break;
+    case MOV_S:
+      set_fpu_register_float(fd_reg(), fs);
+      break;
+    case NEG_S:
+      set_fpu_register_float(fd_reg(), -fs);
+      break;
+    case SQRT_S:
+      lazily_initialize_fast_sqrt(isolate_);
+      set_fpu_register_float(fd_reg(), fast_sqrt(fs, isolate_));
+      break;
+    case RSQRT_S: {
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      lazily_initialize_fast_sqrt(isolate_);
+      float result = 1.0 / fast_sqrt(fs, isolate_);
+      set_fpu_register_float(fd_reg(), result);
+      break;
+    }
+    case RECIP_S: {
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      float result = 1.0 / fs;
+      set_fpu_register_float(fd_reg(), result);
+      break;
+    }
+    case C_F_D:
+      set_fcsr_bit(fcsr_cc, false);
+      break;
+    case C_UN_D:
+      set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
+      break;
+    case C_EQ_D:
+      set_fcsr_bit(fcsr_cc, (fs == ft));
+      break;
+    case C_UEQ_D:
+      set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
+      break;
+    case C_OLT_D:
+      set_fcsr_bit(fcsr_cc, (fs < ft));
+      break;
+    case C_ULT_D:
+      set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
+      break;
+    case C_OLE_D:
+      set_fcsr_bit(fcsr_cc, (fs <= ft));
+      break;
+    case C_ULE_D:
+      set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
+      break;
+    case CVT_D_S:
+      set_fpu_register_double(fd_reg(), static_cast<double>(fs));
+      break;
+    case SEL:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_fpu_register_float(fd_reg(), (fd_int & 0x1) == 0 ? fs : ft);
+      break;
+    case CLASS_S: {  // Mips32r6 instruction
+      // Convert float input to uint32_t for easier bit manipulation
+      float fs = get_fpu_register_float(fs_reg());
+      uint32_t classed = bit_cast<uint32_t>(fs);
+
+      // Extracting sign, exponent and mantissa from the input float
+      uint32_t sign = (classed >> 31) & 1;
+      uint32_t exponent = (classed >> 23) & 0x000000ff;
+      uint32_t mantissa = classed & 0x007fffff;
+      uint32_t result;
+      float fResult;
+
+      // Setting flags if input float is negative infinity,
+      // positive infinity, negative zero or positive zero
+      bool negInf = (classed == 0xFF800000);
+      bool posInf = (classed == 0x7F800000);
+      bool negZero = (classed == 0x80000000);
+      bool posZero = (classed == 0x00000000);
+
+      bool signalingNan;
+      bool quietNan;
+      bool negSubnorm;
+      bool posSubnorm;
+      bool negNorm;
+      bool posNorm;
+
+      // Setting flags if float is NaN
+      signalingNan = false;
+      quietNan = false;
+      if (!negInf && !posInf && (exponent == 0xff)) {
+        quietNan = ((mantissa & 0x00200000) == 0) &&
+                   ((mantissa & (0x00200000 - 1)) == 0);
+        signalingNan = !quietNan;
+      }
+
+      // Setting flags if float is subnormal number
+      posSubnorm = false;
+      negSubnorm = false;
+      if ((exponent == 0) && (mantissa != 0)) {
+        DCHECK(sign == 0 || sign == 1);
+        posSubnorm = (sign == 0);
+        negSubnorm = (sign == 1);
+      }
+
+      // Setting flags if float is normal number
+      posNorm = false;
+      negNorm = false;
+      if (!posSubnorm && !negSubnorm && !posInf && !negInf && !signalingNan &&
+          !quietNan && !negZero && !posZero) {
+        DCHECK(sign == 0 || sign == 1);
+        posNorm = (sign == 0);
+        negNorm = (sign == 1);
+      }
+
+      // Calculating result according to description of CLASS.S instruction
+      result = (posZero << 9) | (posSubnorm << 8) | (posNorm << 7) |
+               (posInf << 6) | (negZero << 5) | (negSubnorm << 4) |
+               (negNorm << 3) | (negInf << 2) | (quietNan << 1) | signalingNan;
+
+      DCHECK(result != 0);
+
+      fResult = bit_cast<float>(result);
+      set_fpu_register_float(fd_reg(), fResult);
+
+      break;
+    }
+    case SELEQZ_C:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_fpu_register_float(fd_reg(), (ft_int & 0x1) == 0
+                                           ? get_fpu_register_float(fs_reg())
+                                           : 0.0);
+      break;
+    case SELNEZ_C:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_fpu_register_float(fd_reg(), (ft_int & 0x1) != 0
+                                           ? get_fpu_register_float(fs_reg())
+                                           : 0.0);
+      break;
+    case MOVZ_C: {
+      DCHECK(IsMipsArchVariant(kMips32r2));
+      if (rt() == 0) {
+        set_fpu_register_float(fd_reg(), fs);
       }
       break;
-    case SPECIAL:
-      switch (instr->FunctionFieldRaw()) {
-        case JR: {
-          Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
-              current_pc+Instruction::kInstrSize);
-          BranchDelayInstructionDecode(branch_delay_instr);
-          set_pc(next_pc);
-          pc_modified_ = true;
-          break;
+    }
+    case MOVN_C: {
+      DCHECK(IsMipsArchVariant(kMips32r2));
+      if (rt() != 0) {
+        set_fpu_register_float(fd_reg(), fs);
+      }
+      break;
+    }
+    case MOVF: {
+      // Same function field for MOVT.D and MOVF.D
+      uint32_t ft_cc = (ft_reg() >> 2) & 0x7;
+      ft_cc = get_fcsr_condition_bit(ft_cc);
+
+      if (get_instr()->Bit(16)) {  // Read Tf bit.
+        // MOVT.D
+        if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg(), fs);
+      } else {
+        // MOVF.D
+        if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg(), fs);
+      }
+      break;
+    }
+    case TRUNC_W_S: {  // Truncate single to word (round towards 0).
+      float rounded = trunc(fs);
+      int32_t result = static_cast<int32_t>(rounded);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case TRUNC_L_S: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      float rounded = trunc(fs);
+      int64_t i64 = static_cast<int64_t>(rounded);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
         }
-        case JALR: {
-          Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
-              current_pc+Instruction::kInstrSize);
-          BranchDelayInstructionDecode(branch_delay_instr);
-          set_register(return_addr_reg,
-                       current_pc + 2 * Instruction::kInstrSize);
-          set_pc(next_pc);
-          pc_modified_ = true;
-          break;
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case FLOOR_W_S:  // Round double to word towards negative infinity.
+    {
+      float rounded = std::floor(fs);
+      int32_t result = static_cast<int32_t>(rounded);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case FLOOR_L_S: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      float rounded = std::floor(fs);
+      int64_t i64 = static_cast<int64_t>(rounded);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
         }
-        // Instructions using HI and LO registers.
-        case MULT:
-          if (!IsMipsArchVariant(kMips32r6)) {
-            set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
-            set_register(HI, static_cast<int32_t>(i64hilo >> 32));
-          } else {
-            switch (instr->SaValue()) {
-              case MUL_OP:
-                set_register(rd_reg,
-                    static_cast<int32_t>(i64hilo & 0xffffffff));
-                break;
-              case MUH_OP:
-                set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
-                break;
-              default:
-                UNIMPLEMENTED_MIPS();
-                break;
-            }
-          }
-          break;
-        case MULTU:
-          if (!IsMipsArchVariant(kMips32r6)) {
-            set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
-            set_register(HI, static_cast<int32_t>(u64hilo >> 32));
-          } else {
-            switch (instr->SaValue()) {
-              case MUL_OP:
-                set_register(rd_reg,
-                    static_cast<int32_t>(u64hilo & 0xffffffff));
-                break;
-              case MUH_OP:
-                set_register(rd_reg, static_cast<int32_t>(u64hilo >> 32));
-                break;
-              default:
-                UNIMPLEMENTED_MIPS();
-                break;
-            }
-          }
-          break;
-        case DIV:
-          if (IsMipsArchVariant(kMips32r6)) {
-            switch (instr->SaValue()) {
-              case DIV_OP:
-                if (rs == INT_MIN && rt == -1) {
-                  set_register(rd_reg, INT_MIN);
-                } else if (rt != 0) {
-                  set_register(rd_reg, rs / rt);
-                }
-                break;
-              case MOD_OP:
-                if (rs == INT_MIN && rt == -1) {
-                  set_register(rd_reg, 0);
-                } else if (rt != 0) {
-                  set_register(rd_reg, rs % rt);
-                }
-                break;
-              default:
-                UNIMPLEMENTED_MIPS();
-                break;
-            }
-          } else {
-            // Divide by zero and overflow was not checked in the
-            // configuration step - div and divu do not raise exceptions. On
-            // division by 0 the result will be UNPREDICTABLE. On overflow
-            // (INT_MIN/-1), return INT_MIN which is what the hardware does.
-            if (rs == INT_MIN && rt == -1) {
-              set_register(LO, INT_MIN);
-              set_register(HI, 0);
-            } else if (rt != 0) {
-              set_register(LO, rs / rt);
-              set_register(HI, rs % rt);
-            }
-          }
-          break;
-        case DIVU:
-          if (IsMipsArchVariant(kMips32r6)) {
-            switch (instr->SaValue()) {
-              case DIV_OP:
-                if (rt_u != 0) {
-                  set_register(rd_reg, rs_u / rt_u);
-                }
-                break;
-              case MOD_OP:
-                if (rt_u != 0) {
-                  set_register(rd_reg, rs_u % rt_u);
-                }
-                break;
-              default:
-                UNIMPLEMENTED_MIPS();
-                break;
-              }
-          } else {
-            if (rt_u != 0) {
-              set_register(LO, rs_u / rt_u);
-              set_register(HI, rs_u % rt_u);
-            }
-          }
-          break;
-        // Break and trap instructions.
-        case BREAK:
-        case TGE:
-        case TGEU:
-        case TLT:
-        case TLTU:
-        case TEQ:
-        case TNE:
-          if (do_interrupt) {
-            SoftwareInterrupt(instr);
-          }
-          break;
-        // Conditional moves.
-        case MOVN:
-          if (rt) set_register(rd_reg, rs);
-          break;
-        case MOVCI: {
-          uint32_t cc = instr->FBccValue();
-          uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
-          if (instr->Bit(16)) {  // Read Tf bit.
-            if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
-          } else {
-            if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
-          }
-          break;
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case ROUND_W_S: {
+      float rounded = std::floor(fs + 0.5);
+      int32_t result = static_cast<int32_t>(rounded);
+      if ((result & 1) != 0 && result - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        result--;
+      }
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+      break;
+    }
+    case ROUND_L_S: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      float rounded = std::floor(fs + 0.5);
+      int64_t result = static_cast<int64_t>(rounded);
+      if ((result & 1) != 0 && result - fs == 0.5) {
+        // If the number is halfway between two integers,
+        // round to the even one.
+        result--;
+      }
+      int64_t i64 = static_cast<int64_t>(result);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
         }
-        case MOVZ:
-          if (!rt) set_register(rd_reg, rs);
-          break;
-        default:  // For other special opcodes we do the default operation.
-          set_register(rd_reg, alu_out);
+      } else {
+        UNSUPPORTED();
       }
       break;
-    case SPECIAL2:
-      switch (instr->FunctionFieldRaw()) {
-        case MUL:
-          set_register(rd_reg, alu_out);
-          // HI and LO are UNPREDICTABLE after the operation.
-          set_register(LO, Unpredictable);
-          set_register(HI, Unpredictable);
-          break;
-        default:  // For other special2 opcodes we do the default operation.
-          set_register(rd_reg, alu_out);
+    }
+    case CEIL_W_S:  // Round double to word towards positive infinity.
+    {
+      float rounded = std::ceil(fs);
+      int32_t result = static_cast<int32_t>(rounded);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+    } break;
+    case CEIL_L_S: {  // Mips32r2 instruction.
+      DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
+      float rounded = std::ceil(fs);
+      int64_t i64 = static_cast<int64_t>(rounded);
+      if (IsFp64Mode()) {
+        set_fpu_register(fd_reg(), i64);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
       }
       break;
-    case SPECIAL3:
-      switch (instr->FunctionFieldRaw()) {
-        case INS:
-          // Ins instr leaves result in Rt, rather than Rd.
-          set_register(rt_reg, alu_out);
-          break;
-        case EXT:
-          // Ext instr leaves result in Rt, rather than Rd.
-          set_register(rt_reg, alu_out);
-          break;
-        default:
-          UNREACHABLE();
+    }
+    case MIN:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_float(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else {
+        set_fpu_register_float(fd_reg(), (fs >= ft) ? ft : fs);
       }
       break;
-    // Unimplemented opcodes raised an error in the configuration step before,
-    // so we can use the default here to set the destination register in common
-    // cases.
+    case MAX:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_float(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else {
+        set_fpu_register_float(fd_reg(), (fs <= ft) ? ft : fs);
+      }
+      break;
+    case MINA:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_float(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else {
+        float result;
+        if (fabs(fs) > fabs(ft)) {
+          result = ft;
+        } else if (fabs(fs) < fabs(ft)) {
+          result = fs;
+        } else {
+          result = (fs < ft ? fs : ft);
+        }
+        set_fpu_register_float(fd_reg(), result);
+      }
+      break;
+    case MAXA:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      fs = get_fpu_register_float(fs_reg());
+      if (std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else if (std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), ft);
+      } else if (!std::isnan(fs) && std::isnan(ft)) {
+        set_fpu_register_float(fd_reg(), fs);
+      } else {
+        float result;
+        if (fabs(fs) < fabs(ft)) {
+          result = ft;
+        } else if (fabs(fs) > fabs(ft)) {
+          result = fs;
+        } else {
+          result = (fs > ft ? fs : ft);
+        }
+        set_fpu_register_float(fd_reg(), result);
+      }
+      break;
+    case CVT_L_S: {
+      if (IsFp64Mode()) {
+        int64_t result;
+        float rounded;
+        round64_according_to_fcsr(fs, rounded, result, fs);
+        set_fpu_register(fd_reg(), result);
+        if (set_fcsr_round64_error(fs, rounded)) {
+          set_fpu_register_invalid_result64(fs, rounded);
+        }
+      } else {
+        UNSUPPORTED();
+      }
+      break;
+    }
+    case CVT_W_S: {
+      float rounded;
+      int32_t result;
+      round_according_to_fcsr(fs, rounded, result, fs);
+      set_fpu_register_word(fd_reg(), result);
+      if (set_fcsr_round_error(fs, rounded)) {
+        set_fpu_register_word_invalid_result(fs, rounded);
+      }
+      break;
+    }
     default:
-      set_register(rd_reg, alu_out);
+      // CVT_W_S CVT_L_S  ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
+      // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
+      UNREACHABLE();
   }
 }
 
 
-// Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq).
+void Simulator::DecodeTypeRegisterLRsType() {
+  double fs = get_fpu_register_double(fs_reg());
+  double ft = get_fpu_register_double(ft_reg());
+  switch (get_instr()->FunctionFieldRaw()) {
+    case CVT_D_L:  // Mips32r2 instruction.
+      // Watch the signs here, we want 2 32-bit vals
+      // to make a sign-64.
+      int64_t i64;
+      if (IsFp64Mode()) {
+        i64 = get_fpu_register(fs_reg());
+      } else {
+        i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg()));
+        i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg() + 1)) << 32;
+      }
+      set_fpu_register_double(fd_reg(), static_cast<double>(i64));
+      break;
+    case CVT_S_L:
+      if (IsFp64Mode()) {
+        i64 = get_fpu_register(fs_reg());
+      } else {
+        i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg()));
+        i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg() + 1)) << 32;
+      }
+      set_fpu_register_float(fd_reg(), static_cast<float>(i64));
+      break;
+    case CMP_AF:  // Mips64r6 CMP.D instructions.
+      set_fpu_register(fd_reg(), 0);
+      break;
+    case CMP_UN:
+      if (std::isnan(fs) || std::isnan(ft)) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_EQ:
+      if (fs == ft) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_UEQ:
+      if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_LT:
+      if (fs < ft) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_ULT:
+      if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_LE:
+      if (fs <= ft) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_ULE:
+      if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_OR:
+      if (!std::isnan(fs) && !std::isnan(ft)) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_UNE:
+      if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    case CMP_NE:
+      if (fs != ft && (!std::isnan(fs) && !std::isnan(ft))) {
+        set_fpu_register(fd_reg(), -1);
+      } else {
+        set_fpu_register(fd_reg(), 0);
+      }
+      break;
+    default:
+      UNREACHABLE();
+  }
+}
+
+
+void Simulator::DecodeTypeRegisterCOP1() {
+  switch (get_instr()->RsFieldRaw()) {
+    case CFC1:
+      // At the moment only FCSR is supported.
+      DCHECK(fs_reg() == kFCSRRegister);
+      set_register(rt_reg(), FCSR_);
+      break;
+    case MFC1:
+      set_register(rt_reg(), get_fpu_register_word(fs_reg()));
+      break;
+    case MFHC1:
+      set_register(rt_reg(), get_fpu_register_hi_word(fs_reg()));
+      break;
+    case CTC1: {
+      // At the moment only FCSR is supported.
+      DCHECK(fs_reg() == kFCSRRegister);
+      int32_t reg = registers_[rt_reg()];
+      if (IsMipsArchVariant(kMips32r6)) {
+        FCSR_ = reg | kFCSRNaN2008FlagMask;
+      } else {
+        DCHECK(IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kMips32r2));
+        FCSR_ = reg & ~kFCSRNaN2008FlagMask;
+      }
+      break;
+    }
+    case MTC1:
+      // Hardware writes upper 32-bits to zero on mtc1.
+      set_fpu_register_hi_word(fs_reg(), 0);
+      set_fpu_register_word(fs_reg(), registers_[rt_reg()]);
+      break;
+    case MTHC1:
+      set_fpu_register_hi_word(fs_reg(), registers_[rt_reg()]);
+      break;
+    case S: {
+      DecodeTypeRegisterSRsType();
+      break;
+    }
+    case D:
+      DecodeTypeRegisterDRsType();
+      break;
+    case W:
+      DecodeTypeRegisterWRsType();
+      break;
+    case L:
+      DecodeTypeRegisterLRsType();
+      break;
+    case PS:
+      // Not implemented.
+      UNREACHABLE();
+    default:
+      UNREACHABLE();
+  }
+}
+
+
+void Simulator::DecodeTypeRegisterCOP1X() {
+  switch (get_instr()->FunctionFieldRaw()) {
+    case MADD_D:
+      double fr, ft, fs;
+      fr = get_fpu_register_double(fr_reg());
+      fs = get_fpu_register_double(fs_reg());
+      ft = get_fpu_register_double(ft_reg());
+      set_fpu_register_double(fd_reg(), fs * ft + fr);
+      break;
+    default:
+      UNREACHABLE();
+  }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL() {
+  int64_t alu_out = 0x12345678;
+  int64_t i64hilo = 0;
+  uint64_t u64hilo = 0;
+  bool do_interrupt = false;
+
+  switch (get_instr()->FunctionFieldRaw()) {
+    case SELEQZ_S:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_register(rd_reg(), rt() == 0 ? rs() : 0);
+      break;
+    case SELNEZ_S:
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      set_register(rd_reg(), rt() != 0 ? rs() : 0);
+      break;
+    case JR: {
+      int32_t next_pc = rs();
+      int32_t current_pc = get_pc();
+      Instruction* branch_delay_instr =
+          reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
+      BranchDelayInstructionDecode(branch_delay_instr);
+      set_pc(next_pc);
+      pc_modified_ = true;
+      break;
+    }
+    case JALR: {
+      int32_t next_pc = rs();
+      int32_t return_addr_reg = rd_reg();
+      int32_t current_pc = get_pc();
+      Instruction* branch_delay_instr =
+          reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
+      BranchDelayInstructionDecode(branch_delay_instr);
+      set_register(return_addr_reg, current_pc + 2 * Instruction::kInstrSize);
+      set_pc(next_pc);
+      pc_modified_ = true;
+      break;
+    }
+    case SLL:
+      alu_out = rt() << sa();
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    case SRL:
+      if (rs_reg() == 0) {
+        // Regular logical right shift of a word by a fixed number of
+        // bits instruction. RS field is always equal to 0.
+        alu_out = rt_u() >> sa();
+      } else {
+        // Logical right-rotate of a word by a fixed number of bits. This
+        // is special case of SRL instruction, added in MIPS32 Release 2.
+        // RS field is equal to 00001.
+        alu_out = base::bits::RotateRight32(rt_u(), sa());
+      }
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    case SRA:
+      alu_out = rt() >> sa();
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    case SLLV:
+      alu_out = rt() << rs();
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    case SRLV:
+      if (sa() == 0) {
+        // Regular logical right-shift of a word by a variable number of
+        // bits instruction. SA field is always equal to 0.
+        alu_out = rt_u() >> rs();
+      } else {
+        // Logical right-rotate of a word by a variable number of bits.
+        // This is special case od SRLV instruction, added in MIPS32
+        // Release 2. SA field is equal to 00001.
+        alu_out = base::bits::RotateRight32(rt_u(), rs_u());
+      }
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    case SRAV:
+      SetResult(rd_reg(), rt() >> rs());
+      break;
+    case LSA: {
+      DCHECK(IsMipsArchVariant(kMips32r6));
+      int8_t sa = lsa_sa() + 1;
+      int32_t _rt = rt();
+      int32_t _rs = rs();
+      int32_t res = _rs << sa;
+      res += _rt;
+      DCHECK_EQ(res, (rs() << (lsa_sa() + 1)) + rt());
+      SetResult(rd_reg(), (rs() << (lsa_sa() + 1)) + rt());
+      break;
+    }
+    case MFHI:  // MFHI == CLZ on R6.
+      if (!IsMipsArchVariant(kMips32r6)) {
+        DCHECK(sa() == 0);
+        alu_out = get_register(HI);
+      } else {
+        // MIPS spec: If no bits were set in GPR rs, the result written to
+        // GPR rd is 32.
+        DCHECK(sa() == 1);
+        alu_out = base::bits::CountLeadingZeros32(rs_u());
+      }
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    case MFLO:
+      alu_out = get_register(LO);
+      SetResult(rd_reg(), static_cast<int32_t>(alu_out));
+      break;
+    // Instructions using HI and LO registers.
+    case MULT:
+      i64hilo = static_cast<int64_t>(rs()) * static_cast<int64_t>(rt());
+      if (!IsMipsArchVariant(kMips32r6)) {
+        set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
+        set_register(HI, static_cast<int32_t>(i64hilo >> 32));
+      } else {
+        switch (sa()) {
+          case MUL_OP:
+            set_register(rd_reg(), static_cast<int32_t>(i64hilo & 0xffffffff));
+            break;
+          case MUH_OP:
+            set_register(rd_reg(), static_cast<int32_t>(i64hilo >> 32));
+            break;
+          default:
+            UNIMPLEMENTED_MIPS();
+            break;
+        }
+      }
+      break;
+    case MULTU:
+      u64hilo = static_cast<uint64_t>(rs_u()) * static_cast<uint64_t>(rt_u());
+      if (!IsMipsArchVariant(kMips32r6)) {
+        set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
+        set_register(HI, static_cast<int32_t>(u64hilo >> 32));
+      } else {
+        switch (sa()) {
+          case MUL_OP:
+            set_register(rd_reg(), static_cast<int32_t>(u64hilo & 0xffffffff));
+            break;
+          case MUH_OP:
+            set_register(rd_reg(), static_cast<int32_t>(u64hilo >> 32));
+            break;
+          default:
+            UNIMPLEMENTED_MIPS();
+            break;
+        }
+      }
+      break;
+    case DIV:
+      if (IsMipsArchVariant(kMips32r6)) {
+        switch (get_instr()->SaValue()) {
+          case DIV_OP:
+            if (rs() == INT_MIN && rt() == -1) {
+              set_register(rd_reg(), INT_MIN);
+            } else if (rt() != 0) {
+              set_register(rd_reg(), rs() / rt());
+            }
+            break;
+          case MOD_OP:
+            if (rs() == INT_MIN && rt() == -1) {
+              set_register(rd_reg(), 0);
+            } else if (rt() != 0) {
+              set_register(rd_reg(), rs() % rt());
+            }
+            break;
+          default:
+            UNIMPLEMENTED_MIPS();
+            break;
+        }
+      } else {
+        // Divide by zero and overflow was not checked in the
+        // configuration step - div and divu do not raise exceptions. On
+        // division by 0 the result will be UNPREDICTABLE. On overflow
+        // (INT_MIN/-1), return INT_MIN which is what the hardware does.
+        if (rs() == INT_MIN && rt() == -1) {
+          set_register(LO, INT_MIN);
+          set_register(HI, 0);
+        } else if (rt() != 0) {
+          set_register(LO, rs() / rt());
+          set_register(HI, rs() % rt());
+        }
+      }
+      break;
+    case DIVU:
+      if (IsMipsArchVariant(kMips32r6)) {
+        switch (get_instr()->SaValue()) {
+          case DIV_OP:
+            if (rt_u() != 0) {
+              set_register(rd_reg(), rs_u() / rt_u());
+            }
+            break;
+          case MOD_OP:
+            if (rt_u() != 0) {
+              set_register(rd_reg(), rs_u() % rt_u());
+            }
+            break;
+          default:
+            UNIMPLEMENTED_MIPS();
+            break;
+        }
+      } else {
+        if (rt_u() != 0) {
+          set_register(LO, rs_u() / rt_u());
+          set_register(HI, rs_u() % rt_u());
+        }
+      }
+      break;
+    case ADD:
+      if (HaveSameSign(rs(), rt())) {
+        if (rs() > 0) {
+          if (rs() <= (Registers::kMaxValue - rt())) {
+            SignalException(kIntegerOverflow);
+          }
+        } else if (rs() < 0) {
+          if (rs() >= (Registers::kMinValue - rt())) {
+            SignalException(kIntegerUnderflow);
+          }
+        }
+      }
+      SetResult(rd_reg(), rs() + rt());
+      break;
+    case ADDU:
+      SetResult(rd_reg(), rs() + rt());
+      break;
+    case SUB:
+      if (!HaveSameSign(rs(), rt())) {
+        if (rs() > 0) {
+          if (rs() <= (Registers::kMaxValue + rt())) {
+            SignalException(kIntegerOverflow);
+          }
+        } else if (rs() < 0) {
+          if (rs() >= (Registers::kMinValue + rt())) {
+            SignalException(kIntegerUnderflow);
+          }
+        }
+      }
+      SetResult(rd_reg(), rs() - rt());
+      break;
+    case SUBU:
+      SetResult(rd_reg(), rs() - rt());
+      break;
+    case AND:
+      SetResult(rd_reg(), rs() & rt());
+      break;
+    case OR:
+      SetResult(rd_reg(), rs() | rt());
+      break;
+    case XOR:
+      SetResult(rd_reg(), rs() ^ rt());
+      break;
+    case NOR:
+      SetResult(rd_reg(), ~(rs() | rt()));
+      break;
+    case SLT:
+      SetResult(rd_reg(), rs() < rt() ? 1 : 0);
+      break;
+    case SLTU:
+      SetResult(rd_reg(), rs_u() < rt_u() ? 1 : 0);
+      break;
+    // Break and trap instructions.
+    case BREAK:
+      do_interrupt = true;
+      break;
+    case TGE:
+      do_interrupt = rs() >= rt();
+      break;
+    case TGEU:
+      do_interrupt = rs_u() >= rt_u();
+      break;
+    case TLT:
+      do_interrupt = rs() < rt();
+      break;
+    case TLTU:
+      do_interrupt = rs_u() < rt_u();
+      break;
+    case TEQ:
+      do_interrupt = rs() == rt();
+      break;
+    case TNE:
+      do_interrupt = rs() != rt();
+      break;
+    // Conditional moves.
+    case MOVN:
+      if (rt()) {
+        set_register(rd_reg(), rs());
+        TraceRegWr(rs());
+      }
+      break;
+    case MOVCI: {
+      uint32_t cc = get_instr()->FBccValue();
+      uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
+      if (get_instr()->Bit(16)) {  // Read Tf bit.
+        if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg(), rs());
+      } else {
+        if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg(), rs());
+      }
+      break;
+    }
+    case MOVZ:
+      if (!rt()) {
+        set_register(rd_reg(), rs());
+        TraceRegWr(rs());
+      }
+      break;
+    default:
+      UNREACHABLE();
+  }
+  if (do_interrupt) {
+    SoftwareInterrupt(get_instr());
+  }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL2() {
+  int32_t alu_out;
+  switch (get_instr()->FunctionFieldRaw()) {
+    case MUL:
+      // Only the lower 32 bits are kept.
+      alu_out = rs_u() * rt_u();
+      // HI and LO are UNPREDICTABLE after the operation.
+      set_register(LO, Unpredictable);
+      set_register(HI, Unpredictable);
+      break;
+    case CLZ:
+      // MIPS32 spec: If no bits were set in GPR rs, the result written to
+      // GPR rd is 32.
+      alu_out = base::bits::CountLeadingZeros32(rs_u());
+      break;
+    default:
+      alu_out = 0x12345678;
+      UNREACHABLE();
+  }
+  SetResult(rd_reg(), alu_out);
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL3() {
+  int32_t alu_out;
+  switch (get_instr()->FunctionFieldRaw()) {
+    case INS: {  // Mips32r2 instruction.
+      // Interpret rd field as 5-bit msb of insert.
+      uint16_t msb = rd_reg();
+      // Interpret sa field as 5-bit lsb of insert.
+      uint16_t lsb = sa();
+      uint16_t size = msb - lsb + 1;
+      uint32_t mask = (1 << size) - 1;
+      alu_out = (rt_u() & ~(mask << lsb)) | ((rs_u() & mask) << lsb);
+      // Ins instr leaves result in Rt, rather than Rd.
+      SetResult(rt_reg(), alu_out);
+      break;
+    }
+    case EXT: {  // Mips32r2 instruction.
+      // Interpret rd field as 5-bit msb of extract.
+      uint16_t msb = rd_reg();
+      // Interpret sa field as 5-bit lsb of extract.
+      uint16_t lsb = sa();
+      uint16_t size = msb + 1;
+      uint32_t mask = (1 << size) - 1;
+      alu_out = (rs_u() & (mask << lsb)) >> lsb;
+      SetResult(rt_reg(), alu_out);
+      break;
+    }
+    case BSHFL: {
+      int sa = get_instr()->SaFieldRaw() >> kSaShift;
+      switch (sa) {
+        case BITSWAP: {
+          uint32_t input = static_cast<uint32_t>(rt());
+          uint32_t output = 0;
+          uint8_t i_byte, o_byte;
+
+          // Reverse the bit in byte for each individual byte
+          for (int i = 0; i < 4; i++) {
+            output = output >> 8;
+            i_byte = input & 0xff;
+
+            // Fast way to reverse bits in byte
+            // Devised by Sean Anderson, July 13, 2001
+            o_byte = static_cast<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) |
+                                           (i_byte * 0x8020LU & 0x88440LU)) *
+                                              0x10101LU >>
+                                          16);
+
+            output = output | (static_cast<uint32_t>(o_byte << 24));
+            input = input >> 8;
+          }
+
+          alu_out = static_cast<int32_t>(output);
+          break;
+        }
+        case SEB:
+        case SEH:
+        case WSBH:
+          alu_out = 0x12345678;
+          UNREACHABLE();
+          break;
+        default: {
+          const uint8_t bp = get_instr()->Bp2Value();
+          sa >>= kBp2Bits;
+          switch (sa) {
+            case ALIGN: {
+              if (bp == 0) {
+                alu_out = static_cast<int32_t>(rt());
+              } else {
+                uint32_t rt_hi = rt() << (8 * bp);
+                uint32_t rs_lo = rs() >> (8 * (4 - bp));
+                alu_out = static_cast<int32_t>(rt_hi | rs_lo);
+              }
+              break;
+            }
+            default:
+              alu_out = 0x12345678;
+              UNREACHABLE();
+              break;
+          }
+        }
+      }
+      SetResult(rd_reg(), alu_out);
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+}
+
+
+void Simulator::DecodeTypeRegister(Instruction* instr) {
+  const Opcode op = instr->OpcodeFieldRaw();
+
+  // Set up the variables if needed before executing the instruction.
+  //  ConfigureTypeRegister(instr);
+  set_instr(instr);
+
+  // ---------- Execution.
+  switch (op) {
+    case COP1:
+      DecodeTypeRegisterCOP1();
+      break;
+    case COP1X:
+      DecodeTypeRegisterCOP1X();
+      break;
+    case SPECIAL:
+      DecodeTypeRegisterSPECIAL();
+      break;
+    case SPECIAL2:
+      DecodeTypeRegisterSPECIAL2();
+      break;
+    case SPECIAL3:
+      DecodeTypeRegisterSPECIAL3();
+      break;
+    default:
+      UNREACHABLE();
+  }
+}
+
+
+// Type 2: instructions using a 16, 21 or 26 bits immediate. (e.g. beq, beqc).
 void Simulator::DecodeTypeImmediate(Instruction* instr) {
   // Instruction fields.
-  Opcode   op     = instr->OpcodeFieldRaw();
-  int32_t  rs     = get_register(instr->RsValue());
-  uint32_t rs_u   = static_cast<uint32_t>(rs);
-  int32_t  rt_reg = instr->RtValue();  // Destination register.
-  int32_t  rt     = get_register(rt_reg);
-  int16_t  imm16  = instr->Imm16Value();
+  Opcode op = instr->OpcodeFieldRaw();
+  int32_t rs_reg = instr->RsValue();
+  int32_t rs = get_register(instr->RsValue());
+  uint32_t rs_u = static_cast<uint32_t>(rs);
+  int32_t rt_reg = instr->RtValue();  // Destination register.
+  int32_t rt = get_register(rt_reg);
+  int16_t imm16 = instr->Imm16Value();
 
-  int32_t  ft_reg = instr->FtValue();  // Destination register.
-  int64_t  ft;
+  int32_t ft_reg = instr->FtValue();  // Destination register.
 
   // Zero extended immediate.
-  uint32_t  oe_imm16 = 0xffff & imm16;
+  uint32_t oe_imm16 = 0xffff & imm16;
   // Sign extended immediate.
-  int32_t   se_imm16 = imm16;
+  int32_t se_imm16 = imm16;
 
-  // Get current pc.
-  int32_t current_pc = get_pc();
   // Next pc.
   int32_t next_pc = bad_ra;
 
   // Used for conditional branch instructions.
-  bool do_branch = false;
   bool execute_branch_delay_instruction = false;
 
   // Used for arithmetic instructions.
   int32_t alu_out = 0;
-  // Floating point.
-  double fp_out = 0.0;
-  uint32_t cc, cc_value, fcsr_cc;
 
   // Used for memory instructions.
   int32_t addr = 0x0;
-  // Value to be written in memory.
-  uint32_t mem_value = 0x0;
 
-  // ---------- Configuration (and execution for REGIMM).
+  // Branch instructions common part.
+  auto BranchAndLinkHelper = [this, instr, &next_pc,
+                              &execute_branch_delay_instruction](
+      bool do_branch) {
+    execute_branch_delay_instruction = true;
+    int32_t current_pc = get_pc();
+    if (do_branch) {
+      int16_t imm16 = instr->Imm16Value();
+      next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
+      set_register(31, current_pc + 2 * Instruction::kInstrSize);
+    } else {
+      next_pc = current_pc + 2 * Instruction::kInstrSize;
+    }
+  };
+
+  auto BranchHelper = [this, instr, &next_pc,
+                       &execute_branch_delay_instruction](bool do_branch) {
+    execute_branch_delay_instruction = true;
+    int32_t current_pc = get_pc();
+    if (do_branch) {
+      int16_t imm16 = instr->Imm16Value();
+      next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
+    } else {
+      next_pc = current_pc + 2 * Instruction::kInstrSize;
+    }
+  };
+
+  auto BranchAndLinkCompactHelper = [this, instr, &next_pc](bool do_branch,
+                                                            int bits) {
+    int32_t current_pc = get_pc();
+    CheckForbiddenSlot(current_pc);
+    if (do_branch) {
+      int32_t imm = instr->ImmValue(bits);
+      imm <<= 32 - bits;
+      imm >>= 32 - bits;
+      next_pc = current_pc + (imm << 2) + Instruction::kInstrSize;
+      set_register(31, current_pc + Instruction::kInstrSize);
+    }
+  };
+
+  auto BranchCompactHelper = [&next_pc, this, instr](bool do_branch, int bits) {
+    int32_t current_pc = get_pc();
+    CheckForbiddenSlot(current_pc);
+    if (do_branch) {
+      int32_t imm = instr->ImmValue(bits);
+      imm <<= 32 - bits;
+      imm >>= 32 - bits;
+      next_pc = get_pc() + (imm << 2) + Instruction::kInstrSize;
+    }
+  };
+
+
   switch (op) {
     // ------------- COP1. Coprocessor instructions.
     case COP1:
       switch (instr->RsFieldRaw()) {
-        case BC1:   // Branch on coprocessor condition.
-          cc = instr->FBccValue();
-          fcsr_cc = get_fcsr_condition_bit(cc);
-          cc_value = test_fcsr_bit(fcsr_cc);
-          do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value;
-          execute_branch_delay_instruction = true;
-          // Set next_pc.
-          if (do_branch) {
-            next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
-          } else {
-            next_pc = current_pc + kBranchReturnOffset;
-          }
+        case BC1: {  // Branch on coprocessor condition.
+          // Floating point.
+          uint32_t cc = instr->FBccValue();
+          uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
+          uint32_t cc_value = test_fcsr_bit(fcsr_cc);
+          bool do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value;
+          BranchHelper(do_branch);
           break;
+        }
         case BC1EQZ:
-          ft = get_fpu_register(ft_reg);
-          do_branch = (ft & 0x1) ? false : true;
-          execute_branch_delay_instruction = true;
-          // Set next_pc.
-          if (do_branch) {
-            next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
-          } else {
-            next_pc = current_pc + kBranchReturnOffset;
-          }
+          BranchHelper(!(get_fpu_register(ft_reg) & 0x1));
           break;
         case BC1NEZ:
-          ft = get_fpu_register(ft_reg);
-          do_branch = (ft & 0x1) ? true : false;
-          execute_branch_delay_instruction = true;
-          // Set next_pc.
-          if (do_branch) {
-            next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
-          } else {
-            next_pc = current_pc + kBranchReturnOffset;
-          }
+          BranchHelper(get_fpu_register(ft_reg) & 0x1);
           break;
         default:
           UNREACHABLE();
@@ -2759,96 +3998,213 @@
     case REGIMM:
       switch (instr->RtFieldRaw()) {
         case BLTZ:
-          do_branch = (rs  < 0);
-          break;
-        case BLTZAL:
-          do_branch = rs  < 0;
+          BranchHelper(rs < 0);
           break;
         case BGEZ:
-          do_branch = rs >= 0;
+          BranchHelper(rs >= 0);
+          break;
+        case BLTZAL:
+          BranchAndLinkHelper(rs < 0);
           break;
         case BGEZAL:
-          do_branch = rs >= 0;
+          BranchAndLinkHelper(rs >= 0);
           break;
         default:
           UNREACHABLE();
       }
-      switch (instr->RtFieldRaw()) {
-        case BLTZ:
-        case BLTZAL:
-        case BGEZ:
-        case BGEZAL:
-          // Branch instructions common part.
-          execute_branch_delay_instruction = true;
-          // Set next_pc.
-          if (do_branch) {
-            next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
-            if (instr->IsLinkingInstruction()) {
-              set_register(31, current_pc + kBranchReturnOffset);
-            }
-          } else {
-            next_pc = current_pc + kBranchReturnOffset;
-          }
-        default:
-          break;
-        }
-    break;  // case REGIMM.
+      break;  // case REGIMM.
     // ------------- Branch instructions.
     // When comparing to zero, the encoding of rt field is always 0, so we don't
     // need to replace rt with zero.
     case BEQ:
-      do_branch = (rs == rt);
+      BranchHelper(rs == rt);
       break;
     case BNE:
-      do_branch = rs != rt;
+      BranchHelper(rs != rt);
       break;
-    case BLEZ:
-      do_branch = rs <= 0;
+    case POP06:  // BLEZALC, BGEZALC, BGEUC, BLEZ (pre-r6)
+      if (IsMipsArchVariant(kMips32r6)) {
+        if (rt_reg != 0) {
+          if (rs_reg == 0) {  // BLEZALC
+            BranchAndLinkCompactHelper(rt <= 0, 16);
+          } else {
+            if (rs_reg == rt_reg) {  // BGEZALC
+              BranchAndLinkCompactHelper(rt >= 0, 16);
+            } else {  // BGEUC
+              BranchCompactHelper(
+                  static_cast<uint32_t>(rs) >= static_cast<uint32_t>(rt), 16);
+            }
+          }
+        } else {  // BLEZ
+          BranchHelper(rs <= 0);
+        }
+      } else {  // BLEZ
+        BranchHelper(rs <= 0);
+      }
       break;
-    case BGTZ:
-      do_branch = rs  > 0;
+    case POP07:  // BGTZALC, BLTZALC, BLTUC, BGTZ (pre-r6)
+      if (IsMipsArchVariant(kMips32r6)) {
+        if (rt_reg != 0) {
+          if (rs_reg == 0) {  // BGTZALC
+            BranchAndLinkCompactHelper(rt > 0, 16);
+          } else {
+            if (rt_reg == rs_reg) {  // BLTZALC
+              BranchAndLinkCompactHelper(rt < 0, 16);
+            } else {  // BLTUC
+              BranchCompactHelper(
+                  static_cast<uint32_t>(rs) < static_cast<uint32_t>(rt), 16);
+            }
+          }
+        } else {  // BGTZ
+          BranchHelper(rs > 0);
+        }
+      } else {  // BGTZ
+        BranchHelper(rs > 0);
+      }
       break;
-    // ------------- Arithmetic instructions.
-    case ADDI:
-      if (HaveSameSign(rs, se_imm16)) {
-        if (rs > 0) {
-          exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue - se_imm16);
-        } else if (rs < 0) {
-          exceptions[kIntegerUnderflow] =
-              rs < (Registers::kMinValue - se_imm16);
+    case POP26:  // BLEZC, BGEZC, BGEC/BLEC / BLEZL (pre-r6)
+      if (IsMipsArchVariant(kMips32r6)) {
+        if (rt_reg != 0) {
+          if (rs_reg == 0) {  // BLEZC
+            BranchCompactHelper(rt <= 0, 16);
+          } else {
+            if (rs_reg == rt_reg) {  // BGEZC
+              BranchCompactHelper(rt >= 0, 16);
+            } else {  // BGEC/BLEC
+              BranchCompactHelper(rs >= rt, 16);
+            }
+          }
+        }
+      } else {  // BLEZL
+        BranchAndLinkHelper(rs <= 0);
+      }
+      break;
+    case POP27:  // BGTZC, BLTZC, BLTC/BGTC / BGTZL (pre-r6)
+      if (IsMipsArchVariant(kMips32r6)) {
+        if (rt_reg != 0) {
+          if (rs_reg == 0) {  // BGTZC
+            BranchCompactHelper(rt > 0, 16);
+          } else {
+            if (rs_reg == rt_reg) {  // BLTZC
+              BranchCompactHelper(rt < 0, 16);
+            } else {  // BLTC/BGTC
+              BranchCompactHelper(rs < rt, 16);
+            }
+          }
+        }
+      } else {  // BGTZL
+        BranchAndLinkHelper(rs > 0);
+      }
+      break;
+    case POP66:           // BEQZC, JIC
+      if (rs_reg != 0) {  // BEQZC
+        BranchCompactHelper(rs == 0, 21);
+      } else {  // JIC
+        next_pc = rt + imm16;
+      }
+      break;
+    case POP76:           // BNEZC, JIALC
+      if (rs_reg != 0) {  // BNEZC
+        BranchCompactHelper(rs != 0, 21);
+      } else {  // JIALC
+        set_register(31, get_pc() + Instruction::kInstrSize);
+        next_pc = rt + imm16;
+      }
+      break;
+    case BC:
+      BranchCompactHelper(true, 26);
+      break;
+    case BALC:
+      BranchAndLinkCompactHelper(true, 26);
+      break;
+    case POP10:  // BOVC, BEQZALC, BEQC / ADDI (pre-r6)
+      if (IsMipsArchVariant(kMips32r6)) {
+        if (rs_reg >= rt_reg) {  // BOVC
+          if (HaveSameSign(rs, rt)) {
+            if (rs > 0) {
+              BranchCompactHelper(rs > Registers::kMaxValue - rt, 16);
+            } else if (rs < 0) {
+              BranchCompactHelper(rs < Registers::kMinValue - rt, 16);
+            }
+          }
+        } else {
+          if (rs_reg == 0) {  // BEQZALC
+            BranchAndLinkCompactHelper(rt == 0, 16);
+          } else {  // BEQC
+            BranchCompactHelper(rt == rs, 16);
+          }
+        }
+      } else {  // ADDI
+        if (HaveSameSign(rs, se_imm16)) {
+          if (rs > 0) {
+            if (rs <= Registers::kMaxValue - se_imm16) {
+              SignalException(kIntegerOverflow);
+            }
+          } else if (rs < 0) {
+            if (rs >= Registers::kMinValue - se_imm16) {
+              SignalException(kIntegerUnderflow);
+            }
+          }
+        }
+        SetResult(rt_reg, rs + se_imm16);
+      }
+      break;
+    case POP30:  // BNVC, BNEZALC, BNEC / DADDI (pre-r6)
+      if (IsMipsArchVariant(kMips32r6)) {
+        if (rs_reg >= rt_reg) {  // BNVC
+          if (!HaveSameSign(rs, rt) || rs == 0 || rt == 0) {
+            BranchCompactHelper(true, 16);
+          } else {
+            if (rs > 0) {
+              BranchCompactHelper(rs <= Registers::kMaxValue - rt, 16);
+            } else if (rs < 0) {
+              BranchCompactHelper(rs >= Registers::kMinValue - rt, 16);
+            }
+          }
+        } else {
+          if (rs_reg == 0) {  // BNEZALC
+            BranchAndLinkCompactHelper(rt != 0, 16);
+          } else {  // BNEC
+            BranchCompactHelper(rt != rs, 16);
+          }
         }
       }
-      alu_out = rs + se_imm16;
       break;
+    // ------------- Arithmetic instructions.
     case ADDIU:
-      alu_out = rs + se_imm16;
+      SetResult(rt_reg, rs + se_imm16);
       break;
     case SLTI:
-      alu_out = (rs < se_imm16) ? 1 : 0;
+      SetResult(rt_reg, rs < se_imm16 ? 1 : 0);
       break;
     case SLTIU:
-      alu_out = (rs_u < static_cast<uint32_t>(se_imm16)) ? 1 : 0;
+      SetResult(rt_reg, rs_u < static_cast<uint32_t>(se_imm16) ? 1 : 0);
       break;
     case ANDI:
-        alu_out = rs & oe_imm16;
+      SetResult(rt_reg, rs & oe_imm16);
       break;
     case ORI:
-        alu_out = rs | oe_imm16;
+      SetResult(rt_reg, rs | oe_imm16);
       break;
     case XORI:
-        alu_out = rs ^ oe_imm16;
+      SetResult(rt_reg, rs ^ oe_imm16);
       break;
     case LUI:
-        alu_out = (oe_imm16 << 16);
+      if (rs_reg != 0) {
+        // AUI
+        DCHECK(IsMipsArchVariant(kMips32r6));
+        SetResult(rt_reg, rs + (se_imm16 << 16));
+      } else {
+        // LUI
+        SetResult(rt_reg, oe_imm16 << 16);
+      }
       break;
     // ------------- Memory instructions.
     case LB:
-      addr = rs + se_imm16;
-      alu_out = ReadB(addr);
+      set_register(rt_reg, ReadB(rs + se_imm16));
       break;
     case LH:
-      addr = rs + se_imm16;
-      alu_out = ReadH(addr, instr);
+      set_register(rt_reg, ReadH(rs + se_imm16, instr));
       break;
     case LWL: {
       // al_offset is offset of the effective address within an aligned word.
@@ -2859,19 +4215,17 @@
       alu_out = ReadW(addr, instr);
       alu_out <<= byte_shift * 8;
       alu_out |= rt & mask;
+      set_register(rt_reg, alu_out);
       break;
     }
     case LW:
-      addr = rs + se_imm16;
-      alu_out = ReadW(addr, instr);
+      set_register(rt_reg, ReadW(rs + se_imm16, instr));
       break;
     case LBU:
-      addr = rs + se_imm16;
-      alu_out = ReadBU(addr);
+      set_register(rt_reg, ReadBU(rs + se_imm16));
       break;
     case LHU:
-      addr = rs + se_imm16;
-      alu_out = ReadHU(addr, instr);
+      set_register(rt_reg, ReadHU(rs + se_imm16, instr));
       break;
     case LWR: {
       // al_offset is offset of the effective address within an aligned word.
@@ -2882,134 +4236,103 @@
       alu_out = ReadW(addr, instr);
       alu_out = static_cast<uint32_t> (alu_out) >> al_offset * 8;
       alu_out |= rt & mask;
+      set_register(rt_reg, alu_out);
       break;
     }
     case SB:
-      addr = rs + se_imm16;
+      WriteB(rs + se_imm16, static_cast<int8_t>(rt));
       break;
     case SH:
-      addr = rs + se_imm16;
+      WriteH(rs + se_imm16, static_cast<uint16_t>(rt), instr);
       break;
     case SWL: {
       uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
       uint8_t byte_shift = kPointerAlignmentMask - al_offset;
       uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0;
       addr = rs + se_imm16 - al_offset;
-      mem_value = ReadW(addr, instr) & mask;
+      // Value to be written in memory.
+      uint32_t mem_value = ReadW(addr, instr) & mask;
       mem_value |= static_cast<uint32_t>(rt) >> byte_shift * 8;
+      WriteW(addr, mem_value, instr);
       break;
     }
     case SW:
-      addr = rs + se_imm16;
+      WriteW(rs + se_imm16, rt, instr);
       break;
     case SWR: {
       uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
       uint32_t mask = (1 << al_offset * 8) - 1;
       addr = rs + se_imm16 - al_offset;
-      mem_value = ReadW(addr, instr);
+      uint32_t mem_value = ReadW(addr, instr);
       mem_value = (rt << al_offset * 8) | (mem_value & mask);
+      WriteW(addr, mem_value, instr);
       break;
     }
     case LWC1:
-      addr = rs + se_imm16;
-      alu_out = ReadW(addr, instr);
+      set_fpu_register_hi_word(ft_reg, 0);
+      set_fpu_register_word(ft_reg, ReadW(rs + se_imm16, instr));
       break;
     case LDC1:
-      addr = rs + se_imm16;
-      fp_out = ReadD(addr, instr);
+      set_fpu_register_double(ft_reg, ReadD(rs + se_imm16, instr));
       break;
     case SWC1:
-    case SDC1:
-      addr = rs + se_imm16;
+      WriteW(rs + se_imm16, get_fpu_register_word(ft_reg), instr);
       break;
+    case SDC1:
+      WriteD(rs + se_imm16, get_fpu_register_double(ft_reg), instr);
+      break;
+    // ------------- PC-Relative instructions.
+    case PCREL: {
+      // rt field: checking 5-bits.
+      int32_t imm21 = instr->Imm21Value();
+      int32_t current_pc = get_pc();
+      uint8_t rt = (imm21 >> kImm16Bits);
+      switch (rt) {
+        case ALUIPC:
+          addr = current_pc + (se_imm16 << 16);
+          alu_out = static_cast<int64_t>(~0x0FFFF) & addr;
+          break;
+        case AUIPC:
+          alu_out = current_pc + (se_imm16 << 16);
+          break;
+        default: {
+          int32_t imm19 = instr->Imm19Value();
+          // rt field: checking the most significant 2-bits.
+          rt = (imm21 >> kImm19Bits);
+          switch (rt) {
+            case LWPC: {
+              // Set sign.
+              imm19 <<= (kOpcodeBits + kRsBits + 2);
+              imm19 >>= (kOpcodeBits + kRsBits + 2);
+              addr = current_pc + (imm19 << 2);
+              uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
+              alu_out = *ptr;
+              break;
+            }
+            case ADDIUPC: {
+              int32_t se_imm19 = imm19 | ((imm19 & 0x40000) ? 0xfff80000 : 0);
+              alu_out = current_pc + (se_imm19 << 2);
+              break;
+            }
+            default:
+              UNREACHABLE();
+              break;
+          }
+        }
+      }
+      set_register(rs_reg, alu_out);
+      break;
+    }
     default:
       UNREACHABLE();
   }
 
-  // ---------- Raise exceptions triggered.
-  SignalExceptions();
-
-  // ---------- Execution.
-  switch (op) {
-    // ------------- Branch instructions.
-    case BEQ:
-    case BNE:
-    case BLEZ:
-    case BGTZ:
-      // Branch instructions common part.
-      execute_branch_delay_instruction = true;
-      // Set next_pc.
-      if (do_branch) {
-        next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
-        if (instr->IsLinkingInstruction()) {
-          set_register(31, current_pc + 2* Instruction::kInstrSize);
-        }
-      } else {
-        next_pc = current_pc + 2 * Instruction::kInstrSize;
-      }
-      break;
-    // ------------- Arithmetic instructions.
-    case ADDI:
-    case ADDIU:
-    case SLTI:
-    case SLTIU:
-    case ANDI:
-    case ORI:
-    case XORI:
-    case LUI:
-      set_register(rt_reg, alu_out);
-      break;
-    // ------------- Memory instructions.
-    case LB:
-    case LH:
-    case LWL:
-    case LW:
-    case LBU:
-    case LHU:
-    case LWR:
-      set_register(rt_reg, alu_out);
-      break;
-    case SB:
-      WriteB(addr, static_cast<int8_t>(rt));
-      break;
-    case SH:
-      WriteH(addr, static_cast<uint16_t>(rt), instr);
-      break;
-    case SWL:
-      WriteW(addr, mem_value, instr);
-      break;
-    case SW:
-      WriteW(addr, rt, instr);
-      break;
-    case SWR:
-      WriteW(addr, mem_value, instr);
-      break;
-    case LWC1:
-      set_fpu_register_hi_word(ft_reg, 0);
-      set_fpu_register_word(ft_reg, alu_out);
-      break;
-    case LDC1:
-      set_fpu_register_double(ft_reg, fp_out);
-      break;
-    case SWC1:
-      addr = rs + se_imm16;
-      WriteW(addr, get_fpu_register_word(ft_reg), instr);
-      break;
-    case SDC1:
-      addr = rs + se_imm16;
-      WriteD(addr, get_fpu_register_double(ft_reg), instr);
-      break;
-    default:
-      break;
-  }
-
-
   if (execute_branch_delay_instruction) {
     // Execute branch delay slot
     // We don't check for end_sim_pc. First it should not be met as the current
     // pc is valid. Secondly a jump should always execute its branch delay slot.
     Instruction* branch_delay_instr =
-      reinterpret_cast<Instruction*>(current_pc+Instruction::kInstrSize);
+        reinterpret_cast<Instruction*>(get_pc() + Instruction::kInstrSize);
     BranchDelayInstructionDecode(branch_delay_instr);
   }
 
@@ -3052,17 +4375,15 @@
     CheckICache(isolate_->simulator_i_cache(), instr);
   }
   pc_modified_ = false;
+  v8::internal::EmbeddedVector<char, 256> buffer;
   if (::v8::internal::FLAG_trace_sim) {
+    SNPrintF(trace_buf_, "%s", "");
     disasm::NameConverter converter;
     disasm::Disassembler dasm(converter);
-    // Use a reasonably large buffer.
-    v8::internal::EmbeddedVector<char, 256> buffer;
     dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr));
-    PrintF("  0x%08x  %s\n", reinterpret_cast<intptr_t>(instr),
-        buffer.start());
   }
 
-  switch (instr->InstructionType()) {
+  switch (instr->InstructionType(Instruction::TypeChecks::EXTRA)) {
     case Instruction::kRegisterType:
       DecodeTypeRegister(instr);
       break;
@@ -3075,6 +4396,10 @@
     default:
       UNSUPPORTED();
   }
+  if (::v8::internal::FLAG_trace_sim) {
+    PrintF("  0x%08x  %-44s   %s\n", reinterpret_cast<intptr_t>(instr),
+           buffer.start(), trace_buf_.start());
+  }
   if (!pc_modified_) {
     set_register(pc, reinterpret_cast<int32_t>(instr) +
                  Instruction::kInstrSize);
@@ -3102,7 +4427,7 @@
     while (program_counter != end_sim_pc) {
       Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
       icount_++;
-      if (icount_ == ::v8::internal::FLAG_stop_sim_at) {
+      if (icount_ == static_cast<uint64_t>(::v8::internal::FLAG_stop_sim_at)) {
         MipsDebugger dbg(this);
         dbg.Debug();
       } else {
@@ -3115,6 +4440,9 @@
 
 
 void Simulator::CallInternal(byte* entry) {
+  // Adjust JS-based stack limit to C-based stack limit.
+  isolate_->stack_guard()->AdjustStackLimitForSimulator();
+
   // Prepare to execute the code at entry.
   set_register(pc, reinterpret_cast<int32_t>(entry));
   // Put down marker for end of simulation. The simulator will stop simulation
@@ -3261,7 +4589,8 @@
 
 #undef UNSUPPORTED
 
-} }  // namespace v8::internal
+}  // namespace internal
+}  // namespace v8
 
 #endif  // USE_SIMULATOR