diff options
Diffstat (limited to 'V8Binding/v8/src/arm/simulator-arm.cc')
-rw-r--r-- | V8Binding/v8/src/arm/simulator-arm.cc | 1688 |
1 files changed, 1688 insertions, 0 deletions
diff --git a/V8Binding/v8/src/arm/simulator-arm.cc b/V8Binding/v8/src/arm/simulator-arm.cc new file mode 100644 index 0000000..b8b6663 --- /dev/null +++ b/V8Binding/v8/src/arm/simulator-arm.cc @@ -0,0 +1,1688 @@ +// 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__) |