1 // Copyright 2021 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 // Declares a Simulator for loongisa instructions if we are not generating a
6 // native loongisa binary. This Simulator allows us to run and debug loongisa
7 // code generation on regular desktop machines. V8 calls into generated code via
8 // the GeneratedCode wrapper, which will start execution in the Simulator or
9 // forwards to the real entry on a loongisa HW platform.
10
11 #ifndef V8_EXECUTION_LOONG64_SIMULATOR_LOONG64_H_
12 #define V8_EXECUTION_LOONG64_SIMULATOR_LOONG64_H_
13
14 // globals.h defines USE_SIMULATOR.
15 #include "src/common/globals.h"
16
17 template <typename T>
Compare(const T & a,const T & b)18 int Compare(const T& a, const T& b) {
19 if (a == b)
20 return 0;
21 else if (a < b)
22 return -1;
23 else
24 return 1;
25 }
26
27 // Returns the negative absolute value of its argument.
28 template <typename T,
29 typename = typename std::enable_if<std::is_signed<T>::value>::type>
Nabs(T a)30 T Nabs(T a) {
31 return a < 0 ? a : -a;
32 }
33
34 #if defined(USE_SIMULATOR)
35 // Running with a simulator.
36
37 #include "src/base/hashmap.h"
38 #include "src/base/strings.h"
39 #include "src/codegen/assembler.h"
40 #include "src/codegen/loong64/constants-loong64.h"
41 #include "src/execution/simulator-base.h"
42 #include "src/utils/allocation.h"
43
44 namespace v8 {
45 namespace internal {
46
47 // -----------------------------------------------------------------------------
48 // Utility functions
49
50 class CachePage {
51 public:
52 static const int LINE_VALID = 0;
53 static const int LINE_INVALID = 1;
54
55 static const int kPageShift = 12;
56 static const int kPageSize = 1 << kPageShift;
57 static const int kPageMask = kPageSize - 1;
58 static const int kLineShift = 2; // The cache line is only 4 bytes right now.
59 static const int kLineLength = 1 << kLineShift;
60 static const int kLineMask = kLineLength - 1;
61
CachePage()62 CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); }
63
ValidityByte(int offset)64 char* ValidityByte(int offset) {
65 return &validity_map_[offset >> kLineShift];
66 }
67
CachedData(int offset)68 char* CachedData(int offset) { return &data_[offset]; }
69
70 private:
71 char data_[kPageSize]; // The cached data.
72 static const int kValidityMapSize = kPageSize >> kLineShift;
73 char validity_map_[kValidityMapSize]; // One byte per line.
74 };
75
76 class SimInstructionBase : public InstructionBase {
77 public:
InstructionType()78 Type InstructionType() const { return type_; }
instr()79 inline Instruction* instr() const { return instr_; }
operand()80 inline int32_t operand() const { return operand_; }
81
82 protected:
SimInstructionBase()83 SimInstructionBase() : operand_(-1), instr_(nullptr), type_(kUnsupported) {}
SimInstructionBase(Instruction * instr)84 explicit SimInstructionBase(Instruction* instr) {}
85
86 int32_t operand_;
87 Instruction* instr_;
88 Type type_;
89
90 private:
91 DISALLOW_ASSIGN(SimInstructionBase);
92 };
93
94 class SimInstruction : public InstructionGetters<SimInstructionBase> {
95 public:
SimInstruction()96 SimInstruction() {}
97
SimInstruction(Instruction * instr)98 explicit SimInstruction(Instruction* instr) { *this = instr; }
99
100 SimInstruction& operator=(Instruction* instr) {
101 operand_ = *reinterpret_cast<const int32_t*>(instr);
102 instr_ = instr;
103 type_ = InstructionBase::InstructionType();
104 DCHECK(reinterpret_cast<void*>(&operand_) == this);
105 return *this;
106 }
107 };
108
109 class Simulator : public SimulatorBase {
110 public:
111 friend class Loong64Debugger;
112
113 // Registers are declared in order.
114 enum Register {
115 no_reg = -1,
116 zero_reg = 0,
117 ra,
118 gp,
119 sp,
120 a0,
121 a1,
122 a2,
123 a3,
124 a4,
125 a5,
126 a6,
127 a7,
128 t0,
129 t1,
130 t2,
131 t3,
132 t4,
133 t5,
134 t6,
135 t7,
136 t8,
137 tp,
138 fp,
139 s0,
140 s1,
141 s2,
142 s3,
143 s4,
144 s5,
145 s6,
146 s7,
147 s8,
148 pc, // pc must be the last register.
149 kNumSimuRegisters,
150 // aliases
151 v0 = a0,
152 v1 = a1
153 };
154
155 // Condition flag registers.
156 enum CFRegister {
157 fcc0,
158 fcc1,
159 fcc2,
160 fcc3,
161 fcc4,
162 fcc5,
163 fcc6,
164 fcc7,
165 kNumCFRegisters
166 };
167
168 // Floating point registers.
169 enum FPURegister {
170 f0,
171 f1,
172 f2,
173 f3,
174 f4,
175 f5,
176 f6,
177 f7,
178 f8,
179 f9,
180 f10,
181 f11,
182 f12,
183 f13,
184 f14,
185 f15,
186 f16,
187 f17,
188 f18,
189 f19,
190 f20,
191 f21,
192 f22,
193 f23,
194 f24,
195 f25,
196 f26,
197 f27,
198 f28,
199 f29,
200 f30,
201 f31,
202 kNumFPURegisters
203 };
204
205 explicit Simulator(Isolate* isolate);
206 ~Simulator();
207
208 // The currently executing Simulator instance. Potentially there can be one
209 // for each native thread.
210 V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate);
211
212 // Accessors for register state. Reading the pc value adheres to the LOONG64
213 // architecture specification and is off by a 8 from the currently executing
214 // instruction.
215 void set_register(int reg, int64_t value);
216 void set_register_word(int reg, int32_t value);
217 void set_dw_register(int dreg, const int* dbl);
218 V8_EXPORT_PRIVATE int64_t get_register(int reg) const;
219 double get_double_from_register_pair(int reg);
220 // Same for FPURegisters.
221 void set_fpu_register(int fpureg, int64_t value);
222 void set_fpu_register_word(int fpureg, int32_t value);
223 void set_fpu_register_hi_word(int fpureg, int32_t value);
224 void set_fpu_register_float(int fpureg, float value);
225 void set_fpu_register_double(int fpureg, double value);
226 void set_fpu_register_invalid_result64(float original, float rounded);
227 void set_fpu_register_invalid_result(float original, float rounded);
228 void set_fpu_register_word_invalid_result(float original, float rounded);
229 void set_fpu_register_invalid_result64(double original, double rounded);
230 void set_fpu_register_invalid_result(double original, double rounded);
231 void set_fpu_register_word_invalid_result(double original, double rounded);
232 int64_t get_fpu_register(int fpureg) const;
233 int32_t get_fpu_register_word(int fpureg) const;
234 int32_t get_fpu_register_signed_word(int fpureg) const;
235 int32_t get_fpu_register_hi_word(int fpureg) const;
236 float get_fpu_register_float(int fpureg) const;
237 double get_fpu_register_double(int fpureg) const;
238 void set_cf_register(int cfreg, bool value);
239 bool get_cf_register(int cfreg) const;
240 void set_fcsr_rounding_mode(FPURoundingMode mode);
241 unsigned int get_fcsr_rounding_mode();
242 void set_fcsr_bit(uint32_t cc, bool value);
243 bool test_fcsr_bit(uint32_t cc);
244 bool set_fcsr_round_error(double original, double rounded);
245 bool set_fcsr_round64_error(double original, double rounded);
246 bool set_fcsr_round_error(float original, float rounded);
247 bool set_fcsr_round64_error(float original, float rounded);
248 void round_according_to_fcsr(double toRound, double* rounded,
249 int32_t* rounded_int);
250 void round64_according_to_fcsr(double toRound, double* rounded,
251 int64_t* rounded_int);
252 void round_according_to_fcsr(float toRound, float* rounded,
253 int32_t* rounded_int);
254 void round64_according_to_fcsr(float toRound, float* rounded,
255 int64_t* rounded_int);
256 // Special case of set_register and get_register to access the raw PC value.
257 void set_pc(int64_t value);
258 V8_EXPORT_PRIVATE int64_t get_pc() const;
259
get_sp()260 Address get_sp() const { return static_cast<Address>(get_register(sp)); }
261
262 // Accessor to the internal simulator stack area.
263 uintptr_t StackLimit(uintptr_t c_limit) const;
264
265 // Executes LOONG64 instructions until the PC reaches end_sim_pc.
266 void Execute();
267
268 template <typename Return, typename... Args>
Call(Address entry,Args...args)269 Return Call(Address entry, Args... args) {
270 return VariadicCall<Return>(this, &Simulator::CallImpl, entry, args...);
271 }
272
273 // Alternative: call a 2-argument double function.
274 double CallFP(Address entry, double d0, double d1);
275
276 // Push an address onto the JS stack.
277 uintptr_t PushAddress(uintptr_t address);
278
279 // Pop an address from the JS stack.
280 uintptr_t PopAddress();
281
282 // Debugger input.
283 void set_last_debugger_input(char* input);
last_debugger_input()284 char* last_debugger_input() { return last_debugger_input_; }
285
286 // Redirection support.
287 static void SetRedirectInstruction(Instruction* instruction);
288
289 // ICache checking.
290 static bool ICacheMatch(void* one, void* two);
291 static void FlushICache(base::CustomMatcherHashMap* i_cache, void* start,
292 size_t size);
293
294 // Returns true if pc register contains one of the 'special_values' defined
295 // below (bad_ra, end_sim_pc).
296 bool has_bad_pc() const;
297
298 private:
299 enum special_values {
300 // Known bad pc value to ensure that the simulator does not execute
301 // without being properly setup.
302 bad_ra = -1,
303 // A pc value used to signal the simulator to stop execution. Generally
304 // the ra is set to this value on transition from native C code to
305 // simulated execution, so that the simulator can "return" to the native
306 // C code.
307 end_sim_pc = -2,
308 // Unpredictable value.
309 Unpredictable = 0xbadbeaf
310 };
311
312 V8_EXPORT_PRIVATE intptr_t CallImpl(Address entry, int argument_count,
313 const intptr_t* arguments);
314
315 // Unsupported instructions use Format to print an error and stop execution.
316 void Format(Instruction* instr, const char* format);
317
318 // Helpers for data value tracing.
319 enum TraceType {
320 BYTE,
321 HALF,
322 WORD,
323 DWORD,
324 FLOAT,
325 DOUBLE,
326 FLOAT_DOUBLE,
327 WORD_DWORD
328 };
329
330 // Read and write memory.
331 inline uint32_t ReadBU(int64_t addr);
332 inline int32_t ReadB(int64_t addr);
333 inline void WriteB(int64_t addr, uint8_t value);
334 inline void WriteB(int64_t addr, int8_t value);
335
336 inline uint16_t ReadHU(int64_t addr, Instruction* instr);
337 inline int16_t ReadH(int64_t addr, Instruction* instr);
338 // Note: Overloaded on the sign of the value.
339 inline void WriteH(int64_t addr, uint16_t value, Instruction* instr);
340 inline void WriteH(int64_t addr, int16_t value, Instruction* instr);
341
342 inline uint32_t ReadWU(int64_t addr, Instruction* instr);
343 inline int32_t ReadW(int64_t addr, Instruction* instr, TraceType t = WORD);
344 inline void WriteW(int64_t addr, int32_t value, Instruction* instr);
345 void WriteConditionalW(int64_t addr, int32_t value, Instruction* instr,
346 int32_t rt_reg);
347 inline int64_t Read2W(int64_t addr, Instruction* instr);
348 inline void Write2W(int64_t addr, int64_t value, Instruction* instr);
349 inline void WriteConditional2W(int64_t addr, int64_t value,
350 Instruction* instr, int32_t rt_reg);
351
352 inline double ReadD(int64_t addr, Instruction* instr);
353 inline void WriteD(int64_t addr, double value, Instruction* instr);
354
355 template <typename T>
356 T ReadMem(int64_t addr, Instruction* instr);
357 template <typename T>
358 void WriteMem(int64_t addr, T value, Instruction* instr);
359
360 // Helper for debugging memory access.
361 inline void DieOrDebug();
362
363 void TraceRegWr(int64_t value, TraceType t = DWORD);
364 void TraceMemWr(int64_t addr, int64_t value, TraceType t);
365 void TraceMemRd(int64_t addr, int64_t value, TraceType t = DWORD);
366 template <typename T>
367 void TraceMemRd(int64_t addr, T value);
368 template <typename T>
369 void TraceMemWr(int64_t addr, T value);
370
371 SimInstruction instr_;
372
373 // Executing is handled based on the instruction type.
374 void DecodeTypeOp6();
375 void DecodeTypeOp7();
376 void DecodeTypeOp8();
377 void DecodeTypeOp10();
378 void DecodeTypeOp12();
379 void DecodeTypeOp14();
380 void DecodeTypeOp17();
381 void DecodeTypeOp22();
382
rj_reg()383 inline int32_t rj_reg() const { return instr_.RjValue(); }
rj()384 inline int64_t rj() const { return get_register(rj_reg()); }
rj_u()385 inline uint64_t rj_u() const {
386 return static_cast<uint64_t>(get_register(rj_reg()));
387 }
rk_reg()388 inline int32_t rk_reg() const { return instr_.RkValue(); }
rk()389 inline int64_t rk() const { return get_register(rk_reg()); }
rk_u()390 inline uint64_t rk_u() const {
391 return static_cast<uint64_t>(get_register(rk_reg()));
392 }
rd_reg()393 inline int32_t rd_reg() const { return instr_.RdValue(); }
rd()394 inline int64_t rd() const { return get_register(rd_reg()); }
rd_u()395 inline uint64_t rd_u() const {
396 return static_cast<uint64_t>(get_register(rd_reg()));
397 }
fa_reg()398 inline int32_t fa_reg() const { return instr_.FaValue(); }
fa_float()399 inline float fa_float() const { return get_fpu_register_float(fa_reg()); }
fa_double()400 inline double fa_double() const { return get_fpu_register_double(fa_reg()); }
fj_reg()401 inline int32_t fj_reg() const { return instr_.FjValue(); }
fj_float()402 inline float fj_float() const { return get_fpu_register_float(fj_reg()); }
fj_double()403 inline double fj_double() const { return get_fpu_register_double(fj_reg()); }
fk_reg()404 inline int32_t fk_reg() const { return instr_.FkValue(); }
fk_float()405 inline float fk_float() const { return get_fpu_register_float(fk_reg()); }
fk_double()406 inline double fk_double() const { return get_fpu_register_double(fk_reg()); }
fd_reg()407 inline int32_t fd_reg() const { return instr_.FdValue(); }
fd_float()408 inline float fd_float() const { return get_fpu_register_float(fd_reg()); }
fd_double()409 inline double fd_double() const { return get_fpu_register_double(fd_reg()); }
cj_reg()410 inline int32_t cj_reg() const { return instr_.CjValue(); }
cj()411 inline bool cj() const { return get_cf_register(cj_reg()); }
cd_reg()412 inline int32_t cd_reg() const { return instr_.CdValue(); }
cd()413 inline bool cd() const { return get_cf_register(cd_reg()); }
ca_reg()414 inline int32_t ca_reg() const { return instr_.CaValue(); }
ca()415 inline bool ca() const { return get_cf_register(ca_reg()); }
sa2()416 inline uint32_t sa2() const { return instr_.Sa2Value(); }
sa3()417 inline uint32_t sa3() const { return instr_.Sa3Value(); }
ui5()418 inline uint32_t ui5() const { return instr_.Ui5Value(); }
ui6()419 inline uint32_t ui6() const { return instr_.Ui6Value(); }
lsbw()420 inline uint32_t lsbw() const { return instr_.LsbwValue(); }
msbw()421 inline uint32_t msbw() const { return instr_.MsbwValue(); }
lsbd()422 inline uint32_t lsbd() const { return instr_.LsbdValue(); }
msbd()423 inline uint32_t msbd() const { return instr_.MsbdValue(); }
cond()424 inline uint32_t cond() const { return instr_.CondValue(); }
si12()425 inline int32_t si12() const { return (instr_.Si12Value() << 20) >> 20; }
ui12()426 inline uint32_t ui12() const { return instr_.Ui12Value(); }
si14()427 inline int32_t si14() const { return (instr_.Si14Value() << 18) >> 18; }
si16()428 inline int32_t si16() const { return (instr_.Si16Value() << 16) >> 16; }
si20()429 inline int32_t si20() const { return (instr_.Si20Value() << 12) >> 12; }
430
SetResult(const int32_t rd_reg,const int64_t alu_out)431 inline void SetResult(const int32_t rd_reg, const int64_t alu_out) {
432 set_register(rd_reg, alu_out);
433 TraceRegWr(alu_out);
434 }
435
SetFPUWordResult(int32_t fd_reg,int32_t alu_out)436 inline void SetFPUWordResult(int32_t fd_reg, int32_t alu_out) {
437 set_fpu_register_word(fd_reg, alu_out);
438 TraceRegWr(get_fpu_register(fd_reg), WORD);
439 }
440
SetFPUWordResult2(int32_t fd_reg,int32_t alu_out)441 inline void SetFPUWordResult2(int32_t fd_reg, int32_t alu_out) {
442 set_fpu_register_word(fd_reg, alu_out);
443 TraceRegWr(get_fpu_register(fd_reg));
444 }
445
SetFPUResult(int32_t fd_reg,int64_t alu_out)446 inline void SetFPUResult(int32_t fd_reg, int64_t alu_out) {
447 set_fpu_register(fd_reg, alu_out);
448 TraceRegWr(get_fpu_register(fd_reg));
449 }
450
SetFPUResult2(int32_t fd_reg,int64_t alu_out)451 inline void SetFPUResult2(int32_t fd_reg, int64_t alu_out) {
452 set_fpu_register(fd_reg, alu_out);
453 TraceRegWr(get_fpu_register(fd_reg), DOUBLE);
454 }
455
SetFPUFloatResult(int32_t fd_reg,float alu_out)456 inline void SetFPUFloatResult(int32_t fd_reg, float alu_out) {
457 set_fpu_register_float(fd_reg, alu_out);
458 TraceRegWr(get_fpu_register(fd_reg), FLOAT);
459 }
460
SetFPUDoubleResult(int32_t fd_reg,double alu_out)461 inline void SetFPUDoubleResult(int32_t fd_reg, double alu_out) {
462 set_fpu_register_double(fd_reg, alu_out);
463 TraceRegWr(get_fpu_register(fd_reg), DOUBLE);
464 }
465
466 // Used for breakpoints.
467 void SoftwareInterrupt();
468
469 // Stop helper functions.
470 bool IsWatchpoint(uint64_t code);
471 void PrintWatchpoint(uint64_t code);
472 void HandleStop(uint64_t code, Instruction* instr);
473 bool IsStopInstruction(Instruction* instr);
474 bool IsEnabledStop(uint64_t code);
475 void EnableStop(uint64_t code);
476 void DisableStop(uint64_t code);
477 void IncreaseStopCounter(uint64_t code);
478 void PrintStopInfo(uint64_t code);
479
480 // Executes one instruction.
481 void InstructionDecode(Instruction* instr);
482 // Execute one instruction placed in a branch delay slot.
483
484 // ICache.
485 static void CheckICache(base::CustomMatcherHashMap* i_cache,
486 Instruction* instr);
487 static void FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start,
488 size_t size);
489 static CachePage* GetCachePage(base::CustomMatcherHashMap* i_cache,
490 void* page);
491
492 enum Exception {
493 none,
494 kIntegerOverflow,
495 kIntegerUnderflow,
496 kDivideByZero,
497 kNumExceptions
498 };
499
500 // Exceptions.
501 void SignalException(Exception e);
502
503 // Handle arguments and return value for runtime FP functions.
504 void GetFpArgs(double* x, double* y, int32_t* z);
505 void SetFpResult(const double& result);
506
507 void CallInternal(Address entry);
508
509 // Architecture state.
510 // Registers.
511 int64_t registers_[kNumSimuRegisters];
512 // Floating point Registers.
513 int64_t FPUregisters_[kNumFPURegisters];
514 // Condition flags Registers.
515 bool CFregisters_[kNumCFRegisters];
516 // FPU control register.
517 uint32_t FCSR_;
518
519 // Simulator support.
520 // Allocate 1MB for stack.
521 size_t stack_size_;
522 char* stack_;
523 bool pc_modified_;
524 int64_t icount_;
525 int break_count_;
526 base::EmbeddedVector<char, 128> trace_buf_;
527
528 // Debugger input.
529 char* last_debugger_input_;
530
531 v8::internal::Isolate* isolate_;
532
533 // Registered breakpoints.
534 Instruction* break_pc_;
535 Instr break_instr_;
536
537 // Stop is disabled if bit 31 is set.
538 static const uint32_t kStopDisabledBit = 1 << 31;
539
540 // A stop is enabled, meaning the simulator will stop when meeting the
541 // instruction, if bit 31 of watched_stops_[code].count is unset.
542 // The value watched_stops_[code].count & ~(1 << 31) indicates how many times
543 // the breakpoint was hit or gone through.
544 struct StopCountAndDesc {
545 uint32_t count;
546 char* desc;
547 };
548 StopCountAndDesc watched_stops_[kMaxStopCode + 1];
549
550 // Synchronization primitives.
551 enum class MonitorAccess {
552 Open,
553 RMW,
554 };
555
556 enum class TransactionSize {
557 None = 0,
558 Word = 4,
559 DoubleWord = 8,
560 };
561
562 // The least-significant bits of the address are ignored. The number of bits
563 // is implementation-defined, between 3 and minimum page size.
564 static const uintptr_t kExclusiveTaggedAddrMask = ~((1 << 3) - 1);
565
566 class LocalMonitor {
567 public:
568 LocalMonitor();
569
570 // These functions manage the state machine for the local monitor, but do
571 // not actually perform loads and stores. NotifyStoreConditional only
572 // returns true if the store conditional is allowed; the global monitor will
573 // still have to be checked to see whether the memory should be updated.
574 void NotifyLoad();
575 void NotifyLoadLinked(uintptr_t addr, TransactionSize size);
576 void NotifyStore();
577 bool NotifyStoreConditional(uintptr_t addr, TransactionSize size);
578
579 private:
580 void Clear();
581
582 MonitorAccess access_state_;
583 uintptr_t tagged_addr_;
584 TransactionSize size_;
585 };
586
587 class GlobalMonitor {
588 public:
589 class LinkedAddress {
590 public:
591 LinkedAddress();
592
593 private:
594 friend class GlobalMonitor;
595 // These functions manage the state machine for the global monitor, but do
596 // not actually perform loads and stores.
597 void Clear_Locked();
598 void NotifyLoadLinked_Locked(uintptr_t addr);
599 void NotifyStore_Locked();
600 bool NotifyStoreConditional_Locked(uintptr_t addr,
601 bool is_requesting_thread);
602
603 MonitorAccess access_state_;
604 uintptr_t tagged_addr_;
605 LinkedAddress* next_;
606 LinkedAddress* prev_;
607 // A scd can fail due to background cache evictions. Rather than
608 // simulating this, we'll just occasionally introduce cases where an
609 // store conditional fails. This will happen once after every
610 // kMaxFailureCounter exclusive stores.
611 static const int kMaxFailureCounter = 5;
612 int failure_counter_;
613 };
614
615 // Exposed so it can be accessed by Simulator::{Read,Write}Ex*.
616 base::Mutex mutex;
617
618 void NotifyLoadLinked_Locked(uintptr_t addr, LinkedAddress* linked_address);
619 void NotifyStore_Locked(LinkedAddress* linked_address);
620 bool NotifyStoreConditional_Locked(uintptr_t addr,
621 LinkedAddress* linked_address);
622
623 // Called when the simulator is destroyed.
624 void RemoveLinkedAddress(LinkedAddress* linked_address);
625
626 static GlobalMonitor* Get();
627
628 private:
629 // Private constructor. Call {GlobalMonitor::Get()} to get the singleton.
630 GlobalMonitor() = default;
631 friend class base::LeakyObject<GlobalMonitor>;
632
633 bool IsProcessorInLinkedList_Locked(LinkedAddress* linked_address) const;
634 void PrependProcessor_Locked(LinkedAddress* linked_address);
635
636 LinkedAddress* head_ = nullptr;
637 };
638
639 LocalMonitor local_monitor_;
640 GlobalMonitor::LinkedAddress global_monitor_thread_;
641 };
642
643 } // namespace internal
644 } // namespace v8
645
646 #endif // defined(USE_SIMULATOR)
647 #endif // V8_EXECUTION_LOONG64_SIMULATOR_LOONG64_H_
648