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