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