Index: src/simulator-arm.cc |
diff --git a/src/simulator-arm.cc b/src/simulator-arm.cc |
deleted file mode 100644 |
index 7d96b3a5bc9b6761cbdca3459e5b7716d6a72b6b..0000000000000000000000000000000000000000 |
--- a/src/simulator-arm.cc |
+++ /dev/null |
@@ -1,1687 +0,0 @@ |
-// Copyright 2008 the V8 project authors. All rights reserved. |
-// Redistribution and use in source and binary forms, with or without |
-// modification, are permitted provided that the following conditions are |
-// met: |
-// |
-// * Redistributions of source code must retain the above copyright |
-// notice, this list of conditions and the following disclaimer. |
-// * Redistributions in binary form must reproduce the above |
-// copyright notice, this list of conditions and the following |
-// disclaimer in the documentation and/or other materials provided |
-// with the distribution. |
-// * Neither the name of Google Inc. nor the names of its |
-// contributors may be used to endorse or promote products derived |
-// from this software without specific prior written permission. |
-// |
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- |
-#include <stdlib.h> |
- |
-#include "v8.h" |
- |
-#include "disasm.h" |
-#include "constants-arm.h" |
-#include "simulator-arm.h" |
- |
-#if !defined(__arm__) |
- |
-// Only build the simulator if not compiling for real ARM hardware. |
-namespace assembler { namespace arm { |
- |
-using ::v8::internal::Object; |
-using ::v8::internal::PrintF; |
-using ::v8::internal::OS; |
-using ::v8::internal::ReadLine; |
-using ::v8::internal::DeleteArray; |
- |
-// This macro provides a platform independent use of sscanf. The reason for |
-// SScanF not being implemented in a platform independent was through |
-// ::v8::internal::OS in the same way as SNPrintF is that the Windows C Run-Time |
-// Library does not provide vsscanf. |
-#define SScanF sscanf // NOLINT |
- |
-// The Debugger class is used by the simulator while debugging simulated ARM |
-// code. |
-class Debugger { |
- public: |
- explicit Debugger(Simulator* sim); |
- ~Debugger(); |
- |
- void Stop(Instr* instr); |
- void Debug(); |
- |
- private: |
- static const instr_t kBreakpointInstr = |
- ((AL << 28) | (7 << 25) | (1 << 24) | break_point); |
- static const instr_t kNopInstr = |
- ((AL << 28) | (13 << 21)); |
- |
- Simulator* sim_; |
- |
- bool GetValue(char* desc, int32_t* value); |
- |
- // Set or delete a breakpoint. Returns true if successful. |
- bool SetBreakpoint(Instr* breakpc); |
- bool DeleteBreakpoint(Instr* breakpc); |
- |
- // Undo and redo all breakpoints. This is needed to bracket disassembly and |
- // execution to skip past breakpoints when run from the debugger. |
- void UndoBreakpoints(); |
- void RedoBreakpoints(); |
-}; |
- |
- |
-Debugger::Debugger(Simulator* sim) { |
- sim_ = sim; |
-} |
- |
- |
-Debugger::~Debugger() { |
-} |
- |
- |
- |
-#ifdef GENERATED_CODE_COVERAGE |
-static FILE* coverage_log = NULL; |
- |
- |
-static void InitializeCoverage() { |
- char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG"); |
- if (file_name != NULL) { |
- coverage_log = fopen(file_name, "aw+"); |
- } |
-} |
- |
- |
-void Debugger::Stop(Instr* instr) { |
- char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff); |
- if (strlen(str) > 0) { |
- if (coverage_log != NULL) { |
- fprintf(coverage_log, "%s\n", str); |
- fflush(coverage_log); |
- } |
- instr->SetInstructionBits(0xe1a00000); // Overwrite with nop. |
- } |
- sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); |
-} |
- |
-#else // ndef GENERATED_CODE_COVERAGE |
- |
-static void InitializeCoverage() { |
-} |
- |
- |
-void Debugger::Stop(Instr* instr) { |
- const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff); |
- PrintF("Simulator hit %s\n", str); |
- sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); |
- Debug(); |
-} |
-#endif |
- |
- |
-static const char* reg_names[] = { "r0", "r1", "r2", "r3", |
- "r4", "r5", "r6", "r7", |
- "r8", "r9", "r10", "r11", |
- "r12", "r13", "r14", "r15", |
- "pc", "lr", "sp", "ip", |
- "fp", "sl", ""}; |
- |
-static int reg_nums[] = { 0, 1, 2, 3, |
- 4, 5, 6, 7, |
- 8, 9, 10, 11, |
- 12, 13, 14, 15, |
- 15, 14, 13, 12, |
- 11, 10}; |
- |
- |
-static int RegNameToRegNum(char* name) { |
- int reg = 0; |
- while (*reg_names[reg] != 0) { |
- if (strcmp(reg_names[reg], name) == 0) { |
- return reg_nums[reg]; |
- } |
- reg++; |
- } |
- return -1; |
-} |
- |
- |
-bool Debugger::GetValue(char* desc, int32_t* value) { |
- int regnum = RegNameToRegNum(desc); |
- if (regnum >= 0) { |
- if (regnum == 15) { |
- *value = sim_->get_pc(); |
- } else { |
- *value = sim_->get_register(regnum); |
- } |
- return true; |
- } else { |
- return SScanF(desc, "%i", value) == 1; |
- } |
- return false; |
-} |
- |
- |
-bool Debugger::SetBreakpoint(Instr* breakpc) { |
- // Check if a breakpoint can be set. If not return without any side-effects. |
- if (sim_->break_pc_ != NULL) { |
- return false; |
- } |
- |
- // Set the breakpoint. |
- sim_->break_pc_ = breakpc; |
- sim_->break_instr_ = breakpc->InstructionBits(); |
- // Not setting the breakpoint instruction in the code itself. It will be set |
- // when the debugger shell continues. |
- return true; |
-} |
- |
- |
-bool Debugger::DeleteBreakpoint(Instr* breakpc) { |
- if (sim_->break_pc_ != NULL) { |
- sim_->break_pc_->SetInstructionBits(sim_->break_instr_); |
- } |
- |
- sim_->break_pc_ = NULL; |
- sim_->break_instr_ = 0; |
- return true; |
-} |
- |
- |
-void Debugger::UndoBreakpoints() { |
- if (sim_->break_pc_ != NULL) { |
- sim_->break_pc_->SetInstructionBits(sim_->break_instr_); |
- } |
-} |
- |
- |
-void Debugger::RedoBreakpoints() { |
- if (sim_->break_pc_ != NULL) { |
- sim_->break_pc_->SetInstructionBits(kBreakpointInstr); |
- } |
-} |
- |
- |
-void Debugger::Debug() { |
- intptr_t last_pc = -1; |
- bool done = false; |
- |
-#define COMMAND_SIZE 63 |
-#define ARG_SIZE 255 |
- |
-#define STR(a) #a |
-#define XSTR(a) STR(a) |
- |
- char cmd[COMMAND_SIZE + 1]; |
- char arg1[ARG_SIZE + 1]; |
- char arg2[ARG_SIZE + 1]; |
- |
- // make sure to have a proper terminating character if reaching the limit |
- cmd[COMMAND_SIZE] = 0; |
- arg1[ARG_SIZE] = 0; |
- arg2[ARG_SIZE] = 0; |
- |
- // Undo all set breakpoints while running in the debugger shell. This will |
- // make them invisible to all commands. |
- UndoBreakpoints(); |
- |
- while (!done) { |
- if (last_pc != sim_->get_pc()) { |
- disasm::NameConverter converter; |
- disasm::Disassembler dasm(converter); |
- // use a reasonably large buffer |
- v8::internal::EmbeddedVector<char, 256> buffer; |
- dasm.InstructionDecode(buffer, |
- reinterpret_cast<byte*>(sim_->get_pc())); |
- PrintF(" 0x%x %s\n", sim_->get_pc(), buffer.start()); |
- last_pc = sim_->get_pc(); |
- } |
- char* line = ReadLine("sim> "); |
- if (line == NULL) { |
- break; |
- } else { |
- // Use sscanf to parse the individual parts of the command line. At the |
- // moment no command expects more than two parameters. |
- int args = SScanF(line, |
- "%" XSTR(COMMAND_SIZE) "s " |
- "%" XSTR(ARG_SIZE) "s " |
- "%" XSTR(ARG_SIZE) "s", |
- cmd, arg1, arg2); |
- if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { |
- sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); |
- } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { |
- // Execute the one instruction we broke at with breakpoints disabled. |
- sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); |
- // Leave the debugger shell. |
- done = true; |
- } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { |
- if (args == 2) { |
- int32_t value; |
- if (GetValue(arg1, &value)) { |
- PrintF("%s: %d 0x%x\n", arg1, value, value); |
- } else { |
- PrintF("%s unrecognized\n", arg1); |
- } |
- } else { |
- PrintF("print value\n"); |
- } |
- } else if ((strcmp(cmd, "po") == 0) |
- || (strcmp(cmd, "printobject") == 0)) { |
- if (args == 2) { |
- int32_t value; |
- if (GetValue(arg1, &value)) { |
- Object* obj = reinterpret_cast<Object*>(value); |
- USE(obj); |
- PrintF("%s: \n", arg1); |
-#if defined(DEBUG) |
- obj->PrintLn(); |
-#endif // defined(DEBUG) |
- } else { |
- PrintF("%s unrecognized\n", arg1); |
- } |
- } else { |
- PrintF("printobject value\n"); |
- } |
- } else if (strcmp(cmd, "disasm") == 0) { |
- disasm::NameConverter converter; |
- disasm::Disassembler dasm(converter); |
- // use a reasonably large buffer |
- v8::internal::EmbeddedVector<char, 256> buffer; |
- |
- byte* cur = NULL; |
- byte* end = NULL; |
- |
- if (args == 1) { |
- cur = reinterpret_cast<byte*>(sim_->get_pc()); |
- end = cur + (10 * Instr::kInstrSize); |
- } else if (args == 2) { |
- int32_t value; |
- if (GetValue(arg1, &value)) { |
- cur = reinterpret_cast<byte*>(value); |
- // no length parameter passed, assume 10 instructions |
- end = cur + (10 * Instr::kInstrSize); |
- } |
- } else { |
- int32_t value1; |
- int32_t value2; |
- if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { |
- cur = reinterpret_cast<byte*>(value1); |
- end = cur + (value2 * Instr::kInstrSize); |
- } |
- } |
- |
- while (cur < end) { |
- dasm.InstructionDecode(buffer, cur); |
- PrintF(" 0x%x %s\n", cur, buffer.start()); |
- cur += Instr::kInstrSize; |
- } |
- } else if (strcmp(cmd, "gdb") == 0) { |
- PrintF("relinquishing control to gdb\n"); |
- v8::internal::OS::DebugBreak(); |
- PrintF("regaining control from gdb\n"); |
- } else if (strcmp(cmd, "break") == 0) { |
- if (args == 2) { |
- int32_t value; |
- if (GetValue(arg1, &value)) { |
- if (!SetBreakpoint(reinterpret_cast<Instr*>(value))) { |
- PrintF("setting breakpoint failed\n"); |
- } |
- } else { |
- PrintF("%s unrecognized\n", arg1); |
- } |
- } else { |
- PrintF("break addr\n"); |
- } |
- } else if (strcmp(cmd, "del") == 0) { |
- if (!DeleteBreakpoint(NULL)) { |
- PrintF("deleting breakpoint failed\n"); |
- } |
- } else if (strcmp(cmd, "flags") == 0) { |
- PrintF("N flag: %d; ", sim_->n_flag_); |
- PrintF("Z flag: %d; ", sim_->z_flag_); |
- PrintF("C flag: %d; ", sim_->c_flag_); |
- PrintF("V flag: %d\n", sim_->v_flag_); |
- } else if (strcmp(cmd, "unstop") == 0) { |
- intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize; |
- Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc); |
- if (stop_instr->ConditionField() == special_condition) { |
- stop_instr->SetInstructionBits(kNopInstr); |
- } else { |
- PrintF("Not at debugger stop."); |
- } |
- } else { |
- PrintF("Unknown command: %s\n", cmd); |
- } |
- } |
- DeleteArray(line); |
- } |
- |
- // Add all the breakpoints back to stop execution and enter the debugger |
- // shell when hit. |
- RedoBreakpoints(); |
- |
-#undef COMMAND_SIZE |
-#undef ARG_SIZE |
- |
-#undef STR |
-#undef XSTR |
-} |
- |
- |
-Simulator::Simulator() { |
- // Setup simulator support first. Some of this information is needed to |
- // setup the architecture state. |
- size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack |
- stack_ = reinterpret_cast<char*>(malloc(stack_size)); |
- pc_modified_ = false; |
- icount_ = 0; |
- break_pc_ = NULL; |
- break_instr_ = 0; |
- |
- // Setup architecture state. |
- // All registers are initialized to zero to start with. |
- for (int i = 0; i < num_registers; i++) { |
- registers_[i] = 0; |
- } |
- n_flag_ = false; |
- z_flag_ = false; |
- c_flag_ = false; |
- v_flag_ = false; |
- |
- // 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 |
- // some buffer below. |
- registers_[sp] = reinterpret_cast<int32_t>(stack_) + stack_size - 64; |
- // The lr and pc are initialized to a known bad value that will cause an |
- // access violation if the simulator ever tries to execute it. |
- registers_[pc] = bad_lr; |
- registers_[lr] = bad_lr; |
- InitializeCoverage(); |
-} |
- |
- |
-// Create one simulator per thread and keep it in thread local storage. |
-static v8::internal::Thread::LocalStorageKey simulator_key = |
- v8::internal::Thread::CreateThreadLocalKey(); |
- |
-// Get the active Simulator for the current thread. |
-Simulator* Simulator::current() { |
- Simulator* sim = reinterpret_cast<Simulator*>( |
- v8::internal::Thread::GetThreadLocal(simulator_key)); |
- if (sim == NULL) { |
- // TODO(146): delete the simulator object when a thread goes away. |
- sim = new Simulator(); |
- v8::internal::Thread::SetThreadLocal(simulator_key, sim); |
- } |
- return sim; |
-} |
- |
- |
-// Sets the register in the architecture state. It will also deal with updating |
-// Simulator internal state for special registers such as PC. |
-void Simulator::set_register(int reg, int32_t value) { |
- ASSERT((reg >= 0) && (reg < num_registers)); |
- if (reg == pc) { |
- pc_modified_ = true; |
- } |
- registers_[reg] = value; |
-} |
- |
- |
-// Get the register from the architecture state. This function does handle |
-// the special case of accessing the PC register. |
-int32_t Simulator::get_register(int reg) const { |
- ASSERT((reg >= 0) && (reg < num_registers)); |
- return registers_[reg] + ((reg == pc) ? Instr::kPCReadOffset : 0); |
-} |
- |
- |
-// Raw access to the PC register. |
-void Simulator::set_pc(int32_t value) { |
- pc_modified_ = true; |
- registers_[pc] = value; |
-} |
- |
- |
-// Raw access to the PC register without the special adjustment when reading. |
-int32_t Simulator::get_pc() const { |
- return registers_[pc]; |
-} |
- |
- |
-// For use in calls that take two double values, constructed from r0, r1, r2 |
-// and r3. |
-void Simulator::GetFpArgs(double* x, double* y) { |
- // We use a char buffer to get around the strict-aliasing rules which |
- // otherwise allow the compiler to optimize away the copy. |
- char buffer[2 * sizeof(registers_[0])]; |
- // Registers 0 and 1 -> x. |
- memcpy(buffer, registers_, sizeof(buffer)); |
- memcpy(x, buffer, sizeof(buffer)); |
- // Registers 2 and 3 -> y. |
- memcpy(buffer, registers_ + 2, sizeof(buffer)); |
- memcpy(y, buffer, sizeof(buffer)); |
-} |
- |
- |
-void Simulator::SetFpResult(const double& result) { |
- char buffer[2 * sizeof(registers_[0])]; |
- memcpy(buffer, &result, sizeof(buffer)); |
- // result -> registers 0 and 1. |
- memcpy(registers_, buffer, sizeof(buffer)); |
-} |
- |
- |
-void Simulator::TrashCallerSaveRegisters() { |
- // We don't trash the registers with the return value. |
- registers_[2] = 0x50Bad4U; |
- registers_[3] = 0x50Bad4U; |
- registers_[12] = 0x50Bad4U; |
-} |
- |
- |
-// The ARM cannot do unaligned reads and writes. On some ARM platforms an |
-// interrupt is caused. On others it does a funky rotation thing. For now we |
-// simply disallow unaligned reads, but at some point we may want to move to |
-// emulating the rotate behaviour. Note that simulator runs have the runtime |
-// system running directly on the host system and only generated code is |
-// executed in the simulator. Since the host is typically IA32 we will not |
-// get the correct ARM-like behaviour on unaligned accesses. |
- |
-int Simulator::ReadW(int32_t addr, Instr* instr) { |
- if ((addr & 3) == 0) { |
- intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); |
- return *ptr; |
- } |
- PrintF("Unaligned read at %x\n", addr); |
- UNIMPLEMENTED(); |
- return 0; |
-} |
- |
- |
-void Simulator::WriteW(int32_t addr, int value, Instr* instr) { |
- if ((addr & 3) == 0) { |
- intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); |
- *ptr = value; |
- return; |
- } |
- PrintF("Unaligned write at %x, pc=%p\n", addr, instr); |
- UNIMPLEMENTED(); |
-} |
- |
- |
-uint16_t Simulator::ReadHU(int32_t addr, Instr* instr) { |
- if ((addr & 1) == 0) { |
- uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
- return *ptr; |
- } |
- PrintF("Unaligned read at %x, pc=%p\n", addr, instr); |
- UNIMPLEMENTED(); |
- return 0; |
-} |
- |
- |
-int16_t Simulator::ReadH(int32_t addr, Instr* instr) { |
- if ((addr & 1) == 0) { |
- int16_t* ptr = reinterpret_cast<int16_t*>(addr); |
- return *ptr; |
- } |
- PrintF("Unaligned read at %x\n", addr); |
- UNIMPLEMENTED(); |
- return 0; |
-} |
- |
- |
-void Simulator::WriteH(int32_t addr, uint16_t value, Instr* instr) { |
- if ((addr & 1) == 0) { |
- uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); |
- *ptr = value; |
- return; |
- } |
- PrintF("Unaligned write at %x, pc=%p\n", addr, instr); |
- UNIMPLEMENTED(); |
-} |
- |
- |
-void Simulator::WriteH(int32_t addr, int16_t value, Instr* instr) { |
- if ((addr & 1) == 0) { |
- int16_t* ptr = reinterpret_cast<int16_t*>(addr); |
- *ptr = value; |
- return; |
- } |
- PrintF("Unaligned write at %x, pc=%p\n", addr, instr); |
- UNIMPLEMENTED(); |
-} |
- |
- |
-uint8_t Simulator::ReadBU(int32_t addr) { |
- uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
- return *ptr; |
-} |
- |
- |
-int8_t Simulator::ReadB(int32_t addr) { |
- int8_t* ptr = reinterpret_cast<int8_t*>(addr); |
- return *ptr; |
-} |
- |
- |
-void Simulator::WriteB(int32_t addr, uint8_t value) { |
- uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
- *ptr = value; |
-} |
- |
- |
-void Simulator::WriteB(int32_t addr, int8_t value) { |
- int8_t* ptr = reinterpret_cast<int8_t*>(addr); |
- *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 256 bytes to prevent overrunning the stack when |
- // pushing values. |
- return reinterpret_cast<uintptr_t>(stack_) + 256; |
-} |
- |
- |
-// Unsupported instructions use Format to print an error and stop execution. |
-void Simulator::Format(Instr* instr, const char* format) { |
- PrintF("Simulator found unsupported instruction:\n 0x%x: %s\n", |
- instr, format); |
- UNIMPLEMENTED(); |
-} |
- |
- |
-// Checks if the current instruction should be executed based on its |
-// condition bits. |
-bool Simulator::ConditionallyExecute(Instr* instr) { |
- switch (instr->ConditionField()) { |
- case EQ: return z_flag_; |
- case NE: return !z_flag_; |
- case CS: return c_flag_; |
- case CC: return !c_flag_; |
- case MI: return n_flag_; |
- case PL: return !n_flag_; |
- case VS: return v_flag_; |
- case VC: return !v_flag_; |
- case HI: return c_flag_ && !z_flag_; |
- case LS: return !c_flag_ || z_flag_; |
- case GE: return n_flag_ == v_flag_; |
- case LT: return n_flag_ != v_flag_; |
- case GT: return !z_flag_ && (n_flag_ == v_flag_); |
- case LE: return z_flag_ || (n_flag_ != v_flag_); |
- case AL: return true; |
- default: UNREACHABLE(); |
- } |
- return false; |
-} |
- |
- |
-// Calculate and set the Negative and Zero flags. |
-void Simulator::SetNZFlags(int32_t val) { |
- n_flag_ = (val < 0); |
- z_flag_ = (val == 0); |
-} |
- |
- |
-// Set the Carry flag. |
-void Simulator::SetCFlag(bool val) { |
- c_flag_ = val; |
-} |
- |
- |
-// Set the oVerflow flag. |
-void Simulator::SetVFlag(bool val) { |
- v_flag_ = val; |
-} |
- |
- |
-// Calculate C flag value for additions. |
-bool Simulator::CarryFrom(int32_t left, int32_t right) { |
- uint32_t uleft = static_cast<uint32_t>(left); |
- uint32_t uright = static_cast<uint32_t>(right); |
- uint32_t urest = 0xffffffffU - uleft; |
- |
- return (uright > urest); |
-} |
- |
- |
-// Calculate C flag value for subtractions. |
-bool Simulator::BorrowFrom(int32_t left, int32_t right) { |
- uint32_t uleft = static_cast<uint32_t>(left); |
- uint32_t uright = static_cast<uint32_t>(right); |
- |
- return (uright > uleft); |
-} |
- |
- |
-// Calculate V flag value for additions and subtractions. |
-bool Simulator::OverflowFrom(int32_t alu_out, |
- int32_t left, int32_t right, bool addition) { |
- bool overflow; |
- if (addition) { |
- // operands have the same sign |
- overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) |
- // and operands and result have different sign |
- && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
- } else { |
- // operands have different signs |
- overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) |
- // and first operand and result have different signs |
- && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
- } |
- return overflow; |
-} |
- |
- |
-// Addressing Mode 1 - Data-processing operands: |
-// Get the value based on the shifter_operand with register. |
-int32_t Simulator::GetShiftRm(Instr* instr, bool* carry_out) { |
- Shift shift = instr->ShiftField(); |
- int shift_amount = instr->ShiftAmountField(); |
- int32_t result = get_register(instr->RmField()); |
- if (instr->Bit(4) == 0) { |
- // by immediate |
- if ((shift == ROR) && (shift_amount == 0)) { |
- UNIMPLEMENTED(); |
- return result; |
- } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { |
- shift_amount = 32; |
- } |
- switch (shift) { |
- case ASR: { |
- if (shift_amount == 0) { |
- if (result < 0) { |
- result = 0xffffffff; |
- *carry_out = true; |
- } else { |
- result = 0; |
- *carry_out = false; |
- } |
- } else { |
- result >>= (shift_amount - 1); |
- *carry_out = (result & 1) == 1; |
- result >>= 1; |
- } |
- break; |
- } |
- |
- case LSL: { |
- if (shift_amount == 0) { |
- *carry_out = c_flag_; |
- } else { |
- result <<= (shift_amount - 1); |
- *carry_out = (result < 0); |
- result <<= 1; |
- } |
- break; |
- } |
- |
- case LSR: { |
- if (shift_amount == 0) { |
- result = 0; |
- *carry_out = c_flag_; |
- } else { |
- uint32_t uresult = static_cast<uint32_t>(result); |
- uresult >>= (shift_amount - 1); |
- *carry_out = (uresult & 1) == 1; |
- uresult >>= 1; |
- result = static_cast<int32_t>(uresult); |
- } |
- break; |
- } |
- |
- case ROR: { |
- UNIMPLEMENTED(); |
- break; |
- } |
- |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
- } else { |
- // by register |
- int rs = instr->RsField(); |
- shift_amount = get_register(rs) &0xff; |
- switch (shift) { |
- case ASR: { |
- if (shift_amount == 0) { |
- *carry_out = c_flag_; |
- } else if (shift_amount < 32) { |
- result >>= (shift_amount - 1); |
- *carry_out = (result & 1) == 1; |
- result >>= 1; |
- } else { |
- ASSERT(shift_amount >= 32); |
- if (result < 0) { |
- *carry_out = true; |
- result = 0xffffffff; |
- } else { |
- *carry_out = false; |
- result = 0; |
- } |
- } |
- break; |
- } |
- |
- case LSL: { |
- if (shift_amount == 0) { |
- *carry_out = c_flag_; |
- } else if (shift_amount < 32) { |
- result <<= (shift_amount - 1); |
- *carry_out = (result < 0); |
- result <<= 1; |
- } else if (shift_amount == 32) { |
- *carry_out = (result & 1) == 1; |
- result = 0; |
- } else { |
- ASSERT(shift_amount > 32); |
- *carry_out = false; |
- result = 0; |
- } |
- break; |
- } |
- |
- case LSR: { |
- if (shift_amount == 0) { |
- *carry_out = c_flag_; |
- } else if (shift_amount < 32) { |
- uint32_t uresult = static_cast<uint32_t>(result); |
- uresult >>= (shift_amount - 1); |
- *carry_out = (uresult & 1) == 1; |
- uresult >>= 1; |
- result = static_cast<int32_t>(uresult); |
- } else if (shift_amount == 32) { |
- *carry_out = (result < 0); |
- result = 0; |
- } else { |
- *carry_out = false; |
- result = 0; |
- } |
- break; |
- } |
- |
- case ROR: { |
- UNIMPLEMENTED(); |
- break; |
- } |
- |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
- } |
- return result; |
-} |
- |
- |
-// Addressing Mode 1 - Data-processing operands: |
-// Get the value based on the shifter_operand with immediate. |
-int32_t Simulator::GetImm(Instr* instr, bool* carry_out) { |
- int rotate = instr->RotateField() * 2; |
- int immed8 = instr->Immed8Field(); |
- int imm = (immed8 >> rotate) | (immed8 << (32 - rotate)); |
- *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); |
- return imm; |
-} |
- |
- |
-static int count_bits(int bit_vector) { |
- int count = 0; |
- while (bit_vector != 0) { |
- if ((bit_vector & 1) != 0) { |
- count++; |
- } |
- bit_vector >>= 1; |
- } |
- return count; |
-} |
- |
- |
-// Addressing Mode 4 - Load and Store Multiple |
-void Simulator::HandleRList(Instr* instr, bool load) { |
- int rn = instr->RnField(); |
- int32_t rn_val = get_register(rn); |
- int rlist = instr->RlistField(); |
- int num_regs = count_bits(rlist); |
- |
- intptr_t start_address = 0; |
- intptr_t end_address = 0; |
- switch (instr->PUField()) { |
- case 0: { |
- // Print("da"); |
- UNIMPLEMENTED(); |
- break; |
- } |
- case 1: { |
- // Print("ia"); |
- start_address = rn_val; |
- end_address = rn_val + (num_regs * 4) - 4; |
- rn_val = rn_val + (num_regs * 4); |
- break; |
- } |
- case 2: { |
- // Print("db"); |
- start_address = rn_val - (num_regs * 4); |
- end_address = rn_val - 4; |
- rn_val = start_address; |
- break; |
- } |
- case 3: { |
- // Print("ib"); |
- UNIMPLEMENTED(); |
- break; |
- } |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- intptr_t* address = reinterpret_cast<intptr_t*>(start_address); |
- int reg = 0; |
- while (rlist != 0) { |
- if ((rlist & 1) != 0) { |
- if (load) { |
- set_register(reg, *address); |
- } else { |
- *address = get_register(reg); |
- } |
- address += 1; |
- } |
- reg++; |
- rlist >>= 1; |
- } |
- ASSERT(end_address == ((intptr_t)address) - 4); |
-} |
- |
- |
-// Calls into the V8 runtime are based on this very simple interface. |
-// Note: To be able to return two values from some calls the code in runtime.cc |
-// uses the ObjectPair which is essentially two 32-bit values stuffed into a |
-// 64-bit value. With the code below we assume that all runtime calls return |
-// 64 bits of result. If they don't, the r1 result register contains a bogus |
-// value, which is fine because it is caller-saved. |
-typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0, intptr_t arg1); |
- |
- |
-// Software interrupt instructions are used by the simulator to call into the |
-// C-based V8 runtime. |
-void Simulator::SoftwareInterrupt(Instr* instr) { |
- int swi = instr->SwiField(); |
- switch (swi) { |
- case call_rt_r5: { |
- SimulatorRuntimeCall target = |
- reinterpret_cast<SimulatorRuntimeCall>(get_register(r5)); |
- intptr_t arg0 = get_register(r0); |
- intptr_t arg1 = get_register(r1); |
- int64_t result = target(arg0, arg1); |
- int32_t lo_res = static_cast<int32_t>(result); |
- int32_t hi_res = static_cast<int32_t>(result >> 32); |
- set_register(r0, lo_res); |
- set_register(r1, hi_res); |
- set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); |
- break; |
- } |
- case call_rt_r2: { |
- SimulatorRuntimeCall target = |
- reinterpret_cast<SimulatorRuntimeCall>(get_register(r2)); |
- intptr_t arg0 = get_register(r0); |
- intptr_t arg1 = get_register(r1); |
- int64_t result = target(arg0, arg1); |
- int32_t lo_res = static_cast<int32_t>(result); |
- int32_t hi_res = static_cast<int32_t>(result >> 32); |
- set_register(r0, lo_res); |
- set_register(r1, hi_res); |
- set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); |
- break; |
- } |
- case break_point: { |
- Debugger dbg(this); |
- dbg.Debug(); |
- break; |
- } |
- { |
- double x, y, z; |
- case simulator_fp_add: |
- GetFpArgs(&x, &y); |
- z = x + y; |
- SetFpResult(z); |
- TrashCallerSaveRegisters(); |
- set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); |
- break; |
- case simulator_fp_sub: |
- GetFpArgs(&x, &y); |
- z = x - y; |
- SetFpResult(z); |
- TrashCallerSaveRegisters(); |
- set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); |
- break; |
- case simulator_fp_mul: |
- GetFpArgs(&x, &y); |
- z = x * y; |
- SetFpResult(z); |
- TrashCallerSaveRegisters(); |
- set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); |
- break; |
- } |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
-} |
- |
- |
-// Handle execution based on instruction types. |
- |
-// Instruction types 0 and 1 are both rolled into one function because they |
-// only differ in the handling of the shifter_operand. |
-void Simulator::DecodeType01(Instr* instr) { |
- int type = instr->TypeField(); |
- if ((type == 0) && instr->IsSpecialType0()) { |
- // multiply instruction or extra loads and stores |
- if (instr->Bits(7, 4) == 9) { |
- if (instr->Bit(24) == 0) { |
- // multiply instructions |
- int rd = instr->RdField(); |
- int rm = instr->RmField(); |
- int rs = instr->RsField(); |
- int32_t rs_val = get_register(rs); |
- int32_t rm_val = get_register(rm); |
- if (instr->Bit(23) == 0) { |
- if (instr->Bit(21) == 0) { |
- // Format(instr, "mul'cond's 'rd, 'rm, 'rs"); |
- int32_t alu_out = rm_val * rs_val; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- } |
- } else { |
- Format(instr, "mla'cond's 'rd, 'rm, 'rs, 'rn"); |
- } |
- } else { |
- // Format(instr, "'um'al'cond's 'rn, 'rd, 'rs, 'rm"); |
- int rn = instr->RnField(); |
- int32_t hi_res = 0; |
- int32_t lo_res = 0; |
- if (instr->Bit(22) == 0) { |
- // signed multiply |
- UNIMPLEMENTED(); |
- } else { |
- // unsigned multiply |
- uint64_t left_op = rm_val; |
- uint64_t right_op = rs_val; |
- uint64_t result = left_op * right_op; |
- hi_res = static_cast<int32_t>(result >> 32); |
- lo_res = static_cast<int32_t>(result & 0xffffffff); |
- } |
- set_register(rn, hi_res); |
- set_register(rd, lo_res); |
- if (instr->HasS()) { |
- UNIMPLEMENTED(); |
- } |
- } |
- } else { |
- UNIMPLEMENTED(); // not used by V8 |
- } |
- } else { |
- // extra load/store instructions |
- int rd = instr->RdField(); |
- int rn = instr->RnField(); |
- int32_t rn_val = get_register(rn); |
- int32_t addr = 0; |
- if (instr->Bit(22) == 0) { |
- int rm = instr->RmField(); |
- int32_t rm_val = get_register(rm); |
- switch (instr->PUField()) { |
- case 0: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm"); |
- ASSERT(!instr->HasW()); |
- addr = rn_val; |
- rn_val -= rm_val; |
- set_register(rn, rn_val); |
- break; |
- } |
- case 1: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm"); |
- ASSERT(!instr->HasW()); |
- addr = rn_val; |
- rn_val += rm_val; |
- set_register(rn, rn_val); |
- break; |
- } |
- case 2: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w"); |
- rn_val -= rm_val; |
- addr = rn_val; |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- break; |
- } |
- case 3: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w"); |
- rn_val += rm_val; |
- addr = rn_val; |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- break; |
- } |
- default: { |
- // The PU field is a 2-bit field. |
- UNREACHABLE(); |
- break; |
- } |
- } |
- } else { |
- int32_t imm_val = (instr->ImmedHField() << 4) | instr->ImmedLField(); |
- switch (instr->PUField()) { |
- case 0: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8"); |
- ASSERT(!instr->HasW()); |
- addr = rn_val; |
- rn_val -= imm_val; |
- set_register(rn, rn_val); |
- break; |
- } |
- case 1: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8"); |
- ASSERT(!instr->HasW()); |
- addr = rn_val; |
- rn_val += imm_val; |
- set_register(rn, rn_val); |
- break; |
- } |
- case 2: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w"); |
- rn_val -= imm_val; |
- addr = rn_val; |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- break; |
- } |
- case 3: { |
- // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w"); |
- rn_val += imm_val; |
- addr = rn_val; |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- break; |
- } |
- default: { |
- // The PU field is a 2-bit field. |
- UNREACHABLE(); |
- break; |
- } |
- } |
- } |
- if (instr->HasH()) { |
- if (instr->HasSign()) { |
- if (instr->HasL()) { |
- int16_t val = ReadH(addr, instr); |
- set_register(rd, val); |
- } else { |
- int16_t val = get_register(rd); |
- WriteH(addr, val, instr); |
- } |
- } else { |
- if (instr->HasL()) { |
- uint16_t val = ReadHU(addr, instr); |
- set_register(rd, val); |
- } else { |
- uint16_t val = get_register(rd); |
- WriteH(addr, val, instr); |
- } |
- } |
- } else { |
- // signed byte loads |
- ASSERT(instr->HasSign()); |
- ASSERT(instr->HasL()); |
- int8_t val = ReadB(addr); |
- set_register(rd, val); |
- } |
- return; |
- } |
- } else { |
- int rd = instr->RdField(); |
- int rn = instr->RnField(); |
- int32_t rn_val = get_register(rn); |
- int32_t shifter_operand = 0; |
- bool shifter_carry_out = 0; |
- if (type == 0) { |
- shifter_operand = GetShiftRm(instr, &shifter_carry_out); |
- } else { |
- ASSERT(instr->TypeField() == 1); |
- shifter_operand = GetImm(instr, &shifter_carry_out); |
- } |
- int32_t alu_out; |
- |
- switch (instr->OpcodeField()) { |
- case AND: { |
- // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "and'cond's 'rd, 'rn, 'imm"); |
- alu_out = rn_val & shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } |
- break; |
- } |
- |
- case EOR: { |
- // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "eor'cond's 'rd, 'rn, 'imm"); |
- alu_out = rn_val ^ shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } |
- break; |
- } |
- |
- case SUB: { |
- // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "sub'cond's 'rd, 'rn, 'imm"); |
- alu_out = rn_val - shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(!BorrowFrom(rn_val, shifter_operand)); |
- SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); |
- } |
- break; |
- } |
- |
- case RSB: { |
- // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "rsb'cond's 'rd, 'rn, 'imm"); |
- alu_out = shifter_operand - rn_val; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(!BorrowFrom(shifter_operand, rn_val)); |
- SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false)); |
- } |
- break; |
- } |
- |
- case ADD: { |
- // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "add'cond's 'rd, 'rn, 'imm"); |
- alu_out = rn_val + shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(CarryFrom(rn_val, shifter_operand)); |
- SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); |
- } |
- break; |
- } |
- |
- case ADC: { |
- Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm"); |
- Format(instr, "adc'cond's 'rd, 'rn, 'imm"); |
- break; |
- } |
- |
- case SBC: { |
- Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm"); |
- Format(instr, "sbc'cond's 'rd, 'rn, 'imm"); |
- break; |
- } |
- |
- case RSC: { |
- Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm"); |
- Format(instr, "rsc'cond's 'rd, 'rn, 'imm"); |
- break; |
- } |
- |
- case TST: { |
- if (instr->HasS()) { |
- // Format(instr, "tst'cond 'rn, 'shift_rm"); |
- // Format(instr, "tst'cond 'rn, 'imm"); |
- alu_out = rn_val & shifter_operand; |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } else { |
- UNIMPLEMENTED(); |
- } |
- break; |
- } |
- |
- case TEQ: { |
- if (instr->HasS()) { |
- // Format(instr, "teq'cond 'rn, 'shift_rm"); |
- // Format(instr, "teq'cond 'rn, 'imm"); |
- alu_out = rn_val ^ shifter_operand; |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } else { |
- UNIMPLEMENTED(); |
- } |
- break; |
- } |
- |
- case CMP: { |
- if (instr->HasS()) { |
- // Format(instr, "cmp'cond 'rn, 'shift_rm"); |
- // Format(instr, "cmp'cond 'rn, 'imm"); |
- alu_out = rn_val - shifter_operand; |
- SetNZFlags(alu_out); |
- SetCFlag(!BorrowFrom(rn_val, shifter_operand)); |
- SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); |
- } else { |
- UNIMPLEMENTED(); |
- } |
- break; |
- } |
- |
- case CMN: { |
- if (instr->HasS()) { |
- Format(instr, "cmn'cond 'rn, 'shift_rm"); |
- Format(instr, "cmn'cond 'rn, 'imm"); |
- } else { |
- UNIMPLEMENTED(); |
- } |
- break; |
- } |
- |
- case ORR: { |
- // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "orr'cond's 'rd, 'rn, 'imm"); |
- alu_out = rn_val | shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } |
- break; |
- } |
- |
- case MOV: { |
- // Format(instr, "mov'cond's 'rd, 'shift_rm"); |
- // Format(instr, "mov'cond's 'rd, 'imm"); |
- alu_out = shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } |
- break; |
- } |
- |
- case BIC: { |
- // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm"); |
- // Format(instr, "bic'cond's 'rd, 'rn, 'imm"); |
- alu_out = rn_val & ~shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } |
- break; |
- } |
- |
- case MVN: { |
- // Format(instr, "mvn'cond's 'rd, 'shift_rm"); |
- // Format(instr, "mvn'cond's 'rd, 'imm"); |
- alu_out = ~shifter_operand; |
- set_register(rd, alu_out); |
- if (instr->HasS()) { |
- SetNZFlags(alu_out); |
- SetCFlag(shifter_carry_out); |
- } |
- break; |
- } |
- |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
- } |
-} |
- |
- |
-void Simulator::DecodeType2(Instr* instr) { |
- int rd = instr->RdField(); |
- int rn = instr->RnField(); |
- int32_t rn_val = get_register(rn); |
- int32_t im_val = instr->Offset12Field(); |
- int32_t addr = 0; |
- switch (instr->PUField()) { |
- case 0: { |
- // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); |
- ASSERT(!instr->HasW()); |
- addr = rn_val; |
- rn_val -= im_val; |
- set_register(rn, rn_val); |
- break; |
- } |
- case 1: { |
- // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); |
- ASSERT(!instr->HasW()); |
- addr = rn_val; |
- rn_val += im_val; |
- set_register(rn, rn_val); |
- break; |
- } |
- case 2: { |
- // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); |
- rn_val -= im_val; |
- addr = rn_val; |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- break; |
- } |
- case 3: { |
- // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); |
- rn_val += im_val; |
- addr = rn_val; |
- if (instr->HasW()) { |
- set_register(rn, rn_val); |
- } |
- break; |
- } |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
- if (instr->HasB()) { |
- if (instr->HasL()) { |
- byte val = ReadBU(addr); |
- set_register(rd, val); |
- } else { |
- byte val = get_register(rd); |
- WriteB(addr, val); |
- } |
- } else { |
- if (instr->HasL()) { |
- set_register(rd, ReadW(addr, instr)); |
- } else { |
- WriteW(addr, get_register(rd), instr); |
- } |
- } |
-} |
- |
- |
-void Simulator::DecodeType3(Instr* instr) { |
- int rd = instr->RdField(); |
- int rn = instr->RnField(); |
- int32_t rn_val = get_register(rn); |
- bool shifter_carry_out = 0; |
- int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out); |
- int32_t addr = 0; |
- switch (instr->PUField()) { |
- case 0: { |
- ASSERT(!instr->HasW()); |
- Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); |
- break; |
- } |
- case 1: { |
- ASSERT(!instr->HasW()); |
- Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm"); |
- break; |
- } |
- case 2: { |
- // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); |
- addr = rn_val - shifter_operand; |
- if (instr->HasW()) { |
- set_register(rn, addr); |
- } |
- break; |
- } |
- case 3: { |
- // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); |
- addr = rn_val + shifter_operand; |
- if (instr->HasW()) { |
- set_register(rn, addr); |
- } |
- break; |
- } |
- default: { |
- UNREACHABLE(); |
- break; |
- } |
- } |
- if (instr->HasB()) { |
- UNIMPLEMENTED(); |
- } else { |
- if (instr->HasL()) { |
- set_register(rd, ReadW(addr, instr)); |
- } else { |
- WriteW(addr, get_register(rd), instr); |
- } |
- } |
-} |
- |
- |
-void Simulator::DecodeType4(Instr* instr) { |
- ASSERT(instr->Bit(22) == 0); // only allowed to be set in privileged mode |
- if (instr->HasL()) { |
- // Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); |
- HandleRList(instr, true); |
- } else { |
- // Format(instr, "stm'cond'pu 'rn'w, 'rlist"); |
- HandleRList(instr, false); |
- } |
-} |
- |
- |
-void Simulator::DecodeType5(Instr* instr) { |
- // Format(instr, "b'l'cond 'target"); |
- int off = (instr->SImmed24Field() << 2) + 8; |
- intptr_t pc = get_pc(); |
- if (instr->HasLink()) { |
- set_register(lr, pc + Instr::kInstrSize); |
- } |
- set_pc(pc+off); |
-} |
- |
- |
-void Simulator::DecodeType6(Instr* instr) { |
- UNIMPLEMENTED(); |
-} |
- |
- |
-void Simulator::DecodeType7(Instr* instr) { |
- if (instr->Bit(24) == 1) { |
- // Format(instr, "swi 'swi"); |
- SoftwareInterrupt(instr); |
- } else { |
- UNIMPLEMENTED(); |
- } |
-} |
- |
- |
-// Executes the current instruction. |
-void Simulator::InstructionDecode(Instr* instr) { |
- pc_modified_ = false; |
- if (instr->ConditionField() == special_condition) { |
- Debugger dbg(this); |
- dbg.Stop(instr); |
- return; |
- } |
- if (::v8::internal::FLAG_trace_sim) { |
- 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%x %s\n", instr, buffer.start()); |
- } |
- if (ConditionallyExecute(instr)) { |
- switch (instr->TypeField()) { |
- case 0: |
- case 1: { |
- DecodeType01(instr); |
- break; |
- } |
- case 2: { |
- DecodeType2(instr); |
- break; |
- } |
- case 3: { |
- DecodeType3(instr); |
- break; |
- } |
- case 4: { |
- DecodeType4(instr); |
- break; |
- } |
- case 5: { |
- DecodeType5(instr); |
- break; |
- } |
- case 6: { |
- DecodeType6(instr); |
- break; |
- } |
- case 7: { |
- DecodeType7(instr); |
- break; |
- } |
- default: { |
- UNIMPLEMENTED(); |
- break; |
- } |
- } |
- } |
- if (!pc_modified_) { |
- set_register(pc, reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); |
- } |
-} |
- |
- |
-// |
-void Simulator::Execute() { |
- // Get the PC to simulate. Cannot use the accessor here as we need the |
- // raw PC value and not the one used as input to arithmetic instructions. |
- int program_counter = get_pc(); |
- |
- if (::v8::internal::FLAG_stop_sim_at == 0) { |
- // Fast version of the dispatch loop without checking whether the simulator |
- // should be stopping at a particular executed instruction. |
- while (program_counter != end_sim_pc) { |
- Instr* instr = reinterpret_cast<Instr*>(program_counter); |
- icount_++; |
- InstructionDecode(instr); |
- program_counter = get_pc(); |
- } |
- } else { |
- // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when |
- // we reach the particular instuction count. |
- while (program_counter != end_sim_pc) { |
- Instr* instr = reinterpret_cast<Instr*>(program_counter); |
- icount_++; |
- if (icount_ == ::v8::internal::FLAG_stop_sim_at) { |
- Debugger dbg(this); |
- dbg.Debug(); |
- } else { |
- InstructionDecode(instr); |
- } |
- program_counter = get_pc(); |
- } |
- } |
-} |
- |
- |
-Object* Simulator::Call(int32_t entry, int32_t p0, int32_t p1, int32_t p2, |
- int32_t p3, int32_t p4) { |
- // Setup parameters |
- set_register(r0, p0); |
- set_register(r1, p1); |
- set_register(r2, p2); |
- set_register(r3, p3); |
- intptr_t* stack_pointer = reinterpret_cast<intptr_t*>(get_register(sp)); |
- *(--stack_pointer) = p4; |
- set_register(sp, reinterpret_cast<int32_t>(stack_pointer)); |
- |
- // Prepare to execute the code at entry |
- set_register(pc, entry); |
- // Put down marker for end of simulation. The simulator will stop simulation |
- // when the PC reaches this value. By saving the "end simulation" value into |
- // the LR the simulation stops when returning to this call point. |
- set_register(lr, end_sim_pc); |
- |
- // Remember the values of callee-saved registers. |
- // The code below assumes that r9 is not used as sb (static base) in |
- // simulator code and therefore is regarded as a callee-saved register. |
- int32_t r4_val = get_register(r4); |
- int32_t r5_val = get_register(r5); |
- int32_t r6_val = get_register(r6); |
- int32_t r7_val = get_register(r7); |
- int32_t r8_val = get_register(r8); |
- int32_t r9_val = get_register(r9); |
- int32_t r10_val = get_register(r10); |
- int32_t r11_val = get_register(r11); |
- |
- // Setup the callee-saved registers with a known value. To be able to check |
- // that they are preserved properly across JS execution. |
- int32_t callee_saved_value = icount_; |
- set_register(r4, callee_saved_value); |
- set_register(r5, callee_saved_value); |
- set_register(r6, callee_saved_value); |
- set_register(r7, callee_saved_value); |
- set_register(r8, callee_saved_value); |
- set_register(r9, callee_saved_value); |
- set_register(r10, callee_saved_value); |
- set_register(r11, callee_saved_value); |
- |
- // Start the simulation |
- Execute(); |
- |
- // Check that the callee-saved registers have been preserved. |
- CHECK_EQ(get_register(r4), callee_saved_value); |
- CHECK_EQ(get_register(r5), callee_saved_value); |
- CHECK_EQ(get_register(r6), callee_saved_value); |
- CHECK_EQ(get_register(r7), callee_saved_value); |
- CHECK_EQ(get_register(r8), callee_saved_value); |
- CHECK_EQ(get_register(r9), callee_saved_value); |
- CHECK_EQ(get_register(r10), callee_saved_value); |
- CHECK_EQ(get_register(r11), callee_saved_value); |
- |
- // Restore callee-saved registers with the original value. |
- set_register(r4, r4_val); |
- set_register(r5, r5_val); |
- set_register(r6, r6_val); |
- set_register(r7, r7_val); |
- set_register(r8, r8_val); |
- set_register(r9, r9_val); |
- set_register(r10, r10_val); |
- set_register(r11, r11_val); |
- |
- int result = get_register(r0); |
- return reinterpret_cast<Object*>(result); |
-} |
- |
-} } // namespace assembler::arm |
- |
-#endif // !defined(__arm__) |