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