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 22namespace v8 { 23namespace 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 29typedef 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. 45class SimulatorStack : public v8::internal::AllStatic { 46 public: 47 static inline uintptr_t JsLimitFromCLimit(Isolate* isolate, 48 uintptr_t c_limit) { 49 return c_limit; 50 } 51 52 static inline uintptr_t RegisterCTryCatch(Isolate* isolate, 53 uintptr_t try_catch_address) { 54 USE(isolate); 55 return try_catch_address; 56 } 57 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 80namespace v8 { 81namespace internal { 82 83// ----------------------------------------------------------------------------- 84// Utility functions 85 86class 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 98 CachePage() { 99 memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); 100 } 101 102 char* ValidityByte(int offset) { 103 return &validity_map_[offset >> kLineShift]; 104 } 105 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 116class SimInstructionBase : public InstructionBase { 117 public: 118 Type InstructionType() const { return type_; } 119 inline Instruction* instr() const { return instr_; } 120 inline int32_t operand() const { return operand_; } 121 122 protected: 123 SimInstructionBase() : operand_(-1), instr_(nullptr), type_(kUnsupported) {} 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 134class SimInstruction : public InstructionGetters<SimInstructionBase> { 135 public: 136 SimInstruction() {} 137 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 149class 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 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); 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 // Helpers for data value tracing. 297 enum TraceType { BYTE, HALF, WORD, DWORD, FLOAT, DOUBLE, FLOAT_DOUBLE }; 298 299 // Read and write memory. 300 inline uint32_t ReadBU(int32_t addr); 301 inline int32_t ReadB(int32_t addr); 302 inline void WriteB(int32_t addr, uint8_t value); 303 inline void WriteB(int32_t addr, int8_t value); 304 305 inline uint16_t ReadHU(int32_t addr, Instruction* instr); 306 inline int16_t ReadH(int32_t addr, Instruction* instr); 307 // Note: Overloaded on the sign of the value. 308 inline void WriteH(int32_t addr, uint16_t value, Instruction* instr); 309 inline void WriteH(int32_t addr, int16_t value, Instruction* instr); 310 311 inline int ReadW(int32_t addr, Instruction* instr, TraceType t = WORD); 312 inline void WriteW(int32_t addr, int value, Instruction* instr); 313 314 inline double ReadD(int32_t addr, Instruction* instr); 315 inline void WriteD(int32_t addr, double value, Instruction* instr); 316 317 void TraceRegWr(int32_t value, TraceType t = WORD); 318 void TraceRegWr(int64_t value, TraceType t = DWORD); 319 void TraceMemWr(int32_t addr, int32_t value, TraceType t = WORD); 320 void TraceMemRd(int32_t addr, int32_t value, TraceType t = WORD); 321 void TraceMemWr(int32_t addr, int64_t value, TraceType t = DWORD); 322 void TraceMemRd(int32_t addr, int64_t value, TraceType t = DWORD); 323 EmbeddedVector<char, 128> trace_buf_; 324 325 // Operations depending on endianness. 326 // Get Double Higher / Lower word. 327 inline int32_t GetDoubleHIW(double* addr); 328 inline int32_t GetDoubleLOW(double* addr); 329 // Set Double Higher / Lower word. 330 inline int32_t SetDoubleHIW(double* addr); 331 inline int32_t SetDoubleLOW(double* addr); 332 333 SimInstruction instr_; 334 335 // Executing is handled based on the instruction type. 336 void DecodeTypeRegister(); 337 338 // Functions called from DecodeTypeRegister. 339 void DecodeTypeRegisterCOP1(); 340 341 void DecodeTypeRegisterCOP1X(); 342 343 void DecodeTypeRegisterSPECIAL(); 344 345 void DecodeTypeRegisterSPECIAL2(); 346 347 void DecodeTypeRegisterSPECIAL3(); 348 349 // Called from DecodeTypeRegisterCOP1. 350 void DecodeTypeRegisterSRsType(); 351 352 void DecodeTypeRegisterDRsType(); 353 354 void DecodeTypeRegisterWRsType(); 355 356 void DecodeTypeRegisterLRsType(); 357 358 inline int32_t rs_reg() const { return instr_.RsValue(); } 359 inline int32_t rs() const { return get_register(rs_reg()); } 360 inline uint32_t rs_u() const { 361 return static_cast<uint32_t>(get_register(rs_reg())); 362 } 363 inline int32_t rt_reg() const { return instr_.RtValue(); } 364 inline int32_t rt() const { return get_register(rt_reg()); } 365 inline uint32_t rt_u() const { 366 return static_cast<uint32_t>(get_register(rt_reg())); 367 } 368 inline int32_t rd_reg() const { return instr_.RdValue(); } 369 inline int32_t fr_reg() const { return instr_.FrValue(); } 370 inline int32_t fs_reg() const { return instr_.FsValue(); } 371 inline int32_t ft_reg() const { return instr_.FtValue(); } 372 inline int32_t fd_reg() const { return instr_.FdValue(); } 373 inline int32_t sa() const { return instr_.SaValue(); } 374 inline int32_t lsa_sa() const { return instr_.LsaSaValue(); } 375 376 inline void SetResult(int32_t rd_reg, int32_t alu_out) { 377 set_register(rd_reg, alu_out); 378 TraceRegWr(alu_out); 379 } 380 381 inline void SetFPUWordResult(int32_t fd_reg, int32_t alu_out) { 382 set_fpu_register_word(fd_reg, alu_out); 383 TraceRegWr(get_fpu_register_word(fd_reg)); 384 } 385 386 inline void SetFPUResult(int32_t fd_reg, int64_t alu_out) { 387 set_fpu_register(fd_reg, alu_out); 388 TraceRegWr(get_fpu_register(fd_reg)); 389 } 390 391 inline void SetFPUFloatResult(int32_t fd_reg, float alu_out) { 392 set_fpu_register_float(fd_reg, alu_out); 393 TraceRegWr(get_fpu_register_word(fd_reg), FLOAT); 394 } 395 396 inline void SetFPUDoubleResult(int32_t fd_reg, double alu_out) { 397 set_fpu_register_double(fd_reg, alu_out); 398 TraceRegWr(get_fpu_register(fd_reg), DOUBLE); 399 } 400 401 void DecodeTypeImmediate(); 402 void DecodeTypeJump(); 403 404 // Used for breakpoints and traps. 405 void SoftwareInterrupt(); 406 407 // Compact branch guard. 408 void CheckForbiddenSlot(int32_t current_pc) { 409 Instruction* instr_after_compact_branch = 410 reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); 411 if (instr_after_compact_branch->IsForbiddenAfterBranch()) { 412 V8_Fatal(__FILE__, __LINE__, 413 "Error: Unexpected instruction 0x%08x immediately after a " 414 "compact branch instruction.", 415 *reinterpret_cast<uint32_t*>(instr_after_compact_branch)); 416 } 417 } 418 419 // Stop helper functions. 420 bool IsWatchpoint(uint32_t code); 421 void PrintWatchpoint(uint32_t code); 422 void HandleStop(uint32_t code, Instruction* instr); 423 bool IsStopInstruction(Instruction* instr); 424 bool IsEnabledStop(uint32_t code); 425 void EnableStop(uint32_t code); 426 void DisableStop(uint32_t code); 427 void IncreaseStopCounter(uint32_t code); 428 void PrintStopInfo(uint32_t code); 429 430 431 // Executes one instruction. 432 void InstructionDecode(Instruction* instr); 433 // Execute one instruction placed in a branch delay slot. 434 void BranchDelayInstructionDecode(Instruction* instr) { 435 if (instr->InstructionBits() == nopInstr) { 436 // Short-cut generic nop instructions. They are always valid and they 437 // never change the simulator state. 438 return; 439 } 440 441 if (instr->IsForbiddenInBranchDelay()) { 442 V8_Fatal(__FILE__, __LINE__, 443 "Eror:Unexpected %i opcode in a branch delay slot.", 444 instr->OpcodeValue()); 445 } 446 InstructionDecode(instr); 447 SNPrintF(trace_buf_, " "); 448 } 449 450 // ICache. 451 static void CheckICache(base::CustomMatcherHashMap* i_cache, 452 Instruction* instr); 453 static void FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, 454 int size); 455 static CachePage* GetCachePage(base::CustomMatcherHashMap* i_cache, 456 void* page); 457 458 enum Exception { 459 none, 460 kIntegerOverflow, 461 kIntegerUnderflow, 462 kDivideByZero, 463 kNumExceptions 464 }; 465 466 // Exceptions. 467 void SignalException(Exception e); 468 469 // Runtime call support. 470 static void* RedirectExternalReference(Isolate* isolate, 471 void* external_function, 472 ExternalReference::Type type); 473 474 // Handle arguments and return value for runtime FP functions. 475 void GetFpArgs(double* x, double* y, int32_t* z); 476 void SetFpResult(const double& result); 477 478 void CallInternal(byte* entry); 479 480 // Architecture state. 481 // Registers. 482 int32_t registers_[kNumSimuRegisters]; 483 // Coprocessor Registers. 484 // Note: FP32 mode uses only the lower 32-bit part of each element, 485 // the upper 32-bit is unpredictable. 486 int64_t FPUregisters_[kNumFPURegisters]; 487 // FPU control register. 488 uint32_t FCSR_; 489 490 // Simulator support. 491 // Allocate 1MB for stack. 492 static const size_t stack_size_ = 1 * 1024*1024; 493 char* stack_; 494 bool pc_modified_; 495 uint64_t icount_; 496 int break_count_; 497 498 // Debugger input. 499 char* last_debugger_input_; 500 501 // Icache simulation. 502 base::CustomMatcherHashMap* i_cache_; 503 504 v8::internal::Isolate* isolate_; 505 506 // Registered breakpoints. 507 Instruction* break_pc_; 508 Instr break_instr_; 509 510 // Stop is disabled if bit 31 is set. 511 static const uint32_t kStopDisabledBit = 1 << 31; 512 513 // A stop is enabled, meaning the simulator will stop when meeting the 514 // instruction, if bit 31 of watched_stops_[code].count is unset. 515 // The value watched_stops_[code].count & ~(1 << 31) indicates how many times 516 // the breakpoint was hit or gone through. 517 struct StopCountAndDesc { 518 uint32_t count; 519 char* desc; 520 }; 521 StopCountAndDesc watched_stops_[kMaxStopCode + 1]; 522}; 523 524 525// When running with the simulator transition into simulated execution at this 526// point. 527#define CALL_GENERATED_CODE(isolate, entry, p0, p1, p2, p3, p4) \ 528 reinterpret_cast<Object*>(Simulator::current(isolate)->Call( \ 529 FUNCTION_ADDR(entry), 5, p0, p1, p2, p3, p4)) 530 531#define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ 532 p7, p8) \ 533 Simulator::current(isolate) \ 534 ->Call(entry, 10, p0, p1, p2, p3, NULL, p4, p5, p6, p7, p8) 535 536 537// The simulator has its own stack. Thus it has a different stack limit from 538// the C-based native code. The JS-based limit normally points near the end of 539// the simulator stack. When the C-based limit is exhausted we reflect that by 540// lowering the JS-based limit as well, to make stack checks trigger. 541class SimulatorStack : public v8::internal::AllStatic { 542 public: 543 static inline uintptr_t JsLimitFromCLimit(Isolate* isolate, 544 uintptr_t c_limit) { 545 return Simulator::current(isolate)->StackLimit(c_limit); 546 } 547 548 static inline uintptr_t RegisterCTryCatch(Isolate* isolate, 549 uintptr_t try_catch_address) { 550 Simulator* sim = Simulator::current(isolate); 551 return sim->PushAddress(try_catch_address); 552 } 553 554 static inline void UnregisterCTryCatch(Isolate* isolate) { 555 Simulator::current(isolate)->PopAddress(); 556 } 557}; 558 559} // namespace internal 560} // namespace v8 561 562#endif // !defined(USE_SIMULATOR) 563#endif // V8_MIPS_SIMULATOR_MIPS_H_ 564