| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include <setjmp.h> // NOLINT | |
| 6 #include <stdlib.h> | |
| 7 | |
| 8 #include "vm/globals.h" | |
| 9 #if defined(TARGET_ARCH_MIPS) | |
| 10 | |
| 11 // Only build the simulator if not compiling for real MIPS hardware. | |
| 12 #if defined(USING_SIMULATOR) | |
| 13 | |
| 14 #include "vm/simulator.h" | |
| 15 | |
| 16 #include "vm/assembler.h" | |
| 17 #include "vm/constants_mips.h" | |
| 18 #include "vm/disassembler.h" | |
| 19 #include "vm/lockers.h" | |
| 20 #include "vm/native_arguments.h" | |
| 21 #include "vm/stack_frame.h" | |
| 22 #include "vm/os_thread.h" | |
| 23 | |
| 24 namespace dart { | |
| 25 | |
| 26 DEFINE_FLAG(uint64_t, | |
| 27 trace_sim_after, | |
| 28 ULLONG_MAX, | |
| 29 "Trace simulator execution after instruction count reached."); | |
| 30 DEFINE_FLAG(uint64_t, | |
| 31 stop_sim_at, | |
| 32 ULLONG_MAX, | |
| 33 "Instruction address or instruction count to stop simulator at."); | |
| 34 | |
| 35 | |
| 36 // This macro provides a platform independent use of sscanf. The reason for | |
| 37 // SScanF not being implemented in a platform independent way through | |
| 38 // OS in the same way as SNPrint is that the Windows C Run-Time | |
| 39 // Library does not provide vsscanf. | |
| 40 #define SScanF sscanf // NOLINT | |
| 41 | |
| 42 | |
| 43 // SimulatorSetjmpBuffer are linked together, and the last created one | |
| 44 // is referenced by the Simulator. When an exception is thrown, the exception | |
| 45 // runtime looks at where to jump and finds the corresponding | |
| 46 // SimulatorSetjmpBuffer based on the stack pointer of the exception handler. | |
| 47 // The runtime then does a Longjmp on that buffer to return to the simulator. | |
| 48 class SimulatorSetjmpBuffer { | |
| 49 public: | |
| 50 void Longjmp() { | |
| 51 // "This" is now the last setjmp buffer. | |
| 52 simulator_->set_last_setjmp_buffer(this); | |
| 53 longjmp(buffer_, 1); | |
| 54 } | |
| 55 | |
| 56 explicit SimulatorSetjmpBuffer(Simulator* sim) { | |
| 57 simulator_ = sim; | |
| 58 link_ = sim->last_setjmp_buffer(); | |
| 59 sim->set_last_setjmp_buffer(this); | |
| 60 sp_ = static_cast<uword>(sim->get_register(SP)); | |
| 61 } | |
| 62 | |
| 63 ~SimulatorSetjmpBuffer() { | |
| 64 ASSERT(simulator_->last_setjmp_buffer() == this); | |
| 65 simulator_->set_last_setjmp_buffer(link_); | |
| 66 } | |
| 67 | |
| 68 SimulatorSetjmpBuffer* link() { return link_; } | |
| 69 | |
| 70 uword sp() { return sp_; } | |
| 71 | |
| 72 private: | |
| 73 uword sp_; | |
| 74 Simulator* simulator_; | |
| 75 SimulatorSetjmpBuffer* link_; | |
| 76 jmp_buf buffer_; | |
| 77 | |
| 78 friend class Simulator; | |
| 79 }; | |
| 80 | |
| 81 | |
| 82 // The SimulatorDebugger class is used by the simulator while debugging | |
| 83 // simulated MIPS code. | |
| 84 class SimulatorDebugger { | |
| 85 public: | |
| 86 explicit SimulatorDebugger(Simulator* sim); | |
| 87 ~SimulatorDebugger(); | |
| 88 | |
| 89 void Stop(Instr* instr, const char* message); | |
| 90 void Debug(); | |
| 91 char* ReadLine(const char* prompt); | |
| 92 | |
| 93 private: | |
| 94 Simulator* sim_; | |
| 95 | |
| 96 bool GetValue(char* desc, uint32_t* value); | |
| 97 bool GetFValue(char* desc, double* value); | |
| 98 bool GetDValue(char* desc, double* value); | |
| 99 | |
| 100 static TokenPosition GetApproximateTokenIndex(const Code& code, uword pc); | |
| 101 | |
| 102 static void PrintDartFrame(uword pc, | |
| 103 uword fp, | |
| 104 uword sp, | |
| 105 const Function& function, | |
| 106 TokenPosition token_pos, | |
| 107 bool is_optimized, | |
| 108 bool is_inlined); | |
| 109 void PrintBacktrace(); | |
| 110 | |
| 111 // Set or delete a breakpoint. Returns true if successful. | |
| 112 bool SetBreakpoint(Instr* breakpc); | |
| 113 bool DeleteBreakpoint(Instr* breakpc); | |
| 114 | |
| 115 // Undo and redo all breakpoints. This is needed to bracket disassembly and | |
| 116 // execution to skip past breakpoints when run from the debugger. | |
| 117 void UndoBreakpoints(); | |
| 118 void RedoBreakpoints(); | |
| 119 }; | |
| 120 | |
| 121 | |
| 122 SimulatorDebugger::SimulatorDebugger(Simulator* sim) { | |
| 123 sim_ = sim; | |
| 124 } | |
| 125 | |
| 126 | |
| 127 SimulatorDebugger::~SimulatorDebugger() {} | |
| 128 | |
| 129 | |
| 130 void SimulatorDebugger::Stop(Instr* instr, const char* message) { | |
| 131 OS::Print("Simulator hit %s\n", message); | |
| 132 Debug(); | |
| 133 } | |
| 134 | |
| 135 | |
| 136 static Register LookupCpuRegisterByName(const char* name) { | |
| 137 static const char* kNames[] = { | |
| 138 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | |
| 139 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | |
| 140 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | |
| 141 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", | |
| 142 | |
| 143 "zr", "at", "v0", "v1", "a0", "a1", "a2", "a3", | |
| 144 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", | |
| 145 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", | |
| 146 "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"}; | |
| 147 static const Register kRegisters[] = { | |
| 148 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, | |
| 149 R11, R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, | |
| 150 R22, R23, R24, R25, R26, R27, R28, R29, R30, R31, | |
| 151 | |
| 152 ZR, AT, V0, V1, A0, A1, A2, A3, T0, T1, T2, | |
| 153 T3, T4, T5, T6, T7, S0, S1, S2, S3, S4, S5, | |
| 154 S6, S7, T8, T9, K0, K1, GP, SP, FP, RA}; | |
| 155 ASSERT(ARRAY_SIZE(kNames) == ARRAY_SIZE(kRegisters)); | |
| 156 for (unsigned i = 0; i < ARRAY_SIZE(kNames); i++) { | |
| 157 if (strcmp(kNames[i], name) == 0) { | |
| 158 return kRegisters[i]; | |
| 159 } | |
| 160 } | |
| 161 return kNoRegister; | |
| 162 } | |
| 163 | |
| 164 | |
| 165 static FRegister LookupFRegisterByName(const char* name) { | |
| 166 int reg_nr = -1; | |
| 167 bool ok = SScanF(name, "f%d", ®_nr); | |
| 168 if (ok && (0 <= reg_nr) && (reg_nr < kNumberOfFRegisters)) { | |
| 169 return static_cast<FRegister>(reg_nr); | |
| 170 } | |
| 171 return kNoFRegister; | |
| 172 } | |
| 173 | |
| 174 | |
| 175 bool SimulatorDebugger::GetValue(char* desc, uint32_t* value) { | |
| 176 Register reg = LookupCpuRegisterByName(desc); | |
| 177 if (reg != kNoRegister) { | |
| 178 *value = sim_->get_register(reg); | |
| 179 return true; | |
| 180 } | |
| 181 if (desc[0] == '*') { | |
| 182 uint32_t addr; | |
| 183 if (GetValue(desc + 1, &addr)) { | |
| 184 if (Simulator::IsIllegalAddress(addr)) { | |
| 185 return false; | |
| 186 } | |
| 187 *value = *(reinterpret_cast<uint32_t*>(addr)); | |
| 188 return true; | |
| 189 } | |
| 190 } | |
| 191 if (strcmp("pc", desc) == 0) { | |
| 192 *value = sim_->get_pc(); | |
| 193 return true; | |
| 194 } | |
| 195 bool retval = SScanF(desc, "0x%x", value) == 1; | |
| 196 if (!retval) { | |
| 197 retval = SScanF(desc, "%x", value) == 1; | |
| 198 } | |
| 199 return retval; | |
| 200 } | |
| 201 | |
| 202 | |
| 203 bool SimulatorDebugger::GetFValue(char* desc, double* value) { | |
| 204 FRegister freg = LookupFRegisterByName(desc); | |
| 205 if (freg != kNoFRegister) { | |
| 206 *value = sim_->get_fregister(freg); | |
| 207 return true; | |
| 208 } | |
| 209 if (desc[0] == '*') { | |
| 210 uint32_t addr; | |
| 211 if (GetValue(desc + 1, &addr)) { | |
| 212 if (Simulator::IsIllegalAddress(addr)) { | |
| 213 return false; | |
| 214 } | |
| 215 *value = *(reinterpret_cast<float*>(addr)); | |
| 216 return true; | |
| 217 } | |
| 218 } | |
| 219 return false; | |
| 220 } | |
| 221 | |
| 222 | |
| 223 bool SimulatorDebugger::GetDValue(char* desc, double* value) { | |
| 224 FRegister freg = LookupFRegisterByName(desc); | |
| 225 if (freg != kNoFRegister) { | |
| 226 *value = sim_->get_fregister_double(freg); | |
| 227 return true; | |
| 228 } | |
| 229 if (desc[0] == '*') { | |
| 230 uint32_t addr; | |
| 231 if (GetValue(desc + 1, &addr)) { | |
| 232 if (Simulator::IsIllegalAddress(addr)) { | |
| 233 return false; | |
| 234 } | |
| 235 *value = *(reinterpret_cast<double*>(addr)); | |
| 236 return true; | |
| 237 } | |
| 238 } | |
| 239 return false; | |
| 240 } | |
| 241 | |
| 242 | |
| 243 TokenPosition SimulatorDebugger::GetApproximateTokenIndex(const Code& code, | |
| 244 uword pc) { | |
| 245 TokenPosition token_pos = TokenPosition::kNoSource; | |
| 246 uword pc_offset = pc - code.PayloadStart(); | |
| 247 const PcDescriptors& descriptors = | |
| 248 PcDescriptors::Handle(code.pc_descriptors()); | |
| 249 PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind); | |
| 250 while (iter.MoveNext()) { | |
| 251 if (iter.PcOffset() == pc_offset) { | |
| 252 return iter.TokenPos(); | |
| 253 } else if (!token_pos.IsReal() && (iter.PcOffset() > pc_offset)) { | |
| 254 token_pos = iter.TokenPos(); | |
| 255 } | |
| 256 } | |
| 257 return token_pos; | |
| 258 } | |
| 259 | |
| 260 | |
| 261 void SimulatorDebugger::PrintDartFrame(uword pc, | |
| 262 uword fp, | |
| 263 uword sp, | |
| 264 const Function& function, | |
| 265 TokenPosition token_pos, | |
| 266 bool is_optimized, | |
| 267 bool is_inlined) { | |
| 268 const Script& script = Script::Handle(function.script()); | |
| 269 const String& func_name = String::Handle(function.QualifiedScrubbedName()); | |
| 270 const String& url = String::Handle(script.url()); | |
| 271 intptr_t line = -1; | |
| 272 intptr_t column = -1; | |
| 273 if (token_pos.IsReal()) { | |
| 274 script.GetTokenLocation(token_pos, &line, &column); | |
| 275 } | |
| 276 OS::Print( | |
| 277 "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")\n", pc, | |
| 278 fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "", | |
| 279 func_name.ToCString(), url.ToCString(), line, column); | |
| 280 } | |
| 281 | |
| 282 | |
| 283 void SimulatorDebugger::PrintBacktrace() { | |
| 284 StackFrameIterator frames( | |
| 285 sim_->get_register(FP), sim_->get_register(SP), sim_->get_pc(), | |
| 286 StackFrameIterator::kDontValidateFrames, Thread::Current(), | |
| 287 StackFrameIterator::kNoCrossThreadIteration); | |
| 288 StackFrame* frame = frames.NextFrame(); | |
| 289 ASSERT(frame != NULL); | |
| 290 Function& function = Function::Handle(); | |
| 291 Function& inlined_function = Function::Handle(); | |
| 292 Code& code = Code::Handle(); | |
| 293 Code& unoptimized_code = Code::Handle(); | |
| 294 while (frame != NULL) { | |
| 295 if (frame->IsDartFrame()) { | |
| 296 code = frame->LookupDartCode(); | |
| 297 function = code.function(); | |
| 298 if (code.is_optimized()) { | |
| 299 // For optimized frames, extract all the inlined functions if any | |
| 300 // into the stack trace. | |
| 301 InlinedFunctionsIterator it(code, frame->pc()); | |
| 302 while (!it.Done()) { | |
| 303 // Print each inlined frame with its pc in the corresponding | |
| 304 // unoptimized frame. | |
| 305 inlined_function = it.function(); | |
| 306 unoptimized_code = it.code(); | |
| 307 uword unoptimized_pc = it.pc(); | |
| 308 it.Advance(); | |
| 309 if (!it.Done()) { | |
| 310 PrintDartFrame( | |
| 311 unoptimized_pc, frame->fp(), frame->sp(), inlined_function, | |
| 312 GetApproximateTokenIndex(unoptimized_code, unoptimized_pc), | |
| 313 true, true); | |
| 314 } | |
| 315 } | |
| 316 // Print the optimized inlining frame below. | |
| 317 } | |
| 318 PrintDartFrame(frame->pc(), frame->fp(), frame->sp(), function, | |
| 319 GetApproximateTokenIndex(code, frame->pc()), | |
| 320 code.is_optimized(), false); | |
| 321 } else { | |
| 322 OS::Print("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame\n", | |
| 323 frame->pc(), frame->fp(), frame->sp(), | |
| 324 frame->IsEntryFrame() | |
| 325 ? "entry" | |
| 326 : frame->IsExitFrame() | |
| 327 ? "exit" | |
| 328 : frame->IsStubFrame() ? "stub" : "invalid"); | |
| 329 } | |
| 330 frame = frames.NextFrame(); | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 | |
| 335 bool SimulatorDebugger::SetBreakpoint(Instr* breakpc) { | |
| 336 // Check if a breakpoint can be set. If not return without any side-effects. | |
| 337 if (sim_->break_pc_ != NULL) { | |
| 338 return false; | |
| 339 } | |
| 340 | |
| 341 // Set the breakpoint. | |
| 342 sim_->break_pc_ = breakpc; | |
| 343 sim_->break_instr_ = breakpc->InstructionBits(); | |
| 344 // Not setting the breakpoint instruction in the code itself. It will be set | |
| 345 // when the debugger shell continues. | |
| 346 return true; | |
| 347 } | |
| 348 | |
| 349 | |
| 350 bool SimulatorDebugger::DeleteBreakpoint(Instr* breakpc) { | |
| 351 if (sim_->break_pc_ != NULL) { | |
| 352 sim_->break_pc_->SetInstructionBits(sim_->break_instr_); | |
| 353 } | |
| 354 | |
| 355 sim_->break_pc_ = NULL; | |
| 356 sim_->break_instr_ = 0; | |
| 357 return true; | |
| 358 } | |
| 359 | |
| 360 | |
| 361 void SimulatorDebugger::UndoBreakpoints() { | |
| 362 if (sim_->break_pc_ != NULL) { | |
| 363 sim_->break_pc_->SetInstructionBits(sim_->break_instr_); | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 | |
| 368 void SimulatorDebugger::RedoBreakpoints() { | |
| 369 if (sim_->break_pc_ != NULL) { | |
| 370 sim_->break_pc_->SetInstructionBits(Instr::kSimulatorBreakpointInstruction); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 | |
| 375 void SimulatorDebugger::Debug() { | |
| 376 intptr_t last_pc = -1; | |
| 377 bool done = false; | |
| 378 | |
| 379 #define COMMAND_SIZE 63 | |
| 380 #define ARG_SIZE 255 | |
| 381 | |
| 382 #define STR(a) #a | |
| 383 #define XSTR(a) STR(a) | |
| 384 | |
| 385 char cmd[COMMAND_SIZE + 1]; | |
| 386 char arg1[ARG_SIZE + 1]; | |
| 387 char arg2[ARG_SIZE + 1]; | |
| 388 | |
| 389 // make sure to have a proper terminating character if reaching the limit | |
| 390 cmd[COMMAND_SIZE] = 0; | |
| 391 arg1[ARG_SIZE] = 0; | |
| 392 arg2[ARG_SIZE] = 0; | |
| 393 | |
| 394 // Undo all set breakpoints while running in the debugger shell. This will | |
| 395 // make them invisible to all commands. | |
| 396 UndoBreakpoints(); | |
| 397 | |
| 398 while (!done) { | |
| 399 if (last_pc != sim_->get_pc()) { | |
| 400 last_pc = sim_->get_pc(); | |
| 401 if (Simulator::IsIllegalAddress(last_pc)) { | |
| 402 OS::Print("pc is out of bounds: 0x%" Px "\n", last_pc); | |
| 403 } else { | |
| 404 if (FLAG_support_disassembler) { | |
| 405 Disassembler::Disassemble(last_pc, last_pc + Instr::kInstrSize); | |
| 406 } else { | |
| 407 OS::Print("Disassembler not supported in this mode.\n"); | |
| 408 } | |
| 409 } | |
| 410 } | |
| 411 char* line = ReadLine("sim> "); | |
| 412 if (line == NULL) { | |
| 413 FATAL("ReadLine failed"); | |
| 414 } else { | |
| 415 // Use sscanf to parse the individual parts of the command line. At the | |
| 416 // moment no command expects more than two parameters. | |
| 417 int args = SScanF(line, | |
| 418 "%" XSTR(COMMAND_SIZE) "s " | |
| 419 "%" XSTR(ARG_SIZE) "s " | |
| 420 "%" XSTR(ARG_SIZE) "s", | |
| 421 cmd, arg1, arg2); | |
| 422 if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { | |
| 423 OS::Print( | |
| 424 "c/cont -- continue execution\n" | |
| 425 "disasm -- disassemble instrs at current pc location\n" | |
| 426 " other variants are:\n" | |
| 427 " disasm <address>\n" | |
| 428 " disasm <address> <number_of_instructions>\n" | |
| 429 " by default 10 instrs are disassembled\n" | |
| 430 "del -- delete breakpoints\n" | |
| 431 "gdb -- transfer control to gdb\n" | |
| 432 "h/help -- print this help string\n" | |
| 433 "break <address> -- set break point at specified address\n" | |
| 434 "p/print <reg or icount or value or *addr> -- print integer\n" | |
| 435 "pf/printfloat <freg or *addr> -- print float value\n" | |
| 436 "po/printobject <*reg or *addr> -- print object\n" | |
| 437 "si/stepi -- single step an instruction\n" | |
| 438 "trace -- toggle execution tracing mode\n" | |
| 439 "bt -- print backtrace\n" | |
| 440 "unstop -- if current pc is a stop instr make it a nop\n" | |
| 441 "q/quit -- Quit the debugger and exit the program\n"); | |
| 442 } else if ((strcmp(cmd, "quit") == 0) || (strcmp(cmd, "q") == 0)) { | |
| 443 OS::Print("Quitting\n"); | |
| 444 OS::Exit(0); | |
| 445 } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { | |
| 446 sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); | |
| 447 } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { | |
| 448 // Execute the one instruction we broke at with breakpoints disabled. | |
| 449 sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); | |
| 450 // Leave the debugger shell. | |
| 451 done = true; | |
| 452 } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { | |
| 453 if (args == 2) { | |
| 454 uint32_t value; | |
| 455 if (strcmp(arg1, "icount") == 0) { | |
| 456 const uint64_t icount = sim_->get_icount(); | |
| 457 OS::Print("icount: %" Pu64 " 0x%" Px64 "\n", icount, icount); | |
| 458 } else if (GetValue(arg1, &value)) { | |
| 459 OS::Print("%s: %u 0x%x\n", arg1, value, value); | |
| 460 } else { | |
| 461 OS::Print("%s unrecognized\n", arg1); | |
| 462 } | |
| 463 } else { | |
| 464 OS::Print("print <reg or icount or value or *addr>\n"); | |
| 465 } | |
| 466 } else if ((strcmp(cmd, "pf") == 0) || (strcmp(cmd, "printfloat") == 0)) { | |
| 467 if (args == 2) { | |
| 468 double dvalue; | |
| 469 if (GetFValue(arg1, &dvalue)) { | |
| 470 uint64_t long_value = bit_cast<uint64_t, double>(dvalue); | |
| 471 OS::Print("%s: %llu 0x%llx %.8g\n", arg1, long_value, long_value, | |
| 472 dvalue); | |
| 473 } else { | |
| 474 OS::Print("%s unrecognized\n", arg1); | |
| 475 } | |
| 476 } else { | |
| 477 OS::Print("printfloat <dreg or *addr>\n"); | |
| 478 } | |
| 479 } else if ((strcmp(cmd, "pd") == 0) || | |
| 480 (strcmp(cmd, "printdouble") == 0)) { | |
| 481 if (args == 2) { | |
| 482 double dvalue; | |
| 483 if (GetDValue(arg1, &dvalue)) { | |
| 484 uint64_t long_value = bit_cast<uint64_t, double>(dvalue); | |
| 485 OS::Print("%s: %llu 0x%llx %.8g\n", arg1, long_value, long_value, | |
| 486 dvalue); | |
| 487 } else { | |
| 488 OS::Print("%s unrecognized\n", arg1); | |
| 489 } | |
| 490 } else { | |
| 491 OS::Print("printfloat <dreg or *addr>\n"); | |
| 492 } | |
| 493 } else if ((strcmp(cmd, "po") == 0) || | |
| 494 (strcmp(cmd, "printobject") == 0)) { | |
| 495 if (args == 2) { | |
| 496 uint32_t value; | |
| 497 // Make the dereferencing '*' optional. | |
| 498 if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) || | |
| 499 GetValue(arg1, &value)) { | |
| 500 if (Isolate::Current()->heap()->Contains(value)) { | |
| 501 OS::Print("%s: \n", arg1); | |
| 502 #if defined(DEBUG) | |
| 503 const Object& obj = | |
| 504 Object::Handle(reinterpret_cast<RawObject*>(value)); | |
| 505 obj.Print(); | |
| 506 #endif // defined(DEBUG) | |
| 507 } else { | |
| 508 OS::Print("0x%x is not an object reference\n", value); | |
| 509 } | |
| 510 } else { | |
| 511 OS::Print("%s unrecognized\n", arg1); | |
| 512 } | |
| 513 } else { | |
| 514 OS::Print("printobject <*reg or *addr>\n"); | |
| 515 } | |
| 516 } else if (strcmp(cmd, "disasm") == 0) { | |
| 517 uint32_t start = 0; | |
| 518 uint32_t end = 0; | |
| 519 if (args == 1) { | |
| 520 start = sim_->get_pc(); | |
| 521 end = start + (10 * Instr::kInstrSize); | |
| 522 } else if (args == 2) { | |
| 523 if (GetValue(arg1, &start)) { | |
| 524 // No length parameter passed, assume 10 instructions. | |
| 525 if (Simulator::IsIllegalAddress(start)) { | |
| 526 // If start isn't a valid address, warn and use PC instead. | |
| 527 OS::Print("First argument yields invalid address: 0x%x\n", start); | |
| 528 OS::Print("Using PC instead\n"); | |
| 529 start = sim_->get_pc(); | |
| 530 } | |
| 531 end = start + (10 * Instr::kInstrSize); | |
| 532 } | |
| 533 } else { | |
| 534 uint32_t length; | |
| 535 if (GetValue(arg1, &start) && GetValue(arg2, &length)) { | |
| 536 if (Simulator::IsIllegalAddress(start)) { | |
| 537 // If start isn't a valid address, warn and use PC instead. | |
| 538 OS::Print("First argument yields invalid address: 0x%x\n", start); | |
| 539 OS::Print("Using PC instead\n"); | |
| 540 start = sim_->get_pc(); | |
| 541 } | |
| 542 end = start + (length * Instr::kInstrSize); | |
| 543 } | |
| 544 } | |
| 545 if ((start > 0) && (end > start)) { | |
| 546 if (FLAG_support_disassembler) { | |
| 547 Disassembler::Disassemble(start, end); | |
| 548 } else { | |
| 549 OS::Print("Disassembler not supported in this mode.\n"); | |
| 550 } | |
| 551 } else { | |
| 552 OS::Print("disasm [<address> [<number_of_instructions>]]\n"); | |
| 553 } | |
| 554 } else if (strcmp(cmd, "gdb") == 0) { | |
| 555 OS::Print("relinquishing control to gdb\n"); | |
| 556 OS::DebugBreak(); | |
| 557 OS::Print("regaining control from gdb\n"); | |
| 558 } else if (strcmp(cmd, "break") == 0) { | |
| 559 if (args == 2) { | |
| 560 uint32_t addr; | |
| 561 if (GetValue(arg1, &addr)) { | |
| 562 if (!SetBreakpoint(reinterpret_cast<Instr*>(addr))) { | |
| 563 OS::Print("setting breakpoint failed\n"); | |
| 564 } | |
| 565 } else { | |
| 566 OS::Print("%s unrecognized\n", arg1); | |
| 567 } | |
| 568 } else { | |
| 569 OS::Print("break <addr>\n"); | |
| 570 } | |
| 571 } else if (strcmp(cmd, "del") == 0) { | |
| 572 if (!DeleteBreakpoint(NULL)) { | |
| 573 OS::Print("deleting breakpoint failed\n"); | |
| 574 } | |
| 575 } else if (strcmp(cmd, "unstop") == 0) { | |
| 576 intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize; | |
| 577 Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc); | |
| 578 if (stop_instr->IsBreakPoint()) { | |
| 579 stop_instr->SetInstructionBits(Instr::kNopInstruction); | |
| 580 } else { | |
| 581 OS::Print("Not at debugger stop.\n"); | |
| 582 } | |
| 583 } else if (strcmp(cmd, "trace") == 0) { | |
| 584 if (FLAG_trace_sim_after == ULLONG_MAX) { | |
| 585 FLAG_trace_sim_after = sim_->get_icount(); | |
| 586 OS::Print("execution tracing on\n"); | |
| 587 } else { | |
| 588 FLAG_trace_sim_after = ULLONG_MAX; | |
| 589 OS::Print("execution tracing off\n"); | |
| 590 } | |
| 591 } else if (strcmp(cmd, "bt") == 0) { | |
| 592 PrintBacktrace(); | |
| 593 } else { | |
| 594 OS::Print("Unknown command: %s\n", cmd); | |
| 595 } | |
| 596 } | |
| 597 delete[] line; | |
| 598 } | |
| 599 | |
| 600 // Add all the breakpoints back to stop execution and enter the debugger | |
| 601 // shell when hit. | |
| 602 RedoBreakpoints(); | |
| 603 | |
| 604 #undef COMMAND_SIZE | |
| 605 #undef ARG_SIZE | |
| 606 | |
| 607 #undef STR | |
| 608 #undef XSTR | |
| 609 } | |
| 610 | |
| 611 | |
| 612 char* SimulatorDebugger::ReadLine(const char* prompt) { | |
| 613 char* result = NULL; | |
| 614 char line_buf[256]; | |
| 615 intptr_t offset = 0; | |
| 616 bool keep_going = true; | |
| 617 OS::Print("%s", prompt); | |
| 618 while (keep_going) { | |
| 619 if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) { | |
| 620 // fgets got an error. Just give up. | |
| 621 if (result != NULL) { | |
| 622 delete[] result; | |
| 623 } | |
| 624 return NULL; | |
| 625 } | |
| 626 intptr_t len = strlen(line_buf); | |
| 627 if (len > 1 && line_buf[len - 2] == '\\' && line_buf[len - 1] == '\n') { | |
| 628 // When we read a line that ends with a "\" we remove the escape and | |
| 629 // append the remainder. | |
| 630 line_buf[len - 2] = '\n'; | |
| 631 line_buf[len - 1] = 0; | |
| 632 len -= 1; | |
| 633 } else if ((len > 0) && (line_buf[len - 1] == '\n')) { | |
| 634 // Since we read a new line we are done reading the line. This | |
| 635 // will exit the loop after copying this buffer into the result. | |
| 636 keep_going = false; | |
| 637 } | |
| 638 if (result == NULL) { | |
| 639 // Allocate the initial result and make room for the terminating '\0' | |
| 640 result = new char[len + 1]; | |
| 641 if (result == NULL) { | |
| 642 // OOM, so cannot readline anymore. | |
| 643 return NULL; | |
| 644 } | |
| 645 } else { | |
| 646 // Allocate a new result with enough room for the new addition. | |
| 647 intptr_t new_len = offset + len + 1; | |
| 648 char* new_result = new char[new_len]; | |
| 649 if (new_result == NULL) { | |
| 650 // OOM, free the buffer allocated so far and return NULL. | |
| 651 delete[] result; | |
| 652 return NULL; | |
| 653 } else { | |
| 654 // Copy the existing input into the new array and set the new | |
| 655 // array as the result. | |
| 656 memmove(new_result, result, offset); | |
| 657 delete[] result; | |
| 658 result = new_result; | |
| 659 } | |
| 660 } | |
| 661 // Copy the newly read line into the result. | |
| 662 memmove(result + offset, line_buf, len); | |
| 663 offset += len; | |
| 664 } | |
| 665 ASSERT(result != NULL); | |
| 666 result[offset] = '\0'; | |
| 667 return result; | |
| 668 } | |
| 669 | |
| 670 | |
| 671 // Synchronization primitives support. | |
| 672 Mutex* Simulator::exclusive_access_lock_ = NULL; | |
| 673 Simulator::AddressTag Simulator::exclusive_access_state_[kNumAddressTags] = { | |
| 674 {NULL, 0}}; | |
| 675 int Simulator::next_address_tag_ = 0; | |
| 676 | |
| 677 | |
| 678 void Simulator::InitOnce() { | |
| 679 // Setup exclusive access state lock. | |
| 680 exclusive_access_lock_ = new Mutex(); | |
| 681 } | |
| 682 | |
| 683 | |
| 684 Simulator::Simulator() { | |
| 685 // Setup simulator support first. Some of this information is needed to | |
| 686 // setup the architecture state. | |
| 687 // We allocate the stack here, the size is computed as the sum of | |
| 688 // the size specified by the user and the buffer space needed for | |
| 689 // handling stack overflow exceptions. To be safe in potential | |
| 690 // stack underflows we also add some underflow buffer space. | |
| 691 stack_ = | |
| 692 new char[(OSThread::GetSpecifiedStackSize() + OSThread::kStackSizeBuffer + | |
| 693 kSimulatorStackUnderflowSize)]; | |
| 694 icount_ = 0; | |
| 695 delay_slot_ = false; | |
| 696 break_pc_ = NULL; | |
| 697 break_instr_ = 0; | |
| 698 last_setjmp_buffer_ = NULL; | |
| 699 top_exit_frame_info_ = 0; | |
| 700 | |
| 701 // Setup architecture state. | |
| 702 // All registers are initialized to zero to start with. | |
| 703 for (int i = 0; i < kNumberOfCpuRegisters; i++) { | |
| 704 registers_[i] = 0; | |
| 705 } | |
| 706 pc_ = 0; | |
| 707 // The sp is initialized to point to the bottom (high address) of the | |
| 708 // allocated stack area. | |
| 709 registers_[SP] = StackTop(); | |
| 710 | |
| 711 // All double-precision registers are initialized to zero. | |
| 712 for (int i = 0; i < kNumberOfFRegisters; i++) { | |
| 713 fregisters_[i] = 0.0; | |
| 714 } | |
| 715 fcsr_ = 0; | |
| 716 } | |
| 717 | |
| 718 | |
| 719 Simulator::~Simulator() { | |
| 720 delete[] stack_; | |
| 721 Isolate* isolate = Isolate::Current(); | |
| 722 if (isolate != NULL) { | |
| 723 isolate->set_simulator(NULL); | |
| 724 } | |
| 725 } | |
| 726 | |
| 727 | |
| 728 // When the generated code calls an external reference we need to catch that in | |
| 729 // the simulator. The external reference will be a function compiled for the | |
| 730 // host architecture. We need to call that function instead of trying to | |
| 731 // execute it with the simulator. We do that by redirecting the external | |
| 732 // reference to a break instruction with code 2 that is handled by | |
| 733 // the simulator. We write the original destination of the jump just at a known | |
| 734 // offset from the break instruction so the simulator knows what to call. | |
| 735 class Redirection { | |
| 736 public: | |
| 737 uword address_of_break_instruction() { | |
| 738 return reinterpret_cast<uword>(&break_instruction_); | |
| 739 } | |
| 740 | |
| 741 uword external_function() const { return external_function_; } | |
| 742 | |
| 743 Simulator::CallKind call_kind() const { return call_kind_; } | |
| 744 | |
| 745 int argument_count() const { return argument_count_; } | |
| 746 | |
| 747 static Redirection* Get(uword external_function, | |
| 748 Simulator::CallKind call_kind, | |
| 749 int argument_count) { | |
| 750 Redirection* current; | |
| 751 for (current = list_; current != NULL; current = current->next_) { | |
| 752 if (current->external_function_ == external_function) return current; | |
| 753 } | |
| 754 return new Redirection(external_function, call_kind, argument_count); | |
| 755 } | |
| 756 | |
| 757 static Redirection* FromBreakInstruction(Instr* break_instruction) { | |
| 758 char* addr_of_break = reinterpret_cast<char*>(break_instruction); | |
| 759 char* addr_of_redirection = | |
| 760 addr_of_break - OFFSET_OF(Redirection, break_instruction_); | |
| 761 return reinterpret_cast<Redirection*>(addr_of_redirection); | |
| 762 } | |
| 763 | |
| 764 static uword FunctionForRedirect(uword address_of_break) { | |
| 765 Redirection* current; | |
| 766 for (current = list_; current != NULL; current = current->next_) { | |
| 767 if (current->address_of_break_instruction() == address_of_break) { | |
| 768 return current->external_function_; | |
| 769 } | |
| 770 } | |
| 771 return 0; | |
| 772 } | |
| 773 | |
| 774 private: | |
| 775 Redirection(uword external_function, | |
| 776 Simulator::CallKind call_kind, | |
| 777 int argument_count) | |
| 778 : external_function_(external_function), | |
| 779 call_kind_(call_kind), | |
| 780 argument_count_(argument_count), | |
| 781 break_instruction_(Instr::kSimulatorRedirectInstruction), | |
| 782 next_(list_) { | |
| 783 // Atomically prepend this element to the front of the global list. | |
| 784 // Note: Since elements are never removed, there is no ABA issue. | |
| 785 Redirection* list_head = list_; | |
| 786 do { | |
| 787 next_ = list_head; | |
| 788 list_head = | |
| 789 reinterpret_cast<Redirection*>(AtomicOperations::CompareAndSwapWord( | |
| 790 reinterpret_cast<uword*>(&list_), reinterpret_cast<uword>(next_), | |
| 791 reinterpret_cast<uword>(this))); | |
| 792 } while (list_head != next_); | |
| 793 } | |
| 794 | |
| 795 uword external_function_; | |
| 796 Simulator::CallKind call_kind_; | |
| 797 int argument_count_; | |
| 798 uint32_t break_instruction_; | |
| 799 Redirection* next_; | |
| 800 static Redirection* list_; | |
| 801 }; | |
| 802 | |
| 803 | |
| 804 Redirection* Redirection::list_ = NULL; | |
| 805 | |
| 806 | |
| 807 uword Simulator::RedirectExternalReference(uword function, | |
| 808 CallKind call_kind, | |
| 809 int argument_count) { | |
| 810 Redirection* redirection = | |
| 811 Redirection::Get(function, call_kind, argument_count); | |
| 812 return redirection->address_of_break_instruction(); | |
| 813 } | |
| 814 | |
| 815 | |
| 816 uword Simulator::FunctionForRedirect(uword redirect) { | |
| 817 return Redirection::FunctionForRedirect(redirect); | |
| 818 } | |
| 819 | |
| 820 | |
| 821 // Get the active Simulator for the current isolate. | |
| 822 Simulator* Simulator::Current() { | |
| 823 Simulator* simulator = Isolate::Current()->simulator(); | |
| 824 if (simulator == NULL) { | |
| 825 simulator = new Simulator(); | |
| 826 Isolate::Current()->set_simulator(simulator); | |
| 827 } | |
| 828 return simulator; | |
| 829 } | |
| 830 | |
| 831 | |
| 832 // Sets the register in the architecture state. | |
| 833 void Simulator::set_register(Register reg, int32_t value) { | |
| 834 if (reg != R0) { | |
| 835 registers_[reg] = value; | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 | |
| 840 void Simulator::set_fregister(FRegister reg, int32_t value) { | |
| 841 ASSERT(reg >= 0); | |
| 842 ASSERT(reg < kNumberOfFRegisters); | |
| 843 fregisters_[reg] = value; | |
| 844 } | |
| 845 | |
| 846 | |
| 847 void Simulator::set_fregister_float(FRegister reg, float value) { | |
| 848 ASSERT(reg >= 0); | |
| 849 ASSERT(reg < kNumberOfFRegisters); | |
| 850 fregisters_[reg] = bit_cast<int32_t, float>(value); | |
| 851 } | |
| 852 | |
| 853 | |
| 854 void Simulator::set_fregister_long(FRegister reg, int64_t value) { | |
| 855 ASSERT(reg >= 0); | |
| 856 ASSERT(reg < kNumberOfFRegisters); | |
| 857 ASSERT((reg & 1) == 0); | |
| 858 fregisters_[reg] = Utils::Low32Bits(value); | |
| 859 fregisters_[reg + 1] = Utils::High32Bits(value); | |
| 860 } | |
| 861 | |
| 862 | |
| 863 void Simulator::set_fregister_double(FRegister reg, double value) { | |
| 864 const int64_t ival = bit_cast<int64_t, double>(value); | |
| 865 set_fregister_long(reg, ival); | |
| 866 } | |
| 867 | |
| 868 | |
| 869 void Simulator::set_dregister_bits(DRegister reg, int64_t value) { | |
| 870 ASSERT(reg >= 0); | |
| 871 ASSERT(reg < kNumberOfDRegisters); | |
| 872 FRegister lo = static_cast<FRegister>(reg * 2); | |
| 873 FRegister hi = static_cast<FRegister>((reg * 2) + 1); | |
| 874 set_fregister(lo, Utils::Low32Bits(value)); | |
| 875 set_fregister(hi, Utils::High32Bits(value)); | |
| 876 } | |
| 877 | |
| 878 | |
| 879 void Simulator::set_dregister(DRegister reg, double value) { | |
| 880 ASSERT(reg >= 0); | |
| 881 ASSERT(reg < kNumberOfDRegisters); | |
| 882 set_dregister_bits(reg, bit_cast<int64_t, double>(value)); | |
| 883 } | |
| 884 | |
| 885 | |
| 886 // Get the register from the architecture state. | |
| 887 int32_t Simulator::get_register(Register reg) const { | |
| 888 if (reg == R0) { | |
| 889 return 0; | |
| 890 } | |
| 891 return registers_[reg]; | |
| 892 } | |
| 893 | |
| 894 | |
| 895 int32_t Simulator::get_fregister(FRegister reg) const { | |
| 896 ASSERT((reg >= 0) && (reg < kNumberOfFRegisters)); | |
| 897 return fregisters_[reg]; | |
| 898 } | |
| 899 | |
| 900 | |
| 901 float Simulator::get_fregister_float(FRegister reg) const { | |
| 902 ASSERT(reg >= 0); | |
| 903 ASSERT(reg < kNumberOfFRegisters); | |
| 904 return bit_cast<float, int32_t>(fregisters_[reg]); | |
| 905 } | |
| 906 | |
| 907 | |
| 908 int64_t Simulator::get_fregister_long(FRegister reg) const { | |
| 909 ASSERT(reg >= 0); | |
| 910 ASSERT(reg < kNumberOfFRegisters); | |
| 911 ASSERT((reg & 1) == 0); | |
| 912 const int32_t low = fregisters_[reg]; | |
| 913 const int32_t high = fregisters_[reg + 1]; | |
| 914 const int64_t value = Utils::LowHighTo64Bits(low, high); | |
| 915 return value; | |
| 916 } | |
| 917 | |
| 918 | |
| 919 double Simulator::get_fregister_double(FRegister reg) const { | |
| 920 ASSERT(reg >= 0); | |
| 921 ASSERT(reg < kNumberOfFRegisters); | |
| 922 ASSERT((reg & 1) == 0); | |
| 923 const int64_t value = get_fregister_long(reg); | |
| 924 return bit_cast<double, int64_t>(value); | |
| 925 } | |
| 926 | |
| 927 | |
| 928 int64_t Simulator::get_dregister_bits(DRegister reg) const { | |
| 929 ASSERT(reg >= 0); | |
| 930 ASSERT(reg < kNumberOfDRegisters); | |
| 931 FRegister lo = static_cast<FRegister>(reg * 2); | |
| 932 FRegister hi = static_cast<FRegister>((reg * 2) + 1); | |
| 933 return Utils::LowHighTo64Bits(get_fregister(lo), get_fregister(hi)); | |
| 934 } | |
| 935 | |
| 936 | |
| 937 double Simulator::get_dregister(DRegister reg) const { | |
| 938 ASSERT(reg >= 0); | |
| 939 ASSERT(reg < kNumberOfDRegisters); | |
| 940 const int64_t value = get_dregister_bits(reg); | |
| 941 return bit_cast<double, int64_t>(value); | |
| 942 } | |
| 943 | |
| 944 | |
| 945 void Simulator::UnimplementedInstruction(Instr* instr) { | |
| 946 char buffer[64]; | |
| 947 snprintf(buffer, sizeof(buffer), "Unimplemented instruction: pc=%p\n", instr); | |
| 948 SimulatorDebugger dbg(this); | |
| 949 dbg.Stop(instr, buffer); | |
| 950 FATAL("Cannot continue execution after unimplemented instruction."); | |
| 951 } | |
| 952 | |
| 953 | |
| 954 void Simulator::HandleIllegalAccess(uword addr, Instr* instr) { | |
| 955 uword fault_pc = get_pc(); | |
| 956 // The debugger will not be able to single step past this instruction, but | |
| 957 // it will be possible to disassemble the code and inspect registers. | |
| 958 char buffer[128]; | |
| 959 snprintf(buffer, sizeof(buffer), | |
| 960 "illegal memory access at 0x%" Px ", pc=0x%" Px "\n", addr, | |
| 961 fault_pc); | |
| 962 SimulatorDebugger dbg(this); | |
| 963 dbg.Stop(instr, buffer); | |
| 964 // The debugger will return control in non-interactive mode. | |
| 965 FATAL("Cannot continue execution after illegal memory access."); | |
| 966 } | |
| 967 | |
| 968 | |
| 969 void Simulator::UnalignedAccess(const char* msg, uword addr, Instr* instr) { | |
| 970 // The debugger will not be able to single step past this instruction, but | |
| 971 // it will be possible to disassemble the code and inspect registers. | |
| 972 char buffer[128]; | |
| 973 snprintf(buffer, sizeof(buffer), "pc=%p, unaligned %s at 0x%" Px "\n", instr, | |
| 974 msg, addr); | |
| 975 SimulatorDebugger dbg(this); | |
| 976 dbg.Stop(instr, buffer); | |
| 977 // The debugger will return control in non-interactive mode. | |
| 978 FATAL("Cannot continue execution after unaligned access."); | |
| 979 } | |
| 980 | |
| 981 | |
| 982 // Returns the top of the stack area to enable checking for stack pointer | |
| 983 // validity. | |
| 984 uword Simulator::StackTop() const { | |
| 985 // To be safe in potential stack underflows we leave some buffer above and | |
| 986 // set the stack top. | |
| 987 return StackBase() + | |
| 988 (OSThread::GetSpecifiedStackSize() + OSThread::kStackSizeBuffer); | |
| 989 } | |
| 990 | |
| 991 | |
| 992 bool Simulator::IsTracingExecution() const { | |
| 993 return icount_ > FLAG_trace_sim_after; | |
| 994 } | |
| 995 | |
| 996 | |
| 997 void Simulator::Format(Instr* instr, const char* format) { | |
| 998 OS::PrintErr("Simulator - unknown instruction: %s\n", format); | |
| 999 UNIMPLEMENTED(); | |
| 1000 } | |
| 1001 | |
| 1002 | |
| 1003 int8_t Simulator::ReadB(uword addr) { | |
| 1004 int8_t* ptr = reinterpret_cast<int8_t*>(addr); | |
| 1005 return *ptr; | |
| 1006 } | |
| 1007 | |
| 1008 | |
| 1009 uint8_t Simulator::ReadBU(uword addr) { | |
| 1010 uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); | |
| 1011 return *ptr; | |
| 1012 } | |
| 1013 | |
| 1014 | |
| 1015 int16_t Simulator::ReadH(uword addr, Instr* instr) { | |
| 1016 if ((addr & 1) == 0) { | |
| 1017 int16_t* ptr = reinterpret_cast<int16_t*>(addr); | |
| 1018 return *ptr; | |
| 1019 } | |
| 1020 UnalignedAccess("signed halfword read", addr, instr); | |
| 1021 return 0; | |
| 1022 } | |
| 1023 | |
| 1024 | |
| 1025 uint16_t Simulator::ReadHU(uword addr, Instr* instr) { | |
| 1026 if ((addr & 1) == 0) { | |
| 1027 uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); | |
| 1028 return *ptr; | |
| 1029 } | |
| 1030 UnalignedAccess("unsigned halfword read", addr, instr); | |
| 1031 return 0; | |
| 1032 } | |
| 1033 | |
| 1034 | |
| 1035 intptr_t Simulator::ReadW(uword addr, Instr* instr) { | |
| 1036 if ((addr & 3) == 0) { | |
| 1037 intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); | |
| 1038 return *ptr; | |
| 1039 } | |
| 1040 UnalignedAccess("read", addr, instr); | |
| 1041 return 0; | |
| 1042 } | |
| 1043 | |
| 1044 | |
| 1045 void Simulator::WriteB(uword addr, uint8_t value) { | |
| 1046 uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); | |
| 1047 *ptr = value; | |
| 1048 } | |
| 1049 | |
| 1050 | |
| 1051 void Simulator::WriteH(uword addr, uint16_t value, Instr* instr) { | |
| 1052 if ((addr & 1) == 0) { | |
| 1053 uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); | |
| 1054 *ptr = value; | |
| 1055 return; | |
| 1056 } | |
| 1057 UnalignedAccess("halfword write", addr, instr); | |
| 1058 } | |
| 1059 | |
| 1060 | |
| 1061 void Simulator::WriteW(uword addr, intptr_t value, Instr* instr) { | |
| 1062 if ((addr & 3) == 0) { | |
| 1063 intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); | |
| 1064 *ptr = value; | |
| 1065 return; | |
| 1066 } | |
| 1067 UnalignedAccess("write", addr, instr); | |
| 1068 } | |
| 1069 | |
| 1070 | |
| 1071 double Simulator::ReadD(uword addr, Instr* instr) { | |
| 1072 if ((addr & 7) == 0) { | |
| 1073 double* ptr = reinterpret_cast<double*>(addr); | |
| 1074 return *ptr; | |
| 1075 } | |
| 1076 UnalignedAccess("double-precision floating point read", addr, instr); | |
| 1077 return 0.0; | |
| 1078 } | |
| 1079 | |
| 1080 | |
| 1081 void Simulator::WriteD(uword addr, double value, Instr* instr) { | |
| 1082 if ((addr & 7) == 0) { | |
| 1083 double* ptr = reinterpret_cast<double*>(addr); | |
| 1084 *ptr = value; | |
| 1085 return; | |
| 1086 } | |
| 1087 UnalignedAccess("double-precision floating point write", addr, instr); | |
| 1088 } | |
| 1089 | |
| 1090 | |
| 1091 // Synchronization primitives support. | |
| 1092 void Simulator::SetExclusiveAccess(uword addr) { | |
| 1093 Thread* thread = Thread::Current(); | |
| 1094 ASSERT(thread != NULL); | |
| 1095 DEBUG_ASSERT(exclusive_access_lock_->IsOwnedByCurrentThread()); | |
| 1096 int i = 0; | |
| 1097 // Find an entry for this thread in the exclusive access state. | |
| 1098 while ((i < kNumAddressTags) && | |
| 1099 (exclusive_access_state_[i].thread != thread)) { | |
| 1100 i++; | |
| 1101 } | |
| 1102 // Round-robin replacement of previously used entries. | |
| 1103 if (i == kNumAddressTags) { | |
| 1104 i = next_address_tag_; | |
| 1105 if (++next_address_tag_ == kNumAddressTags) { | |
| 1106 next_address_tag_ = 0; | |
| 1107 } | |
| 1108 exclusive_access_state_[i].thread = thread; | |
| 1109 } | |
| 1110 // Remember the address being reserved. | |
| 1111 exclusive_access_state_[i].addr = addr; | |
| 1112 } | |
| 1113 | |
| 1114 | |
| 1115 bool Simulator::HasExclusiveAccessAndOpen(uword addr) { | |
| 1116 Thread* thread = Thread::Current(); | |
| 1117 ASSERT(thread != NULL); | |
| 1118 ASSERT(addr != 0); | |
| 1119 DEBUG_ASSERT(exclusive_access_lock_->IsOwnedByCurrentThread()); | |
| 1120 bool result = false; | |
| 1121 for (int i = 0; i < kNumAddressTags; i++) { | |
| 1122 if (exclusive_access_state_[i].thread == thread) { | |
| 1123 // Check whether the current thread's address reservation matches. | |
| 1124 if (exclusive_access_state_[i].addr == addr) { | |
| 1125 result = true; | |
| 1126 } | |
| 1127 exclusive_access_state_[i].addr = 0; | |
| 1128 } else if (exclusive_access_state_[i].addr == addr) { | |
| 1129 // Other threads with matching address lose their reservations. | |
| 1130 exclusive_access_state_[i].addr = 0; | |
| 1131 } | |
| 1132 } | |
| 1133 return result; | |
| 1134 } | |
| 1135 | |
| 1136 | |
| 1137 void Simulator::ClearExclusive() { | |
| 1138 MutexLocker ml(exclusive_access_lock_); | |
| 1139 // Remove the reservation for this thread. | |
| 1140 SetExclusiveAccess(0); | |
| 1141 } | |
| 1142 | |
| 1143 | |
| 1144 intptr_t Simulator::ReadExclusiveW(uword addr, Instr* instr) { | |
| 1145 MutexLocker ml(exclusive_access_lock_); | |
| 1146 SetExclusiveAccess(addr); | |
| 1147 return ReadW(addr, instr); | |
| 1148 } | |
| 1149 | |
| 1150 | |
| 1151 intptr_t Simulator::WriteExclusiveW(uword addr, intptr_t value, Instr* instr) { | |
| 1152 MutexLocker ml(exclusive_access_lock_); | |
| 1153 bool write_allowed = HasExclusiveAccessAndOpen(addr); | |
| 1154 if (write_allowed) { | |
| 1155 WriteW(addr, value, instr); | |
| 1156 return 1; // Success. | |
| 1157 } | |
| 1158 return 0; // Failure. | |
| 1159 } | |
| 1160 | |
| 1161 | |
| 1162 uword Simulator::CompareExchange(uword* address, | |
| 1163 uword compare_value, | |
| 1164 uword new_value) { | |
| 1165 MutexLocker ml(exclusive_access_lock_); | |
| 1166 // We do not get a reservation as it would be guaranteed to be found when | |
| 1167 // writing below. No other thread is able to make a reservation while we | |
| 1168 // hold the lock. | |
| 1169 uword value = *address; | |
| 1170 if (value == compare_value) { | |
| 1171 *address = new_value; | |
| 1172 // Same effect on exclusive access state as a successful SC. | |
| 1173 HasExclusiveAccessAndOpen(reinterpret_cast<uword>(address)); | |
| 1174 } else { | |
| 1175 // Same effect on exclusive access state as an LL. | |
| 1176 SetExclusiveAccess(reinterpret_cast<uword>(address)); | |
| 1177 } | |
| 1178 return value; | |
| 1179 } | |
| 1180 | |
| 1181 | |
| 1182 uint32_t Simulator::CompareExchangeUint32(uint32_t* address, | |
| 1183 uint32_t compare_value, | |
| 1184 uint32_t new_value) { | |
| 1185 COMPILE_ASSERT(sizeof(uword) == sizeof(uint32_t)); | |
| 1186 return CompareExchange(reinterpret_cast<uword*>(address), | |
| 1187 static_cast<uword>(compare_value), | |
| 1188 static_cast<uword>(new_value)); | |
| 1189 } | |
| 1190 | |
| 1191 | |
| 1192 // Calls into the Dart runtime are based on this interface. | |
| 1193 typedef void (*SimulatorRuntimeCall)(NativeArguments arguments); | |
| 1194 | |
| 1195 // Calls to leaf Dart runtime functions are based on this interface. | |
| 1196 typedef int32_t (*SimulatorLeafRuntimeCall)(int32_t r0, | |
| 1197 int32_t r1, | |
| 1198 int32_t r2, | |
| 1199 int32_t r3); | |
| 1200 | |
| 1201 // Calls to leaf float Dart runtime functions are based on this interface. | |
| 1202 typedef double (*SimulatorLeafFloatRuntimeCall)(double d0, double d1); | |
| 1203 | |
| 1204 // Calls to native Dart functions are based on this interface. | |
| 1205 typedef void (*SimulatorBootstrapNativeCall)(NativeArguments* arguments); | |
| 1206 typedef void (*SimulatorNativeCall)(NativeArguments* arguments, uword target); | |
| 1207 | |
| 1208 | |
| 1209 void Simulator::DoBreak(Instr* instr) { | |
| 1210 ASSERT(instr->OpcodeField() == SPECIAL); | |
| 1211 ASSERT(instr->FunctionField() == BREAK); | |
| 1212 if (instr->BreakCodeField() == Instr::kStopMessageCode) { | |
| 1213 SimulatorDebugger dbg(this); | |
| 1214 const char* message = *reinterpret_cast<const char**>( | |
| 1215 reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize); | |
| 1216 set_pc(get_pc() + Instr::kInstrSize); | |
| 1217 dbg.Stop(instr, message); | |
| 1218 // Adjust for extra pc increment. | |
| 1219 set_pc(get_pc() - Instr::kInstrSize); | |
| 1220 } else if (instr->BreakCodeField() == Instr::kSimulatorRedirectCode) { | |
| 1221 SimulatorSetjmpBuffer buffer(this); | |
| 1222 | |
| 1223 if (!setjmp(buffer.buffer_)) { | |
| 1224 int32_t saved_ra = get_register(RA); | |
| 1225 Redirection* redirection = Redirection::FromBreakInstruction(instr); | |
| 1226 uword external = redirection->external_function(); | |
| 1227 if (IsTracingExecution()) { | |
| 1228 THR_Print("Call to host function at 0x%" Pd "\n", external); | |
| 1229 } | |
| 1230 | |
| 1231 if ((redirection->call_kind() == kRuntimeCall) || | |
| 1232 (redirection->call_kind() == kBootstrapNativeCall) || | |
| 1233 (redirection->call_kind() == kNativeCall)) { | |
| 1234 // Set the top_exit_frame_info of this simulator to the native stack. | |
| 1235 set_top_exit_frame_info(Thread::GetCurrentStackPointer()); | |
| 1236 } | |
| 1237 if (redirection->call_kind() == kRuntimeCall) { | |
| 1238 NativeArguments arguments; | |
| 1239 ASSERT(sizeof(NativeArguments) == 4 * kWordSize); | |
| 1240 arguments.thread_ = reinterpret_cast<Thread*>(get_register(A0)); | |
| 1241 arguments.argc_tag_ = get_register(A1); | |
| 1242 arguments.argv_ = reinterpret_cast<RawObject**>(get_register(A2)); | |
| 1243 arguments.retval_ = reinterpret_cast<RawObject**>(get_register(A3)); | |
| 1244 SimulatorRuntimeCall target = | |
| 1245 reinterpret_cast<SimulatorRuntimeCall>(external); | |
| 1246 target(arguments); | |
| 1247 set_register(V0, icount_); // Zap result registers from void function. | |
| 1248 set_register(V1, icount_); | |
| 1249 } else if (redirection->call_kind() == kLeafRuntimeCall) { | |
| 1250 int32_t a0 = get_register(A0); | |
| 1251 int32_t a1 = get_register(A1); | |
| 1252 int32_t a2 = get_register(A2); | |
| 1253 int32_t a3 = get_register(A3); | |
| 1254 SimulatorLeafRuntimeCall target = | |
| 1255 reinterpret_cast<SimulatorLeafRuntimeCall>(external); | |
| 1256 a0 = target(a0, a1, a2, a3); | |
| 1257 set_register(V0, a0); // Set returned result from function. | |
| 1258 set_register(V1, icount_); // Zap second result register. | |
| 1259 } else if (redirection->call_kind() == kLeafFloatRuntimeCall) { | |
| 1260 ASSERT((0 <= redirection->argument_count()) && | |
| 1261 (redirection->argument_count() <= 2)); | |
| 1262 // double values are passed and returned in floating point registers. | |
| 1263 SimulatorLeafFloatRuntimeCall target = | |
| 1264 reinterpret_cast<SimulatorLeafFloatRuntimeCall>(external); | |
| 1265 double d0 = 0.0; | |
| 1266 double d6 = get_fregister_double(F12); | |
| 1267 double d7 = get_fregister_double(F14); | |
| 1268 d0 = target(d6, d7); | |
| 1269 set_fregister_double(F0, d0); | |
| 1270 } else if (redirection->call_kind() == kBootstrapNativeCall) { | |
| 1271 ASSERT(redirection->argument_count() == 1); | |
| 1272 NativeArguments* arguments; | |
| 1273 arguments = reinterpret_cast<NativeArguments*>(get_register(A0)); | |
| 1274 SimulatorBootstrapNativeCall target = | |
| 1275 reinterpret_cast<SimulatorBootstrapNativeCall>(external); | |
| 1276 target(arguments); | |
| 1277 set_register(V0, icount_); // Zap result register from void function. | |
| 1278 set_register(V1, icount_); | |
| 1279 } else { | |
| 1280 ASSERT(redirection->call_kind() == kNativeCall); | |
| 1281 NativeArguments* arguments; | |
| 1282 arguments = reinterpret_cast<NativeArguments*>(get_register(A0)); | |
| 1283 uword target_func = get_register(A1); | |
| 1284 SimulatorNativeCall target = | |
| 1285 reinterpret_cast<SimulatorNativeCall>(external); | |
| 1286 target(arguments, target_func); | |
| 1287 set_register(V0, icount_); // Zap result register from void function. | |
| 1288 set_register(V1, icount_); | |
| 1289 } | |
| 1290 set_top_exit_frame_info(0); | |
| 1291 | |
| 1292 // Zap caller-saved registers, since the actual runtime call could have | |
| 1293 // used them. | |
| 1294 set_register(T0, icount_); | |
| 1295 set_register(T1, icount_); | |
| 1296 set_register(T2, icount_); | |
| 1297 set_register(T3, icount_); | |
| 1298 set_register(T4, icount_); | |
| 1299 set_register(T5, icount_); | |
| 1300 set_register(T6, icount_); | |
| 1301 set_register(T7, icount_); | |
| 1302 set_register(T8, icount_); | |
| 1303 set_register(T9, icount_); | |
| 1304 | |
| 1305 set_register(A0, icount_); | |
| 1306 set_register(A1, icount_); | |
| 1307 set_register(A2, icount_); | |
| 1308 set_register(A3, icount_); | |
| 1309 set_register(TMP, icount_); | |
| 1310 set_register(RA, icount_); | |
| 1311 | |
| 1312 // Zap floating point registers. | |
| 1313 int32_t zap_dvalue = icount_; | |
| 1314 for (int i = F4; i <= F18; i++) { | |
| 1315 set_fregister(static_cast<FRegister>(i), zap_dvalue); | |
| 1316 } | |
| 1317 | |
| 1318 // Return. Subtract to account for pc_ increment after return. | |
| 1319 set_pc(saved_ra - Instr::kInstrSize); | |
| 1320 } else { | |
| 1321 // Coming via long jump from a throw. Continue to exception handler. | |
| 1322 set_top_exit_frame_info(0); | |
| 1323 // Adjust for extra pc increment. | |
| 1324 set_pc(get_pc() - Instr::kInstrSize); | |
| 1325 } | |
| 1326 } else if (instr->BreakCodeField() == Instr::kSimulatorBreakCode) { | |
| 1327 SimulatorDebugger dbg(this); | |
| 1328 dbg.Stop(instr, "breakpoint"); | |
| 1329 // Adjust for extra pc increment. | |
| 1330 set_pc(get_pc() - Instr::kInstrSize); | |
| 1331 } else { | |
| 1332 SimulatorDebugger dbg(this); | |
| 1333 set_pc(get_pc() + Instr::kInstrSize); | |
| 1334 char buffer[32]; | |
| 1335 snprintf(buffer, sizeof(buffer), "break #0x%x", instr->BreakCodeField()); | |
| 1336 dbg.Stop(instr, buffer); | |
| 1337 // Adjust for extra pc increment. | |
| 1338 set_pc(get_pc() - Instr::kInstrSize); | |
| 1339 } | |
| 1340 } | |
| 1341 | |
| 1342 | |
| 1343 void Simulator::DecodeSpecial(Instr* instr) { | |
| 1344 ASSERT(instr->OpcodeField() == SPECIAL); | |
| 1345 switch (instr->FunctionField()) { | |
| 1346 case ADDU: { | |
| 1347 ASSERT(instr->SaField() == 0); | |
| 1348 // Format(instr, "addu 'rd, 'rs, 'rt"); | |
| 1349 int32_t rs_val = get_register(instr->RsField()); | |
| 1350 int32_t rt_val = get_register(instr->RtField()); | |
| 1351 set_register(instr->RdField(), rs_val + rt_val); | |
| 1352 break; | |
| 1353 } | |
| 1354 case AND: { | |
| 1355 ASSERT(instr->SaField() == 0); | |
| 1356 // Format(instr, "and 'rd, 'rs, 'rt"); | |
| 1357 int32_t rs_val = get_register(instr->RsField()); | |
| 1358 int32_t rt_val = get_register(instr->RtField()); | |
| 1359 set_register(instr->RdField(), rs_val & rt_val); | |
| 1360 break; | |
| 1361 } | |
| 1362 case BREAK: { | |
| 1363 DoBreak(instr); | |
| 1364 break; | |
| 1365 } | |
| 1366 case DIV: { | |
| 1367 ASSERT(instr->RdField() == 0); | |
| 1368 ASSERT(instr->SaField() == 0); | |
| 1369 // Format(instr, "div 'rs, 'rt"); | |
| 1370 int32_t rs_val = get_register(instr->RsField()); | |
| 1371 int32_t rt_val = get_register(instr->RtField()); | |
| 1372 if (rt_val == 0) { | |
| 1373 // Results are unpredictable, but there is no arithmetic exception. | |
| 1374 set_hi_register(icount_); | |
| 1375 set_lo_register(icount_); | |
| 1376 break; | |
| 1377 } | |
| 1378 | |
| 1379 if ((rs_val == static_cast<int32_t>(0x80000000)) && | |
| 1380 (rt_val == static_cast<int32_t>(0xffffffff))) { | |
| 1381 set_lo_register(0x80000000); | |
| 1382 set_hi_register(0); | |
| 1383 } else { | |
| 1384 set_lo_register(rs_val / rt_val); | |
| 1385 set_hi_register(rs_val % rt_val); | |
| 1386 } | |
| 1387 break; | |
| 1388 } | |
| 1389 case DIVU: { | |
| 1390 ASSERT(instr->RdField() == 0); | |
| 1391 ASSERT(instr->SaField() == 0); | |
| 1392 // Format(instr, "divu 'rs, 'rt"); | |
| 1393 uint32_t rs_val = get_register(instr->RsField()); | |
| 1394 uint32_t rt_val = get_register(instr->RtField()); | |
| 1395 if (rt_val == 0) { | |
| 1396 // Results are unpredictable, but there is no arithmetic exception. | |
| 1397 set_hi_register(icount_); | |
| 1398 set_lo_register(icount_); | |
| 1399 break; | |
| 1400 } | |
| 1401 set_lo_register(rs_val / rt_val); | |
| 1402 set_hi_register(rs_val % rt_val); | |
| 1403 break; | |
| 1404 } | |
| 1405 case JALR: { | |
| 1406 ASSERT(instr->RtField() == R0); | |
| 1407 ASSERT(instr->RsField() != instr->RdField()); | |
| 1408 ASSERT(!delay_slot_); | |
| 1409 // Format(instr, "jalr'hint 'rd, rs"); | |
| 1410 set_register(instr->RdField(), pc_ + 2 * Instr::kInstrSize); | |
| 1411 uword next_pc = get_register(instr->RsField()); | |
| 1412 ExecuteDelaySlot(); | |
| 1413 // Set return address to be the instruction after the delay slot. | |
| 1414 pc_ = next_pc - Instr::kInstrSize; // Account for regular PC increment. | |
| 1415 break; | |
| 1416 } | |
| 1417 case JR: { | |
| 1418 ASSERT(instr->RtField() == R0); | |
| 1419 ASSERT(instr->RdField() == R0); | |
| 1420 ASSERT(!delay_slot_); | |
| 1421 // Format(instr, "jr'hint 'rs"); | |
| 1422 uword next_pc = get_register(instr->RsField()); | |
| 1423 ExecuteDelaySlot(); | |
| 1424 pc_ = next_pc - Instr::kInstrSize; // Account for regular PC increment. | |
| 1425 break; | |
| 1426 } | |
| 1427 case MFHI: { | |
| 1428 ASSERT(instr->RsField() == 0); | |
| 1429 ASSERT(instr->RtField() == 0); | |
| 1430 ASSERT(instr->SaField() == 0); | |
| 1431 // Format(instr, "mfhi 'rd"); | |
| 1432 set_register(instr->RdField(), get_hi_register()); | |
| 1433 break; | |
| 1434 } | |
| 1435 case MFLO: { | |
| 1436 ASSERT(instr->RsField() == 0); | |
| 1437 ASSERT(instr->RtField() == 0); | |
| 1438 ASSERT(instr->SaField() == 0); | |
| 1439 // Format(instr, "mflo 'rd"); | |
| 1440 set_register(instr->RdField(), get_lo_register()); | |
| 1441 break; | |
| 1442 } | |
| 1443 case MOVCI: { | |
| 1444 ASSERT(instr->SaField() == 0); | |
| 1445 ASSERT(instr->Bit(17) == 0); | |
| 1446 int32_t rs_val = get_register(instr->RsField()); | |
| 1447 uint32_t cc, fcsr_cc, test, status; | |
| 1448 cc = instr->Bits(18, 3); | |
| 1449 fcsr_cc = get_fcsr_condition_bit(cc); | |
| 1450 test = instr->Bit(16); | |
| 1451 status = test_fcsr_bit(fcsr_cc); | |
| 1452 if (test == status) { | |
| 1453 set_register(instr->RdField(), rs_val); | |
| 1454 } | |
| 1455 break; | |
| 1456 } | |
| 1457 case MOVN: { | |
| 1458 ASSERT(instr->SaField() == 0); | |
| 1459 // Format(instr, "movn 'rd, 'rs, 'rt"); | |
| 1460 int32_t rt_val = get_register(instr->RtField()); | |
| 1461 int32_t rs_val = get_register(instr->RsField()); | |
| 1462 if (rt_val != 0) { | |
| 1463 set_register(instr->RdField(), rs_val); | |
| 1464 } | |
| 1465 break; | |
| 1466 } | |
| 1467 case MOVZ: { | |
| 1468 ASSERT(instr->SaField() == 0); | |
| 1469 // Format(instr, "movz 'rd, 'rs, 'rt"); | |
| 1470 int32_t rt_val = get_register(instr->RtField()); | |
| 1471 int32_t rs_val = get_register(instr->RsField()); | |
| 1472 if (rt_val == 0) { | |
| 1473 set_register(instr->RdField(), rs_val); | |
| 1474 } | |
| 1475 break; | |
| 1476 } | |
| 1477 case MTHI: { | |
| 1478 ASSERT(instr->RtField() == 0); | |
| 1479 ASSERT(instr->RdField() == 0); | |
| 1480 ASSERT(instr->SaField() == 0); | |
| 1481 // Format(instr, "mthi 'rd"); | |
| 1482 set_hi_register(get_register(instr->RsField())); | |
| 1483 break; | |
| 1484 } | |
| 1485 case MTLO: { | |
| 1486 ASSERT(instr->RtField() == 0); | |
| 1487 ASSERT(instr->RdField() == 0); | |
| 1488 ASSERT(instr->SaField() == 0); | |
| 1489 // Format(instr, "mflo 'rd"); | |
| 1490 set_lo_register(get_register(instr->RsField())); | |
| 1491 break; | |
| 1492 } | |
| 1493 case MULT: { | |
| 1494 ASSERT(instr->RdField() == 0); | |
| 1495 ASSERT(instr->SaField() == 0); | |
| 1496 // Format(instr, "mult 'rs, 'rt"); | |
| 1497 int64_t rs = get_register(instr->RsField()); | |
| 1498 int64_t rt = get_register(instr->RtField()); | |
| 1499 int64_t res = rs * rt; | |
| 1500 set_hi_register(Utils::High32Bits(res)); | |
| 1501 set_lo_register(Utils::Low32Bits(res)); | |
| 1502 break; | |
| 1503 } | |
| 1504 case MULTU: { | |
| 1505 ASSERT(instr->RdField() == 0); | |
| 1506 ASSERT(instr->SaField() == 0); | |
| 1507 // Format(instr, "multu 'rs, 'rt"); | |
| 1508 uint64_t rs = static_cast<uint32_t>(get_register(instr->RsField())); | |
| 1509 uint64_t rt = static_cast<uint32_t>(get_register(instr->RtField())); | |
| 1510 uint64_t res = rs * rt; | |
| 1511 set_hi_register(Utils::High32Bits(res)); | |
| 1512 set_lo_register(Utils::Low32Bits(res)); | |
| 1513 break; | |
| 1514 } | |
| 1515 case NOR: { | |
| 1516 ASSERT(instr->SaField() == 0); | |
| 1517 // Format(instr, "nor 'rd, 'rs, 'rt"); | |
| 1518 int32_t rs_val = get_register(instr->RsField()); | |
| 1519 int32_t rt_val = get_register(instr->RtField()); | |
| 1520 set_register(instr->RdField(), ~(rs_val | rt_val)); | |
| 1521 break; | |
| 1522 } | |
| 1523 case OR: { | |
| 1524 ASSERT(instr->SaField() == 0); | |
| 1525 // Format(instr, "or 'rd, 'rs, 'rt"); | |
| 1526 int32_t rs_val = get_register(instr->RsField()); | |
| 1527 int32_t rt_val = get_register(instr->RtField()); | |
| 1528 set_register(instr->RdField(), rs_val | rt_val); | |
| 1529 break; | |
| 1530 } | |
| 1531 case SLL: { | |
| 1532 ASSERT(instr->RsField() == 0); | |
| 1533 if ((instr->RdField() == R0) && (instr->RtField() == R0) && | |
| 1534 (instr->SaField() == 0)) { | |
| 1535 // Format(instr, "nop"); | |
| 1536 // Nothing to be done for NOP. | |
| 1537 } else { | |
| 1538 int32_t rt_val = get_register(instr->RtField()); | |
| 1539 int sa = instr->SaField(); | |
| 1540 set_register(instr->RdField(), rt_val << sa); | |
| 1541 } | |
| 1542 break; | |
| 1543 } | |
| 1544 case SLLV: { | |
| 1545 ASSERT(instr->SaField() == 0); | |
| 1546 // Format(instr, "sllv 'rd, 'rt, 'rs"); | |
| 1547 int32_t rt_val = get_register(instr->RtField()); | |
| 1548 int32_t rs_val = get_register(instr->RsField()); | |
| 1549 set_register(instr->RdField(), rt_val << (rs_val & 0x1f)); | |
| 1550 break; | |
| 1551 } | |
| 1552 case SLT: { | |
| 1553 ASSERT(instr->SaField() == 0); | |
| 1554 // Format(instr, "slt 'rd, 'rs, 'rt"); | |
| 1555 int32_t rs_val = get_register(instr->RsField()); | |
| 1556 int32_t rt_val = get_register(instr->RtField()); | |
| 1557 set_register(instr->RdField(), rs_val < rt_val ? 1 : 0); | |
| 1558 break; | |
| 1559 } | |
| 1560 case SLTU: { | |
| 1561 ASSERT(instr->SaField() == 0); | |
| 1562 // Format(instr, "sltu 'rd, 'rs, 'rt"); | |
| 1563 uint32_t rs_val = static_cast<uint32_t>(get_register(instr->RsField())); | |
| 1564 uint32_t rt_val = static_cast<uint32_t>(get_register(instr->RtField())); | |
| 1565 set_register(instr->RdField(), rs_val < rt_val ? 1 : 0); | |
| 1566 break; | |
| 1567 } | |
| 1568 case SRA: { | |
| 1569 ASSERT(instr->RsField() == 0); | |
| 1570 // Format(instr, "sra 'rd, 'rt, 'sa"); | |
| 1571 int32_t rt_val = get_register(instr->RtField()); | |
| 1572 int32_t sa = instr->SaField(); | |
| 1573 set_register(instr->RdField(), rt_val >> sa); | |
| 1574 break; | |
| 1575 } | |
| 1576 case SRAV: { | |
| 1577 ASSERT(instr->SaField() == 0); | |
| 1578 // Format(instr, "srav 'rd, 'rt, 'rs"); | |
| 1579 int32_t rt_val = get_register(instr->RtField()); | |
| 1580 int32_t rs_val = get_register(instr->RsField()); | |
| 1581 set_register(instr->RdField(), rt_val >> (rs_val & 0x1f)); | |
| 1582 break; | |
| 1583 } | |
| 1584 case SRL: { | |
| 1585 ASSERT(instr->RsField() == 0); | |
| 1586 // Format(instr, "srl 'rd, 'rt, 'sa"); | |
| 1587 uint32_t rt_val = get_register(instr->RtField()); | |
| 1588 uint32_t sa = instr->SaField(); | |
| 1589 set_register(instr->RdField(), rt_val >> sa); | |
| 1590 break; | |
| 1591 } | |
| 1592 case SRLV: { | |
| 1593 ASSERT(instr->SaField() == 0); | |
| 1594 // Format(instr, "srlv 'rd, 'rt, 'rs"); | |
| 1595 uint32_t rt_val = get_register(instr->RtField()); | |
| 1596 uint32_t rs_val = get_register(instr->RsField()); | |
| 1597 set_register(instr->RdField(), rt_val >> (rs_val & 0x1f)); | |
| 1598 break; | |
| 1599 } | |
| 1600 case SUBU: { | |
| 1601 ASSERT(instr->SaField() == 0); | |
| 1602 // Format(instr, "subu 'rd, 'rs, 'rt"); | |
| 1603 int32_t rs_val = get_register(instr->RsField()); | |
| 1604 int32_t rt_val = get_register(instr->RtField()); | |
| 1605 set_register(instr->RdField(), rs_val - rt_val); | |
| 1606 break; | |
| 1607 } | |
| 1608 case XOR: { | |
| 1609 ASSERT(instr->SaField() == 0); | |
| 1610 // Format(instr, "xor 'rd, 'rs, 'rt"); | |
| 1611 int32_t rs_val = get_register(instr->RsField()); | |
| 1612 int32_t rt_val = get_register(instr->RtField()); | |
| 1613 set_register(instr->RdField(), rs_val ^ rt_val); | |
| 1614 break; | |
| 1615 } | |
| 1616 default: { | |
| 1617 OS::PrintErr("DecodeSpecial: 0x%x\n", instr->InstructionBits()); | |
| 1618 UnimplementedInstruction(instr); | |
| 1619 break; | |
| 1620 } | |
| 1621 } | |
| 1622 } | |
| 1623 | |
| 1624 | |
| 1625 void Simulator::DecodeSpecial2(Instr* instr) { | |
| 1626 ASSERT(instr->OpcodeField() == SPECIAL2); | |
| 1627 switch (instr->FunctionField()) { | |
| 1628 case MADD: { | |
| 1629 ASSERT(instr->RdField() == 0); | |
| 1630 ASSERT(instr->SaField() == 0); | |
| 1631 // Format(instr, "madd 'rs, 'rt"); | |
| 1632 uint32_t lo = get_lo_register(); | |
| 1633 int32_t hi = get_hi_register(); | |
| 1634 int64_t accum = Utils::LowHighTo64Bits(lo, hi); | |
| 1635 int64_t rs = get_register(instr->RsField()); | |
| 1636 int64_t rt = get_register(instr->RtField()); | |
| 1637 int64_t res = accum + rs * rt; | |
| 1638 set_hi_register(Utils::High32Bits(res)); | |
| 1639 set_lo_register(Utils::Low32Bits(res)); | |
| 1640 break; | |
| 1641 } | |
| 1642 case MADDU: { | |
| 1643 ASSERT(instr->RdField() == 0); | |
| 1644 ASSERT(instr->SaField() == 0); | |
| 1645 // Format(instr, "maddu 'rs, 'rt"); | |
| 1646 uint32_t lo = get_lo_register(); | |
| 1647 uint32_t hi = get_hi_register(); | |
| 1648 uint64_t accum = Utils::LowHighTo64Bits(lo, hi); | |
| 1649 uint64_t rs = static_cast<uint32_t>(get_register(instr->RsField())); | |
| 1650 uint64_t rt = static_cast<uint32_t>(get_register(instr->RtField())); | |
| 1651 uint64_t res = accum + rs * rt; | |
| 1652 set_hi_register(Utils::High32Bits(res)); | |
| 1653 set_lo_register(Utils::Low32Bits(res)); | |
| 1654 break; | |
| 1655 } | |
| 1656 case CLO: { | |
| 1657 ASSERT(instr->SaField() == 0); | |
| 1658 ASSERT(instr->RtField() == instr->RdField()); | |
| 1659 // Format(instr, "clo 'rd, 'rs"); | |
| 1660 int32_t rs_val = get_register(instr->RsField()); | |
| 1661 int32_t bitcount = 0; | |
| 1662 while (rs_val < 0) { | |
| 1663 bitcount++; | |
| 1664 rs_val <<= 1; | |
| 1665 } | |
| 1666 set_register(instr->RdField(), bitcount); | |
| 1667 break; | |
| 1668 } | |
| 1669 case CLZ: { | |
| 1670 ASSERT(instr->SaField() == 0); | |
| 1671 ASSERT(instr->RtField() == instr->RdField()); | |
| 1672 // Format(instr, "clz 'rd, 'rs"); | |
| 1673 int32_t rs_val = get_register(instr->RsField()); | |
| 1674 int32_t bitcount = 0; | |
| 1675 if (rs_val != 0) { | |
| 1676 while (rs_val > 0) { | |
| 1677 bitcount++; | |
| 1678 rs_val <<= 1; | |
| 1679 } | |
| 1680 } else { | |
| 1681 bitcount = 32; | |
| 1682 } | |
| 1683 set_register(instr->RdField(), bitcount); | |
| 1684 break; | |
| 1685 } | |
| 1686 default: { | |
| 1687 OS::PrintErr("DecodeSpecial2: 0x%x\n", instr->InstructionBits()); | |
| 1688 UnimplementedInstruction(instr); | |
| 1689 break; | |
| 1690 } | |
| 1691 } | |
| 1692 } | |
| 1693 | |
| 1694 | |
| 1695 void Simulator::DoBranch(Instr* instr, bool taken, bool likely) { | |
| 1696 ASSERT(!delay_slot_); | |
| 1697 int32_t imm_val = instr->SImmField() << 2; | |
| 1698 | |
| 1699 uword next_pc; | |
| 1700 if (taken) { | |
| 1701 // imm_val is added to the address of the instruction following the branch. | |
| 1702 next_pc = pc_ + imm_val + Instr::kInstrSize; | |
| 1703 if (likely) { | |
| 1704 ExecuteDelaySlot(); | |
| 1705 } | |
| 1706 } else { | |
| 1707 next_pc = pc_ + (2 * Instr::kInstrSize); // Next after delay slot. | |
| 1708 } | |
| 1709 if (!likely) { | |
| 1710 ExecuteDelaySlot(); | |
| 1711 } | |
| 1712 pc_ = next_pc - Instr::kInstrSize; | |
| 1713 | |
| 1714 return; | |
| 1715 } | |
| 1716 | |
| 1717 | |
| 1718 void Simulator::DecodeRegImm(Instr* instr) { | |
| 1719 ASSERT(instr->OpcodeField() == REGIMM); | |
| 1720 switch (instr->RegImmFnField()) { | |
| 1721 case BGEZ: { | |
| 1722 // Format(instr, "bgez 'rs, 'dest"); | |
| 1723 int32_t rs_val = get_register(instr->RsField()); | |
| 1724 DoBranch(instr, rs_val >= 0, false); | |
| 1725 break; | |
| 1726 } | |
| 1727 case BGEZAL: { | |
| 1728 int32_t rs_val = get_register(instr->RsField()); | |
| 1729 // Return address is one after the delay slot. | |
| 1730 set_register(RA, pc_ + (2 * Instr::kInstrSize)); | |
| 1731 DoBranch(instr, rs_val >= 0, false); | |
| 1732 break; | |
| 1733 } | |
| 1734 case BLTZAL: { | |
| 1735 int32_t rs_val = get_register(instr->RsField()); | |
| 1736 // Return address is one after the delay slot. | |
| 1737 set_register(RA, pc_ + (2 * Instr::kInstrSize)); | |
| 1738 DoBranch(instr, rs_val < 0, false); | |
| 1739 break; | |
| 1740 } | |
| 1741 case BGEZL: { | |
| 1742 // Format(instr, "bgezl 'rs, 'dest"); | |
| 1743 int32_t rs_val = get_register(instr->RsField()); | |
| 1744 DoBranch(instr, rs_val >= 0, true); | |
| 1745 break; | |
| 1746 } | |
| 1747 case BLTZ: { | |
| 1748 // Format(instr, "bltz 'rs, 'dest"); | |
| 1749 int32_t rs_val = get_register(instr->RsField()); | |
| 1750 DoBranch(instr, rs_val < 0, false); | |
| 1751 break; | |
| 1752 } | |
| 1753 case BLTZL: { | |
| 1754 // Format(instr, "bltzl 'rs, 'dest"); | |
| 1755 int32_t rs_val = get_register(instr->RsField()); | |
| 1756 DoBranch(instr, rs_val < 0, true); | |
| 1757 break; | |
| 1758 } | |
| 1759 default: { | |
| 1760 OS::PrintErr("DecodeRegImm: 0x%x\n", instr->InstructionBits()); | |
| 1761 UnimplementedInstruction(instr); | |
| 1762 break; | |
| 1763 } | |
| 1764 } | |
| 1765 } | |
| 1766 | |
| 1767 | |
| 1768 void Simulator::DecodeCop1(Instr* instr) { | |
| 1769 ASSERT(instr->OpcodeField() == COP1); | |
| 1770 if (instr->HasFormat()) { | |
| 1771 // If the rs field is a valid format, then the function field identifies the | |
| 1772 // instruction. | |
| 1773 double fs_val = get_fregister_double(instr->FsField()); | |
| 1774 double ft_val = get_fregister_double(instr->FtField()); | |
| 1775 uint32_t cc, fcsr_cc; | |
| 1776 cc = instr->FpuCCField(); | |
| 1777 fcsr_cc = get_fcsr_condition_bit(cc); | |
| 1778 switch (instr->Cop1FunctionField()) { | |
| 1779 case COP1_ADD: { | |
| 1780 // Format(instr, "add.'fmt 'fd, 'fs, 'ft"); | |
| 1781 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1782 set_fregister_double(instr->FdField(), fs_val + ft_val); | |
| 1783 break; | |
| 1784 } | |
| 1785 case COP1_SUB: { | |
| 1786 // Format(instr, "sub.'fmt 'fd, 'fs, 'ft"); | |
| 1787 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1788 set_fregister_double(instr->FdField(), fs_val - ft_val); | |
| 1789 break; | |
| 1790 } | |
| 1791 case COP1_MUL: { | |
| 1792 // Format(instr, "mul.'fmt 'fd, 'fs, 'ft"); | |
| 1793 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1794 set_fregister_double(instr->FdField(), fs_val * ft_val); | |
| 1795 break; | |
| 1796 } | |
| 1797 case COP1_DIV: { | |
| 1798 // Format(instr, "div.'fmt 'fd, 'fs, 'ft"); | |
| 1799 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1800 set_fregister_double(instr->FdField(), fs_val / ft_val); | |
| 1801 break; | |
| 1802 } | |
| 1803 case COP1_SQRT: { | |
| 1804 // Format(instr, "sqrt.'fmt 'fd, 'fs"); | |
| 1805 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1806 set_fregister_double(instr->FdField(), sqrt(fs_val)); | |
| 1807 break; | |
| 1808 } | |
| 1809 case COP1_MOV: { | |
| 1810 // Format(instr, "mov.'fmt 'fd, 'fs"); | |
| 1811 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1812 set_fregister_double(instr->FdField(), fs_val); | |
| 1813 break; | |
| 1814 } | |
| 1815 case COP1_NEG: { | |
| 1816 // Format(instr, "neg.'fmt 'fd, 'fs"); | |
| 1817 ASSERT(instr->FormatField() == FMT_D); | |
| 1818 set_fregister_double(instr->FdField(), -fs_val); | |
| 1819 break; | |
| 1820 } | |
| 1821 case COP1_C_F: { | |
| 1822 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1823 ASSERT(instr->FdField() == F0); | |
| 1824 set_fcsr_bit(fcsr_cc, false); | |
| 1825 break; | |
| 1826 } | |
| 1827 case COP1_C_UN: { | |
| 1828 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1829 ASSERT(instr->FdField() == F0); | |
| 1830 set_fcsr_bit(fcsr_cc, isnan(fs_val) || isnan(ft_val)); | |
| 1831 break; | |
| 1832 } | |
| 1833 case COP1_C_EQ: { | |
| 1834 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1835 ASSERT(instr->FdField() == F0); | |
| 1836 set_fcsr_bit(fcsr_cc, (fs_val == ft_val)); | |
| 1837 break; | |
| 1838 } | |
| 1839 case COP1_C_UEQ: { | |
| 1840 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1841 ASSERT(instr->FdField() == F0); | |
| 1842 set_fcsr_bit(fcsr_cc, | |
| 1843 (fs_val == ft_val) || isnan(fs_val) || isnan(ft_val)); | |
| 1844 break; | |
| 1845 } | |
| 1846 case COP1_C_OLT: { | |
| 1847 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1848 ASSERT(instr->FdField() == F0); | |
| 1849 set_fcsr_bit(fcsr_cc, (fs_val < ft_val)); | |
| 1850 break; | |
| 1851 } | |
| 1852 case COP1_C_ULT: { | |
| 1853 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1854 ASSERT(instr->FdField() == F0); | |
| 1855 set_fcsr_bit(fcsr_cc, | |
| 1856 (fs_val < ft_val) || isnan(fs_val) || isnan(ft_val)); | |
| 1857 break; | |
| 1858 } | |
| 1859 case COP1_C_OLE: { | |
| 1860 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1861 ASSERT(instr->FdField() == F0); | |
| 1862 set_fcsr_bit(fcsr_cc, (fs_val <= ft_val)); | |
| 1863 break; | |
| 1864 } | |
| 1865 case COP1_C_ULE: { | |
| 1866 ASSERT(instr->FormatField() == FMT_D); // Only D supported. | |
| 1867 ASSERT(instr->FdField() == F0); | |
| 1868 set_fcsr_bit(fcsr_cc, | |
| 1869 (fs_val <= ft_val) || isnan(fs_val) || isnan(ft_val)); | |
| 1870 break; | |
| 1871 } | |
| 1872 case COP1_TRUNC_W: { | |
| 1873 switch (instr->FormatField()) { | |
| 1874 case FMT_D: { | |
| 1875 double fs_dbl = get_fregister_double(instr->FsField()); | |
| 1876 int32_t fs_int; | |
| 1877 if (isnan(fs_dbl) || isinf(fs_dbl) || (fs_dbl > kMaxInt32) || | |
| 1878 (fs_dbl < kMinInt32)) { | |
| 1879 fs_int = kMaxInt32; | |
| 1880 } else { | |
| 1881 fs_int = static_cast<int32_t>(fs_dbl); | |
| 1882 } | |
| 1883 set_fregister(instr->FdField(), fs_int); | |
| 1884 break; | |
| 1885 } | |
| 1886 default: { | |
| 1887 OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits()); | |
| 1888 UnimplementedInstruction(instr); | |
| 1889 break; | |
| 1890 } | |
| 1891 } | |
| 1892 break; | |
| 1893 } | |
| 1894 case COP1_CVT_D: { | |
| 1895 switch (instr->FormatField()) { | |
| 1896 case FMT_W: { | |
| 1897 int32_t fs_int = get_fregister(instr->FsField()); | |
| 1898 double fs_dbl = static_cast<double>(fs_int); | |
| 1899 set_fregister_double(instr->FdField(), fs_dbl); | |
| 1900 break; | |
| 1901 } | |
| 1902 case FMT_S: { | |
| 1903 float fs_flt = get_fregister_float(instr->FsField()); | |
| 1904 double fs_dbl = static_cast<double>(fs_flt); | |
| 1905 set_fregister_double(instr->FdField(), fs_dbl); | |
| 1906 break; | |
| 1907 } | |
| 1908 default: { | |
| 1909 OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits()); | |
| 1910 UnimplementedInstruction(instr); | |
| 1911 break; | |
| 1912 } | |
| 1913 } | |
| 1914 break; | |
| 1915 } | |
| 1916 case COP1_CVT_S: { | |
| 1917 switch (instr->FormatField()) { | |
| 1918 case FMT_D: { | |
| 1919 double fs_dbl = get_fregister_double(instr->FsField()); | |
| 1920 float fs_flt = static_cast<float>(fs_dbl); | |
| 1921 set_fregister_float(instr->FdField(), fs_flt); | |
| 1922 break; | |
| 1923 } | |
| 1924 default: { | |
| 1925 OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits()); | |
| 1926 UnimplementedInstruction(instr); | |
| 1927 break; | |
| 1928 } | |
| 1929 } | |
| 1930 break; | |
| 1931 } | |
| 1932 default: { | |
| 1933 OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits()); | |
| 1934 UnimplementedInstruction(instr); | |
| 1935 break; | |
| 1936 } | |
| 1937 } | |
| 1938 } else { | |
| 1939 // If the rs field isn't a valid format, then it must be a sub-op. | |
| 1940 switch (instr->Cop1SubField()) { | |
| 1941 case COP1_MF: { | |
| 1942 // Format(instr, "mfc1 'rt, 'fs"); | |
| 1943 ASSERT(instr->Bits(0, 11) == 0); | |
| 1944 int32_t fs_val = get_fregister(instr->FsField()); | |
| 1945 set_register(instr->RtField(), fs_val); | |
| 1946 break; | |
| 1947 } | |
| 1948 case COP1_MT: { | |
| 1949 // Format(instr, "mtc1 'rt, 'fs"); | |
| 1950 ASSERT(instr->Bits(0, 11) == 0); | |
| 1951 int32_t rt_val = get_register(instr->RtField()); | |
| 1952 set_fregister(instr->FsField(), rt_val); | |
| 1953 break; | |
| 1954 } | |
| 1955 case COP1_BC: { | |
| 1956 ASSERT(instr->Bit(17) == 0); | |
| 1957 uint32_t cc, fcsr_cc; | |
| 1958 cc = instr->Bits(18, 3); | |
| 1959 fcsr_cc = get_fcsr_condition_bit(cc); | |
| 1960 if (instr->Bit(16) == 1) { // Branch on true. | |
| 1961 DoBranch(instr, test_fcsr_bit(fcsr_cc), false); | |
| 1962 } else { // Branch on false. | |
| 1963 DoBranch(instr, !test_fcsr_bit(fcsr_cc), false); | |
| 1964 } | |
| 1965 break; | |
| 1966 } | |
| 1967 default: { | |
| 1968 OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits()); | |
| 1969 UnimplementedInstruction(instr); | |
| 1970 break; | |
| 1971 } | |
| 1972 } | |
| 1973 } | |
| 1974 } | |
| 1975 | |
| 1976 | |
| 1977 void Simulator::InstructionDecode(Instr* instr) { | |
| 1978 if (IsTracingExecution()) { | |
| 1979 THR_Print("%" Pu64 " ", icount_); | |
| 1980 const uword start = reinterpret_cast<uword>(instr); | |
| 1981 const uword end = start + Instr::kInstrSize; | |
| 1982 if (FLAG_support_disassembler) { | |
| 1983 Disassembler::Disassemble(start, end); | |
| 1984 } else { | |
| 1985 THR_Print("Disassembler not supported in this mode.\n"); | |
| 1986 } | |
| 1987 } | |
| 1988 | |
| 1989 switch (instr->OpcodeField()) { | |
| 1990 case SPECIAL: { | |
| 1991 DecodeSpecial(instr); | |
| 1992 break; | |
| 1993 } | |
| 1994 case SPECIAL2: { | |
| 1995 DecodeSpecial2(instr); | |
| 1996 break; | |
| 1997 } | |
| 1998 case REGIMM: { | |
| 1999 DecodeRegImm(instr); | |
| 2000 break; | |
| 2001 } | |
| 2002 case COP1: { | |
| 2003 DecodeCop1(instr); | |
| 2004 break; | |
| 2005 } | |
| 2006 case ADDIU: { | |
| 2007 // Format(instr, "addiu 'rt, 'rs, 'imms"); | |
| 2008 int32_t rs_val = get_register(instr->RsField()); | |
| 2009 int32_t imm_val = instr->SImmField(); | |
| 2010 int32_t res = rs_val + imm_val; | |
| 2011 // Rt is set even on overflow. | |
| 2012 set_register(instr->RtField(), res); | |
| 2013 break; | |
| 2014 } | |
| 2015 case ANDI: { | |
| 2016 // Format(instr, "andi 'rt, 'rs, 'immu"); | |
| 2017 int32_t rs_val = get_register(instr->RsField()); | |
| 2018 set_register(instr->RtField(), rs_val & instr->UImmField()); | |
| 2019 break; | |
| 2020 } | |
| 2021 case BEQ: { | |
| 2022 // Format(instr, "beq 'rs, 'rt, 'dest"); | |
| 2023 int32_t rs_val = get_register(instr->RsField()); | |
| 2024 int32_t rt_val = get_register(instr->RtField()); | |
| 2025 DoBranch(instr, rs_val == rt_val, false); | |
| 2026 break; | |
| 2027 } | |
| 2028 case BEQL: { | |
| 2029 // Format(instr, "beql 'rs, 'rt, 'dest"); | |
| 2030 int32_t rs_val = get_register(instr->RsField()); | |
| 2031 int32_t rt_val = get_register(instr->RtField()); | |
| 2032 DoBranch(instr, rs_val == rt_val, true); | |
| 2033 break; | |
| 2034 } | |
| 2035 case BGTZ: { | |
| 2036 ASSERT(instr->RtField() == R0); | |
| 2037 // Format(instr, "bgtz 'rs, 'dest"); | |
| 2038 int32_t rs_val = get_register(instr->RsField()); | |
| 2039 DoBranch(instr, rs_val > 0, false); | |
| 2040 break; | |
| 2041 } | |
| 2042 case BGTZL: { | |
| 2043 ASSERT(instr->RtField() == R0); | |
| 2044 // Format(instr, "bgtzl 'rs, 'dest"); | |
| 2045 int32_t rs_val = get_register(instr->RsField()); | |
| 2046 DoBranch(instr, rs_val > 0, true); | |
| 2047 break; | |
| 2048 } | |
| 2049 case BLEZ: { | |
| 2050 ASSERT(instr->RtField() == R0); | |
| 2051 // Format(instr, "blez 'rs, 'dest"); | |
| 2052 int32_t rs_val = get_register(instr->RsField()); | |
| 2053 DoBranch(instr, rs_val <= 0, false); | |
| 2054 break; | |
| 2055 } | |
| 2056 case BLEZL: { | |
| 2057 ASSERT(instr->RtField() == R0); | |
| 2058 // Format(instr, "blezl 'rs, 'dest"); | |
| 2059 int32_t rs_val = get_register(instr->RsField()); | |
| 2060 DoBranch(instr, rs_val <= 0, true); | |
| 2061 break; | |
| 2062 } | |
| 2063 case BNE: { | |
| 2064 // Format(instr, "bne 'rs, 'rt, 'dest"); | |
| 2065 int32_t rs_val = get_register(instr->RsField()); | |
| 2066 int32_t rt_val = get_register(instr->RtField()); | |
| 2067 DoBranch(instr, rs_val != rt_val, false); | |
| 2068 break; | |
| 2069 } | |
| 2070 case BNEL: { | |
| 2071 // Format(instr, "bnel 'rs, 'rt, 'dest"); | |
| 2072 int32_t rs_val = get_register(instr->RsField()); | |
| 2073 int32_t rt_val = get_register(instr->RtField()); | |
| 2074 DoBranch(instr, rs_val != rt_val, true); | |
| 2075 break; | |
| 2076 } | |
| 2077 case LB: { | |
| 2078 // Format(instr, "lb 'rt, 'imms('rs)"); | |
| 2079 int32_t base_val = get_register(instr->RsField()); | |
| 2080 int32_t imm_val = instr->SImmField(); | |
| 2081 uword addr = base_val + imm_val; | |
| 2082 if (Simulator::IsIllegalAddress(addr)) { | |
| 2083 HandleIllegalAccess(addr, instr); | |
| 2084 } else { | |
| 2085 int32_t res = ReadB(addr); | |
| 2086 set_register(instr->RtField(), res); | |
| 2087 } | |
| 2088 break; | |
| 2089 } | |
| 2090 case LBU: { | |
| 2091 // Format(instr, "lbu 'rt, 'imms('rs)"); | |
| 2092 int32_t base_val = get_register(instr->RsField()); | |
| 2093 int32_t imm_val = instr->SImmField(); | |
| 2094 uword addr = base_val + imm_val; | |
| 2095 if (Simulator::IsIllegalAddress(addr)) { | |
| 2096 HandleIllegalAccess(addr, instr); | |
| 2097 } else { | |
| 2098 uint32_t res = ReadBU(addr); | |
| 2099 set_register(instr->RtField(), res); | |
| 2100 } | |
| 2101 break; | |
| 2102 } | |
| 2103 case LDC1: { | |
| 2104 // Format(instr, "ldc1 'ft, 'imms('rs)"); | |
| 2105 int32_t base_val = get_register(instr->RsField()); | |
| 2106 int32_t imm_val = instr->SImmField(); | |
| 2107 uword addr = base_val + imm_val; | |
| 2108 if (Simulator::IsIllegalAddress(addr)) { | |
| 2109 HandleIllegalAccess(addr, instr); | |
| 2110 } else { | |
| 2111 double value = ReadD(addr, instr); | |
| 2112 set_fregister_double(instr->FtField(), value); | |
| 2113 } | |
| 2114 break; | |
| 2115 } | |
| 2116 case LH: { | |
| 2117 // Format(instr, "lh 'rt, 'imms('rs)"); | |
| 2118 int32_t base_val = get_register(instr->RsField()); | |
| 2119 int32_t imm_val = instr->SImmField(); | |
| 2120 uword addr = base_val + imm_val; | |
| 2121 if (Simulator::IsIllegalAddress(addr)) { | |
| 2122 HandleIllegalAccess(addr, instr); | |
| 2123 } else { | |
| 2124 int32_t res = ReadH(addr, instr); | |
| 2125 set_register(instr->RtField(), res); | |
| 2126 } | |
| 2127 break; | |
| 2128 } | |
| 2129 case LHU: { | |
| 2130 // Format(instr, "lhu 'rt, 'imms('rs)"); | |
| 2131 int32_t base_val = get_register(instr->RsField()); | |
| 2132 int32_t imm_val = instr->SImmField(); | |
| 2133 uword addr = base_val + imm_val; | |
| 2134 if (Simulator::IsIllegalAddress(addr)) { | |
| 2135 HandleIllegalAccess(addr, instr); | |
| 2136 } else { | |
| 2137 int32_t res = ReadHU(addr, instr); | |
| 2138 set_register(instr->RtField(), res); | |
| 2139 } | |
| 2140 break; | |
| 2141 } | |
| 2142 case LUI: { | |
| 2143 ASSERT(instr->RsField() == 0); | |
| 2144 set_register(instr->RtField(), instr->UImmField() << 16); | |
| 2145 break; | |
| 2146 } | |
| 2147 case LL: { | |
| 2148 // Format(instr, "ll 'rt, 'imms('rs)"); | |
| 2149 int32_t base_val = get_register(instr->RsField()); | |
| 2150 int32_t imm_val = instr->SImmField(); | |
| 2151 uword addr = base_val + imm_val; | |
| 2152 if (Simulator::IsIllegalAddress(addr)) { | |
| 2153 HandleIllegalAccess(addr, instr); | |
| 2154 } else { | |
| 2155 int32_t res = ReadExclusiveW(addr, instr); | |
| 2156 set_register(instr->RtField(), res); | |
| 2157 } | |
| 2158 break; | |
| 2159 } | |
| 2160 case LW: { | |
| 2161 // Format(instr, "lw 'rt, 'imms('rs)"); | |
| 2162 int32_t base_val = get_register(instr->RsField()); | |
| 2163 int32_t imm_val = instr->SImmField(); | |
| 2164 uword addr = base_val + imm_val; | |
| 2165 if (Simulator::IsIllegalAddress(addr)) { | |
| 2166 HandleIllegalAccess(addr, instr); | |
| 2167 } else { | |
| 2168 int32_t res = ReadW(addr, instr); | |
| 2169 set_register(instr->RtField(), res); | |
| 2170 } | |
| 2171 break; | |
| 2172 } | |
| 2173 case LWC1: { | |
| 2174 // Format(instr, "lwc1 'ft, 'imms('rs)"); | |
| 2175 int32_t base_val = get_register(instr->RsField()); | |
| 2176 int32_t imm_val = instr->SImmField(); | |
| 2177 uword addr = base_val + imm_val; | |
| 2178 if (Simulator::IsIllegalAddress(addr)) { | |
| 2179 HandleIllegalAccess(addr, instr); | |
| 2180 } else { | |
| 2181 int32_t value = ReadW(addr, instr); | |
| 2182 set_fregister(instr->FtField(), value); | |
| 2183 } | |
| 2184 break; | |
| 2185 } | |
| 2186 case ORI: { | |
| 2187 // Format(instr, "ori 'rt, 'rs, 'immu"); | |
| 2188 int32_t rs_val = get_register(instr->RsField()); | |
| 2189 set_register(instr->RtField(), rs_val | instr->UImmField()); | |
| 2190 break; | |
| 2191 } | |
| 2192 case SB: { | |
| 2193 // Format(instr, "sb 'rt, 'imms('rs)"); | |
| 2194 int32_t rt_val = get_register(instr->RtField()); | |
| 2195 int32_t base_val = get_register(instr->RsField()); | |
| 2196 int32_t imm_val = instr->SImmField(); | |
| 2197 uword addr = base_val + imm_val; | |
| 2198 if (Simulator::IsIllegalAddress(addr)) { | |
| 2199 HandleIllegalAccess(addr, instr); | |
| 2200 } else { | |
| 2201 WriteB(addr, rt_val & 0xff); | |
| 2202 } | |
| 2203 break; | |
| 2204 } | |
| 2205 case SC: { | |
| 2206 // Format(instr, "sc 'rt, 'imms('rs)"); | |
| 2207 int32_t rt_val = get_register(instr->RtField()); | |
| 2208 int32_t base_val = get_register(instr->RsField()); | |
| 2209 int32_t imm_val = instr->SImmField(); | |
| 2210 uword addr = base_val + imm_val; | |
| 2211 if (Simulator::IsIllegalAddress(addr)) { | |
| 2212 HandleIllegalAccess(addr, instr); | |
| 2213 } else { | |
| 2214 intptr_t status = WriteExclusiveW(addr, rt_val, instr); | |
| 2215 set_register(instr->RtField(), status); | |
| 2216 } | |
| 2217 break; | |
| 2218 } | |
| 2219 case SLTI: { | |
| 2220 // Format(instr, "slti 'rt, 'rs, 'imms"); | |
| 2221 int32_t rs_val = get_register(instr->RsField()); | |
| 2222 int32_t imm_val = instr->SImmField(); | |
| 2223 set_register(instr->RtField(), rs_val < imm_val ? 1 : 0); | |
| 2224 break; | |
| 2225 } | |
| 2226 case SLTIU: { | |
| 2227 // Format(instr, "sltiu 'rt, 'rs, 'imms"); | |
| 2228 uint32_t rs_val = get_register(instr->RsField()); | |
| 2229 int32_t imm_val = instr->SImmField(); // Sign extend to 32-bit. | |
| 2230 uint32_t immu_val = static_cast<uint32_t>(imm_val); // Treat as unsigned. | |
| 2231 set_register(instr->RtField(), rs_val < immu_val ? 1 : 0); | |
| 2232 break; | |
| 2233 } | |
| 2234 case SDC1: { | |
| 2235 // Format(instr, "sdc1 'ft, 'imms('rs)"); | |
| 2236 int32_t base_val = get_register(instr->RsField()); | |
| 2237 int32_t imm_val = instr->SImmField(); | |
| 2238 uword addr = base_val + imm_val; | |
| 2239 if (Simulator::IsIllegalAddress(addr)) { | |
| 2240 HandleIllegalAccess(addr, instr); | |
| 2241 } else { | |
| 2242 double value = get_fregister_double(instr->FtField()); | |
| 2243 WriteD(addr, value, instr); | |
| 2244 } | |
| 2245 break; | |
| 2246 } | |
| 2247 case SH: { | |
| 2248 // Format(instr, "sh 'rt, 'imms('rs)"); | |
| 2249 int32_t rt_val = get_register(instr->RtField()); | |
| 2250 int32_t base_val = get_register(instr->RsField()); | |
| 2251 int32_t imm_val = instr->SImmField(); | |
| 2252 uword addr = base_val + imm_val; | |
| 2253 if (Simulator::IsIllegalAddress(addr)) { | |
| 2254 HandleIllegalAccess(addr, instr); | |
| 2255 } else { | |
| 2256 WriteH(addr, rt_val & 0xffff, instr); | |
| 2257 } | |
| 2258 break; | |
| 2259 } | |
| 2260 case SW: { | |
| 2261 // Format(instr, "sw 'rt, 'imms('rs)"); | |
| 2262 int32_t rt_val = get_register(instr->RtField()); | |
| 2263 int32_t base_val = get_register(instr->RsField()); | |
| 2264 int32_t imm_val = instr->SImmField(); | |
| 2265 uword addr = base_val + imm_val; | |
| 2266 if (Simulator::IsIllegalAddress(addr)) { | |
| 2267 HandleIllegalAccess(addr, instr); | |
| 2268 } else { | |
| 2269 WriteW(addr, rt_val, instr); | |
| 2270 } | |
| 2271 break; | |
| 2272 } | |
| 2273 case SWC1: { | |
| 2274 // Format(instr, "swc1 'ft, 'imms('rs)"); | |
| 2275 int32_t base_val = get_register(instr->RsField()); | |
| 2276 int32_t imm_val = instr->SImmField(); | |
| 2277 uword addr = base_val + imm_val; | |
| 2278 if (Simulator::IsIllegalAddress(addr)) { | |
| 2279 HandleIllegalAccess(addr, instr); | |
| 2280 } else { | |
| 2281 int32_t value = get_fregister(instr->FtField()); | |
| 2282 WriteW(addr, value, instr); | |
| 2283 } | |
| 2284 break; | |
| 2285 } | |
| 2286 case XORI: { | |
| 2287 // Format(instr, "xori 'rt, 'rs, 'immu"); | |
| 2288 int32_t rs_val = get_register(instr->RsField()); | |
| 2289 set_register(instr->RtField(), rs_val ^ instr->UImmField()); | |
| 2290 break; | |
| 2291 break; | |
| 2292 } | |
| 2293 default: { | |
| 2294 OS::PrintErr("Undecoded instruction: 0x%x at %p\n", | |
| 2295 instr->InstructionBits(), instr); | |
| 2296 UnimplementedInstruction(instr); | |
| 2297 break; | |
| 2298 } | |
| 2299 } | |
| 2300 pc_ += Instr::kInstrSize; | |
| 2301 } | |
| 2302 | |
| 2303 | |
| 2304 void Simulator::ExecuteDelaySlot() { | |
| 2305 ASSERT(pc_ != kEndSimulatingPC); | |
| 2306 delay_slot_ = true; | |
| 2307 icount_++; | |
| 2308 Instr* instr = Instr::At(pc_ + Instr::kInstrSize); | |
| 2309 if (FLAG_stop_sim_at != ULLONG_MAX) { | |
| 2310 if (icount_ == FLAG_stop_sim_at) { | |
| 2311 SimulatorDebugger dbg(this); | |
| 2312 dbg.Stop(instr, "Instruction count reached"); | |
| 2313 } else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) { | |
| 2314 SimulatorDebugger dbg(this); | |
| 2315 dbg.Stop(instr, "Instruction address reached"); | |
| 2316 } | |
| 2317 } | |
| 2318 InstructionDecode(instr); | |
| 2319 delay_slot_ = false; | |
| 2320 } | |
| 2321 | |
| 2322 | |
| 2323 void Simulator::Execute() { | |
| 2324 if (FLAG_stop_sim_at == ULLONG_MAX) { | |
| 2325 // Fast version of the dispatch loop without checking whether the simulator | |
| 2326 // should be stopping at a particular executed instruction. | |
| 2327 while (pc_ != kEndSimulatingPC) { | |
| 2328 icount_++; | |
| 2329 Instr* instr = Instr::At(pc_); | |
| 2330 if (IsIllegalAddress(pc_)) { | |
| 2331 HandleIllegalAccess(pc_, instr); | |
| 2332 } else { | |
| 2333 InstructionDecode(instr); | |
| 2334 } | |
| 2335 } | |
| 2336 } else { | |
| 2337 // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when | |
| 2338 // we reach the particular instruction count or address. | |
| 2339 while (pc_ != kEndSimulatingPC) { | |
| 2340 Instr* instr = Instr::At(pc_); | |
| 2341 icount_++; | |
| 2342 if (icount_ == FLAG_stop_sim_at) { | |
| 2343 SimulatorDebugger dbg(this); | |
| 2344 dbg.Stop(instr, "Instruction count reached"); | |
| 2345 } else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) { | |
| 2346 SimulatorDebugger dbg(this); | |
| 2347 dbg.Stop(instr, "Instruction address reached"); | |
| 2348 } else if (IsIllegalAddress(pc_)) { | |
| 2349 HandleIllegalAccess(pc_, instr); | |
| 2350 } else { | |
| 2351 InstructionDecode(instr); | |
| 2352 } | |
| 2353 } | |
| 2354 } | |
| 2355 } | |
| 2356 | |
| 2357 | |
| 2358 int64_t Simulator::Call(int32_t entry, | |
| 2359 int32_t parameter0, | |
| 2360 int32_t parameter1, | |
| 2361 int32_t parameter2, | |
| 2362 int32_t parameter3, | |
| 2363 bool fp_return, | |
| 2364 bool fp_args) { | |
| 2365 // Save the SP register before the call so we can restore it. | |
| 2366 int32_t sp_before_call = get_register(SP); | |
| 2367 | |
| 2368 // Setup parameters. | |
| 2369 if (fp_args) { | |
| 2370 set_fregister(F0, parameter0); | |
| 2371 set_fregister(F1, parameter1); | |
| 2372 set_fregister(F2, parameter2); | |
| 2373 set_fregister(F3, parameter3); | |
| 2374 } else { | |
| 2375 set_register(A0, parameter0); | |
| 2376 set_register(A1, parameter1); | |
| 2377 set_register(A2, parameter2); | |
| 2378 set_register(A3, parameter3); | |
| 2379 } | |
| 2380 | |
| 2381 // Make sure the activation frames are properly aligned. | |
| 2382 int32_t stack_pointer = sp_before_call; | |
| 2383 if (OS::ActivationFrameAlignment() > 1) { | |
| 2384 stack_pointer = | |
| 2385 Utils::RoundDown(stack_pointer, OS::ActivationFrameAlignment()); | |
| 2386 } | |
| 2387 set_register(SP, stack_pointer); | |
| 2388 | |
| 2389 // Prepare to execute the code at entry. | |
| 2390 set_pc(entry); | |
| 2391 // Put down marker for end of simulation. The simulator will stop simulation | |
| 2392 // when the PC reaches this value. By saving the "end simulation" value into | |
| 2393 // RA the simulation stops when returning to this call point. | |
| 2394 set_register(RA, kEndSimulatingPC); | |
| 2395 | |
| 2396 // Remember the values of callee-saved registers. | |
| 2397 // The code below assumes that r9 is not used as sb (static base) in | |
| 2398 // simulator code and therefore is regarded as a callee-saved register. | |
| 2399 int32_t r16_val = get_register(R16); | |
| 2400 int32_t r17_val = get_register(R17); | |
| 2401 int32_t r18_val = get_register(R18); | |
| 2402 int32_t r19_val = get_register(R19); | |
| 2403 int32_t r20_val = get_register(R20); | |
| 2404 int32_t r21_val = get_register(R21); | |
| 2405 int32_t r22_val = get_register(R22); | |
| 2406 int32_t r23_val = get_register(R23); | |
| 2407 | |
| 2408 double d10_val = get_dregister(D10); | |
| 2409 double d11_val = get_dregister(D11); | |
| 2410 double d12_val = get_dregister(D12); | |
| 2411 double d13_val = get_dregister(D13); | |
| 2412 double d14_val = get_dregister(D14); | |
| 2413 double d15_val = get_dregister(D15); | |
| 2414 | |
| 2415 // Setup the callee-saved registers with a known value. To be able to check | |
| 2416 // that they are preserved properly across dart execution. | |
| 2417 int32_t callee_saved_value = icount_; | |
| 2418 set_register(R16, callee_saved_value); | |
| 2419 set_register(R17, callee_saved_value); | |
| 2420 set_register(R18, callee_saved_value); | |
| 2421 set_register(R19, callee_saved_value); | |
| 2422 set_register(R20, callee_saved_value); | |
| 2423 set_register(R21, callee_saved_value); | |
| 2424 set_register(R22, callee_saved_value); | |
| 2425 set_register(R23, callee_saved_value); | |
| 2426 | |
| 2427 set_dregister_bits(D10, callee_saved_value); | |
| 2428 set_dregister_bits(D11, callee_saved_value); | |
| 2429 set_dregister_bits(D12, callee_saved_value); | |
| 2430 set_dregister_bits(D13, callee_saved_value); | |
| 2431 set_dregister_bits(D14, callee_saved_value); | |
| 2432 set_dregister_bits(D15, callee_saved_value); | |
| 2433 | |
| 2434 // Start the simulation | |
| 2435 Execute(); | |
| 2436 | |
| 2437 // Check that the callee-saved registers have been preserved. | |
| 2438 ASSERT(callee_saved_value == get_register(R16)); | |
| 2439 ASSERT(callee_saved_value == get_register(R17)); | |
| 2440 ASSERT(callee_saved_value == get_register(R18)); | |
| 2441 ASSERT(callee_saved_value == get_register(R19)); | |
| 2442 ASSERT(callee_saved_value == get_register(R20)); | |
| 2443 ASSERT(callee_saved_value == get_register(R21)); | |
| 2444 ASSERT(callee_saved_value == get_register(R22)); | |
| 2445 ASSERT(callee_saved_value == get_register(R23)); | |
| 2446 | |
| 2447 ASSERT(callee_saved_value == get_dregister_bits(D10)); | |
| 2448 ASSERT(callee_saved_value == get_dregister_bits(D11)); | |
| 2449 ASSERT(callee_saved_value == get_dregister_bits(D12)); | |
| 2450 ASSERT(callee_saved_value == get_dregister_bits(D13)); | |
| 2451 ASSERT(callee_saved_value == get_dregister_bits(D14)); | |
| 2452 ASSERT(callee_saved_value == get_dregister_bits(D15)); | |
| 2453 | |
| 2454 // Restore callee-saved registers with the original value. | |
| 2455 set_register(R16, r16_val); | |
| 2456 set_register(R17, r17_val); | |
| 2457 set_register(R18, r18_val); | |
| 2458 set_register(R19, r19_val); | |
| 2459 set_register(R20, r20_val); | |
| 2460 set_register(R21, r21_val); | |
| 2461 set_register(R22, r22_val); | |
| 2462 set_register(R23, r23_val); | |
| 2463 | |
| 2464 set_dregister(D10, d10_val); | |
| 2465 set_dregister(D11, d11_val); | |
| 2466 set_dregister(D12, d12_val); | |
| 2467 set_dregister(D13, d13_val); | |
| 2468 set_dregister(D14, d14_val); | |
| 2469 set_dregister(D15, d15_val); | |
| 2470 | |
| 2471 // Restore the SP register and return V1:V0. | |
| 2472 set_register(SP, sp_before_call); | |
| 2473 int64_t return_value; | |
| 2474 if (fp_return) { | |
| 2475 return_value = Utils::LowHighTo64Bits(get_fregister(F0), get_fregister(F1)); | |
| 2476 } else { | |
| 2477 return_value = Utils::LowHighTo64Bits(get_register(V0), get_register(V1)); | |
| 2478 } | |
| 2479 return return_value; | |
| 2480 } | |
| 2481 | |
| 2482 | |
| 2483 void Simulator::JumpToFrame(uword pc, uword sp, uword fp, Thread* thread) { | |
| 2484 // Walk over all setjmp buffers (simulated --> C++ transitions) | |
| 2485 // and try to find the setjmp associated with the simulated stack pointer. | |
| 2486 SimulatorSetjmpBuffer* buf = last_setjmp_buffer(); | |
| 2487 while (buf->link() != NULL && buf->link()->sp() <= sp) { | |
| 2488 buf = buf->link(); | |
| 2489 } | |
| 2490 ASSERT(buf != NULL); | |
| 2491 | |
| 2492 // The C++ caller has not cleaned up the stack memory of C++ frames. | |
| 2493 // Prepare for unwinding frames by destroying all the stack resources | |
| 2494 // in the previous C++ frames. | |
| 2495 StackResource::Unwind(thread); | |
| 2496 | |
| 2497 // Unwind the C++ stack and continue simulation in the target frame. | |
| 2498 set_pc(static_cast<int32_t>(pc)); | |
| 2499 set_register(SP, static_cast<int32_t>(sp)); | |
| 2500 set_register(FP, static_cast<int32_t>(fp)); | |
| 2501 set_register(THR, reinterpret_cast<int32_t>(thread)); | |
| 2502 // Set the tag. | |
| 2503 thread->set_vm_tag(VMTag::kDartTagId); | |
| 2504 // Clear top exit frame. | |
| 2505 thread->set_top_exit_frame_info(0); | |
| 2506 // Restore pool pointer. | |
| 2507 int32_t code = | |
| 2508 *reinterpret_cast<int32_t*>(fp + kPcMarkerSlotFromFp * kWordSize); | |
| 2509 int32_t pp = *reinterpret_cast<int32_t*>(code + Code::object_pool_offset() - | |
| 2510 kHeapObjectTag); | |
| 2511 set_register(CODE_REG, code); | |
| 2512 set_register(PP, pp); | |
| 2513 buf->Longjmp(); | |
| 2514 } | |
| 2515 | |
| 2516 } // namespace dart | |
| 2517 | |
| 2518 #endif // defined(USING_SIMULATOR) | |
| 2519 | |
| 2520 #endif // defined TARGET_ARCH_MIPS | |
| OLD | NEW |