| OLD | NEW |
| (Empty) |
| 1 // Copyright 2008 the V8 project authors. All rights reserved. | |
| 2 // Redistribution and use in source and binary forms, with or without | |
| 3 // modification, are permitted provided that the following conditions are | |
| 4 // met: | |
| 5 // | |
| 6 // * Redistributions of source code must retain the above copyright | |
| 7 // notice, this list of conditions and the following disclaimer. | |
| 8 // * Redistributions in binary form must reproduce the above | |
| 9 // copyright notice, this list of conditions and the following | |
| 10 // disclaimer in the documentation and/or other materials provided | |
| 11 // with the distribution. | |
| 12 // * Neither the name of Google Inc. nor the names of its | |
| 13 // contributors may be used to endorse or promote products derived | |
| 14 // from this software without specific prior written permission. | |
| 15 // | |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 27 | |
| 28 #include <stdlib.h> | |
| 29 | |
| 30 #include "v8.h" | |
| 31 | |
| 32 #include "disasm.h" | |
| 33 #include "constants-arm.h" | |
| 34 #include "simulator-arm.h" | |
| 35 | |
| 36 #if !defined(__arm__) | |
| 37 | |
| 38 // Only build the simulator if not compiling for real ARM hardware. | |
| 39 namespace assembler { namespace arm { | |
| 40 | |
| 41 using ::v8::internal::Object; | |
| 42 using ::v8::internal::PrintF; | |
| 43 using ::v8::internal::OS; | |
| 44 using ::v8::internal::ReadLine; | |
| 45 using ::v8::internal::DeleteArray; | |
| 46 | |
| 47 // This macro provides a platform independent use of sscanf. The reason for | |
| 48 // SScanF not being implemented in a platform independent was through | |
| 49 // ::v8::internal::OS in the same way as SNPrintF is that the Windows C Run-Time | |
| 50 // Library does not provide vsscanf. | |
| 51 #define SScanF sscanf // NOLINT | |
| 52 | |
| 53 // The Debugger class is used by the simulator while debugging simulated ARM | |
| 54 // code. | |
| 55 class Debugger { | |
| 56 public: | |
| 57 explicit Debugger(Simulator* sim); | |
| 58 ~Debugger(); | |
| 59 | |
| 60 void Stop(Instr* instr); | |
| 61 void Debug(); | |
| 62 | |
| 63 private: | |
| 64 static const instr_t kBreakpointInstr = | |
| 65 ((AL << 28) | (7 << 25) | (1 << 24) | break_point); | |
| 66 static const instr_t kNopInstr = | |
| 67 ((AL << 28) | (13 << 21)); | |
| 68 | |
| 69 Simulator* sim_; | |
| 70 | |
| 71 bool GetValue(char* desc, int32_t* value); | |
| 72 | |
| 73 // Set or delete a breakpoint. Returns true if successful. | |
| 74 bool SetBreakpoint(Instr* breakpc); | |
| 75 bool DeleteBreakpoint(Instr* breakpc); | |
| 76 | |
| 77 // Undo and redo all breakpoints. This is needed to bracket disassembly and | |
| 78 // execution to skip past breakpoints when run from the debugger. | |
| 79 void UndoBreakpoints(); | |
| 80 void RedoBreakpoints(); | |
| 81 }; | |
| 82 | |
| 83 | |
| 84 Debugger::Debugger(Simulator* sim) { | |
| 85 sim_ = sim; | |
| 86 } | |
| 87 | |
| 88 | |
| 89 Debugger::~Debugger() { | |
| 90 } | |
| 91 | |
| 92 | |
| 93 | |
| 94 #ifdef GENERATED_CODE_COVERAGE | |
| 95 static FILE* coverage_log = NULL; | |
| 96 | |
| 97 | |
| 98 static void InitializeCoverage() { | |
| 99 char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG"); | |
| 100 if (file_name != NULL) { | |
| 101 coverage_log = fopen(file_name, "aw+"); | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 | |
| 106 void Debugger::Stop(Instr* instr) { | |
| 107 char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff); | |
| 108 if (strlen(str) > 0) { | |
| 109 if (coverage_log != NULL) { | |
| 110 fprintf(coverage_log, "%s\n", str); | |
| 111 fflush(coverage_log); | |
| 112 } | |
| 113 instr->SetInstructionBits(0xe1a00000); // Overwrite with nop. | |
| 114 } | |
| 115 sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); | |
| 116 } | |
| 117 | |
| 118 #else // ndef GENERATED_CODE_COVERAGE | |
| 119 | |
| 120 static void InitializeCoverage() { | |
| 121 } | |
| 122 | |
| 123 | |
| 124 void Debugger::Stop(Instr* instr) { | |
| 125 const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff); | |
| 126 PrintF("Simulator hit %s\n", str); | |
| 127 sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); | |
| 128 Debug(); | |
| 129 } | |
| 130 #endif | |
| 131 | |
| 132 | |
| 133 static const char* reg_names[] = { "r0", "r1", "r2", "r3", | |
| 134 "r4", "r5", "r6", "r7", | |
| 135 "r8", "r9", "r10", "r11", | |
| 136 "r12", "r13", "r14", "r15", | |
| 137 "pc", "lr", "sp", "ip", | |
| 138 "fp", "sl", ""}; | |
| 139 | |
| 140 static int reg_nums[] = { 0, 1, 2, 3, | |
| 141 4, 5, 6, 7, | |
| 142 8, 9, 10, 11, | |
| 143 12, 13, 14, 15, | |
| 144 15, 14, 13, 12, | |
| 145 11, 10}; | |
| 146 | |
| 147 | |
| 148 static int RegNameToRegNum(char* name) { | |
| 149 int reg = 0; | |
| 150 while (*reg_names[reg] != 0) { | |
| 151 if (strcmp(reg_names[reg], name) == 0) { | |
| 152 return reg_nums[reg]; | |
| 153 } | |
| 154 reg++; | |
| 155 } | |
| 156 return -1; | |
| 157 } | |
| 158 | |
| 159 | |
| 160 bool Debugger::GetValue(char* desc, int32_t* value) { | |
| 161 int regnum = RegNameToRegNum(desc); | |
| 162 if (regnum >= 0) { | |
| 163 if (regnum == 15) { | |
| 164 *value = sim_->get_pc(); | |
| 165 } else { | |
| 166 *value = sim_->get_register(regnum); | |
| 167 } | |
| 168 return true; | |
| 169 } else { | |
| 170 return SScanF(desc, "%i", value) == 1; | |
| 171 } | |
| 172 return false; | |
| 173 } | |
| 174 | |
| 175 | |
| 176 bool Debugger::SetBreakpoint(Instr* breakpc) { | |
| 177 // Check if a breakpoint can be set. If not return without any side-effects. | |
| 178 if (sim_->break_pc_ != NULL) { | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 // Set the breakpoint. | |
| 183 sim_->break_pc_ = breakpc; | |
| 184 sim_->break_instr_ = breakpc->InstructionBits(); | |
| 185 // Not setting the breakpoint instruction in the code itself. It will be set | |
| 186 // when the debugger shell continues. | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 | |
| 191 bool Debugger::DeleteBreakpoint(Instr* breakpc) { | |
| 192 if (sim_->break_pc_ != NULL) { | |
| 193 sim_->break_pc_->SetInstructionBits(sim_->break_instr_); | |
| 194 } | |
| 195 | |
| 196 sim_->break_pc_ = NULL; | |
| 197 sim_->break_instr_ = 0; | |
| 198 return true; | |
| 199 } | |
| 200 | |
| 201 | |
| 202 void Debugger::UndoBreakpoints() { | |
| 203 if (sim_->break_pc_ != NULL) { | |
| 204 sim_->break_pc_->SetInstructionBits(sim_->break_instr_); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 | |
| 209 void Debugger::RedoBreakpoints() { | |
| 210 if (sim_->break_pc_ != NULL) { | |
| 211 sim_->break_pc_->SetInstructionBits(kBreakpointInstr); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 | |
| 216 void Debugger::Debug() { | |
| 217 intptr_t last_pc = -1; | |
| 218 bool done = false; | |
| 219 | |
| 220 #define COMMAND_SIZE 63 | |
| 221 #define ARG_SIZE 255 | |
| 222 | |
| 223 #define STR(a) #a | |
| 224 #define XSTR(a) STR(a) | |
| 225 | |
| 226 char cmd[COMMAND_SIZE + 1]; | |
| 227 char arg1[ARG_SIZE + 1]; | |
| 228 char arg2[ARG_SIZE + 1]; | |
| 229 | |
| 230 // make sure to have a proper terminating character if reaching the limit | |
| 231 cmd[COMMAND_SIZE] = 0; | |
| 232 arg1[ARG_SIZE] = 0; | |
| 233 arg2[ARG_SIZE] = 0; | |
| 234 | |
| 235 // Undo all set breakpoints while running in the debugger shell. This will | |
| 236 // make them invisible to all commands. | |
| 237 UndoBreakpoints(); | |
| 238 | |
| 239 while (!done) { | |
| 240 if (last_pc != sim_->get_pc()) { | |
| 241 disasm::NameConverter converter; | |
| 242 disasm::Disassembler dasm(converter); | |
| 243 // use a reasonably large buffer | |
| 244 v8::internal::EmbeddedVector<char, 256> buffer; | |
| 245 dasm.InstructionDecode(buffer, | |
| 246 reinterpret_cast<byte*>(sim_->get_pc())); | |
| 247 PrintF(" 0x%x %s\n", sim_->get_pc(), buffer.start()); | |
| 248 last_pc = sim_->get_pc(); | |
| 249 } | |
| 250 char* line = ReadLine("sim> "); | |
| 251 if (line == NULL) { | |
| 252 break; | |
| 253 } else { | |
| 254 // Use sscanf to parse the individual parts of the command line. At the | |
| 255 // moment no command expects more than two parameters. | |
| 256 int args = SScanF(line, | |
| 257 "%" XSTR(COMMAND_SIZE) "s " | |
| 258 "%" XSTR(ARG_SIZE) "s " | |
| 259 "%" XSTR(ARG_SIZE) "s", | |
| 260 cmd, arg1, arg2); | |
| 261 if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { | |
| 262 sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); | |
| 263 } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { | |
| 264 // Execute the one instruction we broke at with breakpoints disabled. | |
| 265 sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); | |
| 266 // Leave the debugger shell. | |
| 267 done = true; | |
| 268 } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { | |
| 269 if (args == 2) { | |
| 270 int32_t value; | |
| 271 if (GetValue(arg1, &value)) { | |
| 272 PrintF("%s: %d 0x%x\n", arg1, value, value); | |
| 273 } else { | |
| 274 PrintF("%s unrecognized\n", arg1); | |
| 275 } | |
| 276 } else { | |
| 277 PrintF("print value\n"); | |
| 278 } | |
| 279 } else if ((strcmp(cmd, "po") == 0) | |
| 280 || (strcmp(cmd, "printobject") == 0)) { | |
| 281 if (args == 2) { | |
| 282 int32_t value; | |
| 283 if (GetValue(arg1, &value)) { | |
| 284 Object* obj = reinterpret_cast<Object*>(value); | |
| 285 USE(obj); | |
| 286 PrintF("%s: \n", arg1); | |
| 287 #if defined(DEBUG) | |
| 288 obj->PrintLn(); | |
| 289 #endif // defined(DEBUG) | |
| 290 } else { | |
| 291 PrintF("%s unrecognized\n", arg1); | |
| 292 } | |
| 293 } else { | |
| 294 PrintF("printobject value\n"); | |
| 295 } | |
| 296 } else if (strcmp(cmd, "disasm") == 0) { | |
| 297 disasm::NameConverter converter; | |
| 298 disasm::Disassembler dasm(converter); | |
| 299 // use a reasonably large buffer | |
| 300 v8::internal::EmbeddedVector<char, 256> buffer; | |
| 301 | |
| 302 byte* cur = NULL; | |
| 303 byte* end = NULL; | |
| 304 | |
| 305 if (args == 1) { | |
| 306 cur = reinterpret_cast<byte*>(sim_->get_pc()); | |
| 307 end = cur + (10 * Instr::kInstrSize); | |
| 308 } else if (args == 2) { | |
| 309 int32_t value; | |
| 310 if (GetValue(arg1, &value)) { | |
| 311 cur = reinterpret_cast<byte*>(value); | |
| 312 // no length parameter passed, assume 10 instructions | |
| 313 end = cur + (10 * Instr::kInstrSize); | |
| 314 } | |
| 315 } else { | |
| 316 int32_t value1; | |
| 317 int32_t value2; | |
| 318 if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { | |
| 319 cur = reinterpret_cast<byte*>(value1); | |
| 320 end = cur + (value2 * Instr::kInstrSize); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 while (cur < end) { | |
| 325 dasm.InstructionDecode(buffer, cur); | |
| 326 PrintF(" 0x%x %s\n", cur, buffer.start()); | |
| 327 cur += Instr::kInstrSize; | |
| 328 } | |
| 329 } else if (strcmp(cmd, "gdb") == 0) { | |
| 330 PrintF("relinquishing control to gdb\n"); | |
| 331 v8::internal::OS::DebugBreak(); | |
| 332 PrintF("regaining control from gdb\n"); | |
| 333 } else if (strcmp(cmd, "break") == 0) { | |
| 334 if (args == 2) { | |
| 335 int32_t value; | |
| 336 if (GetValue(arg1, &value)) { | |
| 337 if (!SetBreakpoint(reinterpret_cast<Instr*>(value))) { | |
| 338 PrintF("setting breakpoint failed\n"); | |
| 339 } | |
| 340 } else { | |
| 341 PrintF("%s unrecognized\n", arg1); | |
| 342 } | |
| 343 } else { | |
| 344 PrintF("break addr\n"); | |
| 345 } | |
| 346 } else if (strcmp(cmd, "del") == 0) { | |
| 347 if (!DeleteBreakpoint(NULL)) { | |
| 348 PrintF("deleting breakpoint failed\n"); | |
| 349 } | |
| 350 } else if (strcmp(cmd, "flags") == 0) { | |
| 351 PrintF("N flag: %d; ", sim_->n_flag_); | |
| 352 PrintF("Z flag: %d; ", sim_->z_flag_); | |
| 353 PrintF("C flag: %d; ", sim_->c_flag_); | |
| 354 PrintF("V flag: %d\n", sim_->v_flag_); | |
| 355 } else if (strcmp(cmd, "unstop") == 0) { | |
| 356 intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize; | |
| 357 Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc); | |
| 358 if (stop_instr->ConditionField() == special_condition) { | |
| 359 stop_instr->SetInstructionBits(kNopInstr); | |
| 360 } else { | |
| 361 PrintF("Not at debugger stop."); | |
| 362 } | |
| 363 } else { | |
| 364 PrintF("Unknown command: %s\n", cmd); | |
| 365 } | |
| 366 } | |
| 367 DeleteArray(line); | |
| 368 } | |
| 369 | |
| 370 // Add all the breakpoints back to stop execution and enter the debugger | |
| 371 // shell when hit. | |
| 372 RedoBreakpoints(); | |
| 373 | |
| 374 #undef COMMAND_SIZE | |
| 375 #undef ARG_SIZE | |
| 376 | |
| 377 #undef STR | |
| 378 #undef XSTR | |
| 379 } | |
| 380 | |
| 381 | |
| 382 Simulator::Simulator() { | |
| 383 // Setup simulator support first. Some of this information is needed to | |
| 384 // setup the architecture state. | |
| 385 size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack | |
| 386 stack_ = reinterpret_cast<char*>(malloc(stack_size)); | |
| 387 pc_modified_ = false; | |
| 388 icount_ = 0; | |
| 389 break_pc_ = NULL; | |
| 390 break_instr_ = 0; | |
| 391 | |
| 392 // Setup architecture state. | |
| 393 // All registers are initialized to zero to start with. | |
| 394 for (int i = 0; i < num_registers; i++) { | |
| 395 registers_[i] = 0; | |
| 396 } | |
| 397 n_flag_ = false; | |
| 398 z_flag_ = false; | |
| 399 c_flag_ = false; | |
| 400 v_flag_ = false; | |
| 401 | |
| 402 // The sp is initialized to point to the bottom (high address) of the | |
| 403 // allocated stack area. To be safe in potential stack underflows we leave | |
| 404 // some buffer below. | |
| 405 registers_[sp] = reinterpret_cast<int32_t>(stack_) + stack_size - 64; | |
| 406 // The lr and pc are initialized to a known bad value that will cause an | |
| 407 // access violation if the simulator ever tries to execute it. | |
| 408 registers_[pc] = bad_lr; | |
| 409 registers_[lr] = bad_lr; | |
| 410 InitializeCoverage(); | |
| 411 } | |
| 412 | |
| 413 | |
| 414 // Create one simulator per thread and keep it in thread local storage. | |
| 415 static v8::internal::Thread::LocalStorageKey simulator_key = | |
| 416 v8::internal::Thread::CreateThreadLocalKey(); | |
| 417 | |
| 418 // Get the active Simulator for the current thread. | |
| 419 Simulator* Simulator::current() { | |
| 420 Simulator* sim = reinterpret_cast<Simulator*>( | |
| 421 v8::internal::Thread::GetThreadLocal(simulator_key)); | |
| 422 if (sim == NULL) { | |
| 423 // TODO(146): delete the simulator object when a thread goes away. | |
| 424 sim = new Simulator(); | |
| 425 v8::internal::Thread::SetThreadLocal(simulator_key, sim); | |
| 426 } | |
| 427 return sim; | |
| 428 } | |
| 429 | |
| 430 | |
| 431 // Sets the register in the architecture state. It will also deal with updating | |
| 432 // Simulator internal state for special registers such as PC. | |
| 433 void Simulator::set_register(int reg, int32_t value) { | |
| 434 ASSERT((reg >= 0) && (reg < num_registers)); | |
| 435 if (reg == pc) { | |
| 436 pc_modified_ = true; | |
| 437 } | |
| 438 registers_[reg] = value; | |
| 439 } | |
| 440 | |
| 441 | |
| 442 // Get the register from the architecture state. This function does handle | |
| 443 // the special case of accessing the PC register. | |
| 444 int32_t Simulator::get_register(int reg) const { | |
| 445 ASSERT((reg >= 0) && (reg < num_registers)); | |
| 446 return registers_[reg] + ((reg == pc) ? Instr::kPCReadOffset : 0); | |
| 447 } | |
| 448 | |
| 449 | |
| 450 // Raw access to the PC register. | |
| 451 void Simulator::set_pc(int32_t value) { | |
| 452 pc_modified_ = true; | |
| 453 registers_[pc] = value; | |
| 454 } | |
| 455 | |
| 456 | |
| 457 // Raw access to the PC register without the special adjustment when reading. | |
| 458 int32_t Simulator::get_pc() const { | |
| 459 return registers_[pc]; | |
| 460 } | |
| 461 | |
| 462 | |
| 463 // For use in calls that take two double values, constructed from r0, r1, r2 | |
| 464 // and r3. | |
| 465 void Simulator::GetFpArgs(double* x, double* y) { | |
| 466 // We use a char buffer to get around the strict-aliasing rules which | |
| 467 // otherwise allow the compiler to optimize away the copy. | |
| 468 char buffer[2 * sizeof(registers_[0])]; | |
| 469 // Registers 0 and 1 -> x. | |
| 470 memcpy(buffer, registers_, sizeof(buffer)); | |
| 471 memcpy(x, buffer, sizeof(buffer)); | |
| 472 // Registers 2 and 3 -> y. | |
| 473 memcpy(buffer, registers_ + 2, sizeof(buffer)); | |
| 474 memcpy(y, buffer, sizeof(buffer)); | |
| 475 } | |
| 476 | |
| 477 | |
| 478 void Simulator::SetFpResult(const double& result) { | |
| 479 char buffer[2 * sizeof(registers_[0])]; | |
| 480 memcpy(buffer, &result, sizeof(buffer)); | |
| 481 // result -> registers 0 and 1. | |
| 482 memcpy(registers_, buffer, sizeof(buffer)); | |
| 483 } | |
| 484 | |
| 485 | |
| 486 void Simulator::TrashCallerSaveRegisters() { | |
| 487 // We don't trash the registers with the return value. | |
| 488 registers_[2] = 0x50Bad4U; | |
| 489 registers_[3] = 0x50Bad4U; | |
| 490 registers_[12] = 0x50Bad4U; | |
| 491 } | |
| 492 | |
| 493 | |
| 494 // The ARM cannot do unaligned reads and writes. On some ARM platforms an | |
| 495 // interrupt is caused. On others it does a funky rotation thing. For now we | |
| 496 // simply disallow unaligned reads, but at some point we may want to move to | |
| 497 // emulating the rotate behaviour. Note that simulator runs have the runtime | |
| 498 // system running directly on the host system and only generated code is | |
| 499 // executed in the simulator. Since the host is typically IA32 we will not | |
| 500 // get the correct ARM-like behaviour on unaligned accesses. | |
| 501 | |
| 502 int Simulator::ReadW(int32_t addr, Instr* instr) { | |
| 503 if ((addr & 3) == 0) { | |
| 504 intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); | |
| 505 return *ptr; | |
| 506 } | |
| 507 PrintF("Unaligned read at %x\n", addr); | |
| 508 UNIMPLEMENTED(); | |
| 509 return 0; | |
| 510 } | |
| 511 | |
| 512 | |
| 513 void Simulator::WriteW(int32_t addr, int value, Instr* instr) { | |
| 514 if ((addr & 3) == 0) { | |
| 515 intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); | |
| 516 *ptr = value; | |
| 517 return; | |
| 518 } | |
| 519 PrintF("Unaligned write at %x, pc=%p\n", addr, instr); | |
| 520 UNIMPLEMENTED(); | |
| 521 } | |
| 522 | |
| 523 | |
| 524 uint16_t Simulator::ReadHU(int32_t addr, Instr* instr) { | |
| 525 if ((addr & 1) == 0) { | |
| 526 uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); | |
| 527 return *ptr; | |
| 528 } | |
| 529 PrintF("Unaligned read at %x, pc=%p\n", addr, instr); | |
| 530 UNIMPLEMENTED(); | |
| 531 return 0; | |
| 532 } | |
| 533 | |
| 534 | |
| 535 int16_t Simulator::ReadH(int32_t addr, Instr* instr) { | |
| 536 if ((addr & 1) == 0) { | |
| 537 int16_t* ptr = reinterpret_cast<int16_t*>(addr); | |
| 538 return *ptr; | |
| 539 } | |
| 540 PrintF("Unaligned read at %x\n", addr); | |
| 541 UNIMPLEMENTED(); | |
| 542 return 0; | |
| 543 } | |
| 544 | |
| 545 | |
| 546 void Simulator::WriteH(int32_t addr, uint16_t value, Instr* instr) { | |
| 547 if ((addr & 1) == 0) { | |
| 548 uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); | |
| 549 *ptr = value; | |
| 550 return; | |
| 551 } | |
| 552 PrintF("Unaligned write at %x, pc=%p\n", addr, instr); | |
| 553 UNIMPLEMENTED(); | |
| 554 } | |
| 555 | |
| 556 | |
| 557 void Simulator::WriteH(int32_t addr, int16_t value, Instr* instr) { | |
| 558 if ((addr & 1) == 0) { | |
| 559 int16_t* ptr = reinterpret_cast<int16_t*>(addr); | |
| 560 *ptr = value; | |
| 561 return; | |
| 562 } | |
| 563 PrintF("Unaligned write at %x, pc=%p\n", addr, instr); | |
| 564 UNIMPLEMENTED(); | |
| 565 } | |
| 566 | |
| 567 | |
| 568 uint8_t Simulator::ReadBU(int32_t addr) { | |
| 569 uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); | |
| 570 return *ptr; | |
| 571 } | |
| 572 | |
| 573 | |
| 574 int8_t Simulator::ReadB(int32_t addr) { | |
| 575 int8_t* ptr = reinterpret_cast<int8_t*>(addr); | |
| 576 return *ptr; | |
| 577 } | |
| 578 | |
| 579 | |
| 580 void Simulator::WriteB(int32_t addr, uint8_t value) { | |
| 581 uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); | |
| 582 *ptr = value; | |
| 583 } | |
| 584 | |
| 585 | |
| 586 void Simulator::WriteB(int32_t addr, int8_t value) { | |
| 587 int8_t* ptr = reinterpret_cast<int8_t*>(addr); | |
| 588 *ptr = value; | |
| 589 } | |
| 590 | |
| 591 | |
| 592 // Returns the limit of the stack area to enable checking for stack overflows. | |
| 593 uintptr_t Simulator::StackLimit() const { | |
| 594 // Leave a safety margin of 256 bytes to prevent overrunning the stack when | |
| 595 // pushing values. | |
| 596 return reinterpret_cast<uintptr_t>(stack_) + 256; | |
| 597 } | |
| 598 | |
| 599 | |
| 600 // Unsupported instructions use Format to print an error and stop execution. | |
| 601 void Simulator::Format(Instr* instr, const char* format) { | |
| 602 PrintF("Simulator found unsupported instruction:\n 0x%x: %s\n", | |
| 603 instr, format); | |
| 604 UNIMPLEMENTED(); | |
| 605 } | |
| 606 | |
| 607 | |
| 608 // Checks if the current instruction should be executed based on its | |
| 609 // condition bits. | |
| 610 bool Simulator::ConditionallyExecute(Instr* instr) { | |
| 611 switch (instr->ConditionField()) { | |
| 612 case EQ: return z_flag_; | |
| 613 case NE: return !z_flag_; | |
| 614 case CS: return c_flag_; | |
| 615 case CC: return !c_flag_; | |
| 616 case MI: return n_flag_; | |
| 617 case PL: return !n_flag_; | |
| 618 case VS: return v_flag_; | |
| 619 case VC: return !v_flag_; | |
| 620 case HI: return c_flag_ && !z_flag_; | |
| 621 case LS: return !c_flag_ || z_flag_; | |
| 622 case GE: return n_flag_ == v_flag_; | |
| 623 case LT: return n_flag_ != v_flag_; | |
| 624 case GT: return !z_flag_ && (n_flag_ == v_flag_); | |
| 625 case LE: return z_flag_ || (n_flag_ != v_flag_); | |
| 626 case AL: return true; | |
| 627 default: UNREACHABLE(); | |
| 628 } | |
| 629 return false; | |
| 630 } | |
| 631 | |
| 632 | |
| 633 // Calculate and set the Negative and Zero flags. | |
| 634 void Simulator::SetNZFlags(int32_t val) { | |
| 635 n_flag_ = (val < 0); | |
| 636 z_flag_ = (val == 0); | |
| 637 } | |
| 638 | |
| 639 | |
| 640 // Set the Carry flag. | |
| 641 void Simulator::SetCFlag(bool val) { | |
| 642 c_flag_ = val; | |
| 643 } | |
| 644 | |
| 645 | |
| 646 // Set the oVerflow flag. | |
| 647 void Simulator::SetVFlag(bool val) { | |
| 648 v_flag_ = val; | |
| 649 } | |
| 650 | |
| 651 | |
| 652 // Calculate C flag value for additions. | |
| 653 bool Simulator::CarryFrom(int32_t left, int32_t right) { | |
| 654 uint32_t uleft = static_cast<uint32_t>(left); | |
| 655 uint32_t uright = static_cast<uint32_t>(right); | |
| 656 uint32_t urest = 0xffffffffU - uleft; | |
| 657 | |
| 658 return (uright > urest); | |
| 659 } | |
| 660 | |
| 661 | |
| 662 // Calculate C flag value for subtractions. | |
| 663 bool Simulator::BorrowFrom(int32_t left, int32_t right) { | |
| 664 uint32_t uleft = static_cast<uint32_t>(left); | |
| 665 uint32_t uright = static_cast<uint32_t>(right); | |
| 666 | |
| 667 return (uright > uleft); | |
| 668 } | |
| 669 | |
| 670 | |
| 671 // Calculate V flag value for additions and subtractions. | |
| 672 bool Simulator::OverflowFrom(int32_t alu_out, | |
| 673 int32_t left, int32_t right, bool addition) { | |
| 674 bool overflow; | |
| 675 if (addition) { | |
| 676 // operands have the same sign | |
| 677 overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) | |
| 678 // and operands and result have different sign | |
| 679 && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); | |
| 680 } else { | |
| 681 // operands have different signs | |
| 682 overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) | |
| 683 // and first operand and result have different signs | |
| 684 && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); | |
| 685 } | |
| 686 return overflow; | |
| 687 } | |
| 688 | |
| 689 | |
| 690 // Addressing Mode 1 - Data-processing operands: | |
| 691 // Get the value based on the shifter_operand with register. | |
| 692 int32_t Simulator::GetShiftRm(Instr* instr, bool* carry_out) { | |
| 693 Shift shift = instr->ShiftField(); | |
| 694 int shift_amount = instr->ShiftAmountField(); | |
| 695 int32_t result = get_register(instr->RmField()); | |
| 696 if (instr->Bit(4) == 0) { | |
| 697 // by immediate | |
| 698 if ((shift == ROR) && (shift_amount == 0)) { | |
| 699 UNIMPLEMENTED(); | |
| 700 return result; | |
| 701 } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { | |
| 702 shift_amount = 32; | |
| 703 } | |
| 704 switch (shift) { | |
| 705 case ASR: { | |
| 706 if (shift_amount == 0) { | |
| 707 if (result < 0) { | |
| 708 result = 0xffffffff; | |
| 709 *carry_out = true; | |
| 710 } else { | |
| 711 result = 0; | |
| 712 *carry_out = false; | |
| 713 } | |
| 714 } else { | |
| 715 result >>= (shift_amount - 1); | |
| 716 *carry_out = (result & 1) == 1; | |
| 717 result >>= 1; | |
| 718 } | |
| 719 break; | |
| 720 } | |
| 721 | |
| 722 case LSL: { | |
| 723 if (shift_amount == 0) { | |
| 724 *carry_out = c_flag_; | |
| 725 } else { | |
| 726 result <<= (shift_amount - 1); | |
| 727 *carry_out = (result < 0); | |
| 728 result <<= 1; | |
| 729 } | |
| 730 break; | |
| 731 } | |
| 732 | |
| 733 case LSR: { | |
| 734 if (shift_amount == 0) { | |
| 735 result = 0; | |
| 736 *carry_out = c_flag_; | |
| 737 } else { | |
| 738 uint32_t uresult = static_cast<uint32_t>(result); | |
| 739 uresult >>= (shift_amount - 1); | |
| 740 *carry_out = (uresult & 1) == 1; | |
| 741 uresult >>= 1; | |
| 742 result = static_cast<int32_t>(uresult); | |
| 743 } | |
| 744 break; | |
| 745 } | |
| 746 | |
| 747 case ROR: { | |
| 748 UNIMPLEMENTED(); | |
| 749 break; | |
| 750 } | |
| 751 | |
| 752 default: { | |
| 753 UNREACHABLE(); | |
| 754 break; | |
| 755 } | |
| 756 } | |
| 757 } else { | |
| 758 // by register | |
| 759 int rs = instr->RsField(); | |
| 760 shift_amount = get_register(rs) &0xff; | |
| 761 switch (shift) { | |
| 762 case ASR: { | |
| 763 if (shift_amount == 0) { | |
| 764 *carry_out = c_flag_; | |
| 765 } else if (shift_amount < 32) { | |
| 766 result >>= (shift_amount - 1); | |
| 767 *carry_out = (result & 1) == 1; | |
| 768 result >>= 1; | |
| 769 } else { | |
| 770 ASSERT(shift_amount >= 32); | |
| 771 if (result < 0) { | |
| 772 *carry_out = true; | |
| 773 result = 0xffffffff; | |
| 774 } else { | |
| 775 *carry_out = false; | |
| 776 result = 0; | |
| 777 } | |
| 778 } | |
| 779 break; | |
| 780 } | |
| 781 | |
| 782 case LSL: { | |
| 783 if (shift_amount == 0) { | |
| 784 *carry_out = c_flag_; | |
| 785 } else if (shift_amount < 32) { | |
| 786 result <<= (shift_amount - 1); | |
| 787 *carry_out = (result < 0); | |
| 788 result <<= 1; | |
| 789 } else if (shift_amount == 32) { | |
| 790 *carry_out = (result & 1) == 1; | |
| 791 result = 0; | |
| 792 } else { | |
| 793 ASSERT(shift_amount > 32); | |
| 794 *carry_out = false; | |
| 795 result = 0; | |
| 796 } | |
| 797 break; | |
| 798 } | |
| 799 | |
| 800 case LSR: { | |
| 801 if (shift_amount == 0) { | |
| 802 *carry_out = c_flag_; | |
| 803 } else if (shift_amount < 32) { | |
| 804 uint32_t uresult = static_cast<uint32_t>(result); | |
| 805 uresult >>= (shift_amount - 1); | |
| 806 *carry_out = (uresult & 1) == 1; | |
| 807 uresult >>= 1; | |
| 808 result = static_cast<int32_t>(uresult); | |
| 809 } else if (shift_amount == 32) { | |
| 810 *carry_out = (result < 0); | |
| 811 result = 0; | |
| 812 } else { | |
| 813 *carry_out = false; | |
| 814 result = 0; | |
| 815 } | |
| 816 break; | |
| 817 } | |
| 818 | |
| 819 case ROR: { | |
| 820 UNIMPLEMENTED(); | |
| 821 break; | |
| 822 } | |
| 823 | |
| 824 default: { | |
| 825 UNREACHABLE(); | |
| 826 break; | |
| 827 } | |
| 828 } | |
| 829 } | |
| 830 return result; | |
| 831 } | |
| 832 | |
| 833 | |
| 834 // Addressing Mode 1 - Data-processing operands: | |
| 835 // Get the value based on the shifter_operand with immediate. | |
| 836 int32_t Simulator::GetImm(Instr* instr, bool* carry_out) { | |
| 837 int rotate = instr->RotateField() * 2; | |
| 838 int immed8 = instr->Immed8Field(); | |
| 839 int imm = (immed8 >> rotate) | (immed8 << (32 - rotate)); | |
| 840 *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); | |
| 841 return imm; | |
| 842 } | |
| 843 | |
| 844 | |
| 845 static int count_bits(int bit_vector) { | |
| 846 int count = 0; | |
| 847 while (bit_vector != 0) { | |
| 848 if ((bit_vector & 1) != 0) { | |
| 849 count++; | |
| 850 } | |
| 851 bit_vector >>= 1; | |
| 852 } | |
| 853 return count; | |
| 854 } | |
| 855 | |
| 856 | |
| 857 // Addressing Mode 4 - Load and Store Multiple | |
| 858 void Simulator::HandleRList(Instr* instr, bool load) { | |
| 859 int rn = instr->RnField(); | |
| 860 int32_t rn_val = get_register(rn); | |
| 861 int rlist = instr->RlistField(); | |
| 862 int num_regs = count_bits(rlist); | |
| 863 | |
| 864 intptr_t start_address = 0; | |
| 865 intptr_t end_address = 0; | |
| 866 switch (instr->PUField()) { | |
| 867 case 0: { | |
| 868 // Print("da"); | |
| 869 UNIMPLEMENTED(); | |
| 870 break; | |
| 871 } | |
| 872 case 1: { | |
| 873 // Print("ia"); | |
| 874 start_address = rn_val; | |
| 875 end_address = rn_val + (num_regs * 4) - 4; | |
| 876 rn_val = rn_val + (num_regs * 4); | |
| 877 break; | |
| 878 } | |
| 879 case 2: { | |
| 880 // Print("db"); | |
| 881 start_address = rn_val - (num_regs * 4); | |
| 882 end_address = rn_val - 4; | |
| 883 rn_val = start_address; | |
| 884 break; | |
| 885 } | |
| 886 case 3: { | |
| 887 // Print("ib"); | |
| 888 UNIMPLEMENTED(); | |
| 889 break; | |
| 890 } | |
| 891 default: { | |
| 892 UNREACHABLE(); | |
| 893 break; | |
| 894 } | |
| 895 } | |
| 896 if (instr->HasW()) { | |
| 897 set_register(rn, rn_val); | |
| 898 } | |
| 899 intptr_t* address = reinterpret_cast<intptr_t*>(start_address); | |
| 900 int reg = 0; | |
| 901 while (rlist != 0) { | |
| 902 if ((rlist & 1) != 0) { | |
| 903 if (load) { | |
| 904 set_register(reg, *address); | |
| 905 } else { | |
| 906 *address = get_register(reg); | |
| 907 } | |
| 908 address += 1; | |
| 909 } | |
| 910 reg++; | |
| 911 rlist >>= 1; | |
| 912 } | |
| 913 ASSERT(end_address == ((intptr_t)address) - 4); | |
| 914 } | |
| 915 | |
| 916 | |
| 917 // Calls into the V8 runtime are based on this very simple interface. | |
| 918 // Note: To be able to return two values from some calls the code in runtime.cc | |
| 919 // uses the ObjectPair which is essentially two 32-bit values stuffed into a | |
| 920 // 64-bit value. With the code below we assume that all runtime calls return | |
| 921 // 64 bits of result. If they don't, the r1 result register contains a bogus | |
| 922 // value, which is fine because it is caller-saved. | |
| 923 typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0, intptr_t arg1); | |
| 924 | |
| 925 | |
| 926 // Software interrupt instructions are used by the simulator to call into the | |
| 927 // C-based V8 runtime. | |
| 928 void Simulator::SoftwareInterrupt(Instr* instr) { | |
| 929 int swi = instr->SwiField(); | |
| 930 switch (swi) { | |
| 931 case call_rt_r5: { | |
| 932 SimulatorRuntimeCall target = | |
| 933 reinterpret_cast<SimulatorRuntimeCall>(get_register(r5)); | |
| 934 intptr_t arg0 = get_register(r0); | |
| 935 intptr_t arg1 = get_register(r1); | |
| 936 int64_t result = target(arg0, arg1); | |
| 937 int32_t lo_res = static_cast<int32_t>(result); | |
| 938 int32_t hi_res = static_cast<int32_t>(result >> 32); | |
| 939 set_register(r0, lo_res); | |
| 940 set_register(r1, hi_res); | |
| 941 set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); | |
| 942 break; | |
| 943 } | |
| 944 case call_rt_r2: { | |
| 945 SimulatorRuntimeCall target = | |
| 946 reinterpret_cast<SimulatorRuntimeCall>(get_register(r2)); | |
| 947 intptr_t arg0 = get_register(r0); | |
| 948 intptr_t arg1 = get_register(r1); | |
| 949 int64_t result = target(arg0, arg1); | |
| 950 int32_t lo_res = static_cast<int32_t>(result); | |
| 951 int32_t hi_res = static_cast<int32_t>(result >> 32); | |
| 952 set_register(r0, lo_res); | |
| 953 set_register(r1, hi_res); | |
| 954 set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); | |
| 955 break; | |
| 956 } | |
| 957 case break_point: { | |
| 958 Debugger dbg(this); | |
| 959 dbg.Debug(); | |
| 960 break; | |
| 961 } | |
| 962 { | |
| 963 double x, y, z; | |
| 964 case simulator_fp_add: | |
| 965 GetFpArgs(&x, &y); | |
| 966 z = x + y; | |
| 967 SetFpResult(z); | |
| 968 TrashCallerSaveRegisters(); | |
| 969 set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); | |
| 970 break; | |
| 971 case simulator_fp_sub: | |
| 972 GetFpArgs(&x, &y); | |
| 973 z = x - y; | |
| 974 SetFpResult(z); | |
| 975 TrashCallerSaveRegisters(); | |
| 976 set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); | |
| 977 break; | |
| 978 case simulator_fp_mul: | |
| 979 GetFpArgs(&x, &y); | |
| 980 z = x * y; | |
| 981 SetFpResult(z); | |
| 982 TrashCallerSaveRegisters(); | |
| 983 set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); | |
| 984 break; | |
| 985 } | |
| 986 default: { | |
| 987 UNREACHABLE(); | |
| 988 break; | |
| 989 } | |
| 990 } | |
| 991 } | |
| 992 | |
| 993 | |
| 994 // Handle execution based on instruction types. | |
| 995 | |
| 996 // Instruction types 0 and 1 are both rolled into one function because they | |
| 997 // only differ in the handling of the shifter_operand. | |
| 998 void Simulator::DecodeType01(Instr* instr) { | |
| 999 int type = instr->TypeField(); | |
| 1000 if ((type == 0) && instr->IsSpecialType0()) { | |
| 1001 // multiply instruction or extra loads and stores | |
| 1002 if (instr->Bits(7, 4) == 9) { | |
| 1003 if (instr->Bit(24) == 0) { | |
| 1004 // multiply instructions | |
| 1005 int rd = instr->RdField(); | |
| 1006 int rm = instr->RmField(); | |
| 1007 int rs = instr->RsField(); | |
| 1008 int32_t rs_val = get_register(rs); | |
| 1009 int32_t rm_val = get_register(rm); | |
| 1010 if (instr->Bit(23) == 0) { | |
| 1011 if (instr->Bit(21) == 0) { | |
| 1012 // Format(instr, "mul'cond's 'rd, 'rm, 'rs"); | |
| 1013 int32_t alu_out = rm_val * rs_val; | |
| 1014 set_register(rd, alu_out); | |
| 1015 if (instr->HasS()) { | |
| 1016 SetNZFlags(alu_out); | |
| 1017 } | |
| 1018 } else { | |
| 1019 Format(instr, "mla'cond's 'rd, 'rm, 'rs, 'rn"); | |
| 1020 } | |
| 1021 } else { | |
| 1022 // Format(instr, "'um'al'cond's 'rn, 'rd, 'rs, 'rm"); | |
| 1023 int rn = instr->RnField(); | |
| 1024 int32_t hi_res = 0; | |
| 1025 int32_t lo_res = 0; | |
| 1026 if (instr->Bit(22) == 0) { | |
| 1027 // signed multiply | |
| 1028 UNIMPLEMENTED(); | |
| 1029 } else { | |
| 1030 // unsigned multiply | |
| 1031 uint64_t left_op = rm_val; | |
| 1032 uint64_t right_op = rs_val; | |
| 1033 uint64_t result = left_op * right_op; | |
| 1034 hi_res = static_cast<int32_t>(result >> 32); | |
| 1035 lo_res = static_cast<int32_t>(result & 0xffffffff); | |
| 1036 } | |
| 1037 set_register(rn, hi_res); | |
| 1038 set_register(rd, lo_res); | |
| 1039 if (instr->HasS()) { | |
| 1040 UNIMPLEMENTED(); | |
| 1041 } | |
| 1042 } | |
| 1043 } else { | |
| 1044 UNIMPLEMENTED(); // not used by V8 | |
| 1045 } | |
| 1046 } else { | |
| 1047 // extra load/store instructions | |
| 1048 int rd = instr->RdField(); | |
| 1049 int rn = instr->RnField(); | |
| 1050 int32_t rn_val = get_register(rn); | |
| 1051 int32_t addr = 0; | |
| 1052 if (instr->Bit(22) == 0) { | |
| 1053 int rm = instr->RmField(); | |
| 1054 int32_t rm_val = get_register(rm); | |
| 1055 switch (instr->PUField()) { | |
| 1056 case 0: { | |
| 1057 // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm"); | |
| 1058 ASSERT(!instr->HasW()); | |
| 1059 addr = rn_val; | |
| 1060 rn_val -= rm_val; | |
| 1061 set_register(rn, rn_val); | |
| 1062 break; | |
| 1063 } | |
| 1064 case 1: { | |
| 1065 // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm"); | |
| 1066 ASSERT(!instr->HasW()); | |
| 1067 addr = rn_val; | |
| 1068 rn_val += rm_val; | |
| 1069 set_register(rn, rn_val); | |
| 1070 break; | |
| 1071 } | |
| 1072 case 2: { | |
| 1073 // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w"); | |
| 1074 rn_val -= rm_val; | |
| 1075 addr = rn_val; | |
| 1076 if (instr->HasW()) { | |
| 1077 set_register(rn, rn_val); | |
| 1078 } | |
| 1079 break; | |
| 1080 } | |
| 1081 case 3: { | |
| 1082 // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w"); | |
| 1083 rn_val += rm_val; | |
| 1084 addr = rn_val; | |
| 1085 if (instr->HasW()) { | |
| 1086 set_register(rn, rn_val); | |
| 1087 } | |
| 1088 break; | |
| 1089 } | |
| 1090 default: { | |
| 1091 // The PU field is a 2-bit field. | |
| 1092 UNREACHABLE(); | |
| 1093 break; | |
| 1094 } | |
| 1095 } | |
| 1096 } else { | |
| 1097 int32_t imm_val = (instr->ImmedHField() << 4) | instr->ImmedLField(); | |
| 1098 switch (instr->PUField()) { | |
| 1099 case 0: { | |
| 1100 // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8"); | |
| 1101 ASSERT(!instr->HasW()); | |
| 1102 addr = rn_val; | |
| 1103 rn_val -= imm_val; | |
| 1104 set_register(rn, rn_val); | |
| 1105 break; | |
| 1106 } | |
| 1107 case 1: { | |
| 1108 // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8"); | |
| 1109 ASSERT(!instr->HasW()); | |
| 1110 addr = rn_val; | |
| 1111 rn_val += imm_val; | |
| 1112 set_register(rn, rn_val); | |
| 1113 break; | |
| 1114 } | |
| 1115 case 2: { | |
| 1116 // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w"); | |
| 1117 rn_val -= imm_val; | |
| 1118 addr = rn_val; | |
| 1119 if (instr->HasW()) { | |
| 1120 set_register(rn, rn_val); | |
| 1121 } | |
| 1122 break; | |
| 1123 } | |
| 1124 case 3: { | |
| 1125 // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w"); | |
| 1126 rn_val += imm_val; | |
| 1127 addr = rn_val; | |
| 1128 if (instr->HasW()) { | |
| 1129 set_register(rn, rn_val); | |
| 1130 } | |
| 1131 break; | |
| 1132 } | |
| 1133 default: { | |
| 1134 // The PU field is a 2-bit field. | |
| 1135 UNREACHABLE(); | |
| 1136 break; | |
| 1137 } | |
| 1138 } | |
| 1139 } | |
| 1140 if (instr->HasH()) { | |
| 1141 if (instr->HasSign()) { | |
| 1142 if (instr->HasL()) { | |
| 1143 int16_t val = ReadH(addr, instr); | |
| 1144 set_register(rd, val); | |
| 1145 } else { | |
| 1146 int16_t val = get_register(rd); | |
| 1147 WriteH(addr, val, instr); | |
| 1148 } | |
| 1149 } else { | |
| 1150 if (instr->HasL()) { | |
| 1151 uint16_t val = ReadHU(addr, instr); | |
| 1152 set_register(rd, val); | |
| 1153 } else { | |
| 1154 uint16_t val = get_register(rd); | |
| 1155 WriteH(addr, val, instr); | |
| 1156 } | |
| 1157 } | |
| 1158 } else { | |
| 1159 // signed byte loads | |
| 1160 ASSERT(instr->HasSign()); | |
| 1161 ASSERT(instr->HasL()); | |
| 1162 int8_t val = ReadB(addr); | |
| 1163 set_register(rd, val); | |
| 1164 } | |
| 1165 return; | |
| 1166 } | |
| 1167 } else { | |
| 1168 int rd = instr->RdField(); | |
| 1169 int rn = instr->RnField(); | |
| 1170 int32_t rn_val = get_register(rn); | |
| 1171 int32_t shifter_operand = 0; | |
| 1172 bool shifter_carry_out = 0; | |
| 1173 if (type == 0) { | |
| 1174 shifter_operand = GetShiftRm(instr, &shifter_carry_out); | |
| 1175 } else { | |
| 1176 ASSERT(instr->TypeField() == 1); | |
| 1177 shifter_operand = GetImm(instr, &shifter_carry_out); | |
| 1178 } | |
| 1179 int32_t alu_out; | |
| 1180 | |
| 1181 switch (instr->OpcodeField()) { | |
| 1182 case AND: { | |
| 1183 // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm"); | |
| 1184 // Format(instr, "and'cond's 'rd, 'rn, 'imm"); | |
| 1185 alu_out = rn_val & shifter_operand; | |
| 1186 set_register(rd, alu_out); | |
| 1187 if (instr->HasS()) { | |
| 1188 SetNZFlags(alu_out); | |
| 1189 SetCFlag(shifter_carry_out); | |
| 1190 } | |
| 1191 break; | |
| 1192 } | |
| 1193 | |
| 1194 case EOR: { | |
| 1195 // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm"); | |
| 1196 // Format(instr, "eor'cond's 'rd, 'rn, 'imm"); | |
| 1197 alu_out = rn_val ^ shifter_operand; | |
| 1198 set_register(rd, alu_out); | |
| 1199 if (instr->HasS()) { | |
| 1200 SetNZFlags(alu_out); | |
| 1201 SetCFlag(shifter_carry_out); | |
| 1202 } | |
| 1203 break; | |
| 1204 } | |
| 1205 | |
| 1206 case SUB: { | |
| 1207 // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm"); | |
| 1208 // Format(instr, "sub'cond's 'rd, 'rn, 'imm"); | |
| 1209 alu_out = rn_val - shifter_operand; | |
| 1210 set_register(rd, alu_out); | |
| 1211 if (instr->HasS()) { | |
| 1212 SetNZFlags(alu_out); | |
| 1213 SetCFlag(!BorrowFrom(rn_val, shifter_operand)); | |
| 1214 SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); | |
| 1215 } | |
| 1216 break; | |
| 1217 } | |
| 1218 | |
| 1219 case RSB: { | |
| 1220 // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm"); | |
| 1221 // Format(instr, "rsb'cond's 'rd, 'rn, 'imm"); | |
| 1222 alu_out = shifter_operand - rn_val; | |
| 1223 set_register(rd, alu_out); | |
| 1224 if (instr->HasS()) { | |
| 1225 SetNZFlags(alu_out); | |
| 1226 SetCFlag(!BorrowFrom(shifter_operand, rn_val)); | |
| 1227 SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false)); | |
| 1228 } | |
| 1229 break; | |
| 1230 } | |
| 1231 | |
| 1232 case ADD: { | |
| 1233 // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm"); | |
| 1234 // Format(instr, "add'cond's 'rd, 'rn, 'imm"); | |
| 1235 alu_out = rn_val + shifter_operand; | |
| 1236 set_register(rd, alu_out); | |
| 1237 if (instr->HasS()) { | |
| 1238 SetNZFlags(alu_out); | |
| 1239 SetCFlag(CarryFrom(rn_val, shifter_operand)); | |
| 1240 SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); | |
| 1241 } | |
| 1242 break; | |
| 1243 } | |
| 1244 | |
| 1245 case ADC: { | |
| 1246 Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm"); | |
| 1247 Format(instr, "adc'cond's 'rd, 'rn, 'imm"); | |
| 1248 break; | |
| 1249 } | |
| 1250 | |
| 1251 case SBC: { | |
| 1252 Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm"); | |
| 1253 Format(instr, "sbc'cond's 'rd, 'rn, 'imm"); | |
| 1254 break; | |
| 1255 } | |
| 1256 | |
| 1257 case RSC: { | |
| 1258 Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm"); | |
| 1259 Format(instr, "rsc'cond's 'rd, 'rn, 'imm"); | |
| 1260 break; | |
| 1261 } | |
| 1262 | |
| 1263 case TST: { | |
| 1264 if (instr->HasS()) { | |
| 1265 // Format(instr, "tst'cond 'rn, 'shift_rm"); | |
| 1266 // Format(instr, "tst'cond 'rn, 'imm"); | |
| 1267 alu_out = rn_val & shifter_operand; | |
| 1268 SetNZFlags(alu_out); | |
| 1269 SetCFlag(shifter_carry_out); | |
| 1270 } else { | |
| 1271 UNIMPLEMENTED(); | |
| 1272 } | |
| 1273 break; | |
| 1274 } | |
| 1275 | |
| 1276 case TEQ: { | |
| 1277 if (instr->HasS()) { | |
| 1278 // Format(instr, "teq'cond 'rn, 'shift_rm"); | |
| 1279 // Format(instr, "teq'cond 'rn, 'imm"); | |
| 1280 alu_out = rn_val ^ shifter_operand; | |
| 1281 SetNZFlags(alu_out); | |
| 1282 SetCFlag(shifter_carry_out); | |
| 1283 } else { | |
| 1284 UNIMPLEMENTED(); | |
| 1285 } | |
| 1286 break; | |
| 1287 } | |
| 1288 | |
| 1289 case CMP: { | |
| 1290 if (instr->HasS()) { | |
| 1291 // Format(instr, "cmp'cond 'rn, 'shift_rm"); | |
| 1292 // Format(instr, "cmp'cond 'rn, 'imm"); | |
| 1293 alu_out = rn_val - shifter_operand; | |
| 1294 SetNZFlags(alu_out); | |
| 1295 SetCFlag(!BorrowFrom(rn_val, shifter_operand)); | |
| 1296 SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); | |
| 1297 } else { | |
| 1298 UNIMPLEMENTED(); | |
| 1299 } | |
| 1300 break; | |
| 1301 } | |
| 1302 | |
| 1303 case CMN: { | |
| 1304 if (instr->HasS()) { | |
| 1305 Format(instr, "cmn'cond 'rn, 'shift_rm"); | |
| 1306 Format(instr, "cmn'cond 'rn, 'imm"); | |
| 1307 } else { | |
| 1308 UNIMPLEMENTED(); | |
| 1309 } | |
| 1310 break; | |
| 1311 } | |
| 1312 | |
| 1313 case ORR: { | |
| 1314 // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm"); | |
| 1315 // Format(instr, "orr'cond's 'rd, 'rn, 'imm"); | |
| 1316 alu_out = rn_val | shifter_operand; | |
| 1317 set_register(rd, alu_out); | |
| 1318 if (instr->HasS()) { | |
| 1319 SetNZFlags(alu_out); | |
| 1320 SetCFlag(shifter_carry_out); | |
| 1321 } | |
| 1322 break; | |
| 1323 } | |
| 1324 | |
| 1325 case MOV: { | |
| 1326 // Format(instr, "mov'cond's 'rd, 'shift_rm"); | |
| 1327 // Format(instr, "mov'cond's 'rd, 'imm"); | |
| 1328 alu_out = shifter_operand; | |
| 1329 set_register(rd, alu_out); | |
| 1330 if (instr->HasS()) { | |
| 1331 SetNZFlags(alu_out); | |
| 1332 SetCFlag(shifter_carry_out); | |
| 1333 } | |
| 1334 break; | |
| 1335 } | |
| 1336 | |
| 1337 case BIC: { | |
| 1338 // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm"); | |
| 1339 // Format(instr, "bic'cond's 'rd, 'rn, 'imm"); | |
| 1340 alu_out = rn_val & ~shifter_operand; | |
| 1341 set_register(rd, alu_out); | |
| 1342 if (instr->HasS()) { | |
| 1343 SetNZFlags(alu_out); | |
| 1344 SetCFlag(shifter_carry_out); | |
| 1345 } | |
| 1346 break; | |
| 1347 } | |
| 1348 | |
| 1349 case MVN: { | |
| 1350 // Format(instr, "mvn'cond's 'rd, 'shift_rm"); | |
| 1351 // Format(instr, "mvn'cond's 'rd, 'imm"); | |
| 1352 alu_out = ~shifter_operand; | |
| 1353 set_register(rd, alu_out); | |
| 1354 if (instr->HasS()) { | |
| 1355 SetNZFlags(alu_out); | |
| 1356 SetCFlag(shifter_carry_out); | |
| 1357 } | |
| 1358 break; | |
| 1359 } | |
| 1360 | |
| 1361 default: { | |
| 1362 UNREACHABLE(); | |
| 1363 break; | |
| 1364 } | |
| 1365 } | |
| 1366 } | |
| 1367 } | |
| 1368 | |
| 1369 | |
| 1370 void Simulator::DecodeType2(Instr* instr) { | |
| 1371 int rd = instr->RdField(); | |
| 1372 int rn = instr->RnField(); | |
| 1373 int32_t rn_val = get_register(rn); | |
| 1374 int32_t im_val = instr->Offset12Field(); | |
| 1375 int32_t addr = 0; | |
| 1376 switch (instr->PUField()) { | |
| 1377 case 0: { | |
| 1378 // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); | |
| 1379 ASSERT(!instr->HasW()); | |
| 1380 addr = rn_val; | |
| 1381 rn_val -= im_val; | |
| 1382 set_register(rn, rn_val); | |
| 1383 break; | |
| 1384 } | |
| 1385 case 1: { | |
| 1386 // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); | |
| 1387 ASSERT(!instr->HasW()); | |
| 1388 addr = rn_val; | |
| 1389 rn_val += im_val; | |
| 1390 set_register(rn, rn_val); | |
| 1391 break; | |
| 1392 } | |
| 1393 case 2: { | |
| 1394 // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); | |
| 1395 rn_val -= im_val; | |
| 1396 addr = rn_val; | |
| 1397 if (instr->HasW()) { | |
| 1398 set_register(rn, rn_val); | |
| 1399 } | |
| 1400 break; | |
| 1401 } | |
| 1402 case 3: { | |
| 1403 // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); | |
| 1404 rn_val += im_val; | |
| 1405 addr = rn_val; | |
| 1406 if (instr->HasW()) { | |
| 1407 set_register(rn, rn_val); | |
| 1408 } | |
| 1409 break; | |
| 1410 } | |
| 1411 default: { | |
| 1412 UNREACHABLE(); | |
| 1413 break; | |
| 1414 } | |
| 1415 } | |
| 1416 if (instr->HasB()) { | |
| 1417 if (instr->HasL()) { | |
| 1418 byte val = ReadBU(addr); | |
| 1419 set_register(rd, val); | |
| 1420 } else { | |
| 1421 byte val = get_register(rd); | |
| 1422 WriteB(addr, val); | |
| 1423 } | |
| 1424 } else { | |
| 1425 if (instr->HasL()) { | |
| 1426 set_register(rd, ReadW(addr, instr)); | |
| 1427 } else { | |
| 1428 WriteW(addr, get_register(rd), instr); | |
| 1429 } | |
| 1430 } | |
| 1431 } | |
| 1432 | |
| 1433 | |
| 1434 void Simulator::DecodeType3(Instr* instr) { | |
| 1435 int rd = instr->RdField(); | |
| 1436 int rn = instr->RnField(); | |
| 1437 int32_t rn_val = get_register(rn); | |
| 1438 bool shifter_carry_out = 0; | |
| 1439 int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out); | |
| 1440 int32_t addr = 0; | |
| 1441 switch (instr->PUField()) { | |
| 1442 case 0: { | |
| 1443 ASSERT(!instr->HasW()); | |
| 1444 Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); | |
| 1445 break; | |
| 1446 } | |
| 1447 case 1: { | |
| 1448 ASSERT(!instr->HasW()); | |
| 1449 Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm"); | |
| 1450 break; | |
| 1451 } | |
| 1452 case 2: { | |
| 1453 // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); | |
| 1454 addr = rn_val - shifter_operand; | |
| 1455 if (instr->HasW()) { | |
| 1456 set_register(rn, addr); | |
| 1457 } | |
| 1458 break; | |
| 1459 } | |
| 1460 case 3: { | |
| 1461 // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); | |
| 1462 addr = rn_val + shifter_operand; | |
| 1463 if (instr->HasW()) { | |
| 1464 set_register(rn, addr); | |
| 1465 } | |
| 1466 break; | |
| 1467 } | |
| 1468 default: { | |
| 1469 UNREACHABLE(); | |
| 1470 break; | |
| 1471 } | |
| 1472 } | |
| 1473 if (instr->HasB()) { | |
| 1474 UNIMPLEMENTED(); | |
| 1475 } else { | |
| 1476 if (instr->HasL()) { | |
| 1477 set_register(rd, ReadW(addr, instr)); | |
| 1478 } else { | |
| 1479 WriteW(addr, get_register(rd), instr); | |
| 1480 } | |
| 1481 } | |
| 1482 } | |
| 1483 | |
| 1484 | |
| 1485 void Simulator::DecodeType4(Instr* instr) { | |
| 1486 ASSERT(instr->Bit(22) == 0); // only allowed to be set in privileged mode | |
| 1487 if (instr->HasL()) { | |
| 1488 // Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); | |
| 1489 HandleRList(instr, true); | |
| 1490 } else { | |
| 1491 // Format(instr, "stm'cond'pu 'rn'w, 'rlist"); | |
| 1492 HandleRList(instr, false); | |
| 1493 } | |
| 1494 } | |
| 1495 | |
| 1496 | |
| 1497 void Simulator::DecodeType5(Instr* instr) { | |
| 1498 // Format(instr, "b'l'cond 'target"); | |
| 1499 int off = (instr->SImmed24Field() << 2) + 8; | |
| 1500 intptr_t pc = get_pc(); | |
| 1501 if (instr->HasLink()) { | |
| 1502 set_register(lr, pc + Instr::kInstrSize); | |
| 1503 } | |
| 1504 set_pc(pc+off); | |
| 1505 } | |
| 1506 | |
| 1507 | |
| 1508 void Simulator::DecodeType6(Instr* instr) { | |
| 1509 UNIMPLEMENTED(); | |
| 1510 } | |
| 1511 | |
| 1512 | |
| 1513 void Simulator::DecodeType7(Instr* instr) { | |
| 1514 if (instr->Bit(24) == 1) { | |
| 1515 // Format(instr, "swi 'swi"); | |
| 1516 SoftwareInterrupt(instr); | |
| 1517 } else { | |
| 1518 UNIMPLEMENTED(); | |
| 1519 } | |
| 1520 } | |
| 1521 | |
| 1522 | |
| 1523 // Executes the current instruction. | |
| 1524 void Simulator::InstructionDecode(Instr* instr) { | |
| 1525 pc_modified_ = false; | |
| 1526 if (instr->ConditionField() == special_condition) { | |
| 1527 Debugger dbg(this); | |
| 1528 dbg.Stop(instr); | |
| 1529 return; | |
| 1530 } | |
| 1531 if (::v8::internal::FLAG_trace_sim) { | |
| 1532 disasm::NameConverter converter; | |
| 1533 disasm::Disassembler dasm(converter); | |
| 1534 // use a reasonably large buffer | |
| 1535 v8::internal::EmbeddedVector<char, 256> buffer; | |
| 1536 dasm.InstructionDecode(buffer, | |
| 1537 reinterpret_cast<byte*>(instr)); | |
| 1538 PrintF(" 0x%x %s\n", instr, buffer.start()); | |
| 1539 } | |
| 1540 if (ConditionallyExecute(instr)) { | |
| 1541 switch (instr->TypeField()) { | |
| 1542 case 0: | |
| 1543 case 1: { | |
| 1544 DecodeType01(instr); | |
| 1545 break; | |
| 1546 } | |
| 1547 case 2: { | |
| 1548 DecodeType2(instr); | |
| 1549 break; | |
| 1550 } | |
| 1551 case 3: { | |
| 1552 DecodeType3(instr); | |
| 1553 break; | |
| 1554 } | |
| 1555 case 4: { | |
| 1556 DecodeType4(instr); | |
| 1557 break; | |
| 1558 } | |
| 1559 case 5: { | |
| 1560 DecodeType5(instr); | |
| 1561 break; | |
| 1562 } | |
| 1563 case 6: { | |
| 1564 DecodeType6(instr); | |
| 1565 break; | |
| 1566 } | |
| 1567 case 7: { | |
| 1568 DecodeType7(instr); | |
| 1569 break; | |
| 1570 } | |
| 1571 default: { | |
| 1572 UNIMPLEMENTED(); | |
| 1573 break; | |
| 1574 } | |
| 1575 } | |
| 1576 } | |
| 1577 if (!pc_modified_) { | |
| 1578 set_register(pc, reinterpret_cast<int32_t>(instr) + Instr::kInstrSize); | |
| 1579 } | |
| 1580 } | |
| 1581 | |
| 1582 | |
| 1583 // | |
| 1584 void Simulator::Execute() { | |
| 1585 // Get the PC to simulate. Cannot use the accessor here as we need the | |
| 1586 // raw PC value and not the one used as input to arithmetic instructions. | |
| 1587 int program_counter = get_pc(); | |
| 1588 | |
| 1589 if (::v8::internal::FLAG_stop_sim_at == 0) { | |
| 1590 // Fast version of the dispatch loop without checking whether the simulator | |
| 1591 // should be stopping at a particular executed instruction. | |
| 1592 while (program_counter != end_sim_pc) { | |
| 1593 Instr* instr = reinterpret_cast<Instr*>(program_counter); | |
| 1594 icount_++; | |
| 1595 InstructionDecode(instr); | |
| 1596 program_counter = get_pc(); | |
| 1597 } | |
| 1598 } else { | |
| 1599 // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when | |
| 1600 // we reach the particular instuction count. | |
| 1601 while (program_counter != end_sim_pc) { | |
| 1602 Instr* instr = reinterpret_cast<Instr*>(program_counter); | |
| 1603 icount_++; | |
| 1604 if (icount_ == ::v8::internal::FLAG_stop_sim_at) { | |
| 1605 Debugger dbg(this); | |
| 1606 dbg.Debug(); | |
| 1607 } else { | |
| 1608 InstructionDecode(instr); | |
| 1609 } | |
| 1610 program_counter = get_pc(); | |
| 1611 } | |
| 1612 } | |
| 1613 } | |
| 1614 | |
| 1615 | |
| 1616 Object* Simulator::Call(int32_t entry, int32_t p0, int32_t p1, int32_t p2, | |
| 1617 int32_t p3, int32_t p4) { | |
| 1618 // Setup parameters | |
| 1619 set_register(r0, p0); | |
| 1620 set_register(r1, p1); | |
| 1621 set_register(r2, p2); | |
| 1622 set_register(r3, p3); | |
| 1623 intptr_t* stack_pointer = reinterpret_cast<intptr_t*>(get_register(sp)); | |
| 1624 *(--stack_pointer) = p4; | |
| 1625 set_register(sp, reinterpret_cast<int32_t>(stack_pointer)); | |
| 1626 | |
| 1627 // Prepare to execute the code at entry | |
| 1628 set_register(pc, entry); | |
| 1629 // Put down marker for end of simulation. The simulator will stop simulation | |
| 1630 // when the PC reaches this value. By saving the "end simulation" value into | |
| 1631 // the LR the simulation stops when returning to this call point. | |
| 1632 set_register(lr, end_sim_pc); | |
| 1633 | |
| 1634 // Remember the values of callee-saved registers. | |
| 1635 // The code below assumes that r9 is not used as sb (static base) in | |
| 1636 // simulator code and therefore is regarded as a callee-saved register. | |
| 1637 int32_t r4_val = get_register(r4); | |
| 1638 int32_t r5_val = get_register(r5); | |
| 1639 int32_t r6_val = get_register(r6); | |
| 1640 int32_t r7_val = get_register(r7); | |
| 1641 int32_t r8_val = get_register(r8); | |
| 1642 int32_t r9_val = get_register(r9); | |
| 1643 int32_t r10_val = get_register(r10); | |
| 1644 int32_t r11_val = get_register(r11); | |
| 1645 | |
| 1646 // Setup the callee-saved registers with a known value. To be able to check | |
| 1647 // that they are preserved properly across JS execution. | |
| 1648 int32_t callee_saved_value = icount_; | |
| 1649 set_register(r4, callee_saved_value); | |
| 1650 set_register(r5, callee_saved_value); | |
| 1651 set_register(r6, callee_saved_value); | |
| 1652 set_register(r7, callee_saved_value); | |
| 1653 set_register(r8, callee_saved_value); | |
| 1654 set_register(r9, callee_saved_value); | |
| 1655 set_register(r10, callee_saved_value); | |
| 1656 set_register(r11, callee_saved_value); | |
| 1657 | |
| 1658 // Start the simulation | |
| 1659 Execute(); | |
| 1660 | |
| 1661 // Check that the callee-saved registers have been preserved. | |
| 1662 CHECK_EQ(get_register(r4), callee_saved_value); | |
| 1663 CHECK_EQ(get_register(r5), callee_saved_value); | |
| 1664 CHECK_EQ(get_register(r6), callee_saved_value); | |
| 1665 CHECK_EQ(get_register(r7), callee_saved_value); | |
| 1666 CHECK_EQ(get_register(r8), callee_saved_value); | |
| 1667 CHECK_EQ(get_register(r9), callee_saved_value); | |
| 1668 CHECK_EQ(get_register(r10), callee_saved_value); | |
| 1669 CHECK_EQ(get_register(r11), callee_saved_value); | |
| 1670 | |
| 1671 // Restore callee-saved registers with the original value. | |
| 1672 set_register(r4, r4_val); | |
| 1673 set_register(r5, r5_val); | |
| 1674 set_register(r6, r6_val); | |
| 1675 set_register(r7, r7_val); | |
| 1676 set_register(r8, r8_val); | |
| 1677 set_register(r9, r9_val); | |
| 1678 set_register(r10, r10_val); | |
| 1679 set_register(r11, r11_val); | |
| 1680 | |
| 1681 int result = get_register(r0); | |
| 1682 return reinterpret_cast<Object*>(result); | |
| 1683 } | |
| 1684 | |
| 1685 } } // namespace assembler::arm | |
| 1686 | |
| 1687 #endif // !defined(__arm__) | |
| OLD | NEW |