• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   // 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 
rs_reg()358   inline int32_t rs_reg() const { return instr_.RsValue(); }
rs()359   inline int32_t rs() const { return get_register(rs_reg()); }
rs_u()360   inline uint32_t rs_u() const {
361     return static_cast<uint32_t>(get_register(rs_reg()));
362   }
rt_reg()363   inline int32_t rt_reg() const { return instr_.RtValue(); }
rt()364   inline int32_t rt() const { return get_register(rt_reg()); }
rt_u()365   inline uint32_t rt_u() const {
366     return static_cast<uint32_t>(get_register(rt_reg()));
367   }
rd_reg()368   inline int32_t rd_reg() const { return instr_.RdValue(); }
fr_reg()369   inline int32_t fr_reg() const { return instr_.FrValue(); }
fs_reg()370   inline int32_t fs_reg() const { return instr_.FsValue(); }
ft_reg()371   inline int32_t ft_reg() const { return instr_.FtValue(); }
fd_reg()372   inline int32_t fd_reg() const { return instr_.FdValue(); }
sa()373   inline int32_t sa() const { return instr_.SaValue(); }
lsa_sa()374   inline int32_t lsa_sa() const { return instr_.LsaSaValue(); }
375 
SetResult(int32_t rd_reg,int32_t alu_out)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 
SetFPUWordResult(int32_t fd_reg,int32_t alu_out)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 
SetFPUResult(int32_t fd_reg,int64_t alu_out)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 
SetFPUFloatResult(int32_t fd_reg,float alu_out)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 
SetFPUDoubleResult(int32_t fd_reg,double alu_out)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.
CheckForbiddenSlot(int32_t current_pc)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.
BranchDelayInstructionDecode(Instruction * instr)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.
541 class SimulatorStack : public v8::internal::AllStatic {
542  public:
JsLimitFromCLimit(Isolate * isolate,uintptr_t c_limit)543   static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
544                                             uintptr_t c_limit) {
545     return Simulator::current(isolate)->StackLimit(c_limit);
546   }
547 
RegisterCTryCatch(Isolate * isolate,uintptr_t try_catch_address)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 
UnregisterCTryCatch(Isolate * isolate)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