1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 6 // Declares a Simulator for MIPS instructions if we are not generating a native 7 // MIPS binary. This Simulator allows us to run and debug MIPS code generation 8 // on regular desktop machines. 9 // V8 calls into generated code by "calling" the CALL_GENERATED_CODE macro, 10 // which will start execution in the Simulator or forwards to the real entry 11 // on a MIPS HW platform. 12 13 #ifndef V8_MIPS_SIMULATOR_MIPS_H_ 14 #define V8_MIPS_SIMULATOR_MIPS_H_ 15 16 #include "src/allocation.h" 17 #include "src/mips/constants-mips.h" 18 19 #if !defined(USE_SIMULATOR) 20 // Running without a simulator on a native mips platform. 21 22 namespace v8 { 23 namespace internal { 24 25 // When running without a simulator we call the entry directly. 26 #define CALL_GENERATED_CODE(isolate, entry, p0, p1, p2, p3, p4) \ 27 entry(p0, p1, p2, p3, p4) 28 29 typedef int (*mips_regexp_matcher)(String*, int, const byte*, const byte*, 30 void*, int*, int, Address, int, Isolate*); 31 32 33 // Call the generated regexp code directly. The code at the entry address 34 // should act as a function matching the type arm_regexp_matcher. 35 // The fifth argument is a dummy that reserves the space used for 36 // the return address added by the ExitFrame in native calls. 37 #define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ 38 p7, p8) \ 39 (FUNCTION_CAST<mips_regexp_matcher>(entry)(p0, p1, p2, p3, NULL, p4, p5, p6, \ 40 p7, p8)) 41 42 // The stack limit beyond which we will throw stack overflow errors in 43 // generated code. Because generated code on mips uses the C stack, we 44 // just use the C stack limit. 45 class SimulatorStack : public v8::internal::AllStatic { 46 public: JsLimitFromCLimit(Isolate * isolate,uintptr_t c_limit)47 static inline uintptr_t JsLimitFromCLimit(Isolate* isolate, 48 uintptr_t c_limit) { 49 return c_limit; 50 } 51 RegisterCTryCatch(Isolate * isolate,uintptr_t try_catch_address)52 static inline uintptr_t RegisterCTryCatch(Isolate* isolate, 53 uintptr_t try_catch_address) { 54 USE(isolate); 55 return try_catch_address; 56 } 57 UnregisterCTryCatch(Isolate * isolate)58 static inline void UnregisterCTryCatch(Isolate* isolate) { USE(isolate); } 59 }; 60 61 } // namespace internal 62 } // namespace v8 63 64 // Calculated the stack limit beyond which we will throw stack overflow errors. 65 // This macro must be called from a C++ method. It relies on being able to take 66 // the address of "this" to get a value on the current execution stack and then 67 // calculates the stack limit based on that value. 68 // NOTE: The check for overflow is not safe as there is no guarantee that the 69 // running thread has its stack in all memory up to address 0x00000000. 70 #define GENERATED_CODE_STACK_LIMIT(limit) \ 71 (reinterpret_cast<uintptr_t>(this) >= limit ? \ 72 reinterpret_cast<uintptr_t>(this) - limit : 0) 73 74 #else // !defined(USE_SIMULATOR) 75 // Running with a simulator. 76 77 #include "src/assembler.h" 78 #include "src/base/hashmap.h" 79 80 namespace v8 { 81 namespace internal { 82 83 // ----------------------------------------------------------------------------- 84 // Utility functions 85 86 class CachePage { 87 public: 88 static const int LINE_VALID = 0; 89 static const int LINE_INVALID = 1; 90 91 static const int kPageShift = 12; 92 static const int kPageSize = 1 << kPageShift; 93 static const int kPageMask = kPageSize - 1; 94 static const int kLineShift = 2; // The cache line is only 4 bytes right now. 95 static const int kLineLength = 1 << kLineShift; 96 static const int kLineMask = kLineLength - 1; 97 CachePage()98 CachePage() { 99 memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); 100 } 101 ValidityByte(int offset)102 char* ValidityByte(int offset) { 103 return &validity_map_[offset >> kLineShift]; 104 } 105 CachedData(int offset)106 char* CachedData(int offset) { 107 return &data_[offset]; 108 } 109 110 private: 111 char data_[kPageSize]; // The cached data. 112 static const int kValidityMapSize = kPageSize >> kLineShift; 113 char validity_map_[kValidityMapSize]; // One byte per line. 114 }; 115 116 class SimInstructionBase : public InstructionBase { 117 public: InstructionType()118 Type InstructionType() const { return type_; } instr()119 inline Instruction* instr() const { return instr_; } operand()120 inline int32_t operand() const { return operand_; } 121 122 protected: SimInstructionBase()123 SimInstructionBase() : operand_(-1), instr_(nullptr), type_(kUnsupported) {} SimInstructionBase(Instruction * instr)124 explicit SimInstructionBase(Instruction* instr) {} 125 126 int32_t operand_; 127 Instruction* instr_; 128 Type type_; 129 130 private: 131 DISALLOW_ASSIGN(SimInstructionBase); 132 }; 133 134 class SimInstruction : public InstructionGetters<SimInstructionBase> { 135 public: SimInstruction()136 SimInstruction() {} 137 SimInstruction(Instruction * instr)138 explicit SimInstruction(Instruction* instr) { *this = instr; } 139 140 SimInstruction& operator=(Instruction* instr) { 141 operand_ = *reinterpret_cast<const int32_t*>(instr); 142 instr_ = instr; 143 type_ = InstructionBase::InstructionType(); 144 DCHECK(reinterpret_cast<void*>(&operand_) == this); 145 return *this; 146 } 147 }; 148 149 class Simulator { 150 public: 151 friend class MipsDebugger; 152 153 // Registers are declared in order. See SMRL chapter 2. 154 enum Register { 155 no_reg = -1, 156 zero_reg = 0, 157 at, 158 v0, v1, 159 a0, a1, a2, a3, 160 t0, t1, t2, t3, t4, t5, t6, t7, 161 s0, s1, s2, s3, s4, s5, s6, s7, 162 t8, t9, 163 k0, k1, 164 gp, 165 sp, 166 s8, 167 ra, 168 // LO, HI, and pc. 169 LO, 170 HI, 171 pc, // pc must be the last register. 172 kNumSimuRegisters, 173 // aliases 174 fp = s8 175 }; 176 177 // Coprocessor registers. 178 // Generated code will always use doubles. So we will only use even registers. 179 enum FPURegister { 180 f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, 181 f12, f13, f14, f15, // f12 and f14 are arguments FPURegisters. 182 f16, f17, f18, f19, f20, f21, f22, f23, f24, f25, 183 f26, f27, f28, f29, f30, f31, 184 kNumFPURegisters 185 }; 186 187 explicit Simulator(Isolate* isolate); 188 ~Simulator(); 189 190 // The currently executing Simulator instance. Potentially there can be one 191 // for each native thread. 192 static Simulator* current(v8::internal::Isolate* isolate); 193 194 // Accessors for register state. Reading the pc value adheres to the MIPS 195 // architecture specification and is off by a 8 from the currently executing 196 // instruction. 197 void set_register(int reg, int32_t value); 198 void set_dw_register(int dreg, const int* dbl); 199 int32_t get_register(int reg) const; 200 double get_double_from_register_pair(int reg); 201 // Same for FPURegisters. 202 void set_fpu_register(int fpureg, int64_t value); 203 void set_fpu_register_word(int fpureg, int32_t value); 204 void set_fpu_register_hi_word(int fpureg, int32_t value); 205 void set_fpu_register_float(int fpureg, float value); 206 void set_fpu_register_double(int fpureg, double value); 207 void set_fpu_register_invalid_result64(float original, float rounded); 208 void set_fpu_register_invalid_result(float original, float rounded); 209 void set_fpu_register_word_invalid_result(float original, float rounded); 210 void set_fpu_register_invalid_result64(double original, double rounded); 211 void set_fpu_register_invalid_result(double original, double rounded); 212 void set_fpu_register_word_invalid_result(double original, double rounded); 213 int64_t get_fpu_register(int fpureg) const; 214 int32_t get_fpu_register_word(int fpureg) const; 215 int32_t get_fpu_register_signed_word(int fpureg) const; 216 int32_t get_fpu_register_hi_word(int fpureg) const; 217 float get_fpu_register_float(int fpureg) const; 218 double get_fpu_register_double(int fpureg) const; 219 void set_fcsr_bit(uint32_t cc, bool value); 220 bool test_fcsr_bit(uint32_t cc); 221 void set_fcsr_rounding_mode(FPURoundingMode mode); 222 unsigned int get_fcsr_rounding_mode(); 223 bool set_fcsr_round_error(double original, double rounded); 224 bool set_fcsr_round_error(float original, float rounded); 225 bool set_fcsr_round64_error(double original, double rounded); 226 bool set_fcsr_round64_error(float original, float rounded); 227 void round_according_to_fcsr(double toRound, double& rounded, 228 int32_t& rounded_int, double fs); 229 void round_according_to_fcsr(float toRound, float& rounded, 230 int32_t& rounded_int, float fs); 231 void round64_according_to_fcsr(double toRound, double& rounded, 232 int64_t& rounded_int, double fs); 233 void round64_according_to_fcsr(float toRound, float& rounded, 234 int64_t& rounded_int, float fs); 235 // Special case of set_register and get_register to access the raw PC value. 236 void set_pc(int32_t value); 237 int32_t get_pc() const; 238 get_sp()239 Address get_sp() const { 240 return reinterpret_cast<Address>(static_cast<intptr_t>(get_register(sp))); 241 } 242 243 // Accessor to the internal simulator stack area. 244 uintptr_t StackLimit(uintptr_t c_limit) const; 245 246 // Executes MIPS instructions until the PC reaches end_sim_pc. 247 void Execute(); 248 249 // Call on program start. 250 static void Initialize(Isolate* isolate); 251 252 static void TearDown(base::CustomMatcherHashMap* i_cache, Redirection* first); 253 254 // V8 generally calls into generated JS code with 5 parameters and into 255 // generated RegExp code with 7 parameters. This is a convenience function, 256 // which sets up the simulator state and grabs the result on return. 257 int32_t Call(byte* entry, int argument_count, ...); 258 // Alternative: call a 2-argument double function. 259 double CallFP(byte* entry, double d0, double d1); 260 261 // Push an address onto the JS stack. 262 uintptr_t PushAddress(uintptr_t address); 263 264 // Pop an address from the JS stack. 265 uintptr_t PopAddress(); 266 267 // Debugger input. 268 void set_last_debugger_input(char* input); last_debugger_input()269 char* last_debugger_input() { return last_debugger_input_; } 270 271 // ICache checking. 272 static void FlushICache(base::CustomMatcherHashMap* i_cache, void* start, 273 size_t size); 274 275 // Returns true if pc register contains one of the 'special_values' defined 276 // below (bad_ra, end_sim_pc). 277 bool has_bad_pc() const; 278 279 private: 280 enum special_values { 281 // Known bad pc value to ensure that the simulator does not execute 282 // without being properly setup. 283 bad_ra = -1, 284 // A pc value used to signal the simulator to stop execution. Generally 285 // the ra is set to this value on transition from native C code to 286 // simulated execution, so that the simulator can "return" to the native 287 // C code. 288 end_sim_pc = -2, 289 // Unpredictable value. 290 Unpredictable = 0xbadbeaf 291 }; 292 293 // Unsupported instructions use Format to print an error and stop execution. 294 void Format(Instruction* instr, const char* format); 295 296 // Read and write memory. 297 inline uint32_t ReadBU(int32_t addr); 298 inline int32_t ReadB(int32_t addr); 299 inline void WriteB(int32_t addr, uint8_t value); 300 inline void WriteB(int32_t addr, int8_t value); 301 302 inline uint16_t ReadHU(int32_t addr, Instruction* instr); 303 inline int16_t ReadH(int32_t addr, Instruction* instr); 304 // Note: Overloaded on the sign of the value. 305 inline void WriteH(int32_t addr, uint16_t value, Instruction* instr); 306 inline void WriteH(int32_t addr, int16_t value, Instruction* instr); 307 308 inline int ReadW(int32_t addr, Instruction* instr); 309 inline void WriteW(int32_t addr, int value, Instruction* instr); 310 311 inline double ReadD(int32_t addr, Instruction* instr); 312 inline void WriteD(int32_t addr, double value, Instruction* instr); 313 314 // Helpers for data value tracing. 315 enum TraceType { 316 BYTE, 317 HALF, 318 WORD 319 // DWORD, 320 // DFLOAT - Floats may have printing issues due to paired lwc1's 321 }; 322 323 void TraceRegWr(int32_t value); 324 void TraceMemWr(int32_t addr, int32_t value, TraceType t); 325 void TraceMemRd(int32_t addr, int32_t value); 326 EmbeddedVector<char, 128> trace_buf_; 327 328 // Operations depending on endianness. 329 // Get Double Higher / Lower word. 330 inline int32_t GetDoubleHIW(double* addr); 331 inline int32_t GetDoubleLOW(double* addr); 332 // Set Double Higher / Lower word. 333 inline int32_t SetDoubleHIW(double* addr); 334 inline int32_t SetDoubleLOW(double* addr); 335 336 SimInstruction instr_; 337 338 // Executing is handled based on the instruction type. 339 void DecodeTypeRegister(); 340 341 // Functions called from DecodeTypeRegister. 342 void DecodeTypeRegisterCOP1(); 343 344 void DecodeTypeRegisterCOP1X(); 345 346 void DecodeTypeRegisterSPECIAL(); 347 348 void DecodeTypeRegisterSPECIAL2(); 349 350 void DecodeTypeRegisterSPECIAL3(); 351 352 // Called from DecodeTypeRegisterCOP1. 353 void DecodeTypeRegisterSRsType(); 354 355 void DecodeTypeRegisterDRsType(); 356 357 void DecodeTypeRegisterWRsType(); 358 359 void DecodeTypeRegisterLRsType(); 360 rs_reg()361 inline int32_t rs_reg() const { return instr_.RsValue(); } rs()362 inline int32_t rs() const { return get_register(rs_reg()); } rs_u()363 inline uint32_t rs_u() const { 364 return static_cast<uint32_t>(get_register(rs_reg())); 365 } rt_reg()366 inline int32_t rt_reg() const { return instr_.RtValue(); } rt()367 inline int32_t rt() const { return get_register(rt_reg()); } rt_u()368 inline uint32_t rt_u() const { 369 return static_cast<uint32_t>(get_register(rt_reg())); 370 } rd_reg()371 inline int32_t rd_reg() const { return instr_.RdValue(); } fr_reg()372 inline int32_t fr_reg() const { return instr_.FrValue(); } fs_reg()373 inline int32_t fs_reg() const { return instr_.FsValue(); } ft_reg()374 inline int32_t ft_reg() const { return instr_.FtValue(); } fd_reg()375 inline int32_t fd_reg() const { return instr_.FdValue(); } sa()376 inline int32_t sa() const { return instr_.SaValue(); } lsa_sa()377 inline int32_t lsa_sa() const { return instr_.LsaSaValue(); } 378 SetResult(int32_t rd_reg,int32_t alu_out)379 inline void SetResult(int32_t rd_reg, int32_t alu_out) { 380 set_register(rd_reg, alu_out); 381 TraceRegWr(alu_out); 382 } 383 384 void DecodeTypeImmediate(); 385 void DecodeTypeJump(); 386 387 // Used for breakpoints and traps. 388 void SoftwareInterrupt(); 389 390 // Compact branch guard. CheckForbiddenSlot(int32_t current_pc)391 void CheckForbiddenSlot(int32_t current_pc) { 392 Instruction* instr_after_compact_branch = 393 reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); 394 if (instr_after_compact_branch->IsForbiddenAfterBranch()) { 395 V8_Fatal(__FILE__, __LINE__, 396 "Error: Unexpected instruction 0x%08x immediately after a " 397 "compact branch instruction.", 398 *reinterpret_cast<uint32_t*>(instr_after_compact_branch)); 399 } 400 } 401 402 // Stop helper functions. 403 bool IsWatchpoint(uint32_t code); 404 void PrintWatchpoint(uint32_t code); 405 void HandleStop(uint32_t code, Instruction* instr); 406 bool IsStopInstruction(Instruction* instr); 407 bool IsEnabledStop(uint32_t code); 408 void EnableStop(uint32_t code); 409 void DisableStop(uint32_t code); 410 void IncreaseStopCounter(uint32_t code); 411 void PrintStopInfo(uint32_t code); 412 413 414 // Executes one instruction. 415 void InstructionDecode(Instruction* instr); 416 // Execute one instruction placed in a branch delay slot. BranchDelayInstructionDecode(Instruction * instr)417 void BranchDelayInstructionDecode(Instruction* instr) { 418 if (instr->InstructionBits() == nopInstr) { 419 // Short-cut generic nop instructions. They are always valid and they 420 // never change the simulator state. 421 return; 422 } 423 424 if (instr->IsForbiddenInBranchDelay()) { 425 V8_Fatal(__FILE__, __LINE__, 426 "Eror:Unexpected %i opcode in a branch delay slot.", 427 instr->OpcodeValue()); 428 } 429 InstructionDecode(instr); 430 SNPrintF(trace_buf_, " "); 431 } 432 433 // ICache. 434 static void CheckICache(base::CustomMatcherHashMap* i_cache, 435 Instruction* instr); 436 static void FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, 437 int size); 438 static CachePage* GetCachePage(base::CustomMatcherHashMap* i_cache, 439 void* page); 440 441 enum Exception { 442 none, 443 kIntegerOverflow, 444 kIntegerUnderflow, 445 kDivideByZero, 446 kNumExceptions 447 }; 448 449 // Exceptions. 450 void SignalException(Exception e); 451 452 // Runtime call support. 453 static void* RedirectExternalReference(Isolate* isolate, 454 void* external_function, 455 ExternalReference::Type type); 456 457 // Handle arguments and return value for runtime FP functions. 458 void GetFpArgs(double* x, double* y, int32_t* z); 459 void SetFpResult(const double& result); 460 461 void CallInternal(byte* entry); 462 463 // Architecture state. 464 // Registers. 465 int32_t registers_[kNumSimuRegisters]; 466 // Coprocessor Registers. 467 // Note: FP32 mode uses only the lower 32-bit part of each element, 468 // the upper 32-bit is unpredictable. 469 int64_t FPUregisters_[kNumFPURegisters]; 470 // FPU control register. 471 uint32_t FCSR_; 472 473 // Simulator support. 474 // Allocate 1MB for stack. 475 static const size_t stack_size_ = 1 * 1024*1024; 476 char* stack_; 477 bool pc_modified_; 478 uint64_t icount_; 479 int break_count_; 480 481 // Debugger input. 482 char* last_debugger_input_; 483 484 // Icache simulation. 485 base::CustomMatcherHashMap* i_cache_; 486 487 v8::internal::Isolate* isolate_; 488 489 // Registered breakpoints. 490 Instruction* break_pc_; 491 Instr break_instr_; 492 493 // Stop is disabled if bit 31 is set. 494 static const uint32_t kStopDisabledBit = 1 << 31; 495 496 // A stop is enabled, meaning the simulator will stop when meeting the 497 // instruction, if bit 31 of watched_stops_[code].count is unset. 498 // The value watched_stops_[code].count & ~(1 << 31) indicates how many times 499 // the breakpoint was hit or gone through. 500 struct StopCountAndDesc { 501 uint32_t count; 502 char* desc; 503 }; 504 StopCountAndDesc watched_stops_[kMaxStopCode + 1]; 505 }; 506 507 508 // When running with the simulator transition into simulated execution at this 509 // point. 510 #define CALL_GENERATED_CODE(isolate, entry, p0, p1, p2, p3, p4) \ 511 reinterpret_cast<Object*>(Simulator::current(isolate)->Call( \ 512 FUNCTION_ADDR(entry), 5, p0, p1, p2, p3, p4)) 513 514 #define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ 515 p7, p8) \ 516 Simulator::current(isolate) \ 517 ->Call(entry, 10, p0, p1, p2, p3, NULL, p4, p5, p6, p7, p8) 518 519 520 // The simulator has its own stack. Thus it has a different stack limit from 521 // the C-based native code. The JS-based limit normally points near the end of 522 // the simulator stack. When the C-based limit is exhausted we reflect that by 523 // lowering the JS-based limit as well, to make stack checks trigger. 524 class SimulatorStack : public v8::internal::AllStatic { 525 public: JsLimitFromCLimit(Isolate * isolate,uintptr_t c_limit)526 static inline uintptr_t JsLimitFromCLimit(Isolate* isolate, 527 uintptr_t c_limit) { 528 return Simulator::current(isolate)->StackLimit(c_limit); 529 } 530 RegisterCTryCatch(Isolate * isolate,uintptr_t try_catch_address)531 static inline uintptr_t RegisterCTryCatch(Isolate* isolate, 532 uintptr_t try_catch_address) { 533 Simulator* sim = Simulator::current(isolate); 534 return sim->PushAddress(try_catch_address); 535 } 536 UnregisterCTryCatch(Isolate * isolate)537 static inline void UnregisterCTryCatch(Isolate* isolate) { 538 Simulator::current(isolate)->PopAddress(); 539 } 540 }; 541 542 } // namespace internal 543 } // namespace v8 544 545 #endif // !defined(USE_SIMULATOR) 546 #endif // V8_MIPS_SIMULATOR_MIPS_H_ 547