Index: src/arm/simulator-arm.cc |
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9737e953998b417d2fed149d916503c74a2fb0f4 |
--- /dev/null |
+++ b/src/arm/simulator-arm.cc |
@@ -0,0 +1,1687 @@ |
+// 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 "arm/constants-arm.h" |
+#include "arm/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__) |