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