1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80: */
3 // Copyright 2011 the V8 project authors. All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following
12 // disclaimer in the documentation and/or other materials provided
13 // with the distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "jit/mips64/Simulator-mips64.h"
31
32 #include "mozilla/Casting.h"
33 #include "mozilla/FloatingPoint.h"
34 #include "mozilla/IntegerPrintfMacros.h"
35 #include "mozilla/Likely.h"
36 #include "mozilla/MathAlgorithms.h"
37
38 #include <float.h>
39 #include <limits>
40
41 #include "jit/AtomicOperations.h"
42 #include "jit/mips64/Assembler-mips64.h"
43 #include "js/UniquePtr.h"
44 #include "js/Utility.h"
45 #include "threading/LockGuard.h"
46 #include "vm/Runtime.h"
47 #include "wasm/WasmInstance.h"
48 #include "wasm/WasmSignalHandlers.h"
49
50 #define I8(v) static_cast<int8_t>(v)
51 #define I16(v) static_cast<int16_t>(v)
52 #define U16(v) static_cast<uint16_t>(v)
53 #define I32(v) static_cast<int32_t>(v)
54 #define U32(v) static_cast<uint32_t>(v)
55 #define I64(v) static_cast<int64_t>(v)
56 #define U64(v) static_cast<uint64_t>(v)
57 #define I128(v) static_cast<__int128_t>(v)
58 #define U128(v) static_cast<__uint128_t>(v)
59
60 #define I32_CHECK(v) \
61 ({ \
62 MOZ_ASSERT(I64(I32(v)) == I64(v)); \
63 I32((v)); \
64 })
65
66 namespace js {
67 namespace jit {
68
69 static const Instr kCallRedirInstr =
70 op_special | MAX_BREAK_CODE << FunctionBits | ff_break;
71
72 // Utils functions.
GetFCSRConditionBit(uint32_t cc)73 static uint32_t GetFCSRConditionBit(uint32_t cc) {
74 if (cc == 0) {
75 return 23;
76 }
77 return 24 + cc;
78 }
79
80 // -----------------------------------------------------------------------------
81 // MIPS assembly various constants.
82
83 class SimInstruction {
84 public:
85 enum {
86 kInstrSize = 4,
87 // On MIPS PC cannot actually be directly accessed. We behave as if PC was
88 // always the value of the current instruction being executed.
89 kPCReadOffset = 0
90 };
91
92 // Get the raw instruction bits.
instructionBits() const93 inline Instr instructionBits() const {
94 return *reinterpret_cast<const Instr*>(this);
95 }
96
97 // Set the raw instruction bits to value.
setInstructionBits(Instr value)98 inline void setInstructionBits(Instr value) {
99 *reinterpret_cast<Instr*>(this) = value;
100 }
101
102 // Read one particular bit out of the instruction bits.
bit(int nr) const103 inline int bit(int nr) const { return (instructionBits() >> nr) & 1; }
104
105 // Read a bit field out of the instruction bits.
bits(int hi,int lo) const106 inline int bits(int hi, int lo) const {
107 return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1);
108 }
109
110 // Instruction type.
111 enum Type { kRegisterType, kImmediateType, kJumpType, kUnsupported = -1 };
112
113 // Get the encoding type of the instruction.
114 Type instructionType() const;
115
116 // Accessors for the different named fields used in the MIPS encoding.
opcodeValue() const117 inline OpcodeField opcodeValue() const {
118 return static_cast<OpcodeField>(
119 bits(OpcodeShift + OpcodeBits - 1, OpcodeShift));
120 }
121
rsValue() const122 inline int rsValue() const {
123 MOZ_ASSERT(instructionType() == kRegisterType ||
124 instructionType() == kImmediateType);
125 return bits(RSShift + RSBits - 1, RSShift);
126 }
127
rtValue() const128 inline int rtValue() const {
129 MOZ_ASSERT(instructionType() == kRegisterType ||
130 instructionType() == kImmediateType);
131 return bits(RTShift + RTBits - 1, RTShift);
132 }
133
rdValue() const134 inline int rdValue() const {
135 MOZ_ASSERT(instructionType() == kRegisterType);
136 return bits(RDShift + RDBits - 1, RDShift);
137 }
138
saValue() const139 inline int saValue() const {
140 MOZ_ASSERT(instructionType() == kRegisterType);
141 return bits(SAShift + SABits - 1, SAShift);
142 }
143
functionValue() const144 inline int functionValue() const {
145 MOZ_ASSERT(instructionType() == kRegisterType ||
146 instructionType() == kImmediateType);
147 return bits(FunctionShift + FunctionBits - 1, FunctionShift);
148 }
149
fdValue() const150 inline int fdValue() const { return bits(FDShift + FDBits - 1, FDShift); }
151
fsValue() const152 inline int fsValue() const { return bits(FSShift + FSBits - 1, FSShift); }
153
ftValue() const154 inline int ftValue() const { return bits(FTShift + FTBits - 1, FTShift); }
155
frValue() const156 inline int frValue() const { return bits(FRShift + FRBits - 1, FRShift); }
157
158 // Float Compare condition code instruction bits.
fcccValue() const159 inline int fcccValue() const {
160 return bits(FCccShift + FCccBits - 1, FCccShift);
161 }
162
163 // Float Branch condition code instruction bits.
fbccValue() const164 inline int fbccValue() const {
165 return bits(FBccShift + FBccBits - 1, FBccShift);
166 }
167
168 // Float Branch true/false instruction bit.
fbtrueValue() const169 inline int fbtrueValue() const {
170 return bits(FBtrueShift + FBtrueBits - 1, FBtrueShift);
171 }
172
173 // Return the fields at their original place in the instruction encoding.
opcodeFieldRaw() const174 inline OpcodeField opcodeFieldRaw() const {
175 return static_cast<OpcodeField>(instructionBits() & OpcodeMask);
176 }
177
rsFieldRaw() const178 inline int rsFieldRaw() const {
179 MOZ_ASSERT(instructionType() == kRegisterType ||
180 instructionType() == kImmediateType);
181 return instructionBits() & RSMask;
182 }
183
184 // Same as above function, but safe to call within instructionType().
rsFieldRawNoAssert() const185 inline int rsFieldRawNoAssert() const { return instructionBits() & RSMask; }
186
rtFieldRaw() const187 inline int rtFieldRaw() const {
188 MOZ_ASSERT(instructionType() == kRegisterType ||
189 instructionType() == kImmediateType);
190 return instructionBits() & RTMask;
191 }
192
rdFieldRaw() const193 inline int rdFieldRaw() const {
194 MOZ_ASSERT(instructionType() == kRegisterType);
195 return instructionBits() & RDMask;
196 }
197
saFieldRaw() const198 inline int saFieldRaw() const {
199 MOZ_ASSERT(instructionType() == kRegisterType);
200 return instructionBits() & SAMask;
201 }
202
functionFieldRaw() const203 inline int functionFieldRaw() const {
204 return instructionBits() & FunctionMask;
205 }
206
207 // Get the secondary field according to the opcode.
secondaryValue() const208 inline int secondaryValue() const {
209 OpcodeField op = opcodeFieldRaw();
210 switch (op) {
211 case op_special:
212 case op_special2:
213 return functionValue();
214 case op_cop1:
215 return rsValue();
216 case op_regimm:
217 return rtValue();
218 default:
219 return ff_null;
220 }
221 }
222
imm16Value() const223 inline int32_t imm16Value() const {
224 MOZ_ASSERT(instructionType() == kImmediateType);
225 return bits(Imm16Shift + Imm16Bits - 1, Imm16Shift);
226 }
227
imm26Value() const228 inline int32_t imm26Value() const {
229 MOZ_ASSERT(instructionType() == kJumpType);
230 return bits(Imm26Shift + Imm26Bits - 1, Imm26Shift);
231 }
232
233 // Say if the instruction should not be used in a branch delay slot.
234 bool isForbiddenInBranchDelay() const;
235 // Say if the instruction 'links'. e.g. jal, bal.
236 bool isLinkingInstruction() const;
237 // Say if the instruction is a debugger break/trap.
238 bool isTrap() const;
239
240 private:
241 SimInstruction() = delete;
242 SimInstruction(const SimInstruction& other) = delete;
243 void operator=(const SimInstruction& other) = delete;
244 };
245
isForbiddenInBranchDelay() const246 bool SimInstruction::isForbiddenInBranchDelay() const {
247 const int op = opcodeFieldRaw();
248 switch (op) {
249 case op_j:
250 case op_jal:
251 case op_beq:
252 case op_bne:
253 case op_blez:
254 case op_bgtz:
255 case op_beql:
256 case op_bnel:
257 case op_blezl:
258 case op_bgtzl:
259 return true;
260 case op_regimm:
261 switch (rtFieldRaw()) {
262 case rt_bltz:
263 case rt_bgez:
264 case rt_bltzal:
265 case rt_bgezal:
266 return true;
267 default:
268 return false;
269 };
270 break;
271 case op_special:
272 switch (functionFieldRaw()) {
273 case ff_jr:
274 case ff_jalr:
275 return true;
276 default:
277 return false;
278 };
279 break;
280 default:
281 return false;
282 };
283 }
284
isLinkingInstruction() const285 bool SimInstruction::isLinkingInstruction() const {
286 const int op = opcodeFieldRaw();
287 switch (op) {
288 case op_jal:
289 return true;
290 case op_regimm:
291 switch (rtFieldRaw()) {
292 case rt_bgezal:
293 case rt_bltzal:
294 return true;
295 default:
296 return false;
297 };
298 case op_special:
299 switch (functionFieldRaw()) {
300 case ff_jalr:
301 return true;
302 default:
303 return false;
304 };
305 default:
306 return false;
307 };
308 }
309
isTrap() const310 bool SimInstruction::isTrap() const {
311 if (opcodeFieldRaw() != op_special) {
312 return false;
313 } else {
314 switch (functionFieldRaw()) {
315 case ff_break:
316 return instructionBits() != kCallRedirInstr;
317 case ff_tge:
318 case ff_tgeu:
319 case ff_tlt:
320 case ff_tltu:
321 case ff_teq:
322 case ff_tne:
323 return bits(15, 6) != kWasmTrapCode;
324 default:
325 return false;
326 };
327 }
328 }
329
instructionType() const330 SimInstruction::Type SimInstruction::instructionType() const {
331 switch (opcodeFieldRaw()) {
332 case op_special:
333 switch (functionFieldRaw()) {
334 case ff_jr:
335 case ff_jalr:
336 case ff_sync:
337 case ff_break:
338 case ff_sll:
339 case ff_dsll:
340 case ff_dsll32:
341 case ff_srl:
342 case ff_dsrl:
343 case ff_dsrl32:
344 case ff_sra:
345 case ff_dsra:
346 case ff_dsra32:
347 case ff_sllv:
348 case ff_dsllv:
349 case ff_srlv:
350 case ff_dsrlv:
351 case ff_srav:
352 case ff_dsrav:
353 case ff_mfhi:
354 case ff_mflo:
355 case ff_mult:
356 case ff_dmult:
357 case ff_multu:
358 case ff_dmultu:
359 case ff_div:
360 case ff_ddiv:
361 case ff_divu:
362 case ff_ddivu:
363 case ff_add:
364 case ff_dadd:
365 case ff_addu:
366 case ff_daddu:
367 case ff_sub:
368 case ff_dsub:
369 case ff_subu:
370 case ff_dsubu:
371 case ff_and:
372 case ff_or:
373 case ff_xor:
374 case ff_nor:
375 case ff_slt:
376 case ff_sltu:
377 case ff_tge:
378 case ff_tgeu:
379 case ff_tlt:
380 case ff_tltu:
381 case ff_teq:
382 case ff_tne:
383 case ff_movz:
384 case ff_movn:
385 case ff_movci:
386 return kRegisterType;
387 default:
388 return kUnsupported;
389 };
390 break;
391 case op_special2:
392 switch (functionFieldRaw()) {
393 case ff_mul:
394 case ff_clz:
395 case ff_dclz:
396 return kRegisterType;
397 default:
398 return kUnsupported;
399 };
400 break;
401 case op_special3:
402 switch (functionFieldRaw()) {
403 case ff_ins:
404 case ff_dins:
405 case ff_dinsm:
406 case ff_dinsu:
407 case ff_ext:
408 case ff_dext:
409 case ff_dextm:
410 case ff_dextu:
411 case ff_bshfl:
412 return kRegisterType;
413 default:
414 return kUnsupported;
415 };
416 break;
417 case op_cop1: // Coprocessor instructions.
418 switch (rsFieldRawNoAssert()) {
419 case rs_bc1: // Branch on coprocessor condition.
420 return kImmediateType;
421 default:
422 return kRegisterType;
423 };
424 break;
425 case op_cop1x:
426 return kRegisterType;
427 // 16 bits Immediate type instructions. e.g.: addi dest, src, imm16.
428 case op_regimm:
429 case op_beq:
430 case op_bne:
431 case op_blez:
432 case op_bgtz:
433 case op_addi:
434 case op_daddi:
435 case op_addiu:
436 case op_daddiu:
437 case op_slti:
438 case op_sltiu:
439 case op_andi:
440 case op_ori:
441 case op_xori:
442 case op_lui:
443 case op_beql:
444 case op_bnel:
445 case op_blezl:
446 case op_bgtzl:
447 case op_lb:
448 case op_lbu:
449 case op_lh:
450 case op_lhu:
451 case op_lw:
452 case op_lwu:
453 case op_lwl:
454 case op_lwr:
455 case op_ll:
456 case op_lld:
457 case op_ld:
458 case op_ldl:
459 case op_ldr:
460 case op_sb:
461 case op_sh:
462 case op_sw:
463 case op_swl:
464 case op_swr:
465 case op_sc:
466 case op_scd:
467 case op_sd:
468 case op_sdl:
469 case op_sdr:
470 case op_lwc1:
471 case op_ldc1:
472 case op_swc1:
473 case op_sdc1:
474 return kImmediateType;
475 // 26 bits immediate type instructions. e.g.: j imm26.
476 case op_j:
477 case op_jal:
478 return kJumpType;
479 default:
480 return kUnsupported;
481 };
482 return kUnsupported;
483 }
484
485 // C/C++ argument slots size.
486 const int kCArgSlotCount = 0;
487 const int kCArgsSlotsSize = kCArgSlotCount * sizeof(uintptr_t);
488 const int kBranchReturnOffset = 2 * SimInstruction::kInstrSize;
489
490 class CachePage {
491 public:
492 static const int LINE_VALID = 0;
493 static const int LINE_INVALID = 1;
494
495 static const int kPageShift = 12;
496 static const int kPageSize = 1 << kPageShift;
497 static const int kPageMask = kPageSize - 1;
498 static const int kLineShift = 2; // The cache line is only 4 bytes right now.
499 static const int kLineLength = 1 << kLineShift;
500 static const int kLineMask = kLineLength - 1;
501
CachePage()502 CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); }
503
validityByte(int offset)504 char* validityByte(int offset) {
505 return &validity_map_[offset >> kLineShift];
506 }
507
cachedData(int offset)508 char* cachedData(int offset) { return &data_[offset]; }
509
510 private:
511 char data_[kPageSize]; // The cached data.
512 static const int kValidityMapSize = kPageSize >> kLineShift;
513 char validity_map_[kValidityMapSize]; // One byte per line.
514 };
515
516 // Protects the icache() and redirection() properties of the
517 // Simulator.
518 class AutoLockSimulatorCache : public LockGuard<Mutex> {
519 using Base = LockGuard<Mutex>;
520
521 public:
AutoLockSimulatorCache()522 explicit AutoLockSimulatorCache()
523 : Base(SimulatorProcess::singleton_->cacheLock_) {}
524 };
525
526 mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
527 SimulatorProcess::ICacheCheckingDisableCount(
528 1); // Checking is disabled by default.
529 SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
530
531 int64_t Simulator::StopSimAt = -1;
532
Create()533 Simulator* Simulator::Create() {
534 auto sim = MakeUnique<Simulator>();
535 if (!sim) {
536 return nullptr;
537 }
538
539 if (!sim->init()) {
540 return nullptr;
541 }
542
543 int64_t stopAt;
544 char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
545 if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) {
546 fprintf(stderr, "\nStopping simulation at icount %" PRIi64 "\n", stopAt);
547 Simulator::StopSimAt = stopAt;
548 }
549
550 return sim.release();
551 }
552
Destroy(Simulator * sim)553 void Simulator::Destroy(Simulator* sim) { js_delete(sim); }
554
555 // The MipsDebugger class is used by the simulator while debugging simulated
556 // code.
557 class MipsDebugger {
558 public:
MipsDebugger(Simulator * sim)559 explicit MipsDebugger(Simulator* sim) : sim_(sim) {}
560
561 void stop(SimInstruction* instr);
562 void debug();
563 // Print all registers with a nice formatting.
564 void printAllRegs();
565 void printAllRegsIncludingFPU();
566
567 private:
568 // We set the breakpoint code to 0xfffff to easily recognize it.
569 static const Instr kBreakpointInstr = op_special | ff_break | 0xfffff << 6;
570 static const Instr kNopInstr = op_special | ff_sll;
571
572 Simulator* sim_;
573
574 int64_t getRegisterValue(int regnum);
575 int64_t getFPURegisterValueLong(int regnum);
576 float getFPURegisterValueFloat(int regnum);
577 double getFPURegisterValueDouble(int regnum);
578 bool getValue(const char* desc, int64_t* value);
579
580 // Set or delete a breakpoint. Returns true if successful.
581 bool setBreakpoint(SimInstruction* breakpc);
582 bool deleteBreakpoint(SimInstruction* breakpc);
583
584 // Undo and redo all breakpoints. This is needed to bracket disassembly and
585 // execution to skip past breakpoints when run from the debugger.
586 void undoBreakpoints();
587 void redoBreakpoints();
588 };
589
UNSUPPORTED()590 static void UNSUPPORTED() {
591 printf("Unsupported instruction.\n");
592 MOZ_CRASH();
593 }
594
stop(SimInstruction * instr)595 void MipsDebugger::stop(SimInstruction* instr) {
596 // Get the stop code.
597 uint32_t code = instr->bits(25, 6);
598 // Retrieve the encoded address, which comes just after this stop.
599 char* msg =
600 *reinterpret_cast<char**>(sim_->get_pc() + SimInstruction::kInstrSize);
601 // Update this stop description.
602 if (!sim_->watchedStops_[code].desc_) {
603 sim_->watchedStops_[code].desc_ = msg;
604 }
605 // Print the stop message and code if it is not the default code.
606 if (code != kMaxStopCode) {
607 printf("Simulator hit stop %u: %s\n", code, msg);
608 } else {
609 printf("Simulator hit %s\n", msg);
610 }
611 sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize);
612 debug();
613 }
614
getRegisterValue(int regnum)615 int64_t MipsDebugger::getRegisterValue(int regnum) {
616 if (regnum == kPCRegister) {
617 return sim_->get_pc();
618 }
619 return sim_->getRegister(regnum);
620 }
621
getFPURegisterValueLong(int regnum)622 int64_t MipsDebugger::getFPURegisterValueLong(int regnum) {
623 return sim_->getFpuRegister(regnum);
624 }
625
getFPURegisterValueFloat(int regnum)626 float MipsDebugger::getFPURegisterValueFloat(int regnum) {
627 return sim_->getFpuRegisterFloat(regnum);
628 }
629
getFPURegisterValueDouble(int regnum)630 double MipsDebugger::getFPURegisterValueDouble(int regnum) {
631 return sim_->getFpuRegisterDouble(regnum);
632 }
633
getValue(const char * desc,int64_t * value)634 bool MipsDebugger::getValue(const char* desc, int64_t* value) {
635 Register reg = Register::FromName(desc);
636 if (reg != InvalidReg) {
637 *value = getRegisterValue(reg.code());
638 return true;
639 }
640
641 if (strncmp(desc, "0x", 2) == 0) {
642 return sscanf(desc, "%" PRIu64, reinterpret_cast<uint64_t*>(value)) == 1;
643 }
644 return sscanf(desc, "%" PRIi64, value) == 1;
645 }
646
setBreakpoint(SimInstruction * breakpc)647 bool MipsDebugger::setBreakpoint(SimInstruction* breakpc) {
648 // Check if a breakpoint can be set. If not return without any side-effects.
649 if (sim_->break_pc_ != nullptr) {
650 return false;
651 }
652
653 // Set the breakpoint.
654 sim_->break_pc_ = breakpc;
655 sim_->break_instr_ = breakpc->instructionBits();
656 // Not setting the breakpoint instruction in the code itself. It will be set
657 // when the debugger shell continues.
658 return true;
659 }
660
deleteBreakpoint(SimInstruction * breakpc)661 bool MipsDebugger::deleteBreakpoint(SimInstruction* breakpc) {
662 if (sim_->break_pc_ != nullptr) {
663 sim_->break_pc_->setInstructionBits(sim_->break_instr_);
664 }
665
666 sim_->break_pc_ = nullptr;
667 sim_->break_instr_ = 0;
668 return true;
669 }
670
undoBreakpoints()671 void MipsDebugger::undoBreakpoints() {
672 if (sim_->break_pc_) {
673 sim_->break_pc_->setInstructionBits(sim_->break_instr_);
674 }
675 }
676
redoBreakpoints()677 void MipsDebugger::redoBreakpoints() {
678 if (sim_->break_pc_) {
679 sim_->break_pc_->setInstructionBits(kBreakpointInstr);
680 }
681 }
682
printAllRegs()683 void MipsDebugger::printAllRegs() {
684 int64_t value;
685 for (uint32_t i = 0; i < Registers::Total; i++) {
686 value = getRegisterValue(i);
687 printf("%3s: 0x%016" PRIx64 " %20" PRIi64 " ", Registers::GetName(i),
688 value, value);
689
690 if (i % 2) {
691 printf("\n");
692 }
693 }
694 printf("\n");
695
696 value = getRegisterValue(Simulator::LO);
697 printf(" LO: 0x%016" PRIx64 " %20" PRIi64 " ", value, value);
698 value = getRegisterValue(Simulator::HI);
699 printf(" HI: 0x%016" PRIx64 " %20" PRIi64 "\n", value, value);
700 value = getRegisterValue(Simulator::pc);
701 printf(" pc: 0x%016" PRIx64 "\n", value);
702 }
703
printAllRegsIncludingFPU()704 void MipsDebugger::printAllRegsIncludingFPU() {
705 printAllRegs();
706
707 printf("\n\n");
708 // f0, f1, f2, ... f31.
709 for (uint32_t i = 0; i < FloatRegisters::TotalPhys; i++) {
710 printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n",
711 FloatRegisters::GetName(i), getFPURegisterValueLong(i),
712 getFPURegisterValueFloat(i), getFPURegisterValueDouble(i));
713 }
714 }
715
ReadLine(const char * prompt)716 static char* ReadLine(const char* prompt) {
717 UniqueChars result;
718 char lineBuf[256];
719 int offset = 0;
720 bool keepGoing = true;
721 fprintf(stdout, "%s", prompt);
722 fflush(stdout);
723 while (keepGoing) {
724 if (fgets(lineBuf, sizeof(lineBuf), stdin) == nullptr) {
725 // fgets got an error. Just give up.
726 return nullptr;
727 }
728 int len = strlen(lineBuf);
729 if (len > 0 && lineBuf[len - 1] == '\n') {
730 // Since we read a new line we are done reading the line. This
731 // will exit the loop after copying this buffer into the result.
732 keepGoing = false;
733 }
734 if (!result) {
735 // Allocate the initial result and make room for the terminating '\0'
736 result.reset(js_pod_malloc<char>(len + 1));
737 if (!result) {
738 return nullptr;
739 }
740 } else {
741 // Allocate a new result with enough room for the new addition.
742 int new_len = offset + len + 1;
743 char* new_result = js_pod_malloc<char>(new_len);
744 if (!new_result) {
745 return nullptr;
746 }
747 // Copy the existing input into the new array and set the new
748 // array as the result.
749 memcpy(new_result, result.get(), offset * sizeof(char));
750 result.reset(new_result);
751 }
752 // Copy the newly read line into the result.
753 memcpy(result.get() + offset, lineBuf, len * sizeof(char));
754 offset += len;
755 }
756
757 MOZ_ASSERT(result);
758 result[offset] = '\0';
759 return result.release();
760 }
761
DisassembleInstruction(uint64_t pc)762 static void DisassembleInstruction(uint64_t pc) {
763 uint8_t* bytes = reinterpret_cast<uint8_t*>(pc);
764 char hexbytes[256];
765 sprintf(hexbytes, "0x%x 0x%x 0x%x 0x%x", bytes[0], bytes[1], bytes[2],
766 bytes[3]);
767 char llvmcmd[1024];
768 sprintf(llvmcmd,
769 "bash -c \"echo -n '%p'; echo '%s' | "
770 "llvm-mc -disassemble -arch=mips64el -mcpu=mips64r2 | "
771 "grep -v pure_instructions | grep -v .text\"",
772 static_cast<void*>(bytes), hexbytes);
773 if (system(llvmcmd)) {
774 printf("Cannot disassemble instruction.\n");
775 }
776 }
777
debug()778 void MipsDebugger::debug() {
779 intptr_t lastPC = -1;
780 bool done = false;
781
782 #define COMMAND_SIZE 63
783 #define ARG_SIZE 255
784
785 #define STR(a) #a
786 #define XSTR(a) STR(a)
787
788 char cmd[COMMAND_SIZE + 1];
789 char arg1[ARG_SIZE + 1];
790 char arg2[ARG_SIZE + 1];
791 char* argv[3] = {cmd, arg1, arg2};
792
793 // Make sure to have a proper terminating character if reaching the limit.
794 cmd[COMMAND_SIZE] = 0;
795 arg1[ARG_SIZE] = 0;
796 arg2[ARG_SIZE] = 0;
797
798 // Undo all set breakpoints while running in the debugger shell. This will
799 // make them invisible to all commands.
800 undoBreakpoints();
801
802 while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) {
803 if (lastPC != sim_->get_pc()) {
804 DisassembleInstruction(sim_->get_pc());
805 lastPC = sim_->get_pc();
806 }
807 char* line = ReadLine("sim> ");
808 if (line == nullptr) {
809 break;
810 } else {
811 char* last_input = sim_->lastDebuggerInput();
812 if (strcmp(line, "\n") == 0 && last_input != nullptr) {
813 line = last_input;
814 } else {
815 // Ownership is transferred to sim_;
816 sim_->setLastDebuggerInput(line);
817 }
818 // Use sscanf to parse the individual parts of the command line. At the
819 // moment no command expects more than two parameters.
820 int argc = sscanf(line,
821 "%" XSTR(COMMAND_SIZE) "s "
822 "%" XSTR(ARG_SIZE) "s "
823 "%" XSTR(ARG_SIZE) "s",
824 cmd, arg1, arg2);
825 if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
826 SimInstruction* instr =
827 reinterpret_cast<SimInstruction*>(sim_->get_pc());
828 if (!instr->isTrap()) {
829 sim_->instructionDecode(
830 reinterpret_cast<SimInstruction*>(sim_->get_pc()));
831 } else {
832 // Allow si to jump over generated breakpoints.
833 printf("/!\\ Jumping over generated breakpoint.\n");
834 sim_->set_pc(sim_->get_pc() + SimInstruction::kInstrSize);
835 }
836 } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
837 // Execute the one instruction we broke at with breakpoints disabled.
838 sim_->instructionDecode(
839 reinterpret_cast<SimInstruction*>(sim_->get_pc()));
840 // Leave the debugger shell.
841 done = true;
842 } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
843 if (argc == 2) {
844 int64_t value;
845 if (strcmp(arg1, "all") == 0) {
846 printAllRegs();
847 } else if (strcmp(arg1, "allf") == 0) {
848 printAllRegsIncludingFPU();
849 } else {
850 Register reg = Register::FromName(arg1);
851 FloatRegisters::Encoding fReg = FloatRegisters::FromName(arg1);
852 if (reg != InvalidReg) {
853 value = getRegisterValue(reg.code());
854 printf("%s: 0x%016" PRIi64 " %20" PRIi64 " \n", arg1, value,
855 value);
856 } else if (fReg != FloatRegisters::Invalid) {
857 printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n",
858 FloatRegisters::GetName(fReg),
859 getFPURegisterValueLong(fReg),
860 getFPURegisterValueFloat(fReg),
861 getFPURegisterValueDouble(fReg));
862 } else {
863 printf("%s unrecognized\n", arg1);
864 }
865 }
866 } else {
867 printf("print <register> or print <fpu register> single\n");
868 }
869 } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
870 int64_t* cur = nullptr;
871 int64_t* end = nullptr;
872 int next_arg = 1;
873
874 if (strcmp(cmd, "stack") == 0) {
875 cur = reinterpret_cast<int64_t*>(sim_->getRegister(Simulator::sp));
876 } else { // Command "mem".
877 int64_t value;
878 if (!getValue(arg1, &value)) {
879 printf("%s unrecognized\n", arg1);
880 continue;
881 }
882 cur = reinterpret_cast<int64_t*>(value);
883 next_arg++;
884 }
885
886 int64_t words;
887 if (argc == next_arg) {
888 words = 10;
889 } else {
890 if (!getValue(argv[next_arg], &words)) {
891 words = 10;
892 }
893 }
894 end = cur + words;
895
896 while (cur < end) {
897 printf(" %p: 0x%016" PRIx64 " %20" PRIi64, cur, *cur, *cur);
898 printf("\n");
899 cur++;
900 }
901
902 } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) ||
903 (strcmp(cmd, "di") == 0)) {
904 uint8_t* cur = nullptr;
905 uint8_t* end = nullptr;
906
907 if (argc == 1) {
908 cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
909 end = cur + (10 * SimInstruction::kInstrSize);
910 } else if (argc == 2) {
911 Register reg = Register::FromName(arg1);
912 if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) {
913 // The argument is an address or a register name.
914 int64_t value;
915 if (getValue(arg1, &value)) {
916 cur = reinterpret_cast<uint8_t*>(value);
917 // Disassemble 10 instructions at <arg1>.
918 end = cur + (10 * SimInstruction::kInstrSize);
919 }
920 } else {
921 // The argument is the number of instructions.
922 int64_t value;
923 if (getValue(arg1, &value)) {
924 cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
925 // Disassemble <arg1> instructions.
926 end = cur + (value * SimInstruction::kInstrSize);
927 }
928 }
929 } else {
930 int64_t value1;
931 int64_t value2;
932 if (getValue(arg1, &value1) && getValue(arg2, &value2)) {
933 cur = reinterpret_cast<uint8_t*>(value1);
934 end = cur + (value2 * SimInstruction::kInstrSize);
935 }
936 }
937
938 while (cur < end) {
939 DisassembleInstruction(uint64_t(cur));
940 cur += SimInstruction::kInstrSize;
941 }
942 } else if (strcmp(cmd, "gdb") == 0) {
943 printf("relinquishing control to gdb\n");
944 asm("int $3");
945 printf("regaining control from gdb\n");
946 } else if (strcmp(cmd, "break") == 0) {
947 if (argc == 2) {
948 int64_t value;
949 if (getValue(arg1, &value)) {
950 if (!setBreakpoint(reinterpret_cast<SimInstruction*>(value))) {
951 printf("setting breakpoint failed\n");
952 }
953 } else {
954 printf("%s unrecognized\n", arg1);
955 }
956 } else {
957 printf("break <address>\n");
958 }
959 } else if (strcmp(cmd, "del") == 0) {
960 if (!deleteBreakpoint(nullptr)) {
961 printf("deleting breakpoint failed\n");
962 }
963 } else if (strcmp(cmd, "flags") == 0) {
964 printf("No flags on MIPS !\n");
965 } else if (strcmp(cmd, "stop") == 0) {
966 int64_t value;
967 intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize;
968 SimInstruction* stop_instr = reinterpret_cast<SimInstruction*>(stop_pc);
969 SimInstruction* msg_address = reinterpret_cast<SimInstruction*>(
970 stop_pc + SimInstruction::kInstrSize);
971 if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
972 // Remove the current stop.
973 if (sim_->isStopInstruction(stop_instr)) {
974 stop_instr->setInstructionBits(kNopInstr);
975 msg_address->setInstructionBits(kNopInstr);
976 } else {
977 printf("Not at debugger stop.\n");
978 }
979 } else if (argc == 3) {
980 // Print information about all/the specified breakpoint(s).
981 if (strcmp(arg1, "info") == 0) {
982 if (strcmp(arg2, "all") == 0) {
983 printf("Stop information:\n");
984 for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode;
985 i++) {
986 sim_->printStopInfo(i);
987 }
988 } else if (getValue(arg2, &value)) {
989 sim_->printStopInfo(value);
990 } else {
991 printf("Unrecognized argument.\n");
992 }
993 } else if (strcmp(arg1, "enable") == 0) {
994 // Enable all/the specified breakpoint(s).
995 if (strcmp(arg2, "all") == 0) {
996 for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode;
997 i++) {
998 sim_->enableStop(i);
999 }
1000 } else if (getValue(arg2, &value)) {
1001 sim_->enableStop(value);
1002 } else {
1003 printf("Unrecognized argument.\n");
1004 }
1005 } else if (strcmp(arg1, "disable") == 0) {
1006 // Disable all/the specified breakpoint(s).
1007 if (strcmp(arg2, "all") == 0) {
1008 for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode;
1009 i++) {
1010 sim_->disableStop(i);
1011 }
1012 } else if (getValue(arg2, &value)) {
1013 sim_->disableStop(value);
1014 } else {
1015 printf("Unrecognized argument.\n");
1016 }
1017 }
1018 } else {
1019 printf("Wrong usage. Use help command for more information.\n");
1020 }
1021 } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
1022 printf("cont\n");
1023 printf(" continue execution (alias 'c')\n");
1024 printf("stepi\n");
1025 printf(" step one instruction (alias 'si')\n");
1026 printf("print <register>\n");
1027 printf(" print register content (alias 'p')\n");
1028 printf(" use register name 'all' to print all registers\n");
1029 printf("printobject <register>\n");
1030 printf(" print an object from a register (alias 'po')\n");
1031 printf("stack [<words>]\n");
1032 printf(" dump stack content, default dump 10 words)\n");
1033 printf("mem <address> [<words>]\n");
1034 printf(" dump memory content, default dump 10 words)\n");
1035 printf("flags\n");
1036 printf(" print flags\n");
1037 printf("disasm [<instructions>]\n");
1038 printf("disasm [<address/register>]\n");
1039 printf("disasm [[<address/register>] <instructions>]\n");
1040 printf(" disassemble code, default is 10 instructions\n");
1041 printf(" from pc (alias 'di')\n");
1042 printf("gdb\n");
1043 printf(" enter gdb\n");
1044 printf("break <address>\n");
1045 printf(" set a break point on the address\n");
1046 printf("del\n");
1047 printf(" delete the breakpoint\n");
1048 printf("stop feature:\n");
1049 printf(" Description:\n");
1050 printf(" Stops are debug instructions inserted by\n");
1051 printf(" the Assembler::stop() function.\n");
1052 printf(" When hitting a stop, the Simulator will\n");
1053 printf(" stop and and give control to the Debugger.\n");
1054 printf(" All stop codes are watched:\n");
1055 printf(" - They can be enabled / disabled: the Simulator\n");
1056 printf(" will / won't stop when hitting them.\n");
1057 printf(" - The Simulator keeps track of how many times they \n");
1058 printf(" are met. (See the info command.) Going over a\n");
1059 printf(" disabled stop still increases its counter. \n");
1060 printf(" Commands:\n");
1061 printf(" stop info all/<code> : print infos about number <code>\n");
1062 printf(" or all stop(s).\n");
1063 printf(" stop enable/disable all/<code> : enables / disables\n");
1064 printf(" all or number <code> stop(s)\n");
1065 printf(" stop unstop\n");
1066 printf(" ignore the stop instruction at the current location\n");
1067 printf(" from now on\n");
1068 } else {
1069 printf("Unknown command: %s\n", cmd);
1070 }
1071 }
1072 }
1073
1074 // Add all the breakpoints back to stop execution and enter the debugger
1075 // shell when hit.
1076 redoBreakpoints();
1077
1078 #undef COMMAND_SIZE
1079 #undef ARG_SIZE
1080
1081 #undef STR
1082 #undef XSTR
1083 }
1084
AllOnOnePage(uintptr_t start,int size)1085 static bool AllOnOnePage(uintptr_t start, int size) {
1086 intptr_t start_page = (start & ~CachePage::kPageMask);
1087 intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
1088 return start_page == end_page;
1089 }
1090
setLastDebuggerInput(char * input)1091 void Simulator::setLastDebuggerInput(char* input) {
1092 js_free(lastDebuggerInput_);
1093 lastDebuggerInput_ = input;
1094 }
1095
GetCachePageLocked(SimulatorProcess::ICacheMap & i_cache,void * page)1096 static CachePage* GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache,
1097 void* page) {
1098 SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
1099 if (p) {
1100 return p->value();
1101 }
1102 AutoEnterOOMUnsafeRegion oomUnsafe;
1103 CachePage* new_page = js_new<CachePage>();
1104 if (!new_page || !i_cache.add(p, page, new_page)) {
1105 oomUnsafe.crash("Simulator CachePage");
1106 }
1107 return new_page;
1108 }
1109
1110 // Flush from start up to and not including start + size.
FlushOnePageLocked(SimulatorProcess::ICacheMap & i_cache,intptr_t start,int size)1111 static void FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache,
1112 intptr_t start, int size) {
1113 MOZ_ASSERT(size <= CachePage::kPageSize);
1114 MOZ_ASSERT(AllOnOnePage(start, size - 1));
1115 MOZ_ASSERT((start & CachePage::kLineMask) == 0);
1116 MOZ_ASSERT((size & CachePage::kLineMask) == 0);
1117 void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
1118 int offset = (start & CachePage::kPageMask);
1119 CachePage* cache_page = GetCachePageLocked(i_cache, page);
1120 char* valid_bytemap = cache_page->validityByte(offset);
1121 memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
1122 }
1123
FlushICacheLocked(SimulatorProcess::ICacheMap & i_cache,void * start_addr,size_t size)1124 static void FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache,
1125 void* start_addr, size_t size) {
1126 intptr_t start = reinterpret_cast<intptr_t>(start_addr);
1127 int intra_line = (start & CachePage::kLineMask);
1128 start -= intra_line;
1129 size += intra_line;
1130 size = ((size - 1) | CachePage::kLineMask) + 1;
1131 int offset = (start & CachePage::kPageMask);
1132 while (!AllOnOnePage(start, size - 1)) {
1133 int bytes_to_flush = CachePage::kPageSize - offset;
1134 FlushOnePageLocked(i_cache, start, bytes_to_flush);
1135 start += bytes_to_flush;
1136 size -= bytes_to_flush;
1137 MOZ_ASSERT((start & CachePage::kPageMask) == 0);
1138 offset = 0;
1139 }
1140 if (size != 0) {
1141 FlushOnePageLocked(i_cache, start, size);
1142 }
1143 }
1144
1145 /* static */
checkICacheLocked(SimInstruction * instr)1146 void SimulatorProcess::checkICacheLocked(SimInstruction* instr) {
1147 intptr_t address = reinterpret_cast<intptr_t>(instr);
1148 void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
1149 void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
1150 int offset = (address & CachePage::kPageMask);
1151 CachePage* cache_page = GetCachePageLocked(icache(), page);
1152 char* cache_valid_byte = cache_page->validityByte(offset);
1153 bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
1154 char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
1155
1156 if (cache_hit) {
1157 // Check that the data in memory matches the contents of the I-cache.
1158 int cmpret =
1159 memcmp(reinterpret_cast<void*>(instr), cache_page->cachedData(offset),
1160 SimInstruction::kInstrSize);
1161 MOZ_ASSERT(cmpret == 0);
1162 } else {
1163 // Cache miss. Load memory into the cache.
1164 memcpy(cached_line, line, CachePage::kLineLength);
1165 *cache_valid_byte = CachePage::LINE_VALID;
1166 }
1167 }
1168
hash(const Lookup & l)1169 HashNumber SimulatorProcess::ICacheHasher::hash(const Lookup& l) {
1170 return U32(reinterpret_cast<uintptr_t>(l)) >> 2;
1171 }
1172
match(const Key & k,const Lookup & l)1173 bool SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l) {
1174 MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
1175 MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
1176 return k == l;
1177 }
1178
1179 /* static */
FlushICache(void * start_addr,size_t size)1180 void SimulatorProcess::FlushICache(void* start_addr, size_t size) {
1181 if (!ICacheCheckingDisableCount) {
1182 AutoLockSimulatorCache als;
1183 js::jit::FlushICacheLocked(icache(), start_addr, size);
1184 }
1185 }
1186
Simulator()1187 Simulator::Simulator() {
1188 // Set up simulator support first. Some of this information is needed to
1189 // setup the architecture state.
1190
1191 // Note, allocation and anything that depends on allocated memory is
1192 // deferred until init(), in order to handle OOM properly.
1193
1194 stack_ = nullptr;
1195 stackLimit_ = 0;
1196 pc_modified_ = false;
1197 icount_ = 0;
1198 break_count_ = 0;
1199 break_pc_ = nullptr;
1200 break_instr_ = 0;
1201 single_stepping_ = false;
1202 single_step_callback_ = nullptr;
1203 single_step_callback_arg_ = nullptr;
1204
1205 // Set up architecture state.
1206 // All registers are initialized to zero to start with.
1207 for (int i = 0; i < Register::kNumSimuRegisters; i++) {
1208 registers_[i] = 0;
1209 }
1210 for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) {
1211 FPUregisters_[i] = 0;
1212 }
1213 FCSR_ = 0;
1214 LLBit_ = false;
1215 LLAddr_ = 0;
1216 lastLLValue_ = 0;
1217
1218 // The ra and pc are initialized to a known bad value that will cause an
1219 // access violation if the simulator ever tries to execute it.
1220 registers_[pc] = bad_ra;
1221 registers_[ra] = bad_ra;
1222
1223 for (int i = 0; i < kNumExceptions; i++) {
1224 exceptions[i] = 0;
1225 }
1226
1227 lastDebuggerInput_ = nullptr;
1228 }
1229
init()1230 bool Simulator::init() {
1231 // Allocate 2MB for the stack. Note that we will only use 1MB, see below.
1232 static const size_t stackSize = 2 * 1024 * 1024;
1233 stack_ = js_pod_malloc<char>(stackSize);
1234 if (!stack_) {
1235 return false;
1236 }
1237
1238 // Leave a safety margin of 1MB to prevent overrunning the stack when
1239 // pushing values (total stack size is 2MB).
1240 stackLimit_ = reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024;
1241
1242 // The sp is initialized to point to the bottom (high address) of the
1243 // allocated stack area. To be safe in potential stack underflows we leave
1244 // some buffer below.
1245 registers_[sp] = reinterpret_cast<int64_t>(stack_) + stackSize - 64;
1246
1247 return true;
1248 }
1249
1250 // When the generated code calls an external reference we need to catch that in
1251 // the simulator. The external reference will be a function compiled for the
1252 // host architecture. We need to call that function instead of trying to
1253 // execute it with the simulator. We do that by redirecting the external
1254 // reference to a swi (software-interrupt) instruction that is handled by
1255 // the simulator. We write the original destination of the jump just at a known
1256 // offset from the swi instruction so the simulator knows what to call.
1257 class Redirection {
1258 friend class SimulatorProcess;
1259
1260 // sim's lock must already be held.
Redirection(void * nativeFunction,ABIFunctionType type)1261 Redirection(void* nativeFunction, ABIFunctionType type)
1262 : nativeFunction_(nativeFunction),
1263 swiInstruction_(kCallRedirInstr),
1264 type_(type),
1265 next_(nullptr) {
1266 next_ = SimulatorProcess::redirection();
1267 if (!SimulatorProcess::ICacheCheckingDisableCount) {
1268 FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
1269 SimInstruction::kInstrSize);
1270 }
1271 SimulatorProcess::setRedirection(this);
1272 }
1273
1274 public:
addressOfSwiInstruction()1275 void* addressOfSwiInstruction() { return &swiInstruction_; }
nativeFunction() const1276 void* nativeFunction() const { return nativeFunction_; }
type() const1277 ABIFunctionType type() const { return type_; }
1278
Get(void * nativeFunction,ABIFunctionType type)1279 static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
1280 AutoLockSimulatorCache als;
1281
1282 Redirection* current = SimulatorProcess::redirection();
1283 for (; current != nullptr; current = current->next_) {
1284 if (current->nativeFunction_ == nativeFunction) {
1285 MOZ_ASSERT(current->type() == type);
1286 return current;
1287 }
1288 }
1289
1290 // Note: we can't use js_new here because the constructor is private.
1291 AutoEnterOOMUnsafeRegion oomUnsafe;
1292 Redirection* redir = js_pod_malloc<Redirection>(1);
1293 if (!redir) {
1294 oomUnsafe.crash("Simulator redirection");
1295 }
1296 new (redir) Redirection(nativeFunction, type);
1297 return redir;
1298 }
1299
FromSwiInstruction(SimInstruction * swiInstruction)1300 static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
1301 uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
1302 uint8_t* addrOfRedirection =
1303 addrOfSwi - offsetof(Redirection, swiInstruction_);
1304 return reinterpret_cast<Redirection*>(addrOfRedirection);
1305 }
1306
1307 private:
1308 void* nativeFunction_;
1309 uint32_t swiInstruction_;
1310 ABIFunctionType type_;
1311 Redirection* next_;
1312 };
1313
~Simulator()1314 Simulator::~Simulator() { js_free(stack_); }
1315
SimulatorProcess()1316 SimulatorProcess::SimulatorProcess()
1317 : cacheLock_(mutexid::SimulatorCacheLock), redirection_(nullptr) {
1318 if (getenv("MIPS_SIM_ICACHE_CHECKS")) {
1319 ICacheCheckingDisableCount = 0;
1320 }
1321 }
1322
~SimulatorProcess()1323 SimulatorProcess::~SimulatorProcess() {
1324 Redirection* r = redirection_;
1325 while (r) {
1326 Redirection* next = r->next_;
1327 js_delete(r);
1328 r = next;
1329 }
1330 }
1331
1332 /* static */
RedirectNativeFunction(void * nativeFunction,ABIFunctionType type)1333 void* Simulator::RedirectNativeFunction(void* nativeFunction,
1334 ABIFunctionType type) {
1335 Redirection* redirection = Redirection::Get(nativeFunction, type);
1336 return redirection->addressOfSwiInstruction();
1337 }
1338
1339 // Get the active Simulator for the current thread.
Current()1340 Simulator* Simulator::Current() {
1341 JSContext* cx = TlsContext.get();
1342 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1343 return cx->simulator();
1344 }
1345
1346 // Sets the register in the architecture state. It will also deal with updating
1347 // Simulator internal state for special registers such as PC.
setRegister(int reg,int64_t value)1348 void Simulator::setRegister(int reg, int64_t value) {
1349 MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters));
1350 if (reg == pc) {
1351 pc_modified_ = true;
1352 }
1353
1354 // Zero register always holds 0.
1355 registers_[reg] = (reg == 0) ? 0 : value;
1356 }
1357
setFpuRegister(int fpureg,int64_t value)1358 void Simulator::setFpuRegister(int fpureg, int64_t value) {
1359 MOZ_ASSERT((fpureg >= 0) &&
1360 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1361 FPUregisters_[fpureg] = value;
1362 }
1363
setFpuRegisterLo(int fpureg,int32_t value)1364 void Simulator::setFpuRegisterLo(int fpureg, int32_t value) {
1365 MOZ_ASSERT((fpureg >= 0) &&
1366 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1367 *mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]) = value;
1368 }
1369
setFpuRegisterHi(int fpureg,int32_t value)1370 void Simulator::setFpuRegisterHi(int fpureg, int32_t value) {
1371 MOZ_ASSERT((fpureg >= 0) &&
1372 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1373 *((mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg])) + 1) = value;
1374 }
1375
setFpuRegisterFloat(int fpureg,float value)1376 void Simulator::setFpuRegisterFloat(int fpureg, float value) {
1377 MOZ_ASSERT((fpureg >= 0) &&
1378 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1379 *mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]) = value;
1380 }
1381
setFpuRegisterDouble(int fpureg,double value)1382 void Simulator::setFpuRegisterDouble(int fpureg, double value) {
1383 MOZ_ASSERT((fpureg >= 0) &&
1384 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1385 *mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]) = value;
1386 }
1387
1388 // Get the register from the architecture state. This function does handle
1389 // the special case of accessing the PC register.
getRegister(int reg) const1390 int64_t Simulator::getRegister(int reg) const {
1391 MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters));
1392 if (reg == 0) {
1393 return 0;
1394 }
1395 return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0);
1396 }
1397
getFpuRegister(int fpureg) const1398 int64_t Simulator::getFpuRegister(int fpureg) const {
1399 MOZ_ASSERT((fpureg >= 0) &&
1400 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1401 return FPUregisters_[fpureg];
1402 }
1403
getFpuRegisterLo(int fpureg) const1404 int32_t Simulator::getFpuRegisterLo(int fpureg) const {
1405 MOZ_ASSERT((fpureg >= 0) &&
1406 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1407 return *mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]);
1408 }
1409
getFpuRegisterHi(int fpureg) const1410 int32_t Simulator::getFpuRegisterHi(int fpureg) const {
1411 MOZ_ASSERT((fpureg >= 0) &&
1412 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1413 return *((mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg])) + 1);
1414 }
1415
getFpuRegisterFloat(int fpureg) const1416 float Simulator::getFpuRegisterFloat(int fpureg) const {
1417 MOZ_ASSERT((fpureg >= 0) &&
1418 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1419 return *mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]);
1420 }
1421
getFpuRegisterDouble(int fpureg) const1422 double Simulator::getFpuRegisterDouble(int fpureg) const {
1423 MOZ_ASSERT((fpureg >= 0) &&
1424 (fpureg < Simulator::FPURegister::kNumFPURegisters));
1425 return *mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]);
1426 }
1427
setCallResultDouble(double result)1428 void Simulator::setCallResultDouble(double result) {
1429 setFpuRegisterDouble(f0, result);
1430 }
1431
setCallResultFloat(float result)1432 void Simulator::setCallResultFloat(float result) {
1433 setFpuRegisterFloat(f0, result);
1434 }
1435
setCallResult(int64_t res)1436 void Simulator::setCallResult(int64_t res) { setRegister(v0, res); }
1437
setCallResult(__int128_t res)1438 void Simulator::setCallResult(__int128_t res) {
1439 setRegister(v0, I64(res));
1440 setRegister(v1, I64(res >> 64));
1441 }
1442
1443 // Helper functions for setting and testing the FCSR register's bits.
setFCSRBit(uint32_t cc,bool value)1444 void Simulator::setFCSRBit(uint32_t cc, bool value) {
1445 if (value) {
1446 FCSR_ |= (1 << cc);
1447 } else {
1448 FCSR_ &= ~(1 << cc);
1449 }
1450 }
1451
testFCSRBit(uint32_t cc)1452 bool Simulator::testFCSRBit(uint32_t cc) { return FCSR_ & (1 << cc); }
1453
1454 // Sets the rounding error codes in FCSR based on the result of the rounding.
1455 // Returns true if the operation was invalid.
1456 template <typename T>
setFCSRRoundError(double original,double rounded)1457 bool Simulator::setFCSRRoundError(double original, double rounded) {
1458 bool ret = false;
1459
1460 setFCSRBit(kFCSRInexactCauseBit, false);
1461 setFCSRBit(kFCSRUnderflowCauseBit, false);
1462 setFCSRBit(kFCSROverflowCauseBit, false);
1463 setFCSRBit(kFCSRInvalidOpCauseBit, false);
1464
1465 if (!std::isfinite(original) || !std::isfinite(rounded)) {
1466 setFCSRBit(kFCSRInvalidOpFlagBit, true);
1467 setFCSRBit(kFCSRInvalidOpCauseBit, true);
1468 ret = true;
1469 }
1470
1471 if (original != rounded) {
1472 setFCSRBit(kFCSRInexactFlagBit, true);
1473 setFCSRBit(kFCSRInexactCauseBit, true);
1474 }
1475
1476 if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
1477 setFCSRBit(kFCSRUnderflowFlagBit, true);
1478 setFCSRBit(kFCSRUnderflowCauseBit, true);
1479 ret = true;
1480 }
1481
1482 if ((long double)rounded > (long double)std::numeric_limits<T>::max() ||
1483 (long double)rounded < (long double)std::numeric_limits<T>::min()) {
1484 setFCSRBit(kFCSROverflowFlagBit, true);
1485 setFCSRBit(kFCSROverflowCauseBit, true);
1486 // The reference is not really clear but it seems this is required:
1487 setFCSRBit(kFCSRInvalidOpFlagBit, true);
1488 setFCSRBit(kFCSRInvalidOpCauseBit, true);
1489 ret = true;
1490 }
1491
1492 return ret;
1493 }
1494
1495 // Raw access to the PC register.
set_pc(int64_t value)1496 void Simulator::set_pc(int64_t value) {
1497 pc_modified_ = true;
1498 registers_[pc] = value;
1499 }
1500
has_bad_pc() const1501 bool Simulator::has_bad_pc() const {
1502 return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc));
1503 }
1504
1505 // Raw access to the PC register without the special adjustment when reading.
get_pc() const1506 int64_t Simulator::get_pc() const { return registers_[pc]; }
1507
registerState()1508 JS::ProfilingFrameIterator::RegisterState Simulator::registerState() {
1509 wasm::RegisterState state;
1510 state.pc = (void*)get_pc();
1511 state.fp = (void*)getRegister(fp);
1512 state.sp = (void*)getRegister(sp);
1513 state.lr = (void*)getRegister(ra);
1514 return state;
1515 }
1516
1517 // MIPS memory instructions (except lw(d)l/r , sw(d)l/r) trap on unaligned
1518 // memory access enabling the OS to handle them via trap-and-emulate. Note that
1519 // simulator runs have the runtime system running directly on the host system
1520 // and only generated code is executed in the simulator. Since the host is
1521 // typically IA32 it will not trap on unaligned memory access. We assume that
1522 // that executing correct generated code will not produce unaligned memory
1523 // access, so we explicitly check for address alignment and trap. Note that
1524 // trapping does not occur when executing wasm code, which requires that
1525 // unaligned memory access provides correct result.
1526
readBU(uint64_t addr,SimInstruction * instr)1527 uint8_t Simulator::readBU(uint64_t addr, SimInstruction* instr) {
1528 if (handleWasmSegFault(addr, 1)) {
1529 return 0xff;
1530 }
1531
1532 uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
1533 return *ptr;
1534 }
1535
readB(uint64_t addr,SimInstruction * instr)1536 int8_t Simulator::readB(uint64_t addr, SimInstruction* instr) {
1537 if (handleWasmSegFault(addr, 1)) {
1538 return -1;
1539 }
1540
1541 int8_t* ptr = reinterpret_cast<int8_t*>(addr);
1542 return *ptr;
1543 }
1544
writeB(uint64_t addr,uint8_t value,SimInstruction * instr)1545 void Simulator::writeB(uint64_t addr, uint8_t value, SimInstruction* instr) {
1546 if (handleWasmSegFault(addr, 1)) {
1547 return;
1548 }
1549
1550 uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
1551 *ptr = value;
1552 }
1553
writeB(uint64_t addr,int8_t value,SimInstruction * instr)1554 void Simulator::writeB(uint64_t addr, int8_t value, SimInstruction* instr) {
1555 if (handleWasmSegFault(addr, 1)) {
1556 return;
1557 }
1558
1559 int8_t* ptr = reinterpret_cast<int8_t*>(addr);
1560 *ptr = value;
1561 }
1562
readHU(uint64_t addr,SimInstruction * instr)1563 uint16_t Simulator::readHU(uint64_t addr, SimInstruction* instr) {
1564 if (handleWasmSegFault(addr, 2)) {
1565 return 0xffff;
1566 }
1567
1568 if ((addr & 1) == 0 ||
1569 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1570 uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
1571 return *ptr;
1572 }
1573 printf("Unaligned unsigned halfword read at 0x%016" PRIx64
1574 ", pc=0x%016" PRIxPTR "\n",
1575 addr, reinterpret_cast<intptr_t>(instr));
1576 MOZ_CRASH();
1577 return 0;
1578 }
1579
readH(uint64_t addr,SimInstruction * instr)1580 int16_t Simulator::readH(uint64_t addr, SimInstruction* instr) {
1581 if (handleWasmSegFault(addr, 2)) {
1582 return -1;
1583 }
1584
1585 if ((addr & 1) == 0 ||
1586 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1587 int16_t* ptr = reinterpret_cast<int16_t*>(addr);
1588 return *ptr;
1589 }
1590 printf("Unaligned signed halfword read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR
1591 "\n",
1592 addr, reinterpret_cast<intptr_t>(instr));
1593 MOZ_CRASH();
1594 return 0;
1595 }
1596
writeH(uint64_t addr,uint16_t value,SimInstruction * instr)1597 void Simulator::writeH(uint64_t addr, uint16_t value, SimInstruction* instr) {
1598 if (handleWasmSegFault(addr, 2)) {
1599 return;
1600 }
1601
1602 if ((addr & 1) == 0 ||
1603 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1604 uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
1605 LLBit_ = false;
1606 *ptr = value;
1607 return;
1608 }
1609 printf("Unaligned unsigned halfword write at 0x%016" PRIx64
1610 ", pc=0x%016" PRIxPTR "\n",
1611 addr, reinterpret_cast<intptr_t>(instr));
1612 MOZ_CRASH();
1613 }
1614
writeH(uint64_t addr,int16_t value,SimInstruction * instr)1615 void Simulator::writeH(uint64_t addr, int16_t value, SimInstruction* instr) {
1616 if (handleWasmSegFault(addr, 2)) {
1617 return;
1618 }
1619
1620 if ((addr & 1) == 0 ||
1621 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1622 int16_t* ptr = reinterpret_cast<int16_t*>(addr);
1623 LLBit_ = false;
1624 *ptr = value;
1625 return;
1626 }
1627 printf("Unaligned halfword write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
1628 addr, reinterpret_cast<intptr_t>(instr));
1629 MOZ_CRASH();
1630 }
1631
readWU(uint64_t addr,SimInstruction * instr)1632 uint32_t Simulator::readWU(uint64_t addr, SimInstruction* instr) {
1633 if (handleWasmSegFault(addr, 4)) {
1634 return -1;
1635 }
1636
1637 if ((addr & 3) == 0 ||
1638 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1639 uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
1640 return *ptr;
1641 }
1642 printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1643 reinterpret_cast<intptr_t>(instr));
1644 MOZ_CRASH();
1645 return 0;
1646 }
1647
readW(uint64_t addr,SimInstruction * instr)1648 int32_t Simulator::readW(uint64_t addr, SimInstruction* instr) {
1649 if (handleWasmSegFault(addr, 4)) {
1650 return -1;
1651 }
1652
1653 if ((addr & 3) == 0 ||
1654 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1655 int32_t* ptr = reinterpret_cast<int32_t*>(addr);
1656 return *ptr;
1657 }
1658 printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1659 reinterpret_cast<intptr_t>(instr));
1660 MOZ_CRASH();
1661 return 0;
1662 }
1663
writeW(uint64_t addr,uint32_t value,SimInstruction * instr)1664 void Simulator::writeW(uint64_t addr, uint32_t value, SimInstruction* instr) {
1665 if (handleWasmSegFault(addr, 4)) {
1666 return;
1667 }
1668
1669 if ((addr & 3) == 0 ||
1670 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1671 uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
1672 LLBit_ = false;
1673 *ptr = value;
1674 return;
1675 }
1676 printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1677 reinterpret_cast<intptr_t>(instr));
1678 MOZ_CRASH();
1679 }
1680
writeW(uint64_t addr,int32_t value,SimInstruction * instr)1681 void Simulator::writeW(uint64_t addr, int32_t value, SimInstruction* instr) {
1682 if (handleWasmSegFault(addr, 4)) {
1683 return;
1684 }
1685
1686 if ((addr & 3) == 0 ||
1687 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1688 int32_t* ptr = reinterpret_cast<int32_t*>(addr);
1689 LLBit_ = false;
1690 *ptr = value;
1691 return;
1692 }
1693 printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1694 reinterpret_cast<intptr_t>(instr));
1695 MOZ_CRASH();
1696 }
1697
readDW(uint64_t addr,SimInstruction * instr)1698 int64_t Simulator::readDW(uint64_t addr, SimInstruction* instr) {
1699 if (handleWasmSegFault(addr, 8)) {
1700 return -1;
1701 }
1702
1703 if ((addr & kPointerAlignmentMask) == 0 ||
1704 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1705 intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
1706 return *ptr;
1707 }
1708 printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1709 reinterpret_cast<intptr_t>(instr));
1710 MOZ_CRASH();
1711 return 0;
1712 }
1713
writeDW(uint64_t addr,int64_t value,SimInstruction * instr)1714 void Simulator::writeDW(uint64_t addr, int64_t value, SimInstruction* instr) {
1715 if (handleWasmSegFault(addr, 8)) {
1716 return;
1717 }
1718
1719 if ((addr & kPointerAlignmentMask) == 0 ||
1720 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1721 int64_t* ptr = reinterpret_cast<int64_t*>(addr);
1722 LLBit_ = false;
1723 *ptr = value;
1724 return;
1725 }
1726 printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1727 reinterpret_cast<intptr_t>(instr));
1728 MOZ_CRASH();
1729 }
1730
readD(uint64_t addr,SimInstruction * instr)1731 double Simulator::readD(uint64_t addr, SimInstruction* instr) {
1732 if (handleWasmSegFault(addr, 8)) {
1733 return NAN;
1734 }
1735
1736 if ((addr & kDoubleAlignmentMask) == 0 ||
1737 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1738 double* ptr = reinterpret_cast<double*>(addr);
1739 return *ptr;
1740 }
1741 printf("Unaligned (double) read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
1742 addr, reinterpret_cast<intptr_t>(instr));
1743 MOZ_CRASH();
1744 return 0;
1745 }
1746
writeD(uint64_t addr,double value,SimInstruction * instr)1747 void Simulator::writeD(uint64_t addr, double value, SimInstruction* instr) {
1748 if (handleWasmSegFault(addr, 8)) {
1749 return;
1750 }
1751
1752 if ((addr & kDoubleAlignmentMask) == 0 ||
1753 wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
1754 double* ptr = reinterpret_cast<double*>(addr);
1755 LLBit_ = false;
1756 *ptr = value;
1757 return;
1758 }
1759 printf("Unaligned (double) write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
1760 addr, reinterpret_cast<intptr_t>(instr));
1761 MOZ_CRASH();
1762 }
1763
loadLinkedW(uint64_t addr,SimInstruction * instr)1764 int Simulator::loadLinkedW(uint64_t addr, SimInstruction* instr) {
1765 if ((addr & 3) == 0) {
1766 if (handleWasmSegFault(addr, 4)) {
1767 return -1;
1768 }
1769
1770 volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(addr);
1771 int32_t value = *ptr;
1772 lastLLValue_ = value;
1773 LLAddr_ = addr;
1774 // Note that any memory write or "external" interrupt should reset this
1775 // value to false.
1776 LLBit_ = true;
1777 return value;
1778 }
1779 printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1780 reinterpret_cast<intptr_t>(instr));
1781 MOZ_CRASH();
1782 return 0;
1783 }
1784
storeConditionalW(uint64_t addr,int value,SimInstruction * instr)1785 int Simulator::storeConditionalW(uint64_t addr, int value,
1786 SimInstruction* instr) {
1787 // Correct behavior in this case, as defined by architecture, is to just
1788 // return 0, but there is no point at allowing that. It is certainly an
1789 // indicator of a bug.
1790 if (addr != LLAddr_) {
1791 printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64
1792 ", expected: 0x%016" PRIx64 "\n",
1793 addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
1794 MOZ_CRASH();
1795 }
1796
1797 if ((addr & 3) == 0) {
1798 SharedMem<int32_t*> ptr =
1799 SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
1800
1801 if (!LLBit_) {
1802 return 0;
1803 }
1804
1805 LLBit_ = false;
1806 LLAddr_ = 0;
1807 int32_t expected = int32_t(lastLLValue_);
1808 int32_t old =
1809 AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value));
1810 return (old == expected) ? 1 : 0;
1811 }
1812 printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1813 reinterpret_cast<intptr_t>(instr));
1814 MOZ_CRASH();
1815 return 0;
1816 }
1817
loadLinkedD(uint64_t addr,SimInstruction * instr)1818 int64_t Simulator::loadLinkedD(uint64_t addr, SimInstruction* instr) {
1819 if ((addr & kPointerAlignmentMask) == 0) {
1820 if (handleWasmSegFault(addr, 8)) {
1821 return -1;
1822 }
1823
1824 volatile int64_t* ptr = reinterpret_cast<volatile int64_t*>(addr);
1825 int64_t value = *ptr;
1826 lastLLValue_ = value;
1827 LLAddr_ = addr;
1828 // Note that any memory write or "external" interrupt should reset this
1829 // value to false.
1830 LLBit_ = true;
1831 return value;
1832 }
1833 printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1834 reinterpret_cast<intptr_t>(instr));
1835 MOZ_CRASH();
1836 return 0;
1837 }
1838
storeConditionalD(uint64_t addr,int64_t value,SimInstruction * instr)1839 int Simulator::storeConditionalD(uint64_t addr, int64_t value,
1840 SimInstruction* instr) {
1841 // Correct behavior in this case, as defined by architecture, is to just
1842 // return 0, but there is no point at allowing that. It is certainly an
1843 // indicator of a bug.
1844 if (addr != LLAddr_) {
1845 printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64
1846 ", expected: 0x%016" PRIx64 "\n",
1847 addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
1848 MOZ_CRASH();
1849 }
1850
1851 if ((addr & kPointerAlignmentMask) == 0) {
1852 SharedMem<int64_t*> ptr =
1853 SharedMem<int64_t*>::shared(reinterpret_cast<int64_t*>(addr));
1854
1855 if (!LLBit_) {
1856 return 0;
1857 }
1858
1859 LLBit_ = false;
1860 LLAddr_ = 0;
1861 int64_t expected = lastLLValue_;
1862 int64_t old =
1863 AtomicOperations::compareExchangeSeqCst(ptr, expected, int64_t(value));
1864 return (old == expected) ? 1 : 0;
1865 }
1866 printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
1867 reinterpret_cast<intptr_t>(instr));
1868 MOZ_CRASH();
1869 return 0;
1870 }
1871
stackLimit() const1872 uintptr_t Simulator::stackLimit() const { return stackLimit_; }
1873
addressOfStackLimit()1874 uintptr_t* Simulator::addressOfStackLimit() { return &stackLimit_; }
1875
overRecursed(uintptr_t newsp) const1876 bool Simulator::overRecursed(uintptr_t newsp) const {
1877 if (newsp == 0) {
1878 newsp = getRegister(sp);
1879 }
1880 return newsp <= stackLimit();
1881 }
1882
overRecursedWithExtra(uint32_t extra) const1883 bool Simulator::overRecursedWithExtra(uint32_t extra) const {
1884 uintptr_t newsp = getRegister(sp) - extra;
1885 return newsp <= stackLimit();
1886 }
1887
1888 // Unsupported instructions use format to print an error and stop execution.
format(SimInstruction * instr,const char * format)1889 void Simulator::format(SimInstruction* instr, const char* format) {
1890 printf("Simulator found unsupported instruction:\n 0x%016lx: %s\n",
1891 reinterpret_cast<intptr_t>(instr), format);
1892 MOZ_CRASH();
1893 }
1894
1895 // Note: With the code below we assume that all runtime calls return a 64 bits
1896 // result. If they don't, the v1 result register contains a bogus value, which
1897 // is fine because it is caller-saved.
1898 typedef int64_t (*Prototype_General0)();
1899 typedef int64_t (*Prototype_General1)(int64_t arg0);
1900 typedef int64_t (*Prototype_General2)(int64_t arg0, int64_t arg1);
1901 typedef int64_t (*Prototype_General3)(int64_t arg0, int64_t arg1, int64_t arg2);
1902 typedef int64_t (*Prototype_General4)(int64_t arg0, int64_t arg1, int64_t arg2,
1903 int64_t arg3);
1904 typedef int64_t (*Prototype_General5)(int64_t arg0, int64_t arg1, int64_t arg2,
1905 int64_t arg3, int64_t arg4);
1906 typedef int64_t (*Prototype_General6)(int64_t arg0, int64_t arg1, int64_t arg2,
1907 int64_t arg3, int64_t arg4, int64_t arg5);
1908 typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2,
1909 int64_t arg3, int64_t arg4, int64_t arg5,
1910 int64_t arg6);
1911 typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2,
1912 int64_t arg3, int64_t arg4, int64_t arg5,
1913 int64_t arg6, int64_t arg7);
1914 typedef int32_t (*Prototype_Int_GeneralGeneralGeneralInt64)(int64_t arg0,
1915 int64_t arg1,
1916 int64_t arg2,
1917 int64_t arg3);
1918 typedef int32_t (*Prototype_Int_GeneralGeneralInt64Int64)(int64_t arg0,
1919 int64_t arg1,
1920 int64_t arg2,
1921 int64_t arg3);
1922 typedef double (*Prototype_Double_None)();
1923 typedef double (*Prototype_Double_Double)(double arg0);
1924 typedef double (*Prototype_Double_Int)(int64_t arg0);
1925 typedef int64_t (*Prototype_Int_Double)(double arg0);
1926 typedef int64_t (*Prototype_Int_DoubleIntInt)(double arg0, int64_t arg1,
1927 int64_t arg2);
1928 typedef int64_t (*Prototype_Int_IntDoubleIntInt)(int64_t arg0, double arg1,
1929 int64_t arg2, int64_t arg3);
1930 typedef float (*Prototype_Float32_Float32)(float arg0);
1931 typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
1932 typedef float (*Prototype_Float32_IntInt)(int64_t arg0, int64_t arg1);
1933
1934 typedef double (*Prototype_Double_DoubleInt)(double arg0, int64_t arg1);
1935 typedef double (*Prototype_Double_IntDouble)(int64_t arg0, double arg1);
1936 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
1937 typedef int64_t (*Prototype_Int_IntDouble)(int64_t arg0, double arg1);
1938
1939 typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1,
1940 double arg2);
1941 typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0,
1942 double arg1,
1943 double arg2,
1944 double arg3);
1945
1946 // Software interrupt instructions are used by the simulator to call into C++.
softwareInterrupt(SimInstruction * instr)1947 void Simulator::softwareInterrupt(SimInstruction* instr) {
1948 int32_t func = instr->functionFieldRaw();
1949 uint32_t code = (func == ff_break) ? instr->bits(25, 6) : -1;
1950
1951 // We first check if we met a call_rt_redirected.
1952 if (instr->instructionBits() == kCallRedirInstr) {
1953 #if !defined(USES_N64_ABI)
1954 MOZ_CRASH("Only N64 ABI supported.");
1955 #else
1956 Redirection* redirection = Redirection::FromSwiInstruction(instr);
1957 int64_t arg0 = getRegister(a0);
1958 int64_t arg1 = getRegister(a1);
1959 int64_t arg2 = getRegister(a2);
1960 int64_t arg3 = getRegister(a3);
1961 int64_t arg4 = getRegister(a4);
1962 int64_t arg5 = getRegister(a5);
1963
1964 // This is dodgy but it works because the C entry stubs are never moved.
1965 // See comment in codegen-arm.cc and bug 1242173.
1966 int64_t saved_ra = getRegister(ra);
1967
1968 intptr_t external =
1969 reinterpret_cast<intptr_t>(redirection->nativeFunction());
1970
1971 bool stack_aligned = (getRegister(sp) & (ABIStackAlignment - 1)) == 0;
1972 if (!stack_aligned) {
1973 fprintf(stderr, "Runtime call with unaligned stack!\n");
1974 MOZ_CRASH();
1975 }
1976
1977 if (single_stepping_) {
1978 single_step_callback_(single_step_callback_arg_, this, nullptr);
1979 }
1980
1981 switch (redirection->type()) {
1982 case Args_General0: {
1983 Prototype_General0 target =
1984 reinterpret_cast<Prototype_General0>(external);
1985 int64_t result = target();
1986 setCallResult(result);
1987 break;
1988 }
1989 case Args_General1: {
1990 Prototype_General1 target =
1991 reinterpret_cast<Prototype_General1>(external);
1992 int64_t result = target(arg0);
1993 setCallResult(result);
1994 break;
1995 }
1996 case Args_General2: {
1997 Prototype_General2 target =
1998 reinterpret_cast<Prototype_General2>(external);
1999 int64_t result = target(arg0, arg1);
2000 setCallResult(result);
2001 break;
2002 }
2003 case Args_General3: {
2004 Prototype_General3 target =
2005 reinterpret_cast<Prototype_General3>(external);
2006 int64_t result = target(arg0, arg1, arg2);
2007 if (external == intptr_t(&js::wasm::Instance::wake)) {
2008 result = int32_t(result);
2009 }
2010 setCallResult(result);
2011 break;
2012 }
2013 case Args_General4: {
2014 Prototype_General4 target =
2015 reinterpret_cast<Prototype_General4>(external);
2016 int64_t result = target(arg0, arg1, arg2, arg3);
2017 setCallResult(result);
2018 break;
2019 }
2020 case Args_General5: {
2021 Prototype_General5 target =
2022 reinterpret_cast<Prototype_General5>(external);
2023 int64_t result = target(arg0, arg1, arg2, arg3, arg4);
2024 setCallResult(result);
2025 break;
2026 }
2027 case Args_General6: {
2028 Prototype_General6 target =
2029 reinterpret_cast<Prototype_General6>(external);
2030 int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
2031 setCallResult(result);
2032 break;
2033 }
2034 case Args_General7: {
2035 Prototype_General7 target =
2036 reinterpret_cast<Prototype_General7>(external);
2037 int64_t arg6 = getRegister(a6);
2038 int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
2039 setCallResult(result);
2040 break;
2041 }
2042 case Args_General8: {
2043 Prototype_General8 target =
2044 reinterpret_cast<Prototype_General8>(external);
2045 int64_t arg6 = getRegister(a6);
2046 int64_t arg7 = getRegister(a7);
2047 int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
2048 setCallResult(result);
2049 break;
2050 }
2051 case Args_Double_None: {
2052 Prototype_Double_None target =
2053 reinterpret_cast<Prototype_Double_None>(external);
2054 double dresult = target();
2055 setCallResultDouble(dresult);
2056 break;
2057 }
2058 case Args_Int_Double: {
2059 double dval0 = getFpuRegisterDouble(12);
2060 Prototype_Int_Double target =
2061 reinterpret_cast<Prototype_Int_Double>(external);
2062 int64_t result = target(dval0);
2063 if (external == intptr_t((int32_t(*)(double))JS::ToInt32)) {
2064 result = int32_t(result);
2065 }
2066 setRegister(v0, result);
2067 break;
2068 }
2069 case Args_Int_GeneralGeneralGeneralInt64: {
2070 Prototype_Int_GeneralGeneralGeneralInt64 target =
2071 reinterpret_cast<Prototype_Int_GeneralGeneralGeneralInt64>(
2072 external);
2073 int64_t result = target(arg0, arg1, arg2, arg3);
2074 if (external == intptr_t(&js::wasm::Instance::wait_i32)) {
2075 result = int32_t(result);
2076 }
2077 setRegister(v0, result);
2078 break;
2079 }
2080 case Args_Int_GeneralGeneralInt64Int64: {
2081 Prototype_Int_GeneralGeneralInt64Int64 target =
2082 reinterpret_cast<Prototype_Int_GeneralGeneralInt64Int64>(external);
2083 int64_t result = target(arg0, arg1, arg2, arg3);
2084 if (external == intptr_t(&js::wasm::Instance::wait_i64)) {
2085 result = int32_t(result);
2086 }
2087 setRegister(v0, result);
2088 break;
2089 }
2090 case Args_Int_DoubleIntInt: {
2091 double dval = getFpuRegisterDouble(12);
2092 Prototype_Int_DoubleIntInt target =
2093 reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
2094 int64_t result = target(dval, arg1, arg2);
2095 setRegister(v0, result);
2096 break;
2097 }
2098 case Args_Int_IntDoubleIntInt: {
2099 double dval = getFpuRegisterDouble(13);
2100 Prototype_Int_IntDoubleIntInt target =
2101 reinterpret_cast<Prototype_Int_IntDoubleIntInt>(external);
2102 int64_t result = target(arg0, dval, arg2, arg3);
2103 setRegister(v0, result);
2104 break;
2105 }
2106 case Args_Double_Double: {
2107 double dval0 = getFpuRegisterDouble(12);
2108 Prototype_Double_Double target =
2109 reinterpret_cast<Prototype_Double_Double>(external);
2110 double dresult = target(dval0);
2111 setCallResultDouble(dresult);
2112 break;
2113 }
2114 case Args_Float32_Float32: {
2115 float fval0;
2116 fval0 = getFpuRegisterFloat(12);
2117 Prototype_Float32_Float32 target =
2118 reinterpret_cast<Prototype_Float32_Float32>(external);
2119 float fresult = target(fval0);
2120 setCallResultFloat(fresult);
2121 break;
2122 }
2123 case Args_Float32_Float32Float32: {
2124 float fval0;
2125 float fval1;
2126 fval0 = getFpuRegisterFloat(12);
2127 fval1 = getFpuRegisterFloat(13);
2128 Prototype_Float32_Float32Float32 target =
2129 reinterpret_cast<Prototype_Float32_Float32Float32>(external);
2130 float fresult = target(fval0, fval1);
2131 setCallResultFloat(fresult);
2132 break;
2133 }
2134 case Args_Float32_IntInt: {
2135 Prototype_Float32_IntInt target =
2136 reinterpret_cast<Prototype_Float32_IntInt>(external);
2137 float fresult = target(arg0, arg1);
2138 setCallResultFloat(fresult);
2139 break;
2140 }
2141 case Args_Double_Int: {
2142 Prototype_Double_Int target =
2143 reinterpret_cast<Prototype_Double_Int>(external);
2144 double dresult = target(arg0);
2145 setCallResultDouble(dresult);
2146 break;
2147 }
2148 case Args_Double_DoubleInt: {
2149 double dval0 = getFpuRegisterDouble(12);
2150 Prototype_Double_DoubleInt target =
2151 reinterpret_cast<Prototype_Double_DoubleInt>(external);
2152 double dresult = target(dval0, arg1);
2153 setCallResultDouble(dresult);
2154 break;
2155 }
2156 case Args_Double_DoubleDouble: {
2157 double dval0 = getFpuRegisterDouble(12);
2158 double dval1 = getFpuRegisterDouble(13);
2159 Prototype_Double_DoubleDouble target =
2160 reinterpret_cast<Prototype_Double_DoubleDouble>(external);
2161 double dresult = target(dval0, dval1);
2162 setCallResultDouble(dresult);
2163 break;
2164 }
2165 case Args_Double_IntDouble: {
2166 double dval1 = getFpuRegisterDouble(13);
2167 Prototype_Double_IntDouble target =
2168 reinterpret_cast<Prototype_Double_IntDouble>(external);
2169 double dresult = target(arg0, dval1);
2170 setCallResultDouble(dresult);
2171 break;
2172 }
2173 case Args_Int_IntDouble: {
2174 double dval1 = getFpuRegisterDouble(13);
2175 Prototype_Int_IntDouble target =
2176 reinterpret_cast<Prototype_Int_IntDouble>(external);
2177 int64_t result = target(arg0, dval1);
2178 setRegister(v0, result);
2179 break;
2180 }
2181 case Args_Double_DoubleDoubleDouble: {
2182 double dval0 = getFpuRegisterDouble(12);
2183 double dval1 = getFpuRegisterDouble(13);
2184 double dval2 = getFpuRegisterDouble(14);
2185 Prototype_Double_DoubleDoubleDouble target =
2186 reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
2187 double dresult = target(dval0, dval1, dval2);
2188 setCallResultDouble(dresult);
2189 break;
2190 }
2191 case Args_Double_DoubleDoubleDoubleDouble: {
2192 double dval0 = getFpuRegisterDouble(12);
2193 double dval1 = getFpuRegisterDouble(13);
2194 double dval2 = getFpuRegisterDouble(14);
2195 double dval3 = getFpuRegisterDouble(15);
2196 Prototype_Double_DoubleDoubleDoubleDouble target =
2197 reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(
2198 external);
2199 double dresult = target(dval0, dval1, dval2, dval3);
2200 setCallResultDouble(dresult);
2201 break;
2202 }
2203 default:
2204 MOZ_CRASH("call");
2205 }
2206
2207 if (single_stepping_) {
2208 single_step_callback_(single_step_callback_arg_, this, nullptr);
2209 }
2210
2211 setRegister(ra, saved_ra);
2212 set_pc(getRegister(ra));
2213 #endif
2214 } else if (func == ff_break && code <= kMaxStopCode) {
2215 if (isWatchpoint(code)) {
2216 printWatchpoint(code);
2217 } else {
2218 increaseStopCounter(code);
2219 handleStop(code, instr);
2220 }
2221 } else {
2222 switch (func) {
2223 case ff_tge:
2224 case ff_tgeu:
2225 case ff_tlt:
2226 case ff_tltu:
2227 case ff_teq:
2228 case ff_tne:
2229 if (instr->bits(15, 6) == kWasmTrapCode) {
2230 uint8_t* newPC;
2231 if (wasm::HandleIllegalInstruction(registerState(), &newPC)) {
2232 set_pc(int64_t(newPC));
2233 return;
2234 }
2235 }
2236 };
2237 // All remaining break_ codes, and all traps are handled here.
2238 MipsDebugger dbg(this);
2239 dbg.debug();
2240 }
2241 }
2242
2243 // Stop helper functions.
isWatchpoint(uint32_t code)2244 bool Simulator::isWatchpoint(uint32_t code) {
2245 return (code <= kMaxWatchpointCode);
2246 }
2247
printWatchpoint(uint32_t code)2248 void Simulator::printWatchpoint(uint32_t code) {
2249 MipsDebugger dbg(this);
2250 ++break_count_;
2251 printf("\n---- break %d marker: %20" PRIi64 " (instr count: %20" PRIi64
2252 ") ----\n",
2253 code, break_count_, icount_);
2254 dbg.printAllRegs(); // Print registers and continue running.
2255 }
2256
handleStop(uint32_t code,SimInstruction * instr)2257 void Simulator::handleStop(uint32_t code, SimInstruction* instr) {
2258 // Stop if it is enabled, otherwise go on jumping over the stop
2259 // and the message address.
2260 if (isEnabledStop(code)) {
2261 MipsDebugger dbg(this);
2262 dbg.stop(instr);
2263 } else {
2264 set_pc(get_pc() + 2 * SimInstruction::kInstrSize);
2265 }
2266 }
2267
isStopInstruction(SimInstruction * instr)2268 bool Simulator::isStopInstruction(SimInstruction* instr) {
2269 int32_t func = instr->functionFieldRaw();
2270 uint32_t code = U32(instr->bits(25, 6));
2271 return (func == ff_break) && code > kMaxWatchpointCode &&
2272 code <= kMaxStopCode;
2273 }
2274
isEnabledStop(uint32_t code)2275 bool Simulator::isEnabledStop(uint32_t code) {
2276 MOZ_ASSERT(code <= kMaxStopCode);
2277 MOZ_ASSERT(code > kMaxWatchpointCode);
2278 return !(watchedStops_[code].count_ & kStopDisabledBit);
2279 }
2280
enableStop(uint32_t code)2281 void Simulator::enableStop(uint32_t code) {
2282 if (!isEnabledStop(code)) {
2283 watchedStops_[code].count_ &= ~kStopDisabledBit;
2284 }
2285 }
2286
disableStop(uint32_t code)2287 void Simulator::disableStop(uint32_t code) {
2288 if (isEnabledStop(code)) {
2289 watchedStops_[code].count_ |= kStopDisabledBit;
2290 }
2291 }
2292
increaseStopCounter(uint32_t code)2293 void Simulator::increaseStopCounter(uint32_t code) {
2294 MOZ_ASSERT(code <= kMaxStopCode);
2295 if ((watchedStops_[code].count_ & ~(1 << 31)) == 0x7fffffff) {
2296 printf(
2297 "Stop counter for code %i has overflowed.\n"
2298 "Enabling this code and reseting the counter to 0.\n",
2299 code);
2300 watchedStops_[code].count_ = 0;
2301 enableStop(code);
2302 } else {
2303 watchedStops_[code].count_++;
2304 }
2305 }
2306
2307 // Print a stop status.
printStopInfo(uint32_t code)2308 void Simulator::printStopInfo(uint32_t code) {
2309 if (code <= kMaxWatchpointCode) {
2310 printf("That is a watchpoint, not a stop.\n");
2311 return;
2312 } else if (code > kMaxStopCode) {
2313 printf("Code too large, only %u stops can be used\n", kMaxStopCode + 1);
2314 return;
2315 }
2316 const char* state = isEnabledStop(code) ? "Enabled" : "Disabled";
2317 int32_t count = watchedStops_[code].count_ & ~kStopDisabledBit;
2318 // Don't print the state of unused breakpoints.
2319 if (count != 0) {
2320 if (watchedStops_[code].desc_) {
2321 printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state,
2322 count, watchedStops_[code].desc_);
2323 } else {
2324 printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state,
2325 count);
2326 }
2327 }
2328 }
2329
signalExceptions()2330 void Simulator::signalExceptions() {
2331 for (int i = 1; i < kNumExceptions; i++) {
2332 if (exceptions[i] != 0) {
2333 MOZ_CRASH("Error: Exception raised.");
2334 }
2335 }
2336 }
2337
2338 // Helper function for decodeTypeRegister.
configureTypeRegister(SimInstruction * instr,int64_t & alu_out,__int128 & i128hilo,unsigned __int128 & u128hilo,int64_t & next_pc,int32_t & return_addr_reg,bool & do_interrupt)2339 void Simulator::configureTypeRegister(SimInstruction* instr, int64_t& alu_out,
2340 __int128& i128hilo,
2341 unsigned __int128& u128hilo,
2342 int64_t& next_pc,
2343 int32_t& return_addr_reg,
2344 bool& do_interrupt) {
2345 // Every local variable declared here needs to be const.
2346 // This is to make sure that changed values are sent back to
2347 // decodeTypeRegister correctly.
2348
2349 // Instruction fields.
2350 const OpcodeField op = instr->opcodeFieldRaw();
2351 const int32_t rs_reg = instr->rsValue();
2352 const int64_t rs = getRegister(rs_reg);
2353 const int32_t rt_reg = instr->rtValue();
2354 const int64_t rt = getRegister(rt_reg);
2355 const int32_t rd_reg = instr->rdValue();
2356 const uint32_t sa = instr->saValue();
2357
2358 const int32_t fs_reg = instr->fsValue();
2359 __int128 temp;
2360
2361 // ---------- Configuration.
2362 switch (op) {
2363 case op_cop1: // Coprocessor instructions.
2364 switch (instr->rsFieldRaw()) {
2365 case rs_bc1: // Handled in DecodeTypeImmed, should never come here.
2366 MOZ_CRASH();
2367 break;
2368 case rs_cfc1:
2369 // At the moment only FCSR is supported.
2370 MOZ_ASSERT(fs_reg == kFCSRRegister);
2371 alu_out = FCSR_;
2372 break;
2373 case rs_mfc1:
2374 alu_out = getFpuRegisterLo(fs_reg);
2375 break;
2376 case rs_dmfc1:
2377 alu_out = getFpuRegister(fs_reg);
2378 break;
2379 case rs_mfhc1:
2380 alu_out = getFpuRegisterHi(fs_reg);
2381 break;
2382 case rs_ctc1:
2383 case rs_mtc1:
2384 case rs_dmtc1:
2385 case rs_mthc1:
2386 // Do the store in the execution step.
2387 break;
2388 case rs_s:
2389 case rs_d:
2390 case rs_w:
2391 case rs_l:
2392 case rs_ps:
2393 // Do everything in the execution step.
2394 break;
2395 default:
2396 MOZ_CRASH();
2397 };
2398 break;
2399 case op_cop1x:
2400 break;
2401 case op_special:
2402 switch (instr->functionFieldRaw()) {
2403 case ff_jr:
2404 case ff_jalr:
2405 next_pc = getRegister(instr->rsValue());
2406 return_addr_reg = instr->rdValue();
2407 break;
2408 case ff_sll:
2409 alu_out = I64(I32(rt) << sa);
2410 break;
2411 case ff_dsll:
2412 alu_out = rt << sa;
2413 break;
2414 case ff_dsll32:
2415 alu_out = rt << (sa + 32);
2416 break;
2417 case ff_srl:
2418 if (rs_reg == 0) {
2419 // Regular logical right shift of a word by a fixed number of
2420 // bits instruction. RS field is always equal to 0.
2421 alu_out = I64(I32(U32(I32_CHECK(rt)) >> sa));
2422 } else {
2423 // Logical right-rotate of a word by a fixed number of bits. This
2424 // is special case of SRL instruction, added in MIPS32 Release 2.
2425 // RS field is equal to 00001.
2426 alu_out = I64(I32((U32(I32_CHECK(rt)) >> sa) |
2427 (U32(I32_CHECK(rt)) << (32 - sa))));
2428 }
2429 break;
2430 case ff_dsrl:
2431 if (rs_reg == 0) {
2432 // Regular logical right shift of a double word by a fixed number of
2433 // bits instruction. RS field is always equal to 0.
2434 alu_out = U64(rt) >> sa;
2435 } else {
2436 // Logical right-rotate of a word by a fixed number of bits. This
2437 // is special case of DSRL instruction, added in MIPS64 Release 2.
2438 // RS field is equal to 00001.
2439 alu_out = (U64(rt) >> sa) | (U64(rt) << (64 - sa));
2440 }
2441 break;
2442 case ff_dsrl32:
2443 if (rs_reg == 0) {
2444 // Regular logical right shift of a double word by a fixed number of
2445 // bits instruction. RS field is always equal to 0.
2446 alu_out = U64(rt) >> (sa + 32);
2447 } else {
2448 // Logical right-rotate of a double word by a fixed number of bits.
2449 // This is special case of DSRL instruction, added in MIPS64
2450 // Release 2. RS field is equal to 00001.
2451 alu_out = (U64(rt) >> (sa + 32)) | (U64(rt) << (64 - (sa + 32)));
2452 }
2453 break;
2454 case ff_sra:
2455 alu_out = I64(I32_CHECK(rt)) >> sa;
2456 break;
2457 case ff_dsra:
2458 alu_out = rt >> sa;
2459 break;
2460 case ff_dsra32:
2461 alu_out = rt >> (sa + 32);
2462 break;
2463 case ff_sllv:
2464 alu_out = I64(I32(rt) << rs);
2465 break;
2466 case ff_dsllv:
2467 alu_out = rt << rs;
2468 break;
2469 case ff_srlv:
2470 if (sa == 0) {
2471 // Regular logical right-shift of a word by a variable number of
2472 // bits instruction. SA field is always equal to 0.
2473 alu_out = I64(I32(U32(I32_CHECK(rt)) >> rs));
2474 } else {
2475 // Logical right-rotate of a word by a variable number of bits.
2476 // This is special case od SRLV instruction, added in MIPS32
2477 // Release 2. SA field is equal to 00001.
2478 alu_out = I64(I32((U32(I32_CHECK(rt)) >> rs) |
2479 (U32(I32_CHECK(rt)) << (32 - rs))));
2480 }
2481 break;
2482 case ff_dsrlv:
2483 if (sa == 0) {
2484 // Regular logical right-shift of a double word by a variable number
2485 // of bits instruction. SA field is always equal to 0.
2486 alu_out = U64(rt) >> rs;
2487 } else {
2488 // Logical right-rotate of a double word by a variable number of
2489 // bits. This is special case od DSRLV instruction, added in MIPS64
2490 // Release 2. SA field is equal to 00001.
2491 alu_out = (U64(rt) >> rs) | (U64(rt) << (64 - rs));
2492 }
2493 break;
2494 case ff_srav:
2495 alu_out = I64(I32_CHECK(rt) >> rs);
2496 break;
2497 case ff_dsrav:
2498 alu_out = rt >> rs;
2499 break;
2500 case ff_mfhi:
2501 alu_out = getRegister(HI);
2502 break;
2503 case ff_mflo:
2504 alu_out = getRegister(LO);
2505 break;
2506 case ff_mult:
2507 i128hilo = I64(U32(I32_CHECK(rs))) * I64(U32(I32_CHECK(rt)));
2508 break;
2509 case ff_dmult:
2510 i128hilo = I128(rs) * I128(rt);
2511 break;
2512 case ff_multu:
2513 u128hilo = U64(U32(I32_CHECK(rs))) * U64(U32(I32_CHECK(rt)));
2514 break;
2515 case ff_dmultu:
2516 u128hilo = U128(rs) * U128(rt);
2517 break;
2518 case ff_add:
2519 alu_out = I32_CHECK(rs) + I32_CHECK(rt);
2520 if ((alu_out << 32) != (alu_out << 31)) {
2521 exceptions[kIntegerOverflow] = 1;
2522 }
2523 alu_out = I32(alu_out);
2524 break;
2525 case ff_dadd:
2526 temp = I128(rs) + I128(rt);
2527 if ((temp << 64) != (temp << 63)) {
2528 exceptions[kIntegerOverflow] = 1;
2529 }
2530 alu_out = I64(temp);
2531 break;
2532 case ff_addu:
2533 alu_out = I32(I32_CHECK(rs) + I32_CHECK(rt));
2534 break;
2535 case ff_daddu:
2536 alu_out = rs + rt;
2537 break;
2538 case ff_sub:
2539 alu_out = I32_CHECK(rs) - I32_CHECK(rt);
2540 if ((alu_out << 32) != (alu_out << 31)) {
2541 exceptions[kIntegerUnderflow] = 1;
2542 }
2543 alu_out = I32(alu_out);
2544 break;
2545 case ff_dsub:
2546 temp = I128(rs) - I128(rt);
2547 if ((temp << 64) != (temp << 63)) {
2548 exceptions[kIntegerUnderflow] = 1;
2549 }
2550 alu_out = I64(temp);
2551 break;
2552 case ff_subu:
2553 alu_out = I32(I32_CHECK(rs) - I32_CHECK(rt));
2554 break;
2555 case ff_dsubu:
2556 alu_out = rs - rt;
2557 break;
2558 case ff_and:
2559 alu_out = rs & rt;
2560 break;
2561 case ff_or:
2562 alu_out = rs | rt;
2563 break;
2564 case ff_xor:
2565 alu_out = rs ^ rt;
2566 break;
2567 case ff_nor:
2568 alu_out = ~(rs | rt);
2569 break;
2570 case ff_slt:
2571 alu_out = I64(rs) < I64(rt) ? 1 : 0;
2572 break;
2573 case ff_sltu:
2574 alu_out = U64(rs) < U64(rt) ? 1 : 0;
2575 break;
2576 case ff_sync:
2577 break;
2578 // Break and trap instructions.
2579 case ff_break:
2580 do_interrupt = true;
2581 break;
2582 case ff_tge:
2583 do_interrupt = rs >= rt;
2584 break;
2585 case ff_tgeu:
2586 do_interrupt = U64(rs) >= U64(rt);
2587 break;
2588 case ff_tlt:
2589 do_interrupt = rs < rt;
2590 break;
2591 case ff_tltu:
2592 do_interrupt = U64(rs) < U64(rt);
2593 break;
2594 case ff_teq:
2595 do_interrupt = rs == rt;
2596 break;
2597 case ff_tne:
2598 do_interrupt = rs != rt;
2599 break;
2600 case ff_movn:
2601 case ff_movz:
2602 case ff_movci:
2603 // No action taken on decode.
2604 break;
2605 case ff_div:
2606 if (I32_CHECK(rs) == INT_MIN && I32_CHECK(rt) == -1) {
2607 i128hilo = U32(INT_MIN);
2608 } else {
2609 uint32_t div = I32_CHECK(rs) / I32_CHECK(rt);
2610 uint32_t mod = I32_CHECK(rs) % I32_CHECK(rt);
2611 i128hilo = (I64(mod) << 32) | div;
2612 }
2613 break;
2614 case ff_ddiv:
2615 if (I64(rs) == INT64_MIN && I64(rt) == -1) {
2616 i128hilo = U64(INT64_MIN);
2617 } else {
2618 uint64_t div = rs / rt;
2619 uint64_t mod = rs % rt;
2620 i128hilo = (I128(mod) << 64) | div;
2621 }
2622 break;
2623 case ff_divu: {
2624 uint32_t div = U32(I32_CHECK(rs)) / U32(I32_CHECK(rt));
2625 uint32_t mod = U32(I32_CHECK(rs)) % U32(I32_CHECK(rt));
2626 i128hilo = (U64(mod) << 32) | div;
2627 } break;
2628 case ff_ddivu:
2629 if (0 == rt) {
2630 i128hilo = (I128(Unpredictable) << 64) | I64(Unpredictable);
2631 } else {
2632 uint64_t div = U64(rs) / U64(rt);
2633 uint64_t mod = U64(rs) % U64(rt);
2634 i128hilo = (I128(mod) << 64) | div;
2635 }
2636 break;
2637 default:
2638 MOZ_CRASH();
2639 };
2640 break;
2641 case op_special2:
2642 switch (instr->functionFieldRaw()) {
2643 case ff_mul:
2644 alu_out = I32(I32_CHECK(rs) *
2645 I32_CHECK(rt)); // Only the lower 32 bits are kept.
2646 break;
2647 case ff_clz:
2648 alu_out = U32(I32_CHECK(rs)) ? __builtin_clz(U32(I32_CHECK(rs))) : 32;
2649 break;
2650 case ff_dclz:
2651 alu_out = U64(rs) ? __builtin_clzl(U64(rs)) : 64;
2652 break;
2653 default:
2654 MOZ_CRASH();
2655 };
2656 break;
2657 case op_special3:
2658 switch (instr->functionFieldRaw()) {
2659 case ff_ins: { // Mips64r2 instruction.
2660 // Interpret rd field as 5-bit msb of insert.
2661 uint16_t msb = rd_reg;
2662 // Interpret sa field as 5-bit lsb of insert.
2663 uint16_t lsb = sa;
2664 uint16_t size = msb - lsb + 1;
2665 uint32_t mask = (1 << size) - 1;
2666 if (lsb > msb) {
2667 alu_out = Unpredictable;
2668 } else {
2669 alu_out = (U32(I32_CHECK(rt)) & ~(mask << lsb)) |
2670 ((U32(I32_CHECK(rs)) & mask) << lsb);
2671 }
2672 break;
2673 }
2674 case ff_dins: { // Mips64r2 instruction.
2675 // Interpret rd field as 5-bit msb of insert.
2676 uint16_t msb = rd_reg;
2677 // Interpret sa field as 5-bit lsb of insert.
2678 uint16_t lsb = sa;
2679 uint16_t size = msb - lsb + 1;
2680 uint64_t mask = (1ul << size) - 1;
2681 if (lsb > msb) {
2682 alu_out = Unpredictable;
2683 } else {
2684 alu_out = (U64(rt) & ~(mask << lsb)) | ((U64(rs) & mask) << lsb);
2685 }
2686 break;
2687 }
2688 case ff_dinsm: { // Mips64r2 instruction.
2689 // Interpret rd field as 5-bit msb of insert.
2690 uint16_t msb = rd_reg;
2691 // Interpret sa field as 5-bit lsb of insert.
2692 uint16_t lsb = sa;
2693 uint16_t size = msb - lsb + 33;
2694 uint64_t mask = (1ul << size) - 1;
2695 alu_out = (U64(rt) & ~(mask << lsb)) | ((U64(rs) & mask) << lsb);
2696 break;
2697 }
2698 case ff_dinsu: { // Mips64r2 instruction.
2699 // Interpret rd field as 5-bit msb of insert.
2700 uint16_t msb = rd_reg;
2701 // Interpret sa field as 5-bit lsb of insert.
2702 uint16_t lsb = sa + 32;
2703 uint16_t size = msb - lsb + 33;
2704 uint64_t mask = (1ul << size) - 1;
2705 if (sa > msb) {
2706 alu_out = Unpredictable;
2707 } else {
2708 alu_out = (U64(rt) & ~(mask << lsb)) | ((U64(rs) & mask) << lsb);
2709 }
2710 break;
2711 }
2712 case ff_ext: { // Mips64r2 instruction.
2713 // Interpret rd field as 5-bit msb of extract.
2714 uint16_t msb = rd_reg;
2715 // Interpret sa field as 5-bit lsb of extract.
2716 uint16_t lsb = sa;
2717 uint16_t size = msb + 1;
2718 uint32_t mask = (1 << size) - 1;
2719 if ((lsb + msb) > 31) {
2720 alu_out = Unpredictable;
2721 } else {
2722 alu_out = (U32(I32_CHECK(rs)) & (mask << lsb)) >> lsb;
2723 }
2724 break;
2725 }
2726 case ff_dext: { // Mips64r2 instruction.
2727 // Interpret rd field as 5-bit msb of extract.
2728 uint16_t msb = rd_reg;
2729 // Interpret sa field as 5-bit lsb of extract.
2730 uint16_t lsb = sa;
2731 uint16_t size = msb + 1;
2732 uint64_t mask = (1ul << size) - 1;
2733 alu_out = (U64(rs) & (mask << lsb)) >> lsb;
2734 break;
2735 }
2736 case ff_dextm: { // Mips64r2 instruction.
2737 // Interpret rd field as 5-bit msb of extract.
2738 uint16_t msb = rd_reg;
2739 // Interpret sa field as 5-bit lsb of extract.
2740 uint16_t lsb = sa;
2741 uint16_t size = msb + 33;
2742 uint64_t mask = (1ul << size) - 1;
2743 if ((lsb + msb + 32 + 1) > 64) {
2744 alu_out = Unpredictable;
2745 } else {
2746 alu_out = (U64(rs) & (mask << lsb)) >> lsb;
2747 }
2748 break;
2749 }
2750 case ff_dextu: { // Mips64r2 instruction.
2751 // Interpret rd field as 5-bit msb of extract.
2752 uint16_t msb = rd_reg;
2753 // Interpret sa field as 5-bit lsb of extract.
2754 uint16_t lsb = sa + 32;
2755 uint16_t size = msb + 1;
2756 uint64_t mask = (1ul << size) - 1;
2757 if ((lsb + msb + 1) > 64) {
2758 alu_out = Unpredictable;
2759 } else {
2760 alu_out = (U64(rs) & (mask << lsb)) >> lsb;
2761 }
2762 break;
2763 }
2764 case ff_bshfl: { // Mips32r2 instruction.
2765 if (16 == sa) { // seb
2766 alu_out = I64(I8(I32_CHECK(rt)));
2767 } else if (24 == sa) { // seh
2768 alu_out = I64(I16(I32_CHECK(rt)));
2769 }
2770 break;
2771 }
2772 default:
2773 MOZ_CRASH();
2774 };
2775 break;
2776 default:
2777 MOZ_CRASH();
2778 };
2779 }
2780
2781 // Handle execution based on instruction types.
decodeTypeRegister(SimInstruction * instr)2782 void Simulator::decodeTypeRegister(SimInstruction* instr) {
2783 // Instruction fields.
2784 const OpcodeField op = instr->opcodeFieldRaw();
2785 const int32_t rs_reg = instr->rsValue();
2786 const int64_t rs = getRegister(rs_reg);
2787 const int32_t rt_reg = instr->rtValue();
2788 const int64_t rt = getRegister(rt_reg);
2789 const int32_t rd_reg = instr->rdValue();
2790
2791 const int32_t fr_reg = instr->frValue();
2792 const int32_t fs_reg = instr->fsValue();
2793 const int32_t ft_reg = instr->ftValue();
2794 const int32_t fd_reg = instr->fdValue();
2795 __int128 i128hilo = 0;
2796 unsigned __int128 u128hilo = 0;
2797
2798 // ALU output.
2799 // It should not be used as is. Instructions using it should always
2800 // initialize it first.
2801 int64_t alu_out = 0x12345678;
2802
2803 // For break and trap instructions.
2804 bool do_interrupt = false;
2805
2806 // For jr and jalr.
2807 // Get current pc.
2808 int64_t current_pc = get_pc();
2809 // Next pc
2810 int64_t next_pc = 0;
2811 int32_t return_addr_reg = 31;
2812
2813 // Set up the variables if needed before executing the instruction.
2814 configureTypeRegister(instr, alu_out, i128hilo, u128hilo, next_pc,
2815 return_addr_reg, do_interrupt);
2816
2817 // ---------- Raise exceptions triggered.
2818 signalExceptions();
2819
2820 // ---------- Execution.
2821 switch (op) {
2822 case op_cop1:
2823 switch (instr->rsFieldRaw()) {
2824 case rs_bc1: // Branch on coprocessor condition.
2825 MOZ_CRASH();
2826 break;
2827 case rs_cfc1:
2828 setRegister(rt_reg, alu_out);
2829 [[fallthrough]];
2830 case rs_mfc1:
2831 setRegister(rt_reg, alu_out);
2832 break;
2833 case rs_dmfc1:
2834 setRegister(rt_reg, alu_out);
2835 break;
2836 case rs_mfhc1:
2837 setRegister(rt_reg, alu_out);
2838 break;
2839 case rs_ctc1:
2840 // At the moment only FCSR is supported.
2841 MOZ_ASSERT(fs_reg == kFCSRRegister);
2842 FCSR_ = registers_[rt_reg];
2843 break;
2844 case rs_mtc1:
2845 setFpuRegisterLo(fs_reg, registers_[rt_reg]);
2846 break;
2847 case rs_dmtc1:
2848 setFpuRegister(fs_reg, registers_[rt_reg]);
2849 break;
2850 case rs_mthc1:
2851 setFpuRegisterHi(fs_reg, registers_[rt_reg]);
2852 break;
2853 case rs_s:
2854 float f, ft_value, fs_value;
2855 uint32_t cc, fcsr_cc;
2856 int64_t i64;
2857 fs_value = getFpuRegisterFloat(fs_reg);
2858 ft_value = getFpuRegisterFloat(ft_reg);
2859 cc = instr->fcccValue();
2860 fcsr_cc = GetFCSRConditionBit(cc);
2861 switch (instr->functionFieldRaw()) {
2862 case ff_add_fmt:
2863 setFpuRegisterFloat(fd_reg, fs_value + ft_value);
2864 break;
2865 case ff_sub_fmt:
2866 setFpuRegisterFloat(fd_reg, fs_value - ft_value);
2867 break;
2868 case ff_mul_fmt:
2869 setFpuRegisterFloat(fd_reg, fs_value * ft_value);
2870 break;
2871 case ff_div_fmt:
2872 setFpuRegisterFloat(fd_reg, fs_value / ft_value);
2873 break;
2874 case ff_abs_fmt:
2875 setFpuRegisterFloat(fd_reg, fabsf(fs_value));
2876 break;
2877 case ff_mov_fmt:
2878 setFpuRegisterFloat(fd_reg, fs_value);
2879 break;
2880 case ff_neg_fmt:
2881 setFpuRegisterFloat(fd_reg, -fs_value);
2882 break;
2883 case ff_sqrt_fmt:
2884 setFpuRegisterFloat(fd_reg, sqrtf(fs_value));
2885 break;
2886 case ff_c_un_fmt:
2887 setFCSRBit(fcsr_cc,
2888 mozilla::IsNaN(fs_value) || mozilla::IsNaN(ft_value));
2889 break;
2890 case ff_c_eq_fmt:
2891 setFCSRBit(fcsr_cc, (fs_value == ft_value));
2892 break;
2893 case ff_c_ueq_fmt:
2894 setFCSRBit(fcsr_cc,
2895 (fs_value == ft_value) || (mozilla::IsNaN(fs_value) ||
2896 mozilla::IsNaN(ft_value)));
2897 break;
2898 case ff_c_olt_fmt:
2899 setFCSRBit(fcsr_cc, (fs_value < ft_value));
2900 break;
2901 case ff_c_ult_fmt:
2902 setFCSRBit(fcsr_cc,
2903 (fs_value < ft_value) || (mozilla::IsNaN(fs_value) ||
2904 mozilla::IsNaN(ft_value)));
2905 break;
2906 case ff_c_ole_fmt:
2907 setFCSRBit(fcsr_cc, (fs_value <= ft_value));
2908 break;
2909 case ff_c_ule_fmt:
2910 setFCSRBit(fcsr_cc,
2911 (fs_value <= ft_value) || (mozilla::IsNaN(fs_value) ||
2912 mozilla::IsNaN(ft_value)));
2913 break;
2914 case ff_cvt_d_fmt:
2915 f = getFpuRegisterFloat(fs_reg);
2916 setFpuRegisterDouble(fd_reg, static_cast<double>(f));
2917 break;
2918 case ff_cvt_w_fmt: // Convert float to word.
2919 // Rounding modes are not yet supported.
2920 MOZ_ASSERT((FCSR_ & 3) == 0);
2921 // In rounding mode 0 it should behave like ROUND.
2922 [[fallthrough]];
2923 case ff_round_w_fmt: { // Round double to word (round half to
2924 // even).
2925 float rounded = std::floor(fs_value + 0.5);
2926 int32_t result = I32(rounded);
2927 if ((result & 1) != 0 && result - fs_value == 0.5) {
2928 // If the number is halfway between two integers,
2929 // round to the even one.
2930 result--;
2931 }
2932 setFpuRegisterLo(fd_reg, result);
2933 if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
2934 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
2935 }
2936 break;
2937 }
2938 case ff_trunc_w_fmt: { // Truncate float to word (round towards 0).
2939 float rounded = truncf(fs_value);
2940 int32_t result = I32(rounded);
2941 setFpuRegisterLo(fd_reg, result);
2942 if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
2943 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
2944 }
2945 break;
2946 }
2947 case ff_floor_w_fmt: { // Round float to word towards negative
2948 // infinity.
2949 float rounded = std::floor(fs_value);
2950 int32_t result = I32(rounded);
2951 setFpuRegisterLo(fd_reg, result);
2952 if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
2953 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
2954 }
2955 break;
2956 }
2957 case ff_ceil_w_fmt: { // Round double to word towards positive
2958 // infinity.
2959 float rounded = std::ceil(fs_value);
2960 int32_t result = I32(rounded);
2961 setFpuRegisterLo(fd_reg, result);
2962 if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
2963 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
2964 }
2965 break;
2966 }
2967 case ff_cvt_l_fmt: // Mips64r2: Truncate float to 64-bit long-word.
2968 // Rounding modes are not yet supported.
2969 MOZ_ASSERT((FCSR_ & 3) == 0);
2970 // In rounding mode 0 it should behave like ROUND.
2971 [[fallthrough]];
2972 case ff_round_l_fmt: { // Mips64r2 instruction.
2973 float rounded = fs_value > 0 ? std::floor(fs_value + 0.5)
2974 : std::ceil(fs_value - 0.5);
2975 i64 = I64(rounded);
2976 setFpuRegister(fd_reg, i64);
2977 if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
2978 setFpuRegister(fd_reg, kFPUInvalidResult64);
2979 }
2980 break;
2981 }
2982 case ff_trunc_l_fmt: { // Mips64r2 instruction.
2983 float rounded = truncf(fs_value);
2984 i64 = I64(rounded);
2985 setFpuRegister(fd_reg, i64);
2986 if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
2987 setFpuRegister(fd_reg, kFPUInvalidResult64);
2988 }
2989 break;
2990 }
2991 case ff_floor_l_fmt: { // Mips64r2 instruction.
2992 float rounded = std::floor(fs_value);
2993 i64 = I64(rounded);
2994 setFpuRegister(fd_reg, i64);
2995 if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
2996 setFpuRegister(fd_reg, kFPUInvalidResult64);
2997 }
2998 break;
2999 }
3000 case ff_ceil_l_fmt: { // Mips64r2 instruction.
3001 float rounded = std::ceil(fs_value);
3002 i64 = I64(rounded);
3003 setFpuRegister(fd_reg, i64);
3004 if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
3005 setFpuRegister(fd_reg, kFPUInvalidResult64);
3006 }
3007 break;
3008 }
3009 case ff_cvt_ps_s:
3010 case ff_c_f_fmt:
3011 MOZ_CRASH();
3012 break;
3013 case ff_movf_fmt:
3014 if (testFCSRBit(fcsr_cc)) {
3015 setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
3016 }
3017 break;
3018 case ff_movz_fmt:
3019 if (rt == 0) {
3020 setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
3021 }
3022 break;
3023 case ff_movn_fmt:
3024 if (rt != 0) {
3025 setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
3026 }
3027 break;
3028 default:
3029 MOZ_CRASH();
3030 }
3031 break;
3032 case rs_d:
3033 double dt_value, ds_value;
3034 ds_value = getFpuRegisterDouble(fs_reg);
3035 dt_value = getFpuRegisterDouble(ft_reg);
3036 cc = instr->fcccValue();
3037 fcsr_cc = GetFCSRConditionBit(cc);
3038 switch (instr->functionFieldRaw()) {
3039 case ff_add_fmt:
3040 setFpuRegisterDouble(fd_reg, ds_value + dt_value);
3041 break;
3042 case ff_sub_fmt:
3043 setFpuRegisterDouble(fd_reg, ds_value - dt_value);
3044 break;
3045 case ff_mul_fmt:
3046 setFpuRegisterDouble(fd_reg, ds_value * dt_value);
3047 break;
3048 case ff_div_fmt:
3049 setFpuRegisterDouble(fd_reg, ds_value / dt_value);
3050 break;
3051 case ff_abs_fmt:
3052 setFpuRegisterDouble(fd_reg, fabs(ds_value));
3053 break;
3054 case ff_mov_fmt:
3055 setFpuRegisterDouble(fd_reg, ds_value);
3056 break;
3057 case ff_neg_fmt:
3058 setFpuRegisterDouble(fd_reg, -ds_value);
3059 break;
3060 case ff_sqrt_fmt:
3061 setFpuRegisterDouble(fd_reg, sqrt(ds_value));
3062 break;
3063 case ff_c_un_fmt:
3064 setFCSRBit(fcsr_cc,
3065 mozilla::IsNaN(ds_value) || mozilla::IsNaN(dt_value));
3066 break;
3067 case ff_c_eq_fmt:
3068 setFCSRBit(fcsr_cc, (ds_value == dt_value));
3069 break;
3070 case ff_c_ueq_fmt:
3071 setFCSRBit(fcsr_cc,
3072 (ds_value == dt_value) || (mozilla::IsNaN(ds_value) ||
3073 mozilla::IsNaN(dt_value)));
3074 break;
3075 case ff_c_olt_fmt:
3076 setFCSRBit(fcsr_cc, (ds_value < dt_value));
3077 break;
3078 case ff_c_ult_fmt:
3079 setFCSRBit(fcsr_cc,
3080 (ds_value < dt_value) || (mozilla::IsNaN(ds_value) ||
3081 mozilla::IsNaN(dt_value)));
3082 break;
3083 case ff_c_ole_fmt:
3084 setFCSRBit(fcsr_cc, (ds_value <= dt_value));
3085 break;
3086 case ff_c_ule_fmt:
3087 setFCSRBit(fcsr_cc,
3088 (ds_value <= dt_value) || (mozilla::IsNaN(ds_value) ||
3089 mozilla::IsNaN(dt_value)));
3090 break;
3091 case ff_cvt_w_fmt: // Convert double to word.
3092 // Rounding modes are not yet supported.
3093 MOZ_ASSERT((FCSR_ & 3) == 0);
3094 // In rounding mode 0 it should behave like ROUND.
3095 [[fallthrough]];
3096 case ff_round_w_fmt: { // Round double to word (round half to
3097 // even).
3098 double rounded = std::floor(ds_value + 0.5);
3099 int32_t result = I32(rounded);
3100 if ((result & 1) != 0 && result - ds_value == 0.5) {
3101 // If the number is halfway between two integers,
3102 // round to the even one.
3103 result--;
3104 }
3105 setFpuRegisterLo(fd_reg, result);
3106 if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
3107 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
3108 }
3109 break;
3110 }
3111 case ff_trunc_w_fmt: { // Truncate double to word (round towards
3112 // 0).
3113 double rounded = trunc(ds_value);
3114 int32_t result = I32(rounded);
3115 setFpuRegisterLo(fd_reg, result);
3116 if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
3117 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
3118 }
3119 break;
3120 }
3121 case ff_floor_w_fmt: { // Round double to word towards negative
3122 // infinity.
3123 double rounded = std::floor(ds_value);
3124 int32_t result = I32(rounded);
3125 setFpuRegisterLo(fd_reg, result);
3126 if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
3127 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
3128 }
3129 break;
3130 }
3131 case ff_ceil_w_fmt: { // Round double to word towards positive
3132 // infinity.
3133 double rounded = std::ceil(ds_value);
3134 int32_t result = I32(rounded);
3135 setFpuRegisterLo(fd_reg, result);
3136 if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
3137 setFpuRegisterLo(fd_reg, kFPUInvalidResult);
3138 }
3139 break;
3140 }
3141 case ff_cvt_s_fmt: // Convert double to float (single).
3142 setFpuRegisterFloat(fd_reg, static_cast<float>(ds_value));
3143 break;
3144 case ff_cvt_l_fmt: // Mips64r2: Truncate double to 64-bit
3145 // long-word.
3146 // Rounding modes are not yet supported.
3147 MOZ_ASSERT((FCSR_ & 3) == 0);
3148 // In rounding mode 0 it should behave like ROUND.
3149 [[fallthrough]];
3150 case ff_round_l_fmt: { // Mips64r2 instruction.
3151 double rounded = ds_value > 0 ? std::floor(ds_value + 0.5)
3152 : std::ceil(ds_value - 0.5);
3153 i64 = I64(rounded);
3154 setFpuRegister(fd_reg, i64);
3155 if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
3156 setFpuRegister(fd_reg, kFPUInvalidResult64);
3157 }
3158 break;
3159 }
3160 case ff_trunc_l_fmt: { // Mips64r2 instruction.
3161 double rounded = trunc(ds_value);
3162 i64 = I64(rounded);
3163 setFpuRegister(fd_reg, i64);
3164 if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
3165 setFpuRegister(fd_reg, kFPUInvalidResult64);
3166 }
3167 break;
3168 }
3169 case ff_floor_l_fmt: { // Mips64r2 instruction.
3170 double rounded = std::floor(ds_value);
3171 i64 = I64(rounded);
3172 setFpuRegister(fd_reg, i64);
3173 if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
3174 setFpuRegister(fd_reg, kFPUInvalidResult64);
3175 }
3176 break;
3177 }
3178 case ff_ceil_l_fmt: { // Mips64r2 instruction.
3179 double rounded = std::ceil(ds_value);
3180 i64 = I64(rounded);
3181 setFpuRegister(fd_reg, i64);
3182 if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
3183 setFpuRegister(fd_reg, kFPUInvalidResult64);
3184 }
3185 break;
3186 }
3187 case ff_c_f_fmt:
3188 MOZ_CRASH();
3189 break;
3190 case ff_movz_fmt:
3191 if (rt == 0) {
3192 setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
3193 }
3194 break;
3195 case ff_movn_fmt:
3196 if (rt != 0) {
3197 setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
3198 }
3199 break;
3200 case ff_movf_fmt:
3201 // location of cc field in MOVF is equal to float branch
3202 // instructions
3203 cc = instr->fbccValue();
3204 fcsr_cc = GetFCSRConditionBit(cc);
3205 if (testFCSRBit(fcsr_cc)) {
3206 setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
3207 }
3208 break;
3209 default:
3210 MOZ_CRASH();
3211 }
3212 break;
3213 case rs_w:
3214 switch (instr->functionFieldRaw()) {
3215 case ff_cvt_s_fmt: // Convert word to float (single).
3216 i64 = getFpuRegisterLo(fs_reg);
3217 setFpuRegisterFloat(fd_reg, static_cast<float>(i64));
3218 break;
3219 case ff_cvt_d_fmt: // Convert word to double.
3220 i64 = getFpuRegisterLo(fs_reg);
3221 setFpuRegisterDouble(fd_reg, static_cast<double>(i64));
3222 break;
3223 default:
3224 MOZ_CRASH();
3225 };
3226 break;
3227 case rs_l:
3228 switch (instr->functionFieldRaw()) {
3229 case ff_cvt_d_fmt: // Mips64r2 instruction.
3230 i64 = getFpuRegister(fs_reg);
3231 setFpuRegisterDouble(fd_reg, static_cast<double>(i64));
3232 break;
3233 case ff_cvt_s_fmt:
3234 i64 = getFpuRegister(fs_reg);
3235 setFpuRegisterFloat(fd_reg, static_cast<float>(i64));
3236 break;
3237 default:
3238 MOZ_CRASH();
3239 }
3240 break;
3241 case rs_ps:
3242 break;
3243 default:
3244 MOZ_CRASH();
3245 };
3246 break;
3247 case op_cop1x:
3248 switch (instr->functionFieldRaw()) {
3249 case ff_madd_s:
3250 float fr, ft, fs;
3251 fr = getFpuRegisterFloat(fr_reg);
3252 fs = getFpuRegisterFloat(fs_reg);
3253 ft = getFpuRegisterFloat(ft_reg);
3254 setFpuRegisterFloat(fd_reg, fs * ft + fr);
3255 break;
3256 case ff_madd_d:
3257 double dr, dt, ds;
3258 dr = getFpuRegisterDouble(fr_reg);
3259 ds = getFpuRegisterDouble(fs_reg);
3260 dt = getFpuRegisterDouble(ft_reg);
3261 setFpuRegisterDouble(fd_reg, ds * dt + dr);
3262 break;
3263 default:
3264 MOZ_CRASH();
3265 };
3266 break;
3267 case op_special:
3268 switch (instr->functionFieldRaw()) {
3269 case ff_jr: {
3270 SimInstruction* branch_delay_instr =
3271 reinterpret_cast<SimInstruction*>(current_pc +
3272 SimInstruction::kInstrSize);
3273 branchDelayInstructionDecode(branch_delay_instr);
3274 set_pc(next_pc);
3275 pc_modified_ = true;
3276 break;
3277 }
3278 case ff_jalr: {
3279 SimInstruction* branch_delay_instr =
3280 reinterpret_cast<SimInstruction*>(current_pc +
3281 SimInstruction::kInstrSize);
3282 setRegister(return_addr_reg,
3283 current_pc + 2 * SimInstruction::kInstrSize);
3284 branchDelayInstructionDecode(branch_delay_instr);
3285 set_pc(next_pc);
3286 pc_modified_ = true;
3287 break;
3288 }
3289 // Instructions using HI and LO registers.
3290 case ff_mult:
3291 setRegister(LO, I32(i128hilo & 0xffffffff));
3292 setRegister(HI, I32(i128hilo >> 32));
3293 break;
3294 case ff_dmult:
3295 setRegister(LO, I64(i128hilo & 0xfffffffffffffffful));
3296 setRegister(HI, I64(i128hilo >> 64));
3297 break;
3298 case ff_multu:
3299 setRegister(LO, I32(u128hilo & 0xffffffff));
3300 setRegister(HI, I32(u128hilo >> 32));
3301 break;
3302 case ff_dmultu:
3303 setRegister(LO, I64(u128hilo & 0xfffffffffffffffful));
3304 setRegister(HI, I64(u128hilo >> 64));
3305 break;
3306 case ff_div:
3307 case ff_divu:
3308 // Divide by zero and overflow was not checked in the configuration
3309 // step - div and divu do not raise exceptions. On division by 0
3310 // the result will be UNPREDICTABLE. On overflow (INT_MIN/-1),
3311 // return INT_MIN which is what the hardware does.
3312 setRegister(LO, I32(i128hilo & 0xffffffff));
3313 setRegister(HI, I32(i128hilo >> 32));
3314 break;
3315 case ff_ddiv:
3316 case ff_ddivu:
3317 // Divide by zero and overflow was not checked in the configuration
3318 // step - div and divu do not raise exceptions. On division by 0
3319 // the result will be UNPREDICTABLE. On overflow (INT_MIN/-1),
3320 // return INT_MIN which is what the hardware does.
3321 setRegister(LO, I64(i128hilo & 0xfffffffffffffffful));
3322 setRegister(HI, I64(i128hilo >> 64));
3323 break;
3324 case ff_sync:
3325 break;
3326 // Break and trap instructions.
3327 case ff_break:
3328 case ff_tge:
3329 case ff_tgeu:
3330 case ff_tlt:
3331 case ff_tltu:
3332 case ff_teq:
3333 case ff_tne:
3334 if (do_interrupt) {
3335 softwareInterrupt(instr);
3336 }
3337 break;
3338 // Conditional moves.
3339 case ff_movn:
3340 if (rt) {
3341 setRegister(rd_reg, rs);
3342 }
3343 break;
3344 case ff_movci: {
3345 uint32_t cc = instr->fbccValue();
3346 uint32_t fcsr_cc = GetFCSRConditionBit(cc);
3347 if (instr->bit(16)) { // Read Tf bit.
3348 if (testFCSRBit(fcsr_cc)) {
3349 setRegister(rd_reg, rs);
3350 }
3351 } else {
3352 if (!testFCSRBit(fcsr_cc)) {
3353 setRegister(rd_reg, rs);
3354 }
3355 }
3356 break;
3357 }
3358 case ff_movz:
3359 if (!rt) {
3360 setRegister(rd_reg, rs);
3361 }
3362 break;
3363 default: // For other special opcodes we do the default operation.
3364 setRegister(rd_reg, alu_out);
3365 };
3366 break;
3367 case op_special2:
3368 switch (instr->functionFieldRaw()) {
3369 case ff_mul:
3370 setRegister(rd_reg, alu_out);
3371 // HI and LO are UNPREDICTABLE after the operation.
3372 setRegister(LO, Unpredictable);
3373 setRegister(HI, Unpredictable);
3374 break;
3375 default: // For other special2 opcodes we do the default operation.
3376 setRegister(rd_reg, alu_out);
3377 }
3378 break;
3379 case op_special3:
3380 switch (instr->functionFieldRaw()) {
3381 case ff_ins:
3382 case ff_dins:
3383 case ff_dinsm:
3384 case ff_dinsu:
3385 // Ins instr leaves result in Rt, rather than Rd.
3386 setRegister(rt_reg, alu_out);
3387 break;
3388 case ff_ext:
3389 case ff_dext:
3390 case ff_dextm:
3391 case ff_dextu:
3392 // Ext instr leaves result in Rt, rather than Rd.
3393 setRegister(rt_reg, alu_out);
3394 break;
3395 case ff_bshfl:
3396 setRegister(rd_reg, alu_out);
3397 break;
3398 default:
3399 MOZ_CRASH();
3400 };
3401 break;
3402 // Unimplemented opcodes raised an error in the configuration step before,
3403 // so we can use the default here to set the destination register in
3404 // common cases.
3405 default:
3406 setRegister(rd_reg, alu_out);
3407 };
3408 }
3409
3410 // Type 2: instructions using a 16 bits immediate. (e.g. addi, beq).
decodeTypeImmediate(SimInstruction * instr)3411 void Simulator::decodeTypeImmediate(SimInstruction* instr) {
3412 // Instruction fields.
3413 OpcodeField op = instr->opcodeFieldRaw();
3414 int64_t rs = getRegister(instr->rsValue());
3415 int32_t rt_reg = instr->rtValue(); // Destination register.
3416 int64_t rt = getRegister(rt_reg);
3417 int16_t imm16 = instr->imm16Value();
3418
3419 int32_t ft_reg = instr->ftValue(); // Destination register.
3420
3421 // Zero extended immediate.
3422 uint32_t oe_imm16 = 0xffff & imm16;
3423 // Sign extended immediate.
3424 int32_t se_imm16 = imm16;
3425
3426 // Get current pc.
3427 int64_t current_pc = get_pc();
3428 // Next pc.
3429 int64_t next_pc = bad_ra;
3430
3431 // Used for conditional branch instructions.
3432 bool do_branch = false;
3433 bool execute_branch_delay_instruction = false;
3434
3435 // Used for arithmetic instructions.
3436 int64_t alu_out = 0;
3437 // Floating point.
3438 double fp_out = 0.0;
3439 uint32_t cc, cc_value, fcsr_cc;
3440
3441 // Used for memory instructions.
3442 uint64_t addr = 0x0;
3443 // Value to be written in memory.
3444 uint64_t mem_value = 0x0;
3445 __int128 temp;
3446
3447 // ---------- Configuration (and execution for op_regimm).
3448 switch (op) {
3449 // ------------- op_cop1. Coprocessor instructions.
3450 case op_cop1:
3451 switch (instr->rsFieldRaw()) {
3452 case rs_bc1: // Branch on coprocessor condition.
3453 cc = instr->fbccValue();
3454 fcsr_cc = GetFCSRConditionBit(cc);
3455 cc_value = testFCSRBit(fcsr_cc);
3456 do_branch = (instr->fbtrueValue()) ? cc_value : !cc_value;
3457 execute_branch_delay_instruction = true;
3458 // Set next_pc.
3459 if (do_branch) {
3460 next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
3461 } else {
3462 next_pc = current_pc + kBranchReturnOffset;
3463 }
3464 break;
3465 default:
3466 MOZ_CRASH();
3467 };
3468 break;
3469 // ------------- op_regimm class.
3470 case op_regimm:
3471 switch (instr->rtFieldRaw()) {
3472 case rt_bltz:
3473 do_branch = (rs < 0);
3474 break;
3475 case rt_bltzal:
3476 do_branch = rs < 0;
3477 break;
3478 case rt_bgez:
3479 do_branch = rs >= 0;
3480 break;
3481 case rt_bgezal:
3482 do_branch = rs >= 0;
3483 break;
3484 default:
3485 MOZ_CRASH();
3486 };
3487 switch (instr->rtFieldRaw()) {
3488 case rt_bltz:
3489 case rt_bltzal:
3490 case rt_bgez:
3491 case rt_bgezal:
3492 // Branch instructions common part.
3493 execute_branch_delay_instruction = true;
3494 // Set next_pc.
3495 if (do_branch) {
3496 next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
3497 if (instr->isLinkingInstruction()) {
3498 setRegister(31, current_pc + kBranchReturnOffset);
3499 }
3500 } else {
3501 next_pc = current_pc + kBranchReturnOffset;
3502 }
3503 break;
3504 default:
3505 break;
3506 };
3507 break; // case op_regimm.
3508 // ------------- Branch instructions.
3509 // When comparing to zero, the encoding of rt field is always 0, so we
3510 // don't need to replace rt with zero.
3511 case op_beq:
3512 do_branch = (rs == rt);
3513 break;
3514 case op_bne:
3515 do_branch = rs != rt;
3516 break;
3517 case op_blez:
3518 do_branch = rs <= 0;
3519 break;
3520 case op_bgtz:
3521 do_branch = rs > 0;
3522 break;
3523 // ------------- Arithmetic instructions.
3524 case op_addi:
3525 alu_out = I32_CHECK(rs) + se_imm16;
3526 if ((alu_out << 32) != (alu_out << 31)) {
3527 exceptions[kIntegerOverflow] = 1;
3528 }
3529 alu_out = I32_CHECK(alu_out);
3530 break;
3531 case op_daddi:
3532 temp = alu_out = rs + se_imm16;
3533 if ((temp << 64) != (temp << 63)) {
3534 exceptions[kIntegerOverflow] = 1;
3535 }
3536 alu_out = I64(temp);
3537 break;
3538 case op_addiu:
3539 alu_out = I32(I32_CHECK(rs) + se_imm16);
3540 break;
3541 case op_daddiu:
3542 alu_out = rs + se_imm16;
3543 break;
3544 case op_slti:
3545 alu_out = (rs < se_imm16) ? 1 : 0;
3546 break;
3547 case op_sltiu:
3548 alu_out = (U64(rs) < U64(se_imm16)) ? 1 : 0;
3549 break;
3550 case op_andi:
3551 alu_out = rs & oe_imm16;
3552 break;
3553 case op_ori:
3554 alu_out = rs | oe_imm16;
3555 break;
3556 case op_xori:
3557 alu_out = rs ^ oe_imm16;
3558 break;
3559 case op_lui:
3560 alu_out = (se_imm16 << 16);
3561 break;
3562 // ------------- Memory instructions.
3563 case op_lbu:
3564 addr = rs + se_imm16;
3565 alu_out = readBU(addr, instr);
3566 break;
3567 case op_lb:
3568 addr = rs + se_imm16;
3569 alu_out = readB(addr, instr);
3570 break;
3571 case op_lhu:
3572 addr = rs + se_imm16;
3573 alu_out = readHU(addr, instr);
3574 break;
3575 case op_lh:
3576 addr = rs + se_imm16;
3577 alu_out = readH(addr, instr);
3578 break;
3579 case op_lwu:
3580 addr = rs + se_imm16;
3581 alu_out = readWU(addr, instr);
3582 break;
3583 case op_lw:
3584 addr = rs + se_imm16;
3585 alu_out = readW(addr, instr);
3586 break;
3587 case op_lwl: {
3588 // al_offset is offset of the effective address within an aligned word.
3589 uint8_t al_offset = (rs + se_imm16) & 3;
3590 uint8_t byte_shift = 3 - al_offset;
3591 uint32_t mask = (1 << byte_shift * 8) - 1;
3592 addr = rs + se_imm16 - al_offset;
3593 alu_out = readW(addr, instr);
3594 alu_out <<= byte_shift * 8;
3595 alu_out |= rt & mask;
3596 break;
3597 }
3598 case op_lwr: {
3599 // al_offset is offset of the effective address within an aligned word.
3600 uint8_t al_offset = (rs + se_imm16) & 3;
3601 uint8_t byte_shift = 3 - al_offset;
3602 uint32_t mask = al_offset ? (~0 << (byte_shift + 1) * 8) : 0;
3603 addr = rs + se_imm16 - al_offset;
3604 alu_out = readW(addr, instr);
3605 alu_out = U32(alu_out) >> al_offset * 8;
3606 alu_out |= rt & mask;
3607 break;
3608 }
3609 case op_ll:
3610 addr = rs + se_imm16;
3611 alu_out = loadLinkedW(addr, instr);
3612 break;
3613 case op_lld:
3614 addr = rs + se_imm16;
3615 alu_out = loadLinkedD(addr, instr);
3616 break;
3617 case op_ld:
3618 addr = rs + se_imm16;
3619 alu_out = readDW(addr, instr);
3620 break;
3621 case op_ldl: {
3622 // al_offset is offset of the effective address within an aligned word.
3623 uint8_t al_offset = (rs + se_imm16) & 7;
3624 uint8_t byte_shift = 7 - al_offset;
3625 uint64_t mask = (1ul << byte_shift * 8) - 1;
3626 addr = rs + se_imm16 - al_offset;
3627 alu_out = readDW(addr, instr);
3628 alu_out <<= byte_shift * 8;
3629 alu_out |= rt & mask;
3630 break;
3631 }
3632 case op_ldr: {
3633 // al_offset is offset of the effective address within an aligned word.
3634 uint8_t al_offset = (rs + se_imm16) & 7;
3635 uint8_t byte_shift = 7 - al_offset;
3636 uint64_t mask = al_offset ? (~0ul << (byte_shift + 1) * 8) : 0;
3637 addr = rs + se_imm16 - al_offset;
3638 alu_out = readDW(addr, instr);
3639 alu_out = U64(alu_out) >> al_offset * 8;
3640 alu_out |= rt & mask;
3641 break;
3642 }
3643 case op_sb:
3644 addr = rs + se_imm16;
3645 break;
3646 case op_sh:
3647 addr = rs + se_imm16;
3648 break;
3649 case op_sw:
3650 addr = rs + se_imm16;
3651 break;
3652 case op_swl: {
3653 uint8_t al_offset = (rs + se_imm16) & 3;
3654 uint8_t byte_shift = 3 - al_offset;
3655 uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0;
3656 addr = rs + se_imm16 - al_offset;
3657 mem_value = readW(addr, instr) & mask;
3658 mem_value |= U32(rt) >> byte_shift * 8;
3659 break;
3660 }
3661 case op_swr: {
3662 uint8_t al_offset = (rs + se_imm16) & 3;
3663 uint32_t mask = (1 << al_offset * 8) - 1;
3664 addr = rs + se_imm16 - al_offset;
3665 mem_value = readW(addr, instr);
3666 mem_value = (rt << al_offset * 8) | (mem_value & mask);
3667 break;
3668 }
3669 case op_sc:
3670 addr = rs + se_imm16;
3671 break;
3672 case op_scd:
3673 addr = rs + se_imm16;
3674 break;
3675 case op_sd:
3676 addr = rs + se_imm16;
3677 break;
3678 case op_sdl: {
3679 uint8_t al_offset = (rs + se_imm16) & 7;
3680 uint8_t byte_shift = 7 - al_offset;
3681 uint64_t mask = byte_shift ? (~0ul << (al_offset + 1) * 8) : 0;
3682 addr = rs + se_imm16 - al_offset;
3683 mem_value = readW(addr, instr) & mask;
3684 mem_value |= U64(rt) >> byte_shift * 8;
3685 break;
3686 }
3687 case op_sdr: {
3688 uint8_t al_offset = (rs + se_imm16) & 7;
3689 uint64_t mask = (1ul << al_offset * 8) - 1;
3690 addr = rs + se_imm16 - al_offset;
3691 mem_value = readW(addr, instr);
3692 mem_value = (rt << al_offset * 8) | (mem_value & mask);
3693 break;
3694 }
3695 case op_lwc1:
3696 addr = rs + se_imm16;
3697 alu_out = readW(addr, instr);
3698 break;
3699 case op_ldc1:
3700 addr = rs + se_imm16;
3701 fp_out = readD(addr, instr);
3702 break;
3703 case op_swc1:
3704 case op_sdc1:
3705 addr = rs + se_imm16;
3706 break;
3707 default:
3708 MOZ_CRASH();
3709 };
3710
3711 // ---------- Raise exceptions triggered.
3712 signalExceptions();
3713
3714 // ---------- Execution.
3715 switch (op) {
3716 // ------------- Branch instructions.
3717 case op_beq:
3718 case op_bne:
3719 case op_blez:
3720 case op_bgtz:
3721 // Branch instructions common part.
3722 execute_branch_delay_instruction = true;
3723 // Set next_pc.
3724 if (do_branch) {
3725 next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
3726 if (instr->isLinkingInstruction()) {
3727 setRegister(31, current_pc + 2 * SimInstruction::kInstrSize);
3728 }
3729 } else {
3730 next_pc = current_pc + 2 * SimInstruction::kInstrSize;
3731 }
3732 break;
3733 // ------------- Arithmetic instructions.
3734 case op_addi:
3735 case op_daddi:
3736 case op_addiu:
3737 case op_daddiu:
3738 case op_slti:
3739 case op_sltiu:
3740 case op_andi:
3741 case op_ori:
3742 case op_xori:
3743 case op_lui:
3744 setRegister(rt_reg, alu_out);
3745 break;
3746 // ------------- Memory instructions.
3747 case op_lbu:
3748 case op_lb:
3749 case op_lhu:
3750 case op_lh:
3751 case op_lwu:
3752 case op_lw:
3753 case op_lwl:
3754 case op_lwr:
3755 case op_ll:
3756 case op_lld:
3757 case op_ld:
3758 case op_ldl:
3759 case op_ldr:
3760 setRegister(rt_reg, alu_out);
3761 break;
3762 case op_sb:
3763 writeB(addr, I8(rt), instr);
3764 break;
3765 case op_sh:
3766 writeH(addr, U16(rt), instr);
3767 break;
3768 case op_sw:
3769 writeW(addr, I32(rt), instr);
3770 break;
3771 case op_swl:
3772 writeW(addr, I32(mem_value), instr);
3773 break;
3774 case op_swr:
3775 writeW(addr, I32(mem_value), instr);
3776 break;
3777 case op_sc:
3778 setRegister(rt_reg, storeConditionalW(addr, I32(rt), instr));
3779 break;
3780 case op_scd:
3781 setRegister(rt_reg, storeConditionalD(addr, rt, instr));
3782 break;
3783 case op_sd:
3784 writeDW(addr, rt, instr);
3785 break;
3786 case op_sdl:
3787 writeDW(addr, mem_value, instr);
3788 break;
3789 case op_sdr:
3790 writeDW(addr, mem_value, instr);
3791 break;
3792 case op_lwc1:
3793 setFpuRegisterLo(ft_reg, alu_out);
3794 break;
3795 case op_ldc1:
3796 setFpuRegisterDouble(ft_reg, fp_out);
3797 break;
3798 case op_swc1:
3799 writeW(addr, getFpuRegisterLo(ft_reg), instr);
3800 break;
3801 case op_sdc1:
3802 writeD(addr, getFpuRegisterDouble(ft_reg), instr);
3803 break;
3804 default:
3805 break;
3806 };
3807
3808 if (execute_branch_delay_instruction) {
3809 // Execute branch delay slot
3810 // We don't check for end_sim_pc. First it should not be met as the current
3811 // pc is valid. Secondly a jump should always execute its branch delay slot.
3812 SimInstruction* branch_delay_instr = reinterpret_cast<SimInstruction*>(
3813 current_pc + SimInstruction::kInstrSize);
3814 branchDelayInstructionDecode(branch_delay_instr);
3815 }
3816
3817 // If needed update pc after the branch delay execution.
3818 if (next_pc != bad_ra) {
3819 set_pc(next_pc);
3820 }
3821 }
3822
3823 // Type 3: instructions using a 26 bits immediate. (e.g. j, jal).
decodeTypeJump(SimInstruction * instr)3824 void Simulator::decodeTypeJump(SimInstruction* instr) {
3825 // Get current pc.
3826 int64_t current_pc = get_pc();
3827 // Get unchanged bits of pc.
3828 int64_t pc_high_bits = current_pc & 0xfffffffff0000000ul;
3829 // Next pc.
3830 int64_t next_pc = pc_high_bits | (instr->imm26Value() << 2);
3831
3832 // Execute branch delay slot.
3833 // We don't check for end_sim_pc. First it should not be met as the current pc
3834 // is valid. Secondly a jump should always execute its branch delay slot.
3835 SimInstruction* branch_delay_instr = reinterpret_cast<SimInstruction*>(
3836 current_pc + SimInstruction::kInstrSize);
3837 branchDelayInstructionDecode(branch_delay_instr);
3838
3839 // Update pc and ra if necessary.
3840 // Do this after the branch delay execution.
3841 if (instr->isLinkingInstruction()) {
3842 setRegister(31, current_pc + 2 * SimInstruction::kInstrSize);
3843 }
3844 set_pc(next_pc);
3845 pc_modified_ = true;
3846 }
3847
3848 // Executes the current instruction.
instructionDecode(SimInstruction * instr)3849 void Simulator::instructionDecode(SimInstruction* instr) {
3850 if (!SimulatorProcess::ICacheCheckingDisableCount) {
3851 AutoLockSimulatorCache als;
3852 SimulatorProcess::checkICacheLocked(instr);
3853 }
3854 pc_modified_ = false;
3855
3856 switch (instr->instructionType()) {
3857 case SimInstruction::kRegisterType:
3858 decodeTypeRegister(instr);
3859 break;
3860 case SimInstruction::kImmediateType:
3861 decodeTypeImmediate(instr);
3862 break;
3863 case SimInstruction::kJumpType:
3864 decodeTypeJump(instr);
3865 break;
3866 default:
3867 UNSUPPORTED();
3868 }
3869 if (!pc_modified_) {
3870 setRegister(pc,
3871 reinterpret_cast<int64_t>(instr) + SimInstruction::kInstrSize);
3872 }
3873 }
3874
branchDelayInstructionDecode(SimInstruction * instr)3875 void Simulator::branchDelayInstructionDecode(SimInstruction* instr) {
3876 if (instr->instructionBits() == NopInst) {
3877 // Short-cut generic nop instructions. They are always valid and they
3878 // never change the simulator state.
3879 return;
3880 }
3881
3882 if (instr->isForbiddenInBranchDelay()) {
3883 MOZ_CRASH("Eror:Unexpected opcode in a branch delay slot.");
3884 }
3885 instructionDecode(instr);
3886 }
3887
enable_single_stepping(SingleStepCallback cb,void * arg)3888 void Simulator::enable_single_stepping(SingleStepCallback cb, void* arg) {
3889 single_stepping_ = true;
3890 single_step_callback_ = cb;
3891 single_step_callback_arg_ = arg;
3892 single_step_callback_(single_step_callback_arg_, this, (void*)get_pc());
3893 }
3894
disable_single_stepping()3895 void Simulator::disable_single_stepping() {
3896 if (!single_stepping_) {
3897 return;
3898 }
3899 single_step_callback_(single_step_callback_arg_, this, (void*)get_pc());
3900 single_stepping_ = false;
3901 single_step_callback_ = nullptr;
3902 single_step_callback_arg_ = nullptr;
3903 }
3904
3905 template <bool enableStopSimAt>
execute()3906 void Simulator::execute() {
3907 if (single_stepping_) {
3908 single_step_callback_(single_step_callback_arg_, this, nullptr);
3909 }
3910
3911 // Get the PC to simulate. Cannot use the accessor here as we need the
3912 // raw PC value and not the one used as input to arithmetic instructions.
3913 int64_t program_counter = get_pc();
3914
3915 while (program_counter != end_sim_pc) {
3916 if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
3917 MipsDebugger dbg(this);
3918 dbg.debug();
3919 } else {
3920 if (single_stepping_) {
3921 single_step_callback_(single_step_callback_arg_, this,
3922 (void*)program_counter);
3923 }
3924 SimInstruction* instr =
3925 reinterpret_cast<SimInstruction*>(program_counter);
3926 instructionDecode(instr);
3927 icount_++;
3928 }
3929 program_counter = get_pc();
3930 }
3931
3932 if (single_stepping_) {
3933 single_step_callback_(single_step_callback_arg_, this, nullptr);
3934 }
3935 }
3936
callInternal(uint8_t * entry)3937 void Simulator::callInternal(uint8_t* entry) {
3938 // Prepare to execute the code at entry.
3939 setRegister(pc, reinterpret_cast<int64_t>(entry));
3940 // Put down marker for end of simulation. The simulator will stop simulation
3941 // when the PC reaches this value. By saving the "end simulation" value into
3942 // the LR the simulation stops when returning to this call point.
3943 setRegister(ra, end_sim_pc);
3944
3945 // Remember the values of callee-saved registers.
3946 // The code below assumes that r9 is not used as sb (static base) in
3947 // simulator code and therefore is regarded as a callee-saved register.
3948 int64_t s0_val = getRegister(s0);
3949 int64_t s1_val = getRegister(s1);
3950 int64_t s2_val = getRegister(s2);
3951 int64_t s3_val = getRegister(s3);
3952 int64_t s4_val = getRegister(s4);
3953 int64_t s5_val = getRegister(s5);
3954 int64_t s6_val = getRegister(s6);
3955 int64_t s7_val = getRegister(s7);
3956 int64_t gp_val = getRegister(gp);
3957 int64_t sp_val = getRegister(sp);
3958 int64_t fp_val = getRegister(fp);
3959
3960 // Set up the callee-saved registers with a known value. To be able to check
3961 // that they are preserved properly across JS execution.
3962 int64_t callee_saved_value = icount_;
3963 setRegister(s0, callee_saved_value);
3964 setRegister(s1, callee_saved_value);
3965 setRegister(s2, callee_saved_value);
3966 setRegister(s3, callee_saved_value);
3967 setRegister(s4, callee_saved_value);
3968 setRegister(s5, callee_saved_value);
3969 setRegister(s6, callee_saved_value);
3970 setRegister(s7, callee_saved_value);
3971 setRegister(gp, callee_saved_value);
3972 setRegister(fp, callee_saved_value);
3973
3974 // Start the simulation.
3975 if (Simulator::StopSimAt != -1) {
3976 execute<true>();
3977 } else {
3978 execute<false>();
3979 }
3980
3981 // Check that the callee-saved registers have been preserved.
3982 MOZ_ASSERT(callee_saved_value == getRegister(s0));
3983 MOZ_ASSERT(callee_saved_value == getRegister(s1));
3984 MOZ_ASSERT(callee_saved_value == getRegister(s2));
3985 MOZ_ASSERT(callee_saved_value == getRegister(s3));
3986 MOZ_ASSERT(callee_saved_value == getRegister(s4));
3987 MOZ_ASSERT(callee_saved_value == getRegister(s5));
3988 MOZ_ASSERT(callee_saved_value == getRegister(s6));
3989 MOZ_ASSERT(callee_saved_value == getRegister(s7));
3990 MOZ_ASSERT(callee_saved_value == getRegister(gp));
3991 MOZ_ASSERT(callee_saved_value == getRegister(fp));
3992
3993 // Restore callee-saved registers with the original value.
3994 setRegister(s0, s0_val);
3995 setRegister(s1, s1_val);
3996 setRegister(s2, s2_val);
3997 setRegister(s3, s3_val);
3998 setRegister(s4, s4_val);
3999 setRegister(s5, s5_val);
4000 setRegister(s6, s6_val);
4001 setRegister(s7, s7_val);
4002 setRegister(gp, gp_val);
4003 setRegister(sp, sp_val);
4004 setRegister(fp, fp_val);
4005 }
4006
call(uint8_t * entry,int argument_count,...)4007 int64_t Simulator::call(uint8_t* entry, int argument_count, ...) {
4008 va_list parameters;
4009 va_start(parameters, argument_count);
4010
4011 int64_t original_stack = getRegister(sp);
4012 // Compute position of stack on entry to generated code.
4013 int64_t entry_stack = original_stack;
4014 if (argument_count > kCArgSlotCount) {
4015 entry_stack = entry_stack - argument_count * sizeof(int64_t);
4016 } else {
4017 entry_stack = entry_stack - kCArgsSlotsSize;
4018 }
4019
4020 entry_stack &= ~U64(ABIStackAlignment - 1);
4021
4022 intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack);
4023
4024 // Setup the arguments.
4025 for (int i = 0; i < argument_count; i++) {
4026 js::jit::Register argReg;
4027 if (GetIntArgReg(i, &argReg)) {
4028 setRegister(argReg.code(), va_arg(parameters, int64_t));
4029 } else {
4030 stack_argument[i] = va_arg(parameters, int64_t);
4031 }
4032 }
4033
4034 va_end(parameters);
4035 setRegister(sp, entry_stack);
4036
4037 callInternal(entry);
4038
4039 // Pop stack passed arguments.
4040 MOZ_ASSERT(entry_stack == getRegister(sp));
4041 setRegister(sp, original_stack);
4042
4043 int64_t result = getRegister(v0);
4044 return result;
4045 }
4046
pushAddress(uintptr_t address)4047 uintptr_t Simulator::pushAddress(uintptr_t address) {
4048 int new_sp = getRegister(sp) - sizeof(uintptr_t);
4049 uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(new_sp);
4050 *stack_slot = address;
4051 setRegister(sp, new_sp);
4052 return new_sp;
4053 }
4054
popAddress()4055 uintptr_t Simulator::popAddress() {
4056 int current_sp = getRegister(sp);
4057 uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(current_sp);
4058 uintptr_t address = *stack_slot;
4059 setRegister(sp, current_sp + sizeof(uintptr_t));
4060 return address;
4061 }
4062
4063 } // namespace jit
4064 } // namespace js
4065
simulator() const4066 js::jit::Simulator* JSContext::simulator() const { return simulator_; }
4067