| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1 | // Copyright 2011 the V8 project authors. All rights reserved. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be | 
|  | 3 | // found in the LICENSE file. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 5 | #include <limits.h> | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 6 | #include <stdarg.h> | 
|  | 7 | #include <stdlib.h> | 
|  | 8 | #include <cmath> | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 9 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 10 | #if V8_TARGET_ARCH_MIPS | 
|  | 11 |  | 
|  | 12 | #include "src/assembler.h" | 
|  | 13 | #include "src/base/bits.h" | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 14 | #include "src/codegen.h" | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 15 | #include "src/disasm.h" | 
|  | 16 | #include "src/mips/constants-mips.h" | 
|  | 17 | #include "src/mips/simulator-mips.h" | 
|  | 18 | #include "src/ostreams.h" | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 19 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 20 |  | 
|  | 21 | // Only build the simulator if not compiling for real MIPS hardware. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 22 | #if defined(USE_SIMULATOR) | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 23 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 24 | namespace v8 { | 
|  | 25 | namespace internal { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 26 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 27 | // Utils functions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 28 | bool HaveSameSign(int32_t a, int32_t b) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 29 | return ((a ^ b) >= 0); | 
|  | 30 | } | 
|  | 31 |  | 
|  | 32 |  | 
|  | 33 | uint32_t get_fcsr_condition_bit(uint32_t cc) { | 
|  | 34 | if (cc == 0) { | 
|  | 35 | return 23; | 
|  | 36 | } else { | 
|  | 37 | return 24 + cc; | 
|  | 38 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 39 | } | 
|  | 40 |  | 
|  | 41 |  | 
|  | 42 | // This macro provides a platform independent use of sscanf. The reason for | 
|  | 43 | // SScanF not being implemented in a platform independent was through | 
|  | 44 | // ::v8::internal::OS in the same way as SNPrintF is that the Windows C Run-Time | 
|  | 45 | // Library does not provide vsscanf. | 
|  | 46 | #define SScanF sscanf  // NOLINT | 
|  | 47 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 48 | // The MipsDebugger class is used by the simulator while debugging simulated | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 49 | // code. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 50 | class MipsDebugger { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 51 | public: | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 52 | explicit MipsDebugger(Simulator* sim) : sim_(sim) { } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 53 | ~MipsDebugger(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 54 |  | 
|  | 55 | void Stop(Instruction* instr); | 
|  | 56 | void Debug(); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 57 | // Print all registers with a nice formatting. | 
|  | 58 | void PrintAllRegs(); | 
|  | 59 | void PrintAllRegsIncludingFPU(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 60 |  | 
|  | 61 | private: | 
|  | 62 | // We set the breakpoint code to 0xfffff to easily recognize it. | 
|  | 63 | static const Instr kBreakpointInstr = SPECIAL | BREAK | 0xfffff << 6; | 
|  | 64 | static const Instr kNopInstr =  0x0; | 
|  | 65 |  | 
|  | 66 | Simulator* sim_; | 
|  | 67 |  | 
|  | 68 | int32_t GetRegisterValue(int regnum); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 69 | int32_t GetFPURegisterValue32(int regnum); | 
|  | 70 | int64_t GetFPURegisterValue64(int regnum); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 71 | float GetFPURegisterValueFloat(int regnum); | 
|  | 72 | double GetFPURegisterValueDouble(int regnum); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 73 | bool GetValue(const char* desc, int32_t* value); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 74 | bool GetValue(const char* desc, int64_t* value); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 75 |  | 
|  | 76 | // Set or delete a breakpoint. Returns true if successful. | 
|  | 77 | bool SetBreakpoint(Instruction* breakpc); | 
|  | 78 | bool DeleteBreakpoint(Instruction* breakpc); | 
|  | 79 |  | 
|  | 80 | // Undo and redo all breakpoints. This is needed to bracket disassembly and | 
|  | 81 | // execution to skip past breakpoints when run from the debugger. | 
|  | 82 | void UndoBreakpoints(); | 
|  | 83 | void RedoBreakpoints(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 84 | }; | 
|  | 85 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 86 |  | 
|  | 87 | MipsDebugger::~MipsDebugger() { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 88 | } | 
|  | 89 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 90 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 91 | #ifdef GENERATED_CODE_COVERAGE | 
|  | 92 | static FILE* coverage_log = NULL; | 
|  | 93 |  | 
|  | 94 |  | 
|  | 95 | static void InitializeCoverage() { | 
|  | 96 | char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG"); | 
|  | 97 | if (file_name != NULL) { | 
|  | 98 | coverage_log = fopen(file_name, "aw+"); | 
|  | 99 | } | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 103 | void MipsDebugger::Stop(Instruction* instr) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 104 | // Get the stop code. | 
|  | 105 | uint32_t code = instr->Bits(25, 6); | 
|  | 106 | // Retrieve the encoded address, which comes just after this stop. | 
|  | 107 | char** msg_address = | 
|  | 108 | reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize); | 
|  | 109 | char* msg = *msg_address; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 110 | DCHECK(msg != NULL); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 111 |  | 
|  | 112 | // Update this stop description. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 113 | if (!watched_stops_[code].desc) { | 
|  | 114 | watched_stops_[code].desc = msg; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 115 | } | 
|  | 116 |  | 
|  | 117 | if (strlen(msg) > 0) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 118 | if (coverage_log != NULL) { | 
|  | 119 | fprintf(coverage_log, "%s\n", str); | 
|  | 120 | fflush(coverage_log); | 
|  | 121 | } | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 122 | // Overwrite the instruction and address with nops. | 
|  | 123 | instr->SetInstructionBits(kNopInstr); | 
|  | 124 | reinterpret_cast<Instr*>(msg_address)->SetInstructionBits(kNopInstr); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 125 | } | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 126 | sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstructionSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 127 | } | 
|  | 128 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 129 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 130 | #else  // GENERATED_CODE_COVERAGE | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 131 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 132 | #define UNSUPPORTED() printf("Sim: Unsupported instruction.\n"); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 133 |  | 
|  | 134 | static void InitializeCoverage() {} | 
|  | 135 |  | 
|  | 136 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 137 | void MipsDebugger::Stop(Instruction* instr) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 138 | // Get the stop code. | 
|  | 139 | uint32_t code = instr->Bits(25, 6); | 
|  | 140 | // Retrieve the encoded address, which comes just after this stop. | 
|  | 141 | char* msg = *reinterpret_cast<char**>(sim_->get_pc() + | 
|  | 142 | Instruction::kInstrSize); | 
|  | 143 | // Update this stop description. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 144 | if (!sim_->watched_stops_[code].desc) { | 
|  | 145 | sim_->watched_stops_[code].desc = msg; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 146 | } | 
|  | 147 | PrintF("Simulator hit %s (%u)\n", msg, code); | 
|  | 148 | sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 149 | Debug(); | 
|  | 150 | } | 
| Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 151 | #endif  // GENERATED_CODE_COVERAGE | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 152 |  | 
|  | 153 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 154 | int32_t MipsDebugger::GetRegisterValue(int regnum) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 155 | if (regnum == kNumSimuRegisters) { | 
|  | 156 | return sim_->get_pc(); | 
|  | 157 | } else { | 
|  | 158 | return sim_->get_register(regnum); | 
|  | 159 | } | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 163 | int32_t MipsDebugger::GetFPURegisterValue32(int regnum) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 164 | if (regnum == kNumFPURegisters) { | 
|  | 165 | return sim_->get_pc(); | 
|  | 166 | } else { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 167 | return sim_->get_fpu_register_word(regnum); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 168 | } | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 172 | int64_t MipsDebugger::GetFPURegisterValue64(int regnum) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 173 | if (regnum == kNumFPURegisters) { | 
|  | 174 | return sim_->get_pc(); | 
|  | 175 | } else { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 176 | return sim_->get_fpu_register(regnum); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 177 | } | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 |  | 
|  | 181 | float MipsDebugger::GetFPURegisterValueFloat(int regnum) { | 
|  | 182 | if (regnum == kNumFPURegisters) { | 
|  | 183 | return sim_->get_pc(); | 
|  | 184 | } else { | 
|  | 185 | return sim_->get_fpu_register_float(regnum); | 
|  | 186 | } | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 |  | 
|  | 190 | double MipsDebugger::GetFPURegisterValueDouble(int regnum) { | 
|  | 191 | if (regnum == kNumFPURegisters) { | 
|  | 192 | return sim_->get_pc(); | 
|  | 193 | } else { | 
|  | 194 | return sim_->get_fpu_register_double(regnum); | 
|  | 195 | } | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 |  | 
|  | 199 | bool MipsDebugger::GetValue(const char* desc, int32_t* value) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 200 | int regnum = Registers::Number(desc); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 201 | int fpuregnum = FPURegisters::Number(desc); | 
|  | 202 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 203 | if (regnum != kInvalidRegister) { | 
|  | 204 | *value = GetRegisterValue(regnum); | 
|  | 205 | return true; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 206 | } else if (fpuregnum != kInvalidFPURegister) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 207 | *value = GetFPURegisterValue32(fpuregnum); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 208 | return true; | 
|  | 209 | } else if (strncmp(desc, "0x", 2) == 0) { | 
|  | 210 | return SScanF(desc, "%x", reinterpret_cast<uint32_t*>(value)) == 1; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 211 | } else { | 
|  | 212 | return SScanF(desc, "%i", value) == 1; | 
|  | 213 | } | 
|  | 214 | return false; | 
|  | 215 | } | 
|  | 216 |  | 
|  | 217 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 218 | bool MipsDebugger::GetValue(const char* desc, int64_t* value) { | 
|  | 219 | int regnum = Registers::Number(desc); | 
|  | 220 | int fpuregnum = FPURegisters::Number(desc); | 
|  | 221 |  | 
|  | 222 | if (regnum != kInvalidRegister) { | 
|  | 223 | *value = GetRegisterValue(regnum); | 
|  | 224 | return true; | 
|  | 225 | } else if (fpuregnum != kInvalidFPURegister) { | 
|  | 226 | *value = GetFPURegisterValue64(fpuregnum); | 
|  | 227 | return true; | 
|  | 228 | } else if (strncmp(desc, "0x", 2) == 0) { | 
|  | 229 | return SScanF(desc + 2, "%" SCNx64, | 
|  | 230 | reinterpret_cast<uint64_t*>(value)) == 1; | 
|  | 231 | } else { | 
|  | 232 | return SScanF(desc, "%" SCNu64, reinterpret_cast<uint64_t*>(value)) == 1; | 
|  | 233 | } | 
|  | 234 | return false; | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 238 | bool MipsDebugger::SetBreakpoint(Instruction* breakpc) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 239 | // Check if a breakpoint can be set. If not return without any side-effects. | 
|  | 240 | if (sim_->break_pc_ != NULL) { | 
|  | 241 | return false; | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 | // Set the breakpoint. | 
|  | 245 | sim_->break_pc_ = breakpc; | 
|  | 246 | sim_->break_instr_ = breakpc->InstructionBits(); | 
|  | 247 | // Not setting the breakpoint instruction in the code itself. It will be set | 
|  | 248 | // when the debugger shell continues. | 
|  | 249 | return true; | 
|  | 250 | } | 
|  | 251 |  | 
|  | 252 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 253 | bool MipsDebugger::DeleteBreakpoint(Instruction* breakpc) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 254 | if (sim_->break_pc_ != NULL) { | 
|  | 255 | sim_->break_pc_->SetInstructionBits(sim_->break_instr_); | 
|  | 256 | } | 
|  | 257 |  | 
|  | 258 | sim_->break_pc_ = NULL; | 
|  | 259 | sim_->break_instr_ = 0; | 
|  | 260 | return true; | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 264 | void MipsDebugger::UndoBreakpoints() { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 265 | if (sim_->break_pc_ != NULL) { | 
|  | 266 | sim_->break_pc_->SetInstructionBits(sim_->break_instr_); | 
|  | 267 | } | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 271 | void MipsDebugger::RedoBreakpoints() { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 272 | if (sim_->break_pc_ != NULL) { | 
|  | 273 | sim_->break_pc_->SetInstructionBits(kBreakpointInstr); | 
|  | 274 | } | 
|  | 275 | } | 
|  | 276 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 277 |  | 
|  | 278 | void MipsDebugger::PrintAllRegs() { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 279 | #define REG_INFO(n) Registers::Name(n), GetRegisterValue(n), GetRegisterValue(n) | 
|  | 280 |  | 
|  | 281 | PrintF("\n"); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 282 | // at, v0, a0. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 283 | PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 284 | REG_INFO(1), REG_INFO(2), REG_INFO(4)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 285 | // v1, a1. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 286 | PrintF("%26s\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 287 | "", REG_INFO(3), REG_INFO(5)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 288 | // a2. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 289 | PrintF("%26s\t%26s\t%3s: 0x%08x %10d\n", "", "", REG_INFO(6)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 290 | // a3. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 291 | PrintF("%26s\t%26s\t%3s: 0x%08x %10d\n", "", "", REG_INFO(7)); | 
|  | 292 | PrintF("\n"); | 
|  | 293 | // t0-t7, s0-s7 | 
|  | 294 | for (int i = 0; i < 8; i++) { | 
|  | 295 | PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 296 | REG_INFO(8+i), REG_INFO(16+i)); | 
|  | 297 | } | 
|  | 298 | PrintF("\n"); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 299 | // t8, k0, LO. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 300 | PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 301 | REG_INFO(24), REG_INFO(26), REG_INFO(32)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 302 | // t9, k1, HI. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 303 | PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 304 | REG_INFO(25), REG_INFO(27), REG_INFO(33)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 305 | // sp, fp, gp. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 306 | PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 307 | REG_INFO(29), REG_INFO(30), REG_INFO(28)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 308 | // pc. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 309 | PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n", | 
|  | 310 | REG_INFO(31), REG_INFO(34)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 311 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 312 | #undef REG_INFO | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 313 | #undef FPU_REG_INFO | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 314 | } | 
|  | 315 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 316 |  | 
|  | 317 | void MipsDebugger::PrintAllRegsIncludingFPU() { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 318 | #define FPU_REG_INFO32(n) FPURegisters::Name(n), FPURegisters::Name(n+1), \ | 
|  | 319 | GetFPURegisterValue32(n+1), \ | 
|  | 320 | GetFPURegisterValue32(n), \ | 
|  | 321 | GetFPURegisterValueDouble(n) | 
|  | 322 |  | 
|  | 323 | #define FPU_REG_INFO64(n) FPURegisters::Name(n), \ | 
|  | 324 | GetFPURegisterValue64(n), \ | 
|  | 325 | GetFPURegisterValueDouble(n) | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 326 |  | 
|  | 327 | PrintAllRegs(); | 
|  | 328 |  | 
|  | 329 | PrintF("\n\n"); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 330 | // f0, f1, f2, ... f31. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 331 | // This must be a compile-time switch, | 
|  | 332 | // compiler will throw out warnings otherwise. | 
|  | 333 | if (kFpuMode == kFP64) { | 
|  | 334 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(0) ); | 
|  | 335 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(1) ); | 
|  | 336 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(2) ); | 
|  | 337 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(3) ); | 
|  | 338 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(4) ); | 
|  | 339 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(5) ); | 
|  | 340 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(6) ); | 
|  | 341 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(7) ); | 
|  | 342 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(8) ); | 
|  | 343 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(9) ); | 
|  | 344 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(10)); | 
|  | 345 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(11)); | 
|  | 346 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(12)); | 
|  | 347 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(13)); | 
|  | 348 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(14)); | 
|  | 349 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(15)); | 
|  | 350 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(16)); | 
|  | 351 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(17)); | 
|  | 352 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(18)); | 
|  | 353 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(19)); | 
|  | 354 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(20)); | 
|  | 355 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(21)); | 
|  | 356 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(22)); | 
|  | 357 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(23)); | 
|  | 358 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(24)); | 
|  | 359 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(25)); | 
|  | 360 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(26)); | 
|  | 361 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(27)); | 
|  | 362 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(28)); | 
|  | 363 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(29)); | 
|  | 364 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(30)); | 
|  | 365 | PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(31)); | 
|  | 366 | } else { | 
|  | 367 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(0) ); | 
|  | 368 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(2) ); | 
|  | 369 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(4) ); | 
|  | 370 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(6) ); | 
|  | 371 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(8) ); | 
|  | 372 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(10)); | 
|  | 373 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(12)); | 
|  | 374 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(14)); | 
|  | 375 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(16)); | 
|  | 376 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(18)); | 
|  | 377 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(20)); | 
|  | 378 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(22)); | 
|  | 379 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(24)); | 
|  | 380 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(26)); | 
|  | 381 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(28)); | 
|  | 382 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(30)); | 
|  | 383 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 384 |  | 
|  | 385 | #undef REG_INFO | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 386 | #undef FPU_REG_INFO32 | 
|  | 387 | #undef FPU_REG_INFO64 | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 388 | } | 
|  | 389 |  | 
|  | 390 |  | 
|  | 391 | void MipsDebugger::Debug() { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 392 | intptr_t last_pc = -1; | 
|  | 393 | bool done = false; | 
|  | 394 |  | 
|  | 395 | #define COMMAND_SIZE 63 | 
|  | 396 | #define ARG_SIZE 255 | 
|  | 397 |  | 
|  | 398 | #define STR(a) #a | 
|  | 399 | #define XSTR(a) STR(a) | 
|  | 400 |  | 
|  | 401 | char cmd[COMMAND_SIZE + 1]; | 
|  | 402 | char arg1[ARG_SIZE + 1]; | 
|  | 403 | char arg2[ARG_SIZE + 1]; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 404 | char* argv[3] = { cmd, arg1, arg2 }; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 405 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 406 | // Make sure to have a proper terminating character if reaching the limit. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 407 | cmd[COMMAND_SIZE] = 0; | 
|  | 408 | arg1[ARG_SIZE] = 0; | 
|  | 409 | arg2[ARG_SIZE] = 0; | 
|  | 410 |  | 
|  | 411 | // Undo all set breakpoints while running in the debugger shell. This will | 
|  | 412 | // make them invisible to all commands. | 
|  | 413 | UndoBreakpoints(); | 
|  | 414 |  | 
|  | 415 | while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) { | 
|  | 416 | if (last_pc != sim_->get_pc()) { | 
|  | 417 | disasm::NameConverter converter; | 
|  | 418 | disasm::Disassembler dasm(converter); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 419 | // Use a reasonably large buffer. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 420 | v8::internal::EmbeddedVector<char, 256> buffer; | 
|  | 421 | dasm.InstructionDecode(buffer, | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 422 | reinterpret_cast<byte*>(sim_->get_pc())); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 423 | PrintF("  0x%08x  %s\n", sim_->get_pc(), buffer.start()); | 
|  | 424 | last_pc = sim_->get_pc(); | 
|  | 425 | } | 
|  | 426 | char* line = ReadLine("sim> "); | 
|  | 427 | if (line == NULL) { | 
|  | 428 | break; | 
|  | 429 | } else { | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 430 | char* last_input = sim_->last_debugger_input(); | 
|  | 431 | if (strcmp(line, "\n") == 0 && last_input != NULL) { | 
|  | 432 | line = last_input; | 
|  | 433 | } else { | 
|  | 434 | // Ownership is transferred to sim_; | 
|  | 435 | sim_->set_last_debugger_input(line); | 
|  | 436 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 437 | // Use sscanf to parse the individual parts of the command line. At the | 
|  | 438 | // moment no command expects more than two parameters. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 439 | int argc = SScanF(line, | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 440 | "%" XSTR(COMMAND_SIZE) "s " | 
|  | 441 | "%" XSTR(ARG_SIZE) "s " | 
|  | 442 | "%" XSTR(ARG_SIZE) "s", | 
|  | 443 | cmd, arg1, arg2); | 
|  | 444 | if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 445 | Instruction* instr = reinterpret_cast<Instruction*>(sim_->get_pc()); | 
|  | 446 | if (!(instr->IsTrap()) || | 
|  | 447 | instr->InstructionBits() == rtCallRedirInstr) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 448 | sim_->InstructionDecode( | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 449 | reinterpret_cast<Instruction*>(sim_->get_pc())); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 450 | } else { | 
|  | 451 | // Allow si to jump over generated breakpoints. | 
|  | 452 | PrintF("/!\\ Jumping over generated breakpoint.\n"); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 453 | sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 454 | } | 
|  | 455 | } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { | 
|  | 456 | // Execute the one instruction we broke at with breakpoints disabled. | 
|  | 457 | sim_->InstructionDecode(reinterpret_cast<Instruction*>(sim_->get_pc())); | 
|  | 458 | // Leave the debugger shell. | 
|  | 459 | done = true; | 
|  | 460 | } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 461 | if (argc == 2) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 462 | if (strcmp(arg1, "all") == 0) { | 
|  | 463 | PrintAllRegs(); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 464 | } else if (strcmp(arg1, "allf") == 0) { | 
|  | 465 | PrintAllRegsIncludingFPU(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 466 | } else { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 467 | int regnum = Registers::Number(arg1); | 
|  | 468 | int fpuregnum = FPURegisters::Number(arg1); | 
|  | 469 |  | 
|  | 470 | if (regnum != kInvalidRegister) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 471 | int32_t value; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 472 | value = GetRegisterValue(regnum); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 473 | PrintF("%s: 0x%08x %d \n", arg1, value, value); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 474 | } else if (fpuregnum != kInvalidFPURegister) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 475 | if (IsFp64Mode()) { | 
|  | 476 | int64_t value; | 
|  | 477 | double dvalue; | 
|  | 478 | value = GetFPURegisterValue64(fpuregnum); | 
|  | 479 | dvalue = GetFPURegisterValueDouble(fpuregnum); | 
|  | 480 | PrintF("%3s: 0x%016llx %16.4e\n", | 
|  | 481 | FPURegisters::Name(fpuregnum), value, dvalue); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 482 | } else { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 483 | if (fpuregnum % 2 == 1) { | 
|  | 484 | int32_t value; | 
|  | 485 | float fvalue; | 
|  | 486 | value = GetFPURegisterValue32(fpuregnum); | 
|  | 487 | fvalue = GetFPURegisterValueFloat(fpuregnum); | 
|  | 488 | PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue); | 
|  | 489 | } else { | 
|  | 490 | double dfvalue; | 
|  | 491 | int32_t lvalue1 = GetFPURegisterValue32(fpuregnum); | 
|  | 492 | int32_t lvalue2 = GetFPURegisterValue32(fpuregnum + 1); | 
|  | 493 | dfvalue = GetFPURegisterValueDouble(fpuregnum); | 
|  | 494 | PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", | 
|  | 495 | FPURegisters::Name(fpuregnum+1), | 
|  | 496 | FPURegisters::Name(fpuregnum), | 
|  | 497 | lvalue1, | 
|  | 498 | lvalue2, | 
|  | 499 | dfvalue); | 
|  | 500 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 501 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 502 | } else { | 
|  | 503 | PrintF("%s unrecognized\n", arg1); | 
|  | 504 | } | 
|  | 505 | } | 
|  | 506 | } else { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 507 | if (argc == 3) { | 
|  | 508 | if (strcmp(arg2, "single") == 0) { | 
|  | 509 | int32_t value; | 
|  | 510 | float fvalue; | 
|  | 511 | int fpuregnum = FPURegisters::Number(arg1); | 
|  | 512 |  | 
|  | 513 | if (fpuregnum != kInvalidFPURegister) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 514 | value = GetFPURegisterValue32(fpuregnum); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 515 | fvalue = GetFPURegisterValueFloat(fpuregnum); | 
|  | 516 | PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue); | 
|  | 517 | } else { | 
|  | 518 | PrintF("%s unrecognized\n", arg1); | 
|  | 519 | } | 
|  | 520 | } else { | 
|  | 521 | PrintF("print <fpu register> single\n"); | 
|  | 522 | } | 
|  | 523 | } else { | 
|  | 524 | PrintF("print <register> or print <fpu register> single\n"); | 
|  | 525 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 526 | } | 
|  | 527 | } else if ((strcmp(cmd, "po") == 0) | 
|  | 528 | || (strcmp(cmd, "printobject") == 0)) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 529 | if (argc == 2) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 530 | int32_t value; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 531 | OFStream os(stdout); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 532 | if (GetValue(arg1, &value)) { | 
|  | 533 | Object* obj = reinterpret_cast<Object*>(value); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 534 | os << arg1 << ": \n"; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 535 | #ifdef DEBUG | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 536 | obj->Print(os); | 
|  | 537 | os << "\n"; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 538 | #else | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 539 | os << Brief(obj) << "\n"; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 540 | #endif | 
|  | 541 | } else { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 542 | os << arg1 << " unrecognized\n"; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 543 | } | 
|  | 544 | } else { | 
|  | 545 | PrintF("printobject <value>\n"); | 
|  | 546 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 547 | } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { | 
|  | 548 | int32_t* cur = NULL; | 
|  | 549 | int32_t* end = NULL; | 
|  | 550 | int next_arg = 1; | 
|  | 551 |  | 
|  | 552 | if (strcmp(cmd, "stack") == 0) { | 
|  | 553 | cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 554 | } else {  // Command "mem". | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 555 | int32_t value; | 
|  | 556 | if (!GetValue(arg1, &value)) { | 
|  | 557 | PrintF("%s unrecognized\n", arg1); | 
|  | 558 | continue; | 
|  | 559 | } | 
|  | 560 | cur = reinterpret_cast<int32_t*>(value); | 
|  | 561 | next_arg++; | 
|  | 562 | } | 
|  | 563 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 564 | // TODO(palfia): optimize this. | 
|  | 565 | if (IsFp64Mode()) { | 
|  | 566 | int64_t words; | 
|  | 567 | if (argc == next_arg) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 568 | words = 10; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 569 | } else { | 
|  | 570 | if (!GetValue(argv[next_arg], &words)) { | 
|  | 571 | words = 10; | 
|  | 572 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 573 | } | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 574 | end = cur + words; | 
|  | 575 | } else { | 
|  | 576 | int32_t words; | 
|  | 577 | if (argc == next_arg) { | 
|  | 578 | words = 10; | 
|  | 579 | } else { | 
|  | 580 | if (!GetValue(argv[next_arg], &words)) { | 
|  | 581 | words = 10; | 
|  | 582 | } | 
|  | 583 | } | 
|  | 584 | end = cur + words; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 585 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 586 |  | 
|  | 587 | while (cur < end) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 588 | PrintF("  0x%08x:  0x%08x %10d", | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 589 | reinterpret_cast<intptr_t>(cur), *cur, *cur); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 590 | HeapObject* obj = reinterpret_cast<HeapObject*>(*cur); | 
|  | 591 | int value = *cur; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 592 | Heap* current_heap = sim_->isolate_->heap(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 593 | if (((value & 1) == 0) || current_heap->Contains(obj)) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 594 | PrintF(" ("); | 
|  | 595 | if ((value & 1) == 0) { | 
|  | 596 | PrintF("smi %d", value / 2); | 
|  | 597 | } else { | 
|  | 598 | obj->ShortPrint(); | 
|  | 599 | } | 
|  | 600 | PrintF(")"); | 
|  | 601 | } | 
|  | 602 | PrintF("\n"); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 603 | cur++; | 
|  | 604 | } | 
|  | 605 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 606 | } else if ((strcmp(cmd, "disasm") == 0) || | 
|  | 607 | (strcmp(cmd, "dpc") == 0) || | 
|  | 608 | (strcmp(cmd, "di") == 0)) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 609 | disasm::NameConverter converter; | 
|  | 610 | disasm::Disassembler dasm(converter); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 611 | // Use a reasonably large buffer. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 612 | v8::internal::EmbeddedVector<char, 256> buffer; | 
|  | 613 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 614 | byte* cur = NULL; | 
|  | 615 | byte* end = NULL; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 616 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 617 | if (argc == 1) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 618 | cur = reinterpret_cast<byte*>(sim_->get_pc()); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 619 | end = cur + (10 * Instruction::kInstrSize); | 
|  | 620 | } else if (argc == 2) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 621 | int regnum = Registers::Number(arg1); | 
|  | 622 | if (regnum != kInvalidRegister || strncmp(arg1, "0x", 2) == 0) { | 
|  | 623 | // The argument is an address or a register name. | 
|  | 624 | int32_t value; | 
|  | 625 | if (GetValue(arg1, &value)) { | 
|  | 626 | cur = reinterpret_cast<byte*>(value); | 
|  | 627 | // Disassemble 10 instructions at <arg1>. | 
|  | 628 | end = cur + (10 * Instruction::kInstrSize); | 
|  | 629 | } | 
|  | 630 | } else { | 
|  | 631 | // The argument is the number of instructions. | 
|  | 632 | int32_t value; | 
|  | 633 | if (GetValue(arg1, &value)) { | 
|  | 634 | cur = reinterpret_cast<byte*>(sim_->get_pc()); | 
|  | 635 | // Disassemble <arg1> instructions. | 
|  | 636 | end = cur + (value * Instruction::kInstrSize); | 
|  | 637 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 638 | } | 
|  | 639 | } else { | 
|  | 640 | int32_t value1; | 
|  | 641 | int32_t value2; | 
|  | 642 | if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 643 | cur = reinterpret_cast<byte*>(value1); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 644 | end = cur + (value2 * Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 645 | } | 
|  | 646 | } | 
|  | 647 |  | 
|  | 648 | while (cur < end) { | 
|  | 649 | dasm.InstructionDecode(buffer, cur); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 650 | PrintF("  0x%08x  %s\n", | 
|  | 651 | reinterpret_cast<intptr_t>(cur), buffer.start()); | 
|  | 652 | cur += Instruction::kInstrSize; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 653 | } | 
|  | 654 | } else if (strcmp(cmd, "gdb") == 0) { | 
|  | 655 | PrintF("relinquishing control to gdb\n"); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 656 | v8::base::OS::DebugBreak(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 657 | PrintF("regaining control from gdb\n"); | 
|  | 658 | } else if (strcmp(cmd, "break") == 0) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 659 | if (argc == 2) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 660 | int32_t value; | 
|  | 661 | if (GetValue(arg1, &value)) { | 
|  | 662 | if (!SetBreakpoint(reinterpret_cast<Instruction*>(value))) { | 
|  | 663 | PrintF("setting breakpoint failed\n"); | 
|  | 664 | } | 
|  | 665 | } else { | 
|  | 666 | PrintF("%s unrecognized\n", arg1); | 
|  | 667 | } | 
|  | 668 | } else { | 
|  | 669 | PrintF("break <address>\n"); | 
|  | 670 | } | 
|  | 671 | } else if (strcmp(cmd, "del") == 0) { | 
|  | 672 | if (!DeleteBreakpoint(NULL)) { | 
|  | 673 | PrintF("deleting breakpoint failed\n"); | 
|  | 674 | } | 
|  | 675 | } else if (strcmp(cmd, "flags") == 0) { | 
|  | 676 | PrintF("No flags on MIPS !\n"); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 677 | } else if (strcmp(cmd, "stop") == 0) { | 
|  | 678 | int32_t value; | 
|  | 679 | intptr_t stop_pc = sim_->get_pc() - | 
|  | 680 | 2 * Instruction::kInstrSize; | 
|  | 681 | Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc); | 
|  | 682 | Instruction* msg_address = | 
|  | 683 | reinterpret_cast<Instruction*>(stop_pc + | 
|  | 684 | Instruction::kInstrSize); | 
|  | 685 | if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { | 
|  | 686 | // Remove the current stop. | 
|  | 687 | if (sim_->IsStopInstruction(stop_instr)) { | 
|  | 688 | stop_instr->SetInstructionBits(kNopInstr); | 
|  | 689 | msg_address->SetInstructionBits(kNopInstr); | 
|  | 690 | } else { | 
|  | 691 | PrintF("Not at debugger stop.\n"); | 
|  | 692 | } | 
|  | 693 | } else if (argc == 3) { | 
|  | 694 | // Print information about all/the specified breakpoint(s). | 
|  | 695 | if (strcmp(arg1, "info") == 0) { | 
|  | 696 | if (strcmp(arg2, "all") == 0) { | 
|  | 697 | PrintF("Stop information:\n"); | 
|  | 698 | for (uint32_t i = kMaxWatchpointCode + 1; | 
|  | 699 | i <= kMaxStopCode; | 
|  | 700 | i++) { | 
|  | 701 | sim_->PrintStopInfo(i); | 
|  | 702 | } | 
|  | 703 | } else if (GetValue(arg2, &value)) { | 
|  | 704 | sim_->PrintStopInfo(value); | 
|  | 705 | } else { | 
|  | 706 | PrintF("Unrecognized argument.\n"); | 
|  | 707 | } | 
|  | 708 | } else if (strcmp(arg1, "enable") == 0) { | 
|  | 709 | // Enable all/the specified breakpoint(s). | 
|  | 710 | if (strcmp(arg2, "all") == 0) { | 
|  | 711 | for (uint32_t i = kMaxWatchpointCode + 1; | 
|  | 712 | i <= kMaxStopCode; | 
|  | 713 | i++) { | 
|  | 714 | sim_->EnableStop(i); | 
|  | 715 | } | 
|  | 716 | } else if (GetValue(arg2, &value)) { | 
|  | 717 | sim_->EnableStop(value); | 
|  | 718 | } else { | 
|  | 719 | PrintF("Unrecognized argument.\n"); | 
|  | 720 | } | 
|  | 721 | } else if (strcmp(arg1, "disable") == 0) { | 
|  | 722 | // Disable all/the specified breakpoint(s). | 
|  | 723 | if (strcmp(arg2, "all") == 0) { | 
|  | 724 | for (uint32_t i = kMaxWatchpointCode + 1; | 
|  | 725 | i <= kMaxStopCode; | 
|  | 726 | i++) { | 
|  | 727 | sim_->DisableStop(i); | 
|  | 728 | } | 
|  | 729 | } else if (GetValue(arg2, &value)) { | 
|  | 730 | sim_->DisableStop(value); | 
|  | 731 | } else { | 
|  | 732 | PrintF("Unrecognized argument.\n"); | 
|  | 733 | } | 
|  | 734 | } | 
|  | 735 | } else { | 
|  | 736 | PrintF("Wrong usage. Use help command for more information.\n"); | 
|  | 737 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 738 | } else if ((strcmp(cmd, "stat") == 0) || (strcmp(cmd, "st") == 0)) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 739 | // Print registers and disassemble. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 740 | PrintAllRegs(); | 
|  | 741 | PrintF("\n"); | 
|  | 742 |  | 
|  | 743 | disasm::NameConverter converter; | 
|  | 744 | disasm::Disassembler dasm(converter); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 745 | // Use a reasonably large buffer. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 746 | v8::internal::EmbeddedVector<char, 256> buffer; | 
|  | 747 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 748 | byte* cur = NULL; | 
|  | 749 | byte* end = NULL; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 750 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 751 | if (argc == 1) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 752 | cur = reinterpret_cast<byte*>(sim_->get_pc()); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 753 | end = cur + (10 * Instruction::kInstrSize); | 
|  | 754 | } else if (argc == 2) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 755 | int32_t value; | 
|  | 756 | if (GetValue(arg1, &value)) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 757 | cur = reinterpret_cast<byte*>(value); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 758 | // no length parameter passed, assume 10 instructions | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 759 | end = cur + (10 * Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 760 | } | 
|  | 761 | } else { | 
|  | 762 | int32_t value1; | 
|  | 763 | int32_t value2; | 
|  | 764 | if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 765 | cur = reinterpret_cast<byte*>(value1); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 766 | end = cur + (value2 * Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 767 | } | 
|  | 768 | } | 
|  | 769 |  | 
|  | 770 | while (cur < end) { | 
|  | 771 | dasm.InstructionDecode(buffer, cur); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 772 | PrintF("  0x%08x  %s\n", | 
|  | 773 | reinterpret_cast<intptr_t>(cur), buffer.start()); | 
|  | 774 | cur += Instruction::kInstrSize; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 775 | } | 
|  | 776 | } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { | 
|  | 777 | PrintF("cont\n"); | 
|  | 778 | PrintF("  continue execution (alias 'c')\n"); | 
|  | 779 | PrintF("stepi\n"); | 
|  | 780 | PrintF("  step one instruction (alias 'si')\n"); | 
|  | 781 | PrintF("print <register>\n"); | 
|  | 782 | PrintF("  print register content (alias 'p')\n"); | 
|  | 783 | PrintF("  use register name 'all' to print all registers\n"); | 
|  | 784 | PrintF("printobject <register>\n"); | 
|  | 785 | PrintF("  print an object from a register (alias 'po')\n"); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 786 | PrintF("stack [<words>]\n"); | 
|  | 787 | PrintF("  dump stack content, default dump 10 words)\n"); | 
|  | 788 | PrintF("mem <address> [<words>]\n"); | 
|  | 789 | PrintF("  dump memory content, default dump 10 words)\n"); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 790 | PrintF("flags\n"); | 
|  | 791 | PrintF("  print flags\n"); | 
|  | 792 | PrintF("disasm [<instructions>]\n"); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 793 | PrintF("disasm [<address/register>]\n"); | 
|  | 794 | PrintF("disasm [[<address/register>] <instructions>]\n"); | 
|  | 795 | PrintF("  disassemble code, default is 10 instructions\n"); | 
|  | 796 | PrintF("  from pc (alias 'di')\n"); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 797 | PrintF("gdb\n"); | 
|  | 798 | PrintF("  enter gdb\n"); | 
|  | 799 | PrintF("break <address>\n"); | 
|  | 800 | PrintF("  set a break point on the address\n"); | 
|  | 801 | PrintF("del\n"); | 
|  | 802 | PrintF("  delete the breakpoint\n"); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 803 | PrintF("stop feature:\n"); | 
|  | 804 | PrintF("  Description:\n"); | 
|  | 805 | PrintF("    Stops are debug instructions inserted by\n"); | 
|  | 806 | PrintF("    the Assembler::stop() function.\n"); | 
|  | 807 | PrintF("    When hitting a stop, the Simulator will\n"); | 
|  | 808 | PrintF("    stop and and give control to the Debugger.\n"); | 
|  | 809 | PrintF("    All stop codes are watched:\n"); | 
|  | 810 | PrintF("    - They can be enabled / disabled: the Simulator\n"); | 
|  | 811 | PrintF("       will / won't stop when hitting them.\n"); | 
|  | 812 | PrintF("    - The Simulator keeps track of how many times they \n"); | 
|  | 813 | PrintF("      are met. (See the info command.) Going over a\n"); | 
|  | 814 | PrintF("      disabled stop still increases its counter. \n"); | 
|  | 815 | PrintF("  Commands:\n"); | 
|  | 816 | PrintF("    stop info all/<code> : print infos about number <code>\n"); | 
|  | 817 | PrintF("      or all stop(s).\n"); | 
|  | 818 | PrintF("    stop enable/disable all/<code> : enables / disables\n"); | 
|  | 819 | PrintF("      all or number <code> stop(s)\n"); | 
|  | 820 | PrintF("    stop unstop\n"); | 
|  | 821 | PrintF("      ignore the stop instruction at the current location\n"); | 
|  | 822 | PrintF("      from now on\n"); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 823 | } else { | 
|  | 824 | PrintF("Unknown command: %s\n", cmd); | 
|  | 825 | } | 
|  | 826 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 827 | } | 
|  | 828 |  | 
|  | 829 | // Add all the breakpoints back to stop execution and enter the debugger | 
|  | 830 | // shell when hit. | 
|  | 831 | RedoBreakpoints(); | 
|  | 832 |  | 
|  | 833 | #undef COMMAND_SIZE | 
|  | 834 | #undef ARG_SIZE | 
|  | 835 |  | 
|  | 836 | #undef STR | 
|  | 837 | #undef XSTR | 
|  | 838 | } | 
|  | 839 |  | 
|  | 840 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 841 | static bool ICacheMatch(void* one, void* two) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 842 | DCHECK((reinterpret_cast<intptr_t>(one) & CachePage::kPageMask) == 0); | 
|  | 843 | DCHECK((reinterpret_cast<intptr_t>(two) & CachePage::kPageMask) == 0); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 844 | return one == two; | 
|  | 845 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 846 |  | 
|  | 847 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 848 | static uint32_t ICacheHash(void* key) { | 
|  | 849 | return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)) >> 2; | 
|  | 850 | } | 
|  | 851 |  | 
|  | 852 |  | 
|  | 853 | static bool AllOnOnePage(uintptr_t start, int size) { | 
|  | 854 | intptr_t start_page = (start & ~CachePage::kPageMask); | 
|  | 855 | intptr_t end_page = ((start + size) & ~CachePage::kPageMask); | 
|  | 856 | return start_page == end_page; | 
|  | 857 | } | 
|  | 858 |  | 
|  | 859 |  | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 860 | void Simulator::set_last_debugger_input(char* input) { | 
|  | 861 | DeleteArray(last_debugger_input_); | 
|  | 862 | last_debugger_input_ = input; | 
|  | 863 | } | 
|  | 864 |  | 
|  | 865 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 866 | void Simulator::FlushICache(v8::internal::HashMap* i_cache, | 
|  | 867 | void* start_addr, | 
|  | 868 | size_t size) { | 
|  | 869 | intptr_t start = reinterpret_cast<intptr_t>(start_addr); | 
|  | 870 | int intra_line = (start & CachePage::kLineMask); | 
|  | 871 | start -= intra_line; | 
|  | 872 | size += intra_line; | 
|  | 873 | size = ((size - 1) | CachePage::kLineMask) + 1; | 
|  | 874 | int offset = (start & CachePage::kPageMask); | 
|  | 875 | while (!AllOnOnePage(start, size - 1)) { | 
|  | 876 | int bytes_to_flush = CachePage::kPageSize - offset; | 
|  | 877 | FlushOnePage(i_cache, start, bytes_to_flush); | 
|  | 878 | start += bytes_to_flush; | 
|  | 879 | size -= bytes_to_flush; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 880 | DCHECK_EQ(0, start & CachePage::kPageMask); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 881 | offset = 0; | 
|  | 882 | } | 
|  | 883 | if (size != 0) { | 
|  | 884 | FlushOnePage(i_cache, start, size); | 
|  | 885 | } | 
|  | 886 | } | 
|  | 887 |  | 
|  | 888 |  | 
|  | 889 | CachePage* Simulator::GetCachePage(v8::internal::HashMap* i_cache, void* page) { | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 890 | v8::internal::HashMap::Entry* entry = | 
|  | 891 | i_cache->LookupOrInsert(page, ICacheHash(page)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 892 | if (entry->value == NULL) { | 
|  | 893 | CachePage* new_page = new CachePage(); | 
|  | 894 | entry->value = new_page; | 
|  | 895 | } | 
|  | 896 | return reinterpret_cast<CachePage*>(entry->value); | 
|  | 897 | } | 
|  | 898 |  | 
|  | 899 |  | 
|  | 900 | // Flush from start up to and not including start + size. | 
|  | 901 | void Simulator::FlushOnePage(v8::internal::HashMap* i_cache, | 
|  | 902 | intptr_t start, | 
|  | 903 | int size) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 904 | DCHECK(size <= CachePage::kPageSize); | 
|  | 905 | DCHECK(AllOnOnePage(start, size - 1)); | 
|  | 906 | DCHECK((start & CachePage::kLineMask) == 0); | 
|  | 907 | DCHECK((size & CachePage::kLineMask) == 0); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 908 | void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask)); | 
|  | 909 | int offset = (start & CachePage::kPageMask); | 
|  | 910 | CachePage* cache_page = GetCachePage(i_cache, page); | 
|  | 911 | char* valid_bytemap = cache_page->ValidityByte(offset); | 
|  | 912 | memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); | 
|  | 913 | } | 
|  | 914 |  | 
|  | 915 |  | 
|  | 916 | void Simulator::CheckICache(v8::internal::HashMap* i_cache, | 
|  | 917 | Instruction* instr) { | 
|  | 918 | intptr_t address = reinterpret_cast<intptr_t>(instr); | 
|  | 919 | void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask)); | 
|  | 920 | void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask)); | 
|  | 921 | int offset = (address & CachePage::kPageMask); | 
|  | 922 | CachePage* cache_page = GetCachePage(i_cache, page); | 
|  | 923 | char* cache_valid_byte = cache_page->ValidityByte(offset); | 
|  | 924 | bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); | 
|  | 925 | char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask); | 
|  | 926 | if (cache_hit) { | 
|  | 927 | // Check that the data in memory matches the contents of the I-cache. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 928 | CHECK_EQ(0, memcmp(reinterpret_cast<void*>(instr), | 
|  | 929 | cache_page->CachedData(offset), | 
|  | 930 | Instruction::kInstrSize)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 931 | } else { | 
|  | 932 | // Cache miss.  Load memory into the cache. | 
|  | 933 | memcpy(cached_line, line, CachePage::kLineLength); | 
|  | 934 | *cache_valid_byte = CachePage::LINE_VALID; | 
|  | 935 | } | 
|  | 936 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 937 |  | 
|  | 938 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 939 | void Simulator::Initialize(Isolate* isolate) { | 
|  | 940 | if (isolate->simulator_initialized()) return; | 
|  | 941 | isolate->set_simulator_initialized(true); | 
|  | 942 | ::v8::internal::ExternalReference::set_redirector(isolate, | 
|  | 943 | &RedirectExternalReference); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 944 | } | 
|  | 945 |  | 
|  | 946 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 947 | Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 948 | i_cache_ = isolate_->simulator_i_cache(); | 
|  | 949 | if (i_cache_ == NULL) { | 
|  | 950 | i_cache_ = new v8::internal::HashMap(&ICacheMatch); | 
|  | 951 | isolate_->set_simulator_i_cache(i_cache_); | 
|  | 952 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 953 | Initialize(isolate); | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 954 | // Set up simulator support first. Some of this information is needed to | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 955 | // setup the architecture state. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 956 | stack_ = reinterpret_cast<char*>(malloc(stack_size_)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 957 | pc_modified_ = false; | 
|  | 958 | icount_ = 0; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 959 | break_count_ = 0; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 960 | break_pc_ = NULL; | 
|  | 961 | break_instr_ = 0; | 
|  | 962 |  | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 963 | // Set up architecture state. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 964 | // All registers are initialized to zero to start with. | 
|  | 965 | for (int i = 0; i < kNumSimuRegisters; i++) { | 
|  | 966 | registers_[i] = 0; | 
|  | 967 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 968 | for (int i = 0; i < kNumFPURegisters; i++) { | 
|  | 969 | FPUregisters_[i] = 0; | 
|  | 970 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 971 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 972 | FCSR_ = kFCSRNaN2008FlagMask; | 
|  | 973 | } else { | 
|  | 974 | DCHECK(IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kMips32r2)); | 
|  | 975 | FCSR_ = 0; | 
|  | 976 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 977 |  | 
|  | 978 | // The sp is initialized to point to the bottom (high address) of the | 
|  | 979 | // allocated stack area. To be safe in potential stack underflows we leave | 
|  | 980 | // some buffer below. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 981 | registers_[sp] = reinterpret_cast<int32_t>(stack_) + stack_size_ - 64; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 982 | // The ra and pc are initialized to a known bad value that will cause an | 
|  | 983 | // access violation if the simulator ever tries to execute it. | 
|  | 984 | registers_[pc] = bad_ra; | 
|  | 985 | registers_[ra] = bad_ra; | 
|  | 986 | InitializeCoverage(); | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 987 | last_debugger_input_ = NULL; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 988 | } | 
|  | 989 |  | 
|  | 990 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 991 | Simulator::~Simulator() { free(stack_); } | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 992 |  | 
|  | 993 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 994 | // When the generated code calls an external reference we need to catch that in | 
|  | 995 | // the simulator.  The external reference will be a function compiled for the | 
|  | 996 | // host architecture.  We need to call that function instead of trying to | 
|  | 997 | // execute it with the simulator.  We do that by redirecting the external | 
|  | 998 | // reference to a swi (software-interrupt) instruction that is handled by | 
|  | 999 | // the simulator.  We write the original destination of the jump just at a known | 
|  | 1000 | // offset from the swi instruction so the simulator knows what to call. | 
|  | 1001 | class Redirection { | 
|  | 1002 | public: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1003 | Redirection(Isolate* isolate, void* external_function, | 
|  | 1004 | ExternalReference::Type type) | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1005 | : external_function_(external_function), | 
|  | 1006 | swi_instruction_(rtCallRedirInstr), | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1007 | type_(type), | 
|  | 1008 | next_(NULL) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1009 | next_ = isolate->simulator_redirection(); | 
|  | 1010 | Simulator::current(isolate)-> | 
|  | 1011 | FlushICache(isolate->simulator_i_cache(), | 
|  | 1012 | reinterpret_cast<void*>(&swi_instruction_), | 
|  | 1013 | Instruction::kInstrSize); | 
|  | 1014 | isolate->set_simulator_redirection(this); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1015 | } | 
|  | 1016 |  | 
|  | 1017 | void* address_of_swi_instruction() { | 
|  | 1018 | return reinterpret_cast<void*>(&swi_instruction_); | 
|  | 1019 | } | 
|  | 1020 |  | 
|  | 1021 | void* external_function() { return external_function_; } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1022 | ExternalReference::Type type() { return type_; } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1023 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1024 | static Redirection* Get(Isolate* isolate, void* external_function, | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1025 | ExternalReference::Type type) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1026 | Redirection* current = isolate->simulator_redirection(); | 
|  | 1027 | for (; current != NULL; current = current->next_) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1028 | if (current->external_function_ == external_function) return current; | 
|  | 1029 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1030 | return new Redirection(isolate, external_function, type); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1031 | } | 
|  | 1032 |  | 
|  | 1033 | static Redirection* FromSwiInstruction(Instruction* swi_instruction) { | 
|  | 1034 | char* addr_of_swi = reinterpret_cast<char*>(swi_instruction); | 
|  | 1035 | char* addr_of_redirection = | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1036 | addr_of_swi - offsetof(Redirection, swi_instruction_); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1037 | return reinterpret_cast<Redirection*>(addr_of_redirection); | 
|  | 1038 | } | 
|  | 1039 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1040 | static void* ReverseRedirection(int32_t reg) { | 
|  | 1041 | Redirection* redirection = FromSwiInstruction( | 
|  | 1042 | reinterpret_cast<Instruction*>(reinterpret_cast<void*>(reg))); | 
|  | 1043 | return redirection->external_function(); | 
|  | 1044 | } | 
|  | 1045 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1046 | static void DeleteChain(Redirection* redirection) { | 
|  | 1047 | while (redirection != nullptr) { | 
|  | 1048 | Redirection* next = redirection->next_; | 
|  | 1049 | delete redirection; | 
|  | 1050 | redirection = next; | 
|  | 1051 | } | 
|  | 1052 | } | 
|  | 1053 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1054 | private: | 
|  | 1055 | void* external_function_; | 
|  | 1056 | uint32_t swi_instruction_; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1057 | ExternalReference::Type type_; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1058 | Redirection* next_; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1059 | }; | 
|  | 1060 |  | 
|  | 1061 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1062 | // static | 
|  | 1063 | void Simulator::TearDown(HashMap* i_cache, Redirection* first) { | 
|  | 1064 | Redirection::DeleteChain(first); | 
|  | 1065 | if (i_cache != nullptr) { | 
|  | 1066 | for (HashMap::Entry* entry = i_cache->Start(); entry != nullptr; | 
|  | 1067 | entry = i_cache->Next(entry)) { | 
|  | 1068 | delete static_cast<CachePage*>(entry->value); | 
|  | 1069 | } | 
|  | 1070 | delete i_cache; | 
|  | 1071 | } | 
|  | 1072 | } | 
|  | 1073 |  | 
|  | 1074 |  | 
|  | 1075 | void* Simulator::RedirectExternalReference(Isolate* isolate, | 
|  | 1076 | void* external_function, | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1077 | ExternalReference::Type type) { | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1078 | Redirection* redirection = Redirection::Get(isolate, external_function, type); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1079 | return redirection->address_of_swi_instruction(); | 
|  | 1080 | } | 
|  | 1081 |  | 
|  | 1082 |  | 
|  | 1083 | // Get the active Simulator for the current thread. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1084 | Simulator* Simulator::current(Isolate* isolate) { | 
|  | 1085 | v8::internal::Isolate::PerIsolateThreadData* isolate_data = | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1086 | isolate->FindOrAllocatePerThreadDataForThisThread(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1087 | DCHECK(isolate_data != NULL); | 
|  | 1088 | DCHECK(isolate_data != NULL); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1089 |  | 
|  | 1090 | Simulator* sim = isolate_data->simulator(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1091 | if (sim == NULL) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1092 | // TODO(146): delete the simulator object when a thread/isolate goes away. | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1093 | sim = new Simulator(isolate); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1094 | isolate_data->set_simulator(sim); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1095 | } | 
|  | 1096 | return sim; | 
|  | 1097 | } | 
|  | 1098 |  | 
|  | 1099 |  | 
|  | 1100 | // Sets the register in the architecture state. It will also deal with updating | 
|  | 1101 | // Simulator internal state for special registers such as PC. | 
|  | 1102 | void Simulator::set_register(int reg, int32_t value) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1103 | DCHECK((reg >= 0) && (reg < kNumSimuRegisters)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1104 | if (reg == pc) { | 
|  | 1105 | pc_modified_ = true; | 
|  | 1106 | } | 
|  | 1107 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1108 | // Zero register always holds 0. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1109 | registers_[reg] = (reg == 0) ? 0 : value; | 
|  | 1110 | } | 
|  | 1111 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1112 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1113 | void Simulator::set_dw_register(int reg, const int* dbl) { | 
|  | 1114 | DCHECK((reg >= 0) && (reg < kNumSimuRegisters)); | 
|  | 1115 | registers_[reg] = dbl[0]; | 
|  | 1116 | registers_[reg + 1] = dbl[1]; | 
|  | 1117 | } | 
|  | 1118 |  | 
|  | 1119 |  | 
|  | 1120 | void Simulator::set_fpu_register(int fpureg, int64_t value) { | 
|  | 1121 | DCHECK(IsFp64Mode()); | 
|  | 1122 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1123 | FPUregisters_[fpureg] = value; | 
|  | 1124 | } | 
|  | 1125 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1126 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1127 | void Simulator::set_fpu_register_word(int fpureg, int32_t value) { | 
|  | 1128 | // Set ONLY lower 32-bits, leaving upper bits untouched. | 
|  | 1129 | // TODO(plind): big endian issue. | 
|  | 1130 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1131 | int32_t *pword = reinterpret_cast<int32_t*>(&FPUregisters_[fpureg]); | 
|  | 1132 | *pword = value; | 
|  | 1133 | } | 
|  | 1134 |  | 
|  | 1135 |  | 
|  | 1136 | void Simulator::set_fpu_register_hi_word(int fpureg, int32_t value) { | 
|  | 1137 | // Set ONLY upper 32-bits, leaving lower bits untouched. | 
|  | 1138 | // TODO(plind): big endian issue. | 
|  | 1139 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1140 | int32_t *phiword = (reinterpret_cast<int32_t*>(&FPUregisters_[fpureg])) + 1; | 
|  | 1141 | *phiword = value; | 
|  | 1142 | } | 
|  | 1143 |  | 
|  | 1144 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1145 | void Simulator::set_fpu_register_float(int fpureg, float value) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1146 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1147 | *bit_cast<float*>(&FPUregisters_[fpureg]) = value; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1148 | } | 
|  | 1149 |  | 
|  | 1150 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1151 | void Simulator::set_fpu_register_double(int fpureg, double value) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1152 | if (IsFp64Mode()) { | 
|  | 1153 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1154 | *bit_cast<double*>(&FPUregisters_[fpureg]) = value; | 
|  | 1155 | } else { | 
|  | 1156 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0)); | 
|  | 1157 | int64_t i64 = bit_cast<int64_t>(value); | 
|  | 1158 | set_fpu_register_word(fpureg, i64 & 0xffffffff); | 
|  | 1159 | set_fpu_register_word(fpureg + 1, i64 >> 32); | 
|  | 1160 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1161 | } | 
|  | 1162 |  | 
|  | 1163 |  | 
|  | 1164 | // Get the register from the architecture state. This function does handle | 
|  | 1165 | // the special case of accessing the PC register. | 
|  | 1166 | int32_t Simulator::get_register(int reg) const { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1167 | DCHECK((reg >= 0) && (reg < kNumSimuRegisters)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1168 | if (reg == 0) | 
|  | 1169 | return 0; | 
|  | 1170 | else | 
|  | 1171 | return registers_[reg] + ((reg == pc) ? Instruction::kPCReadOffset : 0); | 
|  | 1172 | } | 
|  | 1173 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1174 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1175 | double Simulator::get_double_from_register_pair(int reg) { | 
|  | 1176 | // TODO(plind): bad ABI stuff, refactor or remove. | 
|  | 1177 | DCHECK((reg >= 0) && (reg < kNumSimuRegisters) && ((reg % 2) == 0)); | 
|  | 1178 |  | 
|  | 1179 | double dm_val = 0.0; | 
|  | 1180 | // Read the bits from the unsigned integer register_[] array | 
|  | 1181 | // into the double precision floating point value and return it. | 
|  | 1182 | char buffer[2 * sizeof(registers_[0])]; | 
|  | 1183 | memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); | 
|  | 1184 | memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); | 
|  | 1185 | return(dm_val); | 
|  | 1186 | } | 
|  | 1187 |  | 
|  | 1188 |  | 
|  | 1189 | int64_t Simulator::get_fpu_register(int fpureg) const { | 
|  | 1190 | DCHECK(IsFp64Mode()); | 
|  | 1191 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1192 | return FPUregisters_[fpureg]; | 
|  | 1193 | } | 
|  | 1194 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1195 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1196 | int32_t Simulator::get_fpu_register_word(int fpureg) const { | 
|  | 1197 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1198 | return static_cast<int32_t>(FPUregisters_[fpureg] & 0xffffffff); | 
|  | 1199 | } | 
|  | 1200 |  | 
|  | 1201 |  | 
|  | 1202 | int32_t Simulator::get_fpu_register_signed_word(int fpureg) const { | 
|  | 1203 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1204 | return static_cast<int32_t>(FPUregisters_[fpureg] & 0xffffffff); | 
|  | 1205 | } | 
|  | 1206 |  | 
|  | 1207 |  | 
|  | 1208 | int32_t Simulator::get_fpu_register_hi_word(int fpureg) const { | 
|  | 1209 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1210 | return static_cast<int32_t>((FPUregisters_[fpureg] >> 32) & 0xffffffff); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1211 | } | 
|  | 1212 |  | 
|  | 1213 |  | 
|  | 1214 | float Simulator::get_fpu_register_float(int fpureg) const { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1215 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1216 | return *bit_cast<float*>(const_cast<int64_t*>(&FPUregisters_[fpureg])); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1217 | } | 
|  | 1218 |  | 
|  | 1219 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1220 | double Simulator::get_fpu_register_double(int fpureg) const { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1221 | if (IsFp64Mode()) { | 
|  | 1222 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); | 
|  | 1223 | return *bit_cast<double*>(&FPUregisters_[fpureg]); | 
|  | 1224 | } else { | 
|  | 1225 | DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0)); | 
|  | 1226 | int64_t i64; | 
|  | 1227 | i64 = static_cast<uint32_t>(get_fpu_register_word(fpureg)); | 
|  | 1228 | i64 |= static_cast<uint64_t>(get_fpu_register_word(fpureg + 1)) << 32; | 
|  | 1229 | return bit_cast<double>(i64); | 
|  | 1230 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1231 | } | 
|  | 1232 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1233 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1234 | // Runtime FP routines take up to two double arguments and zero | 
|  | 1235 | // or one integer arguments. All are constructed here, | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1236 | // from a0-a3 or f12 and f14. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1237 | void Simulator::GetFpArgs(double* x, double* y, int32_t* z) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1238 | if (!IsMipsSoftFloatABI) { | 
|  | 1239 | *x = get_fpu_register_double(12); | 
|  | 1240 | *y = get_fpu_register_double(14); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1241 | *z = get_register(a2); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1242 | } else { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1243 | // TODO(plind): bad ABI stuff, refactor or remove. | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1244 | // We use a char buffer to get around the strict-aliasing rules which | 
|  | 1245 | // otherwise allow the compiler to optimize away the copy. | 
|  | 1246 | char buffer[sizeof(*x)]; | 
|  | 1247 | int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer); | 
|  | 1248 |  | 
|  | 1249 | // Registers a0 and a1 -> x. | 
|  | 1250 | reg_buffer[0] = get_register(a0); | 
|  | 1251 | reg_buffer[1] = get_register(a1); | 
|  | 1252 | memcpy(x, buffer, sizeof(buffer)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1253 | // Registers a2 and a3 -> y. | 
|  | 1254 | reg_buffer[0] = get_register(a2); | 
|  | 1255 | reg_buffer[1] = get_register(a3); | 
|  | 1256 | memcpy(y, buffer, sizeof(buffer)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1257 | // Register 2 -> z. | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1258 | reg_buffer[0] = get_register(a2); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1259 | memcpy(z, buffer, sizeof(*z)); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1260 | } | 
|  | 1261 | } | 
|  | 1262 |  | 
|  | 1263 |  | 
|  | 1264 | // The return value is either in v0/v1 or f0. | 
|  | 1265 | void Simulator::SetFpResult(const double& result) { | 
|  | 1266 | if (!IsMipsSoftFloatABI) { | 
|  | 1267 | set_fpu_register_double(0, result); | 
|  | 1268 | } else { | 
|  | 1269 | char buffer[2 * sizeof(registers_[0])]; | 
|  | 1270 | int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer); | 
|  | 1271 | memcpy(buffer, &result, sizeof(buffer)); | 
|  | 1272 | // Copy result to v0 and v1. | 
|  | 1273 | set_register(v0, reg_buffer[0]); | 
|  | 1274 | set_register(v1, reg_buffer[1]); | 
|  | 1275 | } | 
|  | 1276 | } | 
|  | 1277 |  | 
|  | 1278 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1279 | // Helper functions for setting and testing the FCSR register's bits. | 
|  | 1280 | void Simulator::set_fcsr_bit(uint32_t cc, bool value) { | 
|  | 1281 | if (value) { | 
|  | 1282 | FCSR_ |= (1 << cc); | 
|  | 1283 | } else { | 
|  | 1284 | FCSR_ &= ~(1 << cc); | 
|  | 1285 | } | 
|  | 1286 | } | 
|  | 1287 |  | 
|  | 1288 |  | 
|  | 1289 | bool Simulator::test_fcsr_bit(uint32_t cc) { | 
|  | 1290 | return FCSR_ & (1 << cc); | 
|  | 1291 | } | 
|  | 1292 |  | 
|  | 1293 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1294 | void Simulator::set_fcsr_rounding_mode(FPURoundingMode mode) { | 
|  | 1295 | FCSR_ |= mode & kFPURoundingModeMask; | 
|  | 1296 | } | 
|  | 1297 |  | 
|  | 1298 |  | 
|  | 1299 | unsigned int Simulator::get_fcsr_rounding_mode() { | 
|  | 1300 | return FCSR_ & kFPURoundingModeMask; | 
|  | 1301 | } | 
|  | 1302 |  | 
|  | 1303 |  | 
|  | 1304 | void Simulator::set_fpu_register_word_invalid_result(float original, | 
|  | 1305 | float rounded) { | 
|  | 1306 | if (FCSR_ & kFCSRNaN2008FlagMask) { | 
|  | 1307 | double max_int32 = std::numeric_limits<int32_t>::max(); | 
|  | 1308 | double min_int32 = std::numeric_limits<int32_t>::min(); | 
|  | 1309 | if (std::isnan(original)) { | 
|  | 1310 | set_fpu_register_word(fd_reg(), 0); | 
|  | 1311 | } else if (rounded > max_int32) { | 
|  | 1312 | set_fpu_register_word(fd_reg(), kFPUInvalidResult); | 
|  | 1313 | } else if (rounded < min_int32) { | 
|  | 1314 | set_fpu_register_word(fd_reg(), kFPUInvalidResultNegative); | 
|  | 1315 | } else { | 
|  | 1316 | UNREACHABLE(); | 
|  | 1317 | } | 
|  | 1318 | } else { | 
|  | 1319 | set_fpu_register_word(fd_reg(), kFPUInvalidResult); | 
|  | 1320 | } | 
|  | 1321 | } | 
|  | 1322 |  | 
|  | 1323 |  | 
|  | 1324 | void Simulator::set_fpu_register_invalid_result(float original, float rounded) { | 
|  | 1325 | if (FCSR_ & kFCSRNaN2008FlagMask) { | 
|  | 1326 | double max_int32 = std::numeric_limits<int32_t>::max(); | 
|  | 1327 | double min_int32 = std::numeric_limits<int32_t>::min(); | 
|  | 1328 | if (std::isnan(original)) { | 
|  | 1329 | set_fpu_register(fd_reg(), 0); | 
|  | 1330 | } else if (rounded > max_int32) { | 
|  | 1331 | set_fpu_register(fd_reg(), kFPUInvalidResult); | 
|  | 1332 | } else if (rounded < min_int32) { | 
|  | 1333 | set_fpu_register(fd_reg(), kFPUInvalidResultNegative); | 
|  | 1334 | } else { | 
|  | 1335 | UNREACHABLE(); | 
|  | 1336 | } | 
|  | 1337 | } else { | 
|  | 1338 | set_fpu_register(fd_reg(), kFPUInvalidResult); | 
|  | 1339 | } | 
|  | 1340 | } | 
|  | 1341 |  | 
|  | 1342 |  | 
|  | 1343 | void Simulator::set_fpu_register_invalid_result64(float original, | 
|  | 1344 | float rounded) { | 
|  | 1345 | if (FCSR_ & kFCSRNaN2008FlagMask) { | 
|  | 1346 | // The value of INT64_MAX (2^63-1) can't be represented as double exactly, | 
|  | 1347 | // loading the most accurate representation into max_int64, which is 2^63. | 
|  | 1348 | double max_int64 = std::numeric_limits<int64_t>::max(); | 
|  | 1349 | double min_int64 = std::numeric_limits<int64_t>::min(); | 
|  | 1350 | if (std::isnan(original)) { | 
|  | 1351 | set_fpu_register(fd_reg(), 0); | 
|  | 1352 | } else if (rounded >= max_int64) { | 
|  | 1353 | set_fpu_register(fd_reg(), kFPU64InvalidResult); | 
|  | 1354 | } else if (rounded < min_int64) { | 
|  | 1355 | set_fpu_register(fd_reg(), kFPU64InvalidResultNegative); | 
|  | 1356 | } else { | 
|  | 1357 | UNREACHABLE(); | 
|  | 1358 | } | 
|  | 1359 | } else { | 
|  | 1360 | set_fpu_register(fd_reg(), kFPU64InvalidResult); | 
|  | 1361 | } | 
|  | 1362 | } | 
|  | 1363 |  | 
|  | 1364 |  | 
|  | 1365 | void Simulator::set_fpu_register_word_invalid_result(double original, | 
|  | 1366 | double rounded) { | 
|  | 1367 | if (FCSR_ & kFCSRNaN2008FlagMask) { | 
|  | 1368 | double max_int32 = std::numeric_limits<int32_t>::max(); | 
|  | 1369 | double min_int32 = std::numeric_limits<int32_t>::min(); | 
|  | 1370 | if (std::isnan(original)) { | 
|  | 1371 | set_fpu_register_word(fd_reg(), 0); | 
|  | 1372 | } else if (rounded > max_int32) { | 
|  | 1373 | set_fpu_register_word(fd_reg(), kFPUInvalidResult); | 
|  | 1374 | } else if (rounded < min_int32) { | 
|  | 1375 | set_fpu_register_word(fd_reg(), kFPUInvalidResultNegative); | 
|  | 1376 | } else { | 
|  | 1377 | UNREACHABLE(); | 
|  | 1378 | } | 
|  | 1379 | } else { | 
|  | 1380 | set_fpu_register_word(fd_reg(), kFPUInvalidResult); | 
|  | 1381 | } | 
|  | 1382 | } | 
|  | 1383 |  | 
|  | 1384 |  | 
|  | 1385 | void Simulator::set_fpu_register_invalid_result(double original, | 
|  | 1386 | double rounded) { | 
|  | 1387 | if (FCSR_ & kFCSRNaN2008FlagMask) { | 
|  | 1388 | double max_int32 = std::numeric_limits<int32_t>::max(); | 
|  | 1389 | double min_int32 = std::numeric_limits<int32_t>::min(); | 
|  | 1390 | if (std::isnan(original)) { | 
|  | 1391 | set_fpu_register(fd_reg(), 0); | 
|  | 1392 | } else if (rounded > max_int32) { | 
|  | 1393 | set_fpu_register(fd_reg(), kFPUInvalidResult); | 
|  | 1394 | } else if (rounded < min_int32) { | 
|  | 1395 | set_fpu_register(fd_reg(), kFPUInvalidResultNegative); | 
|  | 1396 | } else { | 
|  | 1397 | UNREACHABLE(); | 
|  | 1398 | } | 
|  | 1399 | } else { | 
|  | 1400 | set_fpu_register(fd_reg(), kFPUInvalidResult); | 
|  | 1401 | } | 
|  | 1402 | } | 
|  | 1403 |  | 
|  | 1404 |  | 
|  | 1405 | void Simulator::set_fpu_register_invalid_result64(double original, | 
|  | 1406 | double rounded) { | 
|  | 1407 | if (FCSR_ & kFCSRNaN2008FlagMask) { | 
|  | 1408 | // The value of INT64_MAX (2^63-1) can't be represented as double exactly, | 
|  | 1409 | // loading the most accurate representation into max_int64, which is 2^63. | 
|  | 1410 | double max_int64 = std::numeric_limits<int64_t>::max(); | 
|  | 1411 | double min_int64 = std::numeric_limits<int64_t>::min(); | 
|  | 1412 | if (std::isnan(original)) { | 
|  | 1413 | set_fpu_register(fd_reg(), 0); | 
|  | 1414 | } else if (rounded >= max_int64) { | 
|  | 1415 | set_fpu_register(fd_reg(), kFPU64InvalidResult); | 
|  | 1416 | } else if (rounded < min_int64) { | 
|  | 1417 | set_fpu_register(fd_reg(), kFPU64InvalidResultNegative); | 
|  | 1418 | } else { | 
|  | 1419 | UNREACHABLE(); | 
|  | 1420 | } | 
|  | 1421 | } else { | 
|  | 1422 | set_fpu_register(fd_reg(), kFPU64InvalidResult); | 
|  | 1423 | } | 
|  | 1424 | } | 
|  | 1425 |  | 
|  | 1426 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1427 | // Sets the rounding error codes in FCSR based on the result of the rounding. | 
|  | 1428 | // Returns true if the operation was invalid. | 
|  | 1429 | bool Simulator::set_fcsr_round_error(double original, double rounded) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1430 | bool ret = false; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1431 | double max_int32 = std::numeric_limits<int32_t>::max(); | 
|  | 1432 | double min_int32 = std::numeric_limits<int32_t>::min(); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1433 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1434 | if (!std::isfinite(original) || !std::isfinite(rounded)) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1435 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1436 | ret = true; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1437 | } | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1438 |  | 
|  | 1439 | if (original != rounded) { | 
|  | 1440 | set_fcsr_bit(kFCSRInexactFlagBit, true); | 
|  | 1441 | } | 
|  | 1442 |  | 
|  | 1443 | if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) { | 
|  | 1444 | set_fcsr_bit(kFCSRUnderflowFlagBit, true); | 
|  | 1445 | ret = true; | 
|  | 1446 | } | 
|  | 1447 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1448 | if (rounded > max_int32 || rounded < min_int32) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1449 | set_fcsr_bit(kFCSROverflowFlagBit, true); | 
|  | 1450 | // The reference is not really clear but it seems this is required: | 
|  | 1451 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1452 | ret = true; | 
|  | 1453 | } | 
|  | 1454 |  | 
|  | 1455 | return ret; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1456 | } | 
|  | 1457 |  | 
|  | 1458 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1459 | // Sets the rounding error codes in FCSR based on the result of the rounding. | 
|  | 1460 | // Returns true if the operation was invalid. | 
|  | 1461 | bool Simulator::set_fcsr_round64_error(double original, double rounded) { | 
|  | 1462 | bool ret = false; | 
|  | 1463 | // The value of INT64_MAX (2^63-1) can't be represented as double exactly, | 
|  | 1464 | // loading the most accurate representation into max_int64, which is 2^63. | 
|  | 1465 | double max_int64 = std::numeric_limits<int64_t>::max(); | 
|  | 1466 | double min_int64 = std::numeric_limits<int64_t>::min(); | 
|  | 1467 |  | 
|  | 1468 | if (!std::isfinite(original) || !std::isfinite(rounded)) { | 
|  | 1469 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1470 | ret = true; | 
|  | 1471 | } | 
|  | 1472 |  | 
|  | 1473 | if (original != rounded) { | 
|  | 1474 | set_fcsr_bit(kFCSRInexactFlagBit, true); | 
|  | 1475 | } | 
|  | 1476 |  | 
|  | 1477 | if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) { | 
|  | 1478 | set_fcsr_bit(kFCSRUnderflowFlagBit, true); | 
|  | 1479 | ret = true; | 
|  | 1480 | } | 
|  | 1481 |  | 
|  | 1482 | if (rounded >= max_int64 || rounded < min_int64) { | 
|  | 1483 | set_fcsr_bit(kFCSROverflowFlagBit, true); | 
|  | 1484 | // The reference is not really clear but it seems this is required: | 
|  | 1485 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1486 | ret = true; | 
|  | 1487 | } | 
|  | 1488 |  | 
|  | 1489 | return ret; | 
|  | 1490 | } | 
|  | 1491 |  | 
|  | 1492 |  | 
|  | 1493 | // Sets the rounding error codes in FCSR based on the result of the rounding. | 
|  | 1494 | // Returns true if the operation was invalid. | 
|  | 1495 | bool Simulator::set_fcsr_round_error(float original, float rounded) { | 
|  | 1496 | bool ret = false; | 
|  | 1497 | double max_int32 = std::numeric_limits<int32_t>::max(); | 
|  | 1498 | double min_int32 = std::numeric_limits<int32_t>::min(); | 
|  | 1499 |  | 
|  | 1500 | if (!std::isfinite(original) || !std::isfinite(rounded)) { | 
|  | 1501 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1502 | ret = true; | 
|  | 1503 | } | 
|  | 1504 |  | 
|  | 1505 | if (original != rounded) { | 
|  | 1506 | set_fcsr_bit(kFCSRInexactFlagBit, true); | 
|  | 1507 | } | 
|  | 1508 |  | 
|  | 1509 | if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) { | 
|  | 1510 | set_fcsr_bit(kFCSRUnderflowFlagBit, true); | 
|  | 1511 | ret = true; | 
|  | 1512 | } | 
|  | 1513 |  | 
|  | 1514 | if (rounded > max_int32 || rounded < min_int32) { | 
|  | 1515 | set_fcsr_bit(kFCSROverflowFlagBit, true); | 
|  | 1516 | // The reference is not really clear but it seems this is required: | 
|  | 1517 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1518 | ret = true; | 
|  | 1519 | } | 
|  | 1520 |  | 
|  | 1521 | return ret; | 
|  | 1522 | } | 
|  | 1523 |  | 
|  | 1524 |  | 
|  | 1525 | // Sets the rounding error codes in FCSR based on the result of the rounding. | 
|  | 1526 | // Returns true if the operation was invalid. | 
|  | 1527 | bool Simulator::set_fcsr_round64_error(float original, float rounded) { | 
|  | 1528 | bool ret = false; | 
|  | 1529 | // The value of INT64_MAX (2^63-1) can't be represented as double exactly, | 
|  | 1530 | // loading the most accurate representation into max_int64, which is 2^63. | 
|  | 1531 | double max_int64 = std::numeric_limits<int64_t>::max(); | 
|  | 1532 | double min_int64 = std::numeric_limits<int64_t>::min(); | 
|  | 1533 |  | 
|  | 1534 | if (!std::isfinite(original) || !std::isfinite(rounded)) { | 
|  | 1535 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1536 | ret = true; | 
|  | 1537 | } | 
|  | 1538 |  | 
|  | 1539 | if (original != rounded) { | 
|  | 1540 | set_fcsr_bit(kFCSRInexactFlagBit, true); | 
|  | 1541 | } | 
|  | 1542 |  | 
|  | 1543 | if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) { | 
|  | 1544 | set_fcsr_bit(kFCSRUnderflowFlagBit, true); | 
|  | 1545 | ret = true; | 
|  | 1546 | } | 
|  | 1547 |  | 
|  | 1548 | if (rounded >= max_int64 || rounded < min_int64) { | 
|  | 1549 | set_fcsr_bit(kFCSROverflowFlagBit, true); | 
|  | 1550 | // The reference is not really clear but it seems this is required: | 
|  | 1551 | set_fcsr_bit(kFCSRInvalidOpFlagBit, true); | 
|  | 1552 | ret = true; | 
|  | 1553 | } | 
|  | 1554 |  | 
|  | 1555 | return ret; | 
|  | 1556 | } | 
|  | 1557 |  | 
|  | 1558 |  | 
|  | 1559 | void Simulator::round_according_to_fcsr(double toRound, double& rounded, | 
|  | 1560 | int32_t& rounded_int, double fs) { | 
|  | 1561 | // 0 RN (round to nearest): Round a result to the nearest | 
|  | 1562 | // representable value; if the result is exactly halfway between | 
|  | 1563 | // two representable values, round to zero. Behave like round_w_d. | 
|  | 1564 |  | 
|  | 1565 | // 1 RZ (round toward zero): Round a result to the closest | 
|  | 1566 | // representable value whose absolute value is less than or | 
|  | 1567 | // equal to the infinitely accurate result. Behave like trunc_w_d. | 
|  | 1568 |  | 
|  | 1569 | // 2 RP (round up, or toward  infinity): Round a result to the | 
|  | 1570 | // next representable value up. Behave like ceil_w_d. | 
|  | 1571 |  | 
|  | 1572 | // 3 RD (round down, or toward −infinity): Round a result to | 
|  | 1573 | // the next representable value down. Behave like floor_w_d. | 
|  | 1574 | switch (get_fcsr_rounding_mode()) { | 
|  | 1575 | case kRoundToNearest: | 
|  | 1576 | rounded = std::floor(fs + 0.5); | 
|  | 1577 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1578 | if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) { | 
|  | 1579 | // If the number is halfway between two integers, | 
|  | 1580 | // round to the even one. | 
|  | 1581 | rounded_int--; | 
|  | 1582 | } | 
|  | 1583 | break; | 
|  | 1584 | case kRoundToZero: | 
|  | 1585 | rounded = trunc(fs); | 
|  | 1586 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1587 | break; | 
|  | 1588 | case kRoundToPlusInf: | 
|  | 1589 | rounded = std::ceil(fs); | 
|  | 1590 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1591 | break; | 
|  | 1592 | case kRoundToMinusInf: | 
|  | 1593 | rounded = std::floor(fs); | 
|  | 1594 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1595 | break; | 
|  | 1596 | } | 
|  | 1597 | } | 
|  | 1598 |  | 
|  | 1599 |  | 
|  | 1600 | void Simulator::round_according_to_fcsr(float toRound, float& rounded, | 
|  | 1601 | int32_t& rounded_int, float fs) { | 
|  | 1602 | // 0 RN (round to nearest): Round a result to the nearest | 
|  | 1603 | // representable value; if the result is exactly halfway between | 
|  | 1604 | // two representable values, round to zero. Behave like round_w_d. | 
|  | 1605 |  | 
|  | 1606 | // 1 RZ (round toward zero): Round a result to the closest | 
|  | 1607 | // representable value whose absolute value is less than or | 
|  | 1608 | // equal to the infinitely accurate result. Behave like trunc_w_d. | 
|  | 1609 |  | 
|  | 1610 | // 2 RP (round up, or toward  infinity): Round a result to the | 
|  | 1611 | // next representable value up. Behave like ceil_w_d. | 
|  | 1612 |  | 
|  | 1613 | // 3 RD (round down, or toward −infinity): Round a result to | 
|  | 1614 | // the next representable value down. Behave like floor_w_d. | 
|  | 1615 | switch (get_fcsr_rounding_mode()) { | 
|  | 1616 | case kRoundToNearest: | 
|  | 1617 | rounded = std::floor(fs + 0.5); | 
|  | 1618 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1619 | if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) { | 
|  | 1620 | // If the number is halfway between two integers, | 
|  | 1621 | // round to the even one. | 
|  | 1622 | rounded_int--; | 
|  | 1623 | } | 
|  | 1624 | break; | 
|  | 1625 | case kRoundToZero: | 
|  | 1626 | rounded = trunc(fs); | 
|  | 1627 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1628 | break; | 
|  | 1629 | case kRoundToPlusInf: | 
|  | 1630 | rounded = std::ceil(fs); | 
|  | 1631 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1632 | break; | 
|  | 1633 | case kRoundToMinusInf: | 
|  | 1634 | rounded = std::floor(fs); | 
|  | 1635 | rounded_int = static_cast<int32_t>(rounded); | 
|  | 1636 | break; | 
|  | 1637 | } | 
|  | 1638 | } | 
|  | 1639 |  | 
|  | 1640 |  | 
|  | 1641 | void Simulator::round64_according_to_fcsr(double toRound, double& rounded, | 
|  | 1642 | int64_t& rounded_int, double fs) { | 
|  | 1643 | // 0 RN (round to nearest): Round a result to the nearest | 
|  | 1644 | // representable value; if the result is exactly halfway between | 
|  | 1645 | // two representable values, round to zero. Behave like round_w_d. | 
|  | 1646 |  | 
|  | 1647 | // 1 RZ (round toward zero): Round a result to the closest | 
|  | 1648 | // representable value whose absolute value is less than or. | 
|  | 1649 | // equal to the infinitely accurate result. Behave like trunc_w_d. | 
|  | 1650 |  | 
|  | 1651 | // 2 RP (round up, or toward +infinity): Round a result to the | 
|  | 1652 | // next representable value up. Behave like ceil_w_d. | 
|  | 1653 |  | 
|  | 1654 | // 3 RN (round down, or toward −infinity): Round a result to | 
|  | 1655 | // the next representable value down. Behave like floor_w_d. | 
|  | 1656 | switch (FCSR_ & 3) { | 
|  | 1657 | case kRoundToNearest: | 
|  | 1658 | rounded = std::floor(fs + 0.5); | 
|  | 1659 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1660 | if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) { | 
|  | 1661 | // If the number is halfway between two integers, | 
|  | 1662 | // round to the even one. | 
|  | 1663 | rounded_int--; | 
|  | 1664 | } | 
|  | 1665 | break; | 
|  | 1666 | case kRoundToZero: | 
|  | 1667 | rounded = trunc(fs); | 
|  | 1668 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1669 | break; | 
|  | 1670 | case kRoundToPlusInf: | 
|  | 1671 | rounded = std::ceil(fs); | 
|  | 1672 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1673 | break; | 
|  | 1674 | case kRoundToMinusInf: | 
|  | 1675 | rounded = std::floor(fs); | 
|  | 1676 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1677 | break; | 
|  | 1678 | } | 
|  | 1679 | } | 
|  | 1680 |  | 
|  | 1681 |  | 
|  | 1682 | void Simulator::round64_according_to_fcsr(float toRound, float& rounded, | 
|  | 1683 | int64_t& rounded_int, float fs) { | 
|  | 1684 | // 0 RN (round to nearest): Round a result to the nearest | 
|  | 1685 | // representable value; if the result is exactly halfway between | 
|  | 1686 | // two representable values, round to zero. Behave like round_w_d. | 
|  | 1687 |  | 
|  | 1688 | // 1 RZ (round toward zero): Round a result to the closest | 
|  | 1689 | // representable value whose absolute value is less than or. | 
|  | 1690 | // equal to the infinitely accurate result. Behave like trunc_w_d. | 
|  | 1691 |  | 
|  | 1692 | // 2 RP (round up, or toward +infinity): Round a result to the | 
|  | 1693 | // next representable value up. Behave like ceil_w_d. | 
|  | 1694 |  | 
|  | 1695 | // 3 RN (round down, or toward −infinity): Round a result to | 
|  | 1696 | // the next representable value down. Behave like floor_w_d. | 
|  | 1697 | switch (FCSR_ & 3) { | 
|  | 1698 | case kRoundToNearest: | 
|  | 1699 | rounded = std::floor(fs + 0.5); | 
|  | 1700 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1701 | if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) { | 
|  | 1702 | // If the number is halfway between two integers, | 
|  | 1703 | // round to the even one. | 
|  | 1704 | rounded_int--; | 
|  | 1705 | } | 
|  | 1706 | break; | 
|  | 1707 | case kRoundToZero: | 
|  | 1708 | rounded = trunc(fs); | 
|  | 1709 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1710 | break; | 
|  | 1711 | case kRoundToPlusInf: | 
|  | 1712 | rounded = std::ceil(fs); | 
|  | 1713 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1714 | break; | 
|  | 1715 | case kRoundToMinusInf: | 
|  | 1716 | rounded = std::floor(fs); | 
|  | 1717 | rounded_int = static_cast<int64_t>(rounded); | 
|  | 1718 | break; | 
|  | 1719 | } | 
|  | 1720 | } | 
|  | 1721 |  | 
|  | 1722 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1723 | // Raw access to the PC register. | 
|  | 1724 | void Simulator::set_pc(int32_t value) { | 
|  | 1725 | pc_modified_ = true; | 
|  | 1726 | registers_[pc] = value; | 
|  | 1727 | } | 
|  | 1728 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1729 |  | 
|  | 1730 | bool Simulator::has_bad_pc() const { | 
|  | 1731 | return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc)); | 
|  | 1732 | } | 
|  | 1733 |  | 
|  | 1734 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1735 | // Raw access to the PC register without the special adjustment when reading. | 
|  | 1736 | int32_t Simulator::get_pc() const { | 
|  | 1737 | return registers_[pc]; | 
|  | 1738 | } | 
|  | 1739 |  | 
|  | 1740 |  | 
|  | 1741 | // The MIPS cannot do unaligned reads and writes.  On some MIPS platforms an | 
|  | 1742 | // interrupt is caused.  On others it does a funky rotation thing.  For now we | 
|  | 1743 | // simply disallow unaligned reads, but at some point we may want to move to | 
|  | 1744 | // emulating the rotate behaviour.  Note that simulator runs have the runtime | 
|  | 1745 | // system running directly on the host system and only generated code is | 
|  | 1746 | // executed in the simulator.  Since the host is typically IA32 we will not | 
|  | 1747 | // get the correct MIPS-like behaviour on unaligned accesses. | 
|  | 1748 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1749 | void Simulator::TraceRegWr(int32_t value) { | 
|  | 1750 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 1751 | SNPrintF(trace_buf_, "%08x", value); | 
|  | 1752 | } | 
|  | 1753 | } | 
|  | 1754 |  | 
|  | 1755 |  | 
|  | 1756 | // TODO(plind): consider making icount_ printing a flag option. | 
|  | 1757 | void Simulator::TraceMemRd(int32_t addr, int32_t value) { | 
|  | 1758 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 1759 | SNPrintF(trace_buf_, "%08x <-- [%08x]    (%" PRIu64 ")", value, addr, | 
|  | 1760 | icount_); | 
|  | 1761 | } | 
|  | 1762 | } | 
|  | 1763 |  | 
|  | 1764 |  | 
|  | 1765 | void Simulator::TraceMemWr(int32_t addr, int32_t value, TraceType t) { | 
|  | 1766 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 1767 | switch (t) { | 
|  | 1768 | case BYTE: | 
|  | 1769 | SNPrintF(trace_buf_, "      %02x --> [%08x]", | 
|  | 1770 | static_cast<int8_t>(value), addr); | 
|  | 1771 | break; | 
|  | 1772 | case HALF: | 
|  | 1773 | SNPrintF(trace_buf_, "    %04x --> [%08x]", static_cast<int16_t>(value), | 
|  | 1774 | addr); | 
|  | 1775 | break; | 
|  | 1776 | case WORD: | 
|  | 1777 | SNPrintF(trace_buf_, "%08x --> [%08x]", value, addr); | 
|  | 1778 | break; | 
|  | 1779 | } | 
|  | 1780 | } | 
|  | 1781 | } | 
|  | 1782 |  | 
|  | 1783 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1784 | int Simulator::ReadW(int32_t addr, Instruction* instr) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1785 | if (addr >=0 && addr < 0x400) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1786 | // This has to be a NULL-dereference, drop into debugger. | 
| Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 1787 | PrintF("Memory read from bad address: 0x%08x, pc=0x%08x\n", | 
|  | 1788 | addr, reinterpret_cast<intptr_t>(instr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1789 | MipsDebugger dbg(this); | 
|  | 1790 | dbg.Debug(); | 
|  | 1791 | } | 
|  | 1792 | if ((addr & kPointerAlignmentMask) == 0) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1793 | intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1794 | TraceMemRd(addr, static_cast<int32_t>(*ptr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1795 | return *ptr; | 
|  | 1796 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1797 | PrintF("Unaligned read at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1798 | addr, | 
|  | 1799 | reinterpret_cast<intptr_t>(instr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1800 | MipsDebugger dbg(this); | 
|  | 1801 | dbg.Debug(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1802 | return 0; | 
|  | 1803 | } | 
|  | 1804 |  | 
|  | 1805 |  | 
|  | 1806 | void Simulator::WriteW(int32_t addr, int value, Instruction* instr) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1807 | if (addr >= 0 && addr < 0x400) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1808 | // This has to be a NULL-dereference, drop into debugger. | 
| Ben Murdoch | 589d697 | 2011-11-30 16:04:58 +0000 | [diff] [blame] | 1809 | PrintF("Memory write to bad address: 0x%08x, pc=0x%08x\n", | 
|  | 1810 | addr, reinterpret_cast<intptr_t>(instr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1811 | MipsDebugger dbg(this); | 
|  | 1812 | dbg.Debug(); | 
|  | 1813 | } | 
|  | 1814 | if ((addr & kPointerAlignmentMask) == 0) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1815 | intptr_t* ptr = reinterpret_cast<intptr_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1816 | TraceMemWr(addr, value, WORD); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1817 | *ptr = value; | 
|  | 1818 | return; | 
|  | 1819 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1820 | PrintF("Unaligned write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1821 | addr, | 
|  | 1822 | reinterpret_cast<intptr_t>(instr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1823 | MipsDebugger dbg(this); | 
|  | 1824 | dbg.Debug(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1825 | } | 
|  | 1826 |  | 
|  | 1827 |  | 
|  | 1828 | double Simulator::ReadD(int32_t addr, Instruction* instr) { | 
|  | 1829 | if ((addr & kDoubleAlignmentMask) == 0) { | 
|  | 1830 | double* ptr = reinterpret_cast<double*>(addr); | 
|  | 1831 | return *ptr; | 
|  | 1832 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1833 | PrintF("Unaligned (double) read at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1834 | addr, | 
|  | 1835 | reinterpret_cast<intptr_t>(instr)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1836 | base::OS::Abort(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1837 | return 0; | 
|  | 1838 | } | 
|  | 1839 |  | 
|  | 1840 |  | 
|  | 1841 | void Simulator::WriteD(int32_t addr, double value, Instruction* instr) { | 
|  | 1842 | if ((addr & kDoubleAlignmentMask) == 0) { | 
|  | 1843 | double* ptr = reinterpret_cast<double*>(addr); | 
|  | 1844 | *ptr = value; | 
|  | 1845 | return; | 
|  | 1846 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1847 | PrintF("Unaligned (double) write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1848 | addr, | 
|  | 1849 | reinterpret_cast<intptr_t>(instr)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1850 | base::OS::Abort(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1851 | } | 
|  | 1852 |  | 
|  | 1853 |  | 
|  | 1854 | uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) { | 
|  | 1855 | if ((addr & 1) == 0) { | 
|  | 1856 | uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1857 | TraceMemRd(addr, static_cast<int32_t>(*ptr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1858 | return *ptr; | 
|  | 1859 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1860 | PrintF("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1861 | addr, | 
|  | 1862 | reinterpret_cast<intptr_t>(instr)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1863 | base::OS::Abort(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1864 | return 0; | 
|  | 1865 | } | 
|  | 1866 |  | 
|  | 1867 |  | 
|  | 1868 | int16_t Simulator::ReadH(int32_t addr, Instruction* instr) { | 
|  | 1869 | if ((addr & 1) == 0) { | 
|  | 1870 | int16_t* ptr = reinterpret_cast<int16_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1871 | TraceMemRd(addr, static_cast<int32_t>(*ptr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1872 | return *ptr; | 
|  | 1873 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1874 | PrintF("Unaligned signed halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1875 | addr, | 
|  | 1876 | reinterpret_cast<intptr_t>(instr)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1877 | base::OS::Abort(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1878 | return 0; | 
|  | 1879 | } | 
|  | 1880 |  | 
|  | 1881 |  | 
|  | 1882 | void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) { | 
|  | 1883 | if ((addr & 1) == 0) { | 
|  | 1884 | uint16_t* ptr = reinterpret_cast<uint16_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1885 | TraceMemWr(addr, value, HALF); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1886 | *ptr = value; | 
|  | 1887 | return; | 
|  | 1888 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1889 | PrintF("Unaligned unsigned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1890 | addr, | 
|  | 1891 | reinterpret_cast<intptr_t>(instr)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1892 | base::OS::Abort(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1893 | } | 
|  | 1894 |  | 
|  | 1895 |  | 
|  | 1896 | void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) { | 
|  | 1897 | if ((addr & 1) == 0) { | 
|  | 1898 | int16_t* ptr = reinterpret_cast<int16_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1899 | TraceMemWr(addr, value, HALF); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1900 | *ptr = value; | 
|  | 1901 | return; | 
|  | 1902 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1903 | PrintF("Unaligned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", | 
|  | 1904 | addr, | 
|  | 1905 | reinterpret_cast<intptr_t>(instr)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1906 | base::OS::Abort(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1907 | } | 
|  | 1908 |  | 
|  | 1909 |  | 
|  | 1910 | uint32_t Simulator::ReadBU(int32_t addr) { | 
|  | 1911 | uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1912 | TraceMemRd(addr, static_cast<int32_t>(*ptr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1913 | return *ptr & 0xff; | 
|  | 1914 | } | 
|  | 1915 |  | 
|  | 1916 |  | 
|  | 1917 | int32_t Simulator::ReadB(int32_t addr) { | 
|  | 1918 | int8_t* ptr = reinterpret_cast<int8_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1919 | TraceMemRd(addr, static_cast<int32_t>(*ptr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1920 | return *ptr; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1921 | } | 
|  | 1922 |  | 
|  | 1923 |  | 
|  | 1924 | void Simulator::WriteB(int32_t addr, uint8_t value) { | 
|  | 1925 | uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1926 | TraceMemWr(addr, value, BYTE); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1927 | *ptr = value; | 
|  | 1928 | } | 
|  | 1929 |  | 
|  | 1930 |  | 
|  | 1931 | void Simulator::WriteB(int32_t addr, int8_t value) { | 
|  | 1932 | int8_t* ptr = reinterpret_cast<int8_t*>(addr); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1933 | TraceMemWr(addr, value, BYTE); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1934 | *ptr = value; | 
|  | 1935 | } | 
|  | 1936 |  | 
|  | 1937 |  | 
|  | 1938 | // Returns the limit of the stack area to enable checking for stack overflows. | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1939 | uintptr_t Simulator::StackLimit(uintptr_t c_limit) const { | 
|  | 1940 | // The simulator uses a separate JS stack. If we have exhausted the C stack, | 
|  | 1941 | // we also drop down the JS limit to reflect the exhaustion on the JS stack. | 
|  | 1942 | if (GetCurrentStackPosition() < c_limit) { | 
|  | 1943 | return reinterpret_cast<uintptr_t>(get_sp()); | 
|  | 1944 | } | 
|  | 1945 |  | 
|  | 1946 | // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes | 
|  | 1947 | // to prevent overrunning the stack when pushing values. | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 1948 | return reinterpret_cast<uintptr_t>(stack_) + 1024; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1949 | } | 
|  | 1950 |  | 
|  | 1951 |  | 
|  | 1952 | // Unsupported instructions use Format to print an error and stop execution. | 
|  | 1953 | void Simulator::Format(Instruction* instr, const char* format) { | 
|  | 1954 | PrintF("Simulator found unsupported instruction:\n 0x%08x: %s\n", | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1955 | reinterpret_cast<intptr_t>(instr), format); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1956 | UNIMPLEMENTED_MIPS(); | 
|  | 1957 | } | 
|  | 1958 |  | 
|  | 1959 |  | 
|  | 1960 | // Calls into the V8 runtime are based on this very simple interface. | 
|  | 1961 | // Note: To be able to return two values from some calls the code in runtime.cc | 
|  | 1962 | // uses the ObjectPair which is essentially two 32-bit values stuffed into a | 
|  | 1963 | // 64-bit value. With the code below we assume that all runtime calls return | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1964 | // 64 bits of result. If they don't, the v1 result register contains a bogus | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1965 | // value, which is fine because it is caller-saved. | 
|  | 1966 | typedef int64_t (*SimulatorRuntimeCall)(int32_t arg0, | 
|  | 1967 | int32_t arg1, | 
|  | 1968 | int32_t arg2, | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1969 | int32_t arg3, | 
|  | 1970 | int32_t arg4, | 
|  | 1971 | int32_t arg5); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1972 |  | 
|  | 1973 | // These prototypes handle the four types of FP calls. | 
|  | 1974 | typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1); | 
|  | 1975 | typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1); | 
|  | 1976 | typedef double (*SimulatorRuntimeFPCall)(double darg0); | 
|  | 1977 | typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1978 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1979 | // This signature supports direct call in to API function native callback | 
|  | 1980 | // (refer to InvocationCallback in v8.h). | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1981 | typedef void (*SimulatorRuntimeDirectApiCall)(int32_t arg0); | 
|  | 1982 | typedef void (*SimulatorRuntimeProfilingApiCall)(int32_t arg0, void* arg1); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1983 |  | 
|  | 1984 | // This signature supports direct call to accessor getter callback. | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1985 | typedef void (*SimulatorRuntimeDirectGetterCall)(int32_t arg0, int32_t arg1); | 
|  | 1986 | typedef void (*SimulatorRuntimeProfilingGetterCall)( | 
|  | 1987 | int32_t arg0, int32_t arg1, void* arg2); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 1988 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1989 | // Software interrupt instructions are used by the simulator to call into the | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1990 | // C-based V8 runtime. They are also used for debugging with simulator. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1991 | void Simulator::SoftwareInterrupt(Instruction* instr) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1992 | // There are several instructions that could get us here, | 
|  | 1993 | // the break_ instruction, or several variants of traps. All | 
|  | 1994 | // Are "SPECIAL" class opcode, and are distinuished by function. | 
|  | 1995 | int32_t func = instr->FunctionFieldRaw(); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 1996 | uint32_t code = (func == BREAK) ? instr->Bits(25, 6) : -1; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 1997 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 1998 | // We first check if we met a call_rt_redirected. | 
|  | 1999 | if (instr->InstructionBits() == rtCallRedirInstr) { | 
|  | 2000 | Redirection* redirection = Redirection::FromSwiInstruction(instr); | 
|  | 2001 | int32_t arg0 = get_register(a0); | 
|  | 2002 | int32_t arg1 = get_register(a1); | 
|  | 2003 | int32_t arg2 = get_register(a2); | 
|  | 2004 | int32_t arg3 = get_register(a3); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2005 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2006 | int32_t* stack_pointer = reinterpret_cast<int32_t*>(get_register(sp)); | 
| Ben Murdoch | 69a99ed | 2011-11-30 16:03:39 +0000 | [diff] [blame] | 2007 | // Args 4 and 5 are on the stack after the reserved space for args 0..3. | 
|  | 2008 | int32_t arg4 = stack_pointer[4]; | 
|  | 2009 | int32_t arg5 = stack_pointer[5]; | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2010 |  | 
|  | 2011 | bool fp_call = | 
|  | 2012 | (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) || | 
|  | 2013 | (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) || | 
|  | 2014 | (redirection->type() == ExternalReference::BUILTIN_FP_CALL) || | 
|  | 2015 | (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL); | 
|  | 2016 |  | 
|  | 2017 | if (!IsMipsSoftFloatABI) { | 
|  | 2018 | // With the hard floating point calling convention, double | 
|  | 2019 | // arguments are passed in FPU registers. Fetch the arguments | 
|  | 2020 | // from there and call the builtin using soft floating point | 
|  | 2021 | // convention. | 
|  | 2022 | switch (redirection->type()) { | 
|  | 2023 | case ExternalReference::BUILTIN_FP_FP_CALL: | 
|  | 2024 | case ExternalReference::BUILTIN_COMPARE_CALL: | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2025 | if (IsFp64Mode()) { | 
|  | 2026 | arg0 = get_fpu_register_word(f12); | 
|  | 2027 | arg1 = get_fpu_register_hi_word(f12); | 
|  | 2028 | arg2 = get_fpu_register_word(f14); | 
|  | 2029 | arg3 = get_fpu_register_hi_word(f14); | 
|  | 2030 | } else { | 
|  | 2031 | arg0 = get_fpu_register_word(f12); | 
|  | 2032 | arg1 = get_fpu_register_word(f13); | 
|  | 2033 | arg2 = get_fpu_register_word(f14); | 
|  | 2034 | arg3 = get_fpu_register_word(f15); | 
|  | 2035 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2036 | break; | 
|  | 2037 | case ExternalReference::BUILTIN_FP_CALL: | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2038 | if (IsFp64Mode()) { | 
|  | 2039 | arg0 = get_fpu_register_word(f12); | 
|  | 2040 | arg1 = get_fpu_register_hi_word(f12); | 
|  | 2041 | } else { | 
|  | 2042 | arg0 = get_fpu_register_word(f12); | 
|  | 2043 | arg1 = get_fpu_register_word(f13); | 
|  | 2044 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2045 | break; | 
|  | 2046 | case ExternalReference::BUILTIN_FP_INT_CALL: | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2047 | if (IsFp64Mode()) { | 
|  | 2048 | arg0 = get_fpu_register_word(f12); | 
|  | 2049 | arg1 = get_fpu_register_hi_word(f12); | 
|  | 2050 | } else { | 
|  | 2051 | arg0 = get_fpu_register_word(f12); | 
|  | 2052 | arg1 = get_fpu_register_word(f13); | 
|  | 2053 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2054 | arg2 = get_register(a2); | 
|  | 2055 | break; | 
|  | 2056 | default: | 
|  | 2057 | break; | 
|  | 2058 | } | 
|  | 2059 | } | 
|  | 2060 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2061 | // This is dodgy but it works because the C entry stubs are never moved. | 
|  | 2062 | // See comment in codegen-arm.cc and bug 1242173. | 
|  | 2063 | int32_t saved_ra = get_register(ra); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2064 |  | 
|  | 2065 | intptr_t external = | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2066 | reinterpret_cast<intptr_t>(redirection->external_function()); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2067 |  | 
|  | 2068 | // Based on CpuFeatures::IsSupported(FPU), Mips will use either hardware | 
|  | 2069 | // FPU, or gcc soft-float routines. Hardware FPU is simulated in this | 
|  | 2070 | // simulator. Soft-float has additional abstraction of ExternalReference, | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2071 | // to support serialization. | 
|  | 2072 | if (fp_call) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2073 | double dval0, dval1;  // one or two double parameters | 
|  | 2074 | int32_t ival;         // zero or one integer parameters | 
|  | 2075 | int64_t iresult = 0;  // integer return value | 
|  | 2076 | double dresult = 0;   // double return value | 
|  | 2077 | GetFpArgs(&dval0, &dval1, &ival); | 
|  | 2078 | SimulatorRuntimeCall generic_target = | 
|  | 2079 | reinterpret_cast<SimulatorRuntimeCall>(external); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2080 | if (::v8::internal::FLAG_trace_sim) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2081 | switch (redirection->type()) { | 
|  | 2082 | case ExternalReference::BUILTIN_FP_FP_CALL: | 
|  | 2083 | case ExternalReference::BUILTIN_COMPARE_CALL: | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2084 | PrintF("Call to host function at %p with args %f, %f", | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2085 | FUNCTION_ADDR(generic_target), dval0, dval1); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2086 | break; | 
|  | 2087 | case ExternalReference::BUILTIN_FP_CALL: | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2088 | PrintF("Call to host function at %p with arg %f", | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2089 | FUNCTION_ADDR(generic_target), dval0); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2090 | break; | 
|  | 2091 | case ExternalReference::BUILTIN_FP_INT_CALL: | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2092 | PrintF("Call to host function at %p with args %f, %d", | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2093 | FUNCTION_ADDR(generic_target), dval0, ival); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2094 | break; | 
|  | 2095 | default: | 
|  | 2096 | UNREACHABLE(); | 
|  | 2097 | break; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2098 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2099 | } | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2100 | switch (redirection->type()) { | 
|  | 2101 | case ExternalReference::BUILTIN_COMPARE_CALL: { | 
|  | 2102 | SimulatorRuntimeCompareCall target = | 
|  | 2103 | reinterpret_cast<SimulatorRuntimeCompareCall>(external); | 
|  | 2104 | iresult = target(dval0, dval1); | 
|  | 2105 | set_register(v0, static_cast<int32_t>(iresult)); | 
|  | 2106 | set_register(v1, static_cast<int32_t>(iresult >> 32)); | 
|  | 2107 | break; | 
|  | 2108 | } | 
|  | 2109 | case ExternalReference::BUILTIN_FP_FP_CALL: { | 
|  | 2110 | SimulatorRuntimeFPFPCall target = | 
|  | 2111 | reinterpret_cast<SimulatorRuntimeFPFPCall>(external); | 
|  | 2112 | dresult = target(dval0, dval1); | 
|  | 2113 | SetFpResult(dresult); | 
|  | 2114 | break; | 
|  | 2115 | } | 
|  | 2116 | case ExternalReference::BUILTIN_FP_CALL: { | 
|  | 2117 | SimulatorRuntimeFPCall target = | 
|  | 2118 | reinterpret_cast<SimulatorRuntimeFPCall>(external); | 
|  | 2119 | dresult = target(dval0); | 
|  | 2120 | SetFpResult(dresult); | 
|  | 2121 | break; | 
|  | 2122 | } | 
|  | 2123 | case ExternalReference::BUILTIN_FP_INT_CALL: { | 
|  | 2124 | SimulatorRuntimeFPIntCall target = | 
|  | 2125 | reinterpret_cast<SimulatorRuntimeFPIntCall>(external); | 
|  | 2126 | dresult = target(dval0, ival); | 
|  | 2127 | SetFpResult(dresult); | 
|  | 2128 | break; | 
|  | 2129 | } | 
|  | 2130 | default: | 
|  | 2131 | UNREACHABLE(); | 
|  | 2132 | break; | 
|  | 2133 | } | 
|  | 2134 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 2135 | switch (redirection->type()) { | 
|  | 2136 | case ExternalReference::BUILTIN_COMPARE_CALL: | 
|  | 2137 | PrintF("Returned %08x\n", static_cast<int32_t>(iresult)); | 
|  | 2138 | break; | 
|  | 2139 | case ExternalReference::BUILTIN_FP_FP_CALL: | 
|  | 2140 | case ExternalReference::BUILTIN_FP_CALL: | 
|  | 2141 | case ExternalReference::BUILTIN_FP_INT_CALL: | 
|  | 2142 | PrintF("Returned %f\n", dresult); | 
|  | 2143 | break; | 
|  | 2144 | default: | 
|  | 2145 | UNREACHABLE(); | 
|  | 2146 | break; | 
|  | 2147 | } | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2148 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2149 | } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2150 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 2151 | PrintF("Call to host function at %p args %08x\n", | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2152 | reinterpret_cast<void*>(external), arg0); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2153 | } | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2154 | SimulatorRuntimeDirectApiCall target = | 
|  | 2155 | reinterpret_cast<SimulatorRuntimeDirectApiCall>(external); | 
|  | 2156 | target(arg0); | 
|  | 2157 | } else if ( | 
|  | 2158 | redirection->type() == ExternalReference::PROFILING_API_CALL) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2159 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 2160 | PrintF("Call to host function at %p args %08x %08x\n", | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2161 | reinterpret_cast<void*>(external), arg0, arg1); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2162 | } | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2163 | SimulatorRuntimeProfilingApiCall target = | 
|  | 2164 | reinterpret_cast<SimulatorRuntimeProfilingApiCall>(external); | 
|  | 2165 | target(arg0, Redirection::ReverseRedirection(arg1)); | 
|  | 2166 | } else if ( | 
|  | 2167 | redirection->type() == ExternalReference::DIRECT_GETTER_CALL) { | 
|  | 2168 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 2169 | PrintF("Call to host function at %p args %08x %08x\n", | 
|  | 2170 | reinterpret_cast<void*>(external), arg0, arg1); | 
|  | 2171 | } | 
|  | 2172 | SimulatorRuntimeDirectGetterCall target = | 
|  | 2173 | reinterpret_cast<SimulatorRuntimeDirectGetterCall>(external); | 
|  | 2174 | target(arg0, arg1); | 
|  | 2175 | } else if ( | 
|  | 2176 | redirection->type() == ExternalReference::PROFILING_GETTER_CALL) { | 
|  | 2177 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 2178 | PrintF("Call to host function at %p args %08x %08x %08x\n", | 
|  | 2179 | reinterpret_cast<void*>(external), arg0, arg1, arg2); | 
|  | 2180 | } | 
|  | 2181 | SimulatorRuntimeProfilingGetterCall target = | 
|  | 2182 | reinterpret_cast<SimulatorRuntimeProfilingGetterCall>(external); | 
|  | 2183 | target(arg0, arg1, Redirection::ReverseRedirection(arg2)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2184 | } else { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2185 | SimulatorRuntimeCall target = | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2186 | reinterpret_cast<SimulatorRuntimeCall>(external); | 
|  | 2187 | if (::v8::internal::FLAG_trace_sim) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2188 | PrintF( | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 2189 | "Call to host function at %p " | 
|  | 2190 | "args %08x, %08x, %08x, %08x, %08x, %08x\n", | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2191 | FUNCTION_ADDR(target), | 
|  | 2192 | arg0, | 
|  | 2193 | arg1, | 
|  | 2194 | arg2, | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2195 | arg3, | 
|  | 2196 | arg4, | 
|  | 2197 | arg5); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2198 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2199 | int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5); | 
|  | 2200 | set_register(v0, static_cast<int32_t>(result)); | 
|  | 2201 | set_register(v1, static_cast<int32_t>(result >> 32)); | 
|  | 2202 | } | 
|  | 2203 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 2204 | PrintF("Returned %08x : %08x\n", get_register(v1), get_register(v0)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2205 | } | 
|  | 2206 | set_register(ra, saved_ra); | 
|  | 2207 | set_pc(get_register(ra)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2208 |  | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2209 | } else if (func == BREAK && code <= kMaxStopCode) { | 
|  | 2210 | if (IsWatchpoint(code)) { | 
|  | 2211 | PrintWatchpoint(code); | 
|  | 2212 | } else { | 
|  | 2213 | IncreaseStopCounter(code); | 
|  | 2214 | HandleStop(code, instr); | 
|  | 2215 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2216 | } else { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2217 | // All remaining break_ codes, and all traps are handled here. | 
|  | 2218 | MipsDebugger dbg(this); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2219 | dbg.Debug(); | 
|  | 2220 | } | 
|  | 2221 | } | 
|  | 2222 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2223 |  | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2224 | // Stop helper functions. | 
|  | 2225 | bool Simulator::IsWatchpoint(uint32_t code) { | 
|  | 2226 | return (code <= kMaxWatchpointCode); | 
|  | 2227 | } | 
|  | 2228 |  | 
|  | 2229 |  | 
|  | 2230 | void Simulator::PrintWatchpoint(uint32_t code) { | 
|  | 2231 | MipsDebugger dbg(this); | 
|  | 2232 | ++break_count_; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2233 | PrintF("\n---- break %d marker: %3d  (instr count: %" PRIu64 | 
|  | 2234 | ") ----------" | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2235 | "----------------------------------", | 
|  | 2236 | code, break_count_, icount_); | 
|  | 2237 | dbg.PrintAllRegs();  // Print registers and continue running. | 
|  | 2238 | } | 
|  | 2239 |  | 
|  | 2240 |  | 
|  | 2241 | void Simulator::HandleStop(uint32_t code, Instruction* instr) { | 
|  | 2242 | // Stop if it is enabled, otherwise go on jumping over the stop | 
|  | 2243 | // and the message address. | 
|  | 2244 | if (IsEnabledStop(code)) { | 
|  | 2245 | MipsDebugger dbg(this); | 
|  | 2246 | dbg.Stop(instr); | 
|  | 2247 | } else { | 
|  | 2248 | set_pc(get_pc() + 2 * Instruction::kInstrSize); | 
|  | 2249 | } | 
|  | 2250 | } | 
|  | 2251 |  | 
|  | 2252 |  | 
|  | 2253 | bool Simulator::IsStopInstruction(Instruction* instr) { | 
|  | 2254 | int32_t func = instr->FunctionFieldRaw(); | 
|  | 2255 | uint32_t code = static_cast<uint32_t>(instr->Bits(25, 6)); | 
|  | 2256 | return (func == BREAK) && code > kMaxWatchpointCode && code <= kMaxStopCode; | 
|  | 2257 | } | 
|  | 2258 |  | 
|  | 2259 |  | 
|  | 2260 | bool Simulator::IsEnabledStop(uint32_t code) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2261 | DCHECK(code <= kMaxStopCode); | 
|  | 2262 | DCHECK(code > kMaxWatchpointCode); | 
|  | 2263 | return !(watched_stops_[code].count & kStopDisabledBit); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2264 | } | 
|  | 2265 |  | 
|  | 2266 |  | 
|  | 2267 | void Simulator::EnableStop(uint32_t code) { | 
|  | 2268 | if (!IsEnabledStop(code)) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2269 | watched_stops_[code].count &= ~kStopDisabledBit; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2270 | } | 
|  | 2271 | } | 
|  | 2272 |  | 
|  | 2273 |  | 
|  | 2274 | void Simulator::DisableStop(uint32_t code) { | 
|  | 2275 | if (IsEnabledStop(code)) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2276 | watched_stops_[code].count |= kStopDisabledBit; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2277 | } | 
|  | 2278 | } | 
|  | 2279 |  | 
|  | 2280 |  | 
|  | 2281 | void Simulator::IncreaseStopCounter(uint32_t code) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2282 | DCHECK(code <= kMaxStopCode); | 
|  | 2283 | if ((watched_stops_[code].count & ~(1 << 31)) == 0x7fffffff) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2284 | PrintF("Stop counter for code %i has overflowed.\n" | 
|  | 2285 | "Enabling this code and reseting the counter to 0.\n", code); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2286 | watched_stops_[code].count = 0; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2287 | EnableStop(code); | 
|  | 2288 | } else { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2289 | watched_stops_[code].count++; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2290 | } | 
|  | 2291 | } | 
|  | 2292 |  | 
|  | 2293 |  | 
|  | 2294 | // Print a stop status. | 
|  | 2295 | void Simulator::PrintStopInfo(uint32_t code) { | 
|  | 2296 | if (code <= kMaxWatchpointCode) { | 
|  | 2297 | PrintF("That is a watchpoint, not a stop.\n"); | 
|  | 2298 | return; | 
|  | 2299 | } else if (code > kMaxStopCode) { | 
|  | 2300 | PrintF("Code too large, only %u stops can be used\n", kMaxStopCode + 1); | 
|  | 2301 | return; | 
|  | 2302 | } | 
|  | 2303 | const char* state = IsEnabledStop(code) ? "Enabled" : "Disabled"; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2304 | int32_t count = watched_stops_[code].count & ~kStopDisabledBit; | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2305 | // Don't print the state of unused breakpoints. | 
|  | 2306 | if (count != 0) { | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2307 | if (watched_stops_[code].desc) { | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2308 | PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2309 | code, code, state, count, watched_stops_[code].desc); | 
| Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 2310 | } else { | 
|  | 2311 | PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n", | 
|  | 2312 | code, code, state, count); | 
|  | 2313 | } | 
|  | 2314 | } | 
|  | 2315 | } | 
|  | 2316 |  | 
|  | 2317 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2318 | void Simulator::SignalException(Exception e) { | 
|  | 2319 | V8_Fatal(__FILE__, __LINE__, "Error: Exception %i raised.", | 
|  | 2320 | static_cast<int>(e)); | 
|  | 2321 | } | 
|  | 2322 |  | 
|  | 2323 |  | 
|  | 2324 | void Simulator::DecodeTypeRegisterDRsType() { | 
|  | 2325 | double ft, fs, fd; | 
|  | 2326 | uint32_t cc, fcsr_cc; | 
|  | 2327 | int64_t i64; | 
|  | 2328 | fs = get_fpu_register_double(fs_reg()); | 
|  | 2329 | ft = (get_instr()->FunctionFieldRaw() != MOVF) | 
|  | 2330 | ? get_fpu_register_double(ft_reg()) | 
|  | 2331 | : 0.0; | 
|  | 2332 | fd = get_fpu_register_double(fd_reg()); | 
|  | 2333 | int64_t ft_int = bit_cast<int64_t>(ft); | 
|  | 2334 | int64_t fd_int = bit_cast<int64_t>(fd); | 
|  | 2335 | cc = get_instr()->FCccValue(); | 
|  | 2336 | fcsr_cc = get_fcsr_condition_bit(cc); | 
|  | 2337 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 2338 | case RINT: { | 
|  | 2339 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2340 | double result, temp, temp_result; | 
|  | 2341 | double upper = std::ceil(fs); | 
|  | 2342 | double lower = std::floor(fs); | 
|  | 2343 | switch (get_fcsr_rounding_mode()) { | 
|  | 2344 | case kRoundToNearest: | 
|  | 2345 | if (upper - fs < fs - lower) { | 
|  | 2346 | result = upper; | 
|  | 2347 | } else if (upper - fs > fs - lower) { | 
|  | 2348 | result = lower; | 
|  | 2349 | } else { | 
|  | 2350 | temp_result = upper / 2; | 
|  | 2351 | double reminder = modf(temp_result, &temp); | 
|  | 2352 | if (reminder == 0) { | 
|  | 2353 | result = upper; | 
|  | 2354 | } else { | 
|  | 2355 | result = lower; | 
|  | 2356 | } | 
|  | 2357 | } | 
|  | 2358 | break; | 
|  | 2359 | case kRoundToZero: | 
|  | 2360 | result = (fs > 0 ? lower : upper); | 
|  | 2361 | break; | 
|  | 2362 | case kRoundToPlusInf: | 
|  | 2363 | result = upper; | 
|  | 2364 | break; | 
|  | 2365 | case kRoundToMinusInf: | 
|  | 2366 | result = lower; | 
|  | 2367 | break; | 
|  | 2368 | } | 
|  | 2369 | set_fpu_register_double(fd_reg(), result); | 
|  | 2370 | if (result != fs) { | 
|  | 2371 | set_fcsr_bit(kFCSRInexactFlagBit, true); | 
|  | 2372 | } | 
|  | 2373 | break; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2374 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2375 | case SEL: | 
|  | 2376 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2377 | set_fpu_register_double(fd_reg(), (fd_int & 0x1) == 0 ? fs : ft); | 
|  | 2378 | break; | 
|  | 2379 | case SELEQZ_C: | 
|  | 2380 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2381 | set_fpu_register_double(fd_reg(), (ft_int & 0x1) == 0 ? fs : 0.0); | 
|  | 2382 | break; | 
|  | 2383 | case SELNEZ_C: | 
|  | 2384 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2385 | set_fpu_register_double(fd_reg(), (ft_int & 0x1) != 0 ? fs : 0.0); | 
|  | 2386 | break; | 
|  | 2387 | case MOVZ_C: { | 
|  | 2388 | DCHECK(IsMipsArchVariant(kMips32r2)); | 
|  | 2389 | if (rt() == 0) { | 
|  | 2390 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2391 | } | 
|  | 2392 | break; | 
|  | 2393 | } | 
|  | 2394 | case MOVN_C: { | 
|  | 2395 | DCHECK(IsMipsArchVariant(kMips32r2)); | 
|  | 2396 | int32_t rt_reg = get_instr()->RtValue(); | 
|  | 2397 | int32_t rt = get_register(rt_reg); | 
|  | 2398 | if (rt != 0) { | 
|  | 2399 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2400 | } | 
|  | 2401 | break; | 
|  | 2402 | } | 
|  | 2403 | case MOVF: { | 
|  | 2404 | // Same function field for MOVT.D and MOVF.D | 
|  | 2405 | uint32_t ft_cc = (ft_reg() >> 2) & 0x7; | 
|  | 2406 | ft_cc = get_fcsr_condition_bit(ft_cc); | 
|  | 2407 | if (get_instr()->Bit(16)) {  // Read Tf bit. | 
|  | 2408 | // MOVT.D | 
|  | 2409 | if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg(), fs); | 
|  | 2410 | } else { | 
|  | 2411 | // MOVF.D | 
|  | 2412 | if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg(), fs); | 
|  | 2413 | } | 
|  | 2414 | break; | 
|  | 2415 | } | 
|  | 2416 | case MIN: | 
|  | 2417 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2418 | fs = get_fpu_register_double(fs_reg()); | 
|  | 2419 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 2420 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2421 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 2422 | set_fpu_register_double(fd_reg(), ft); | 
|  | 2423 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 2424 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2425 | } else { | 
|  | 2426 | set_fpu_register_double(fd_reg(), (fs >= ft) ? ft : fs); | 
|  | 2427 | } | 
|  | 2428 | break; | 
|  | 2429 | case MINA: | 
|  | 2430 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2431 | fs = get_fpu_register_double(fs_reg()); | 
|  | 2432 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 2433 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2434 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 2435 | set_fpu_register_double(fd_reg(), ft); | 
|  | 2436 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 2437 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2438 | } else { | 
|  | 2439 | double result; | 
|  | 2440 | if (fabs(fs) > fabs(ft)) { | 
|  | 2441 | result = ft; | 
|  | 2442 | } else if (fabs(fs) < fabs(ft)) { | 
|  | 2443 | result = fs; | 
|  | 2444 | } else { | 
|  | 2445 | result = (fs < ft ? fs : ft); | 
|  | 2446 | } | 
|  | 2447 | set_fpu_register_double(fd_reg(), result); | 
|  | 2448 | } | 
|  | 2449 | break; | 
|  | 2450 | case MAXA: | 
|  | 2451 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2452 | fs = get_fpu_register_double(fs_reg()); | 
|  | 2453 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 2454 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2455 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 2456 | set_fpu_register_double(fd_reg(), ft); | 
|  | 2457 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 2458 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2459 | } else { | 
|  | 2460 | double result; | 
|  | 2461 | if (fabs(fs) < fabs(ft)) { | 
|  | 2462 | result = ft; | 
|  | 2463 | } else if (fabs(fs) > fabs(ft)) { | 
|  | 2464 | result = fs; | 
|  | 2465 | } else { | 
|  | 2466 | result = (fs > ft ? fs : ft); | 
|  | 2467 | } | 
|  | 2468 | set_fpu_register_double(fd_reg(), result); | 
|  | 2469 | } | 
|  | 2470 | break; | 
|  | 2471 | case MAX: | 
|  | 2472 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2473 | fs = get_fpu_register_double(fs_reg()); | 
|  | 2474 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 2475 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2476 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 2477 | set_fpu_register_double(fd_reg(), ft); | 
|  | 2478 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 2479 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2480 | } else { | 
|  | 2481 | set_fpu_register_double(fd_reg(), (fs <= ft) ? ft : fs); | 
|  | 2482 | } | 
|  | 2483 | break; | 
|  | 2484 | break; | 
|  | 2485 | case ADD_D: | 
|  | 2486 | set_fpu_register_double(fd_reg(), fs + ft); | 
|  | 2487 | break; | 
|  | 2488 | case SUB_D: | 
|  | 2489 | set_fpu_register_double(fd_reg(), fs - ft); | 
|  | 2490 | break; | 
|  | 2491 | case MUL_D: | 
|  | 2492 | set_fpu_register_double(fd_reg(), fs * ft); | 
|  | 2493 | break; | 
|  | 2494 | case DIV_D: | 
|  | 2495 | set_fpu_register_double(fd_reg(), fs / ft); | 
|  | 2496 | break; | 
|  | 2497 | case ABS_D: | 
|  | 2498 | set_fpu_register_double(fd_reg(), fabs(fs)); | 
|  | 2499 | break; | 
|  | 2500 | case MOV_D: | 
|  | 2501 | set_fpu_register_double(fd_reg(), fs); | 
|  | 2502 | break; | 
|  | 2503 | case NEG_D: | 
|  | 2504 | set_fpu_register_double(fd_reg(), -fs); | 
|  | 2505 | break; | 
|  | 2506 | case SQRT_D: | 
|  | 2507 | lazily_initialize_fast_sqrt(isolate_); | 
|  | 2508 | set_fpu_register_double(fd_reg(), fast_sqrt(fs, isolate_)); | 
|  | 2509 | break; | 
|  | 2510 | case RSQRT_D: { | 
|  | 2511 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2512 | lazily_initialize_fast_sqrt(isolate_); | 
|  | 2513 | double result = 1.0 / fast_sqrt(fs, isolate_); | 
|  | 2514 | set_fpu_register_double(fd_reg(), result); | 
|  | 2515 | break; | 
|  | 2516 | } | 
|  | 2517 | case RECIP_D: { | 
|  | 2518 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2519 | double result = 1.0 / fs; | 
|  | 2520 | set_fpu_register_double(fd_reg(), result); | 
|  | 2521 | break; | 
|  | 2522 | } | 
|  | 2523 | case C_UN_D: | 
|  | 2524 | set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); | 
|  | 2525 | break; | 
|  | 2526 | case C_EQ_D: | 
|  | 2527 | set_fcsr_bit(fcsr_cc, (fs == ft)); | 
|  | 2528 | break; | 
|  | 2529 | case C_UEQ_D: | 
|  | 2530 | set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft))); | 
|  | 2531 | break; | 
|  | 2532 | case C_OLT_D: | 
|  | 2533 | set_fcsr_bit(fcsr_cc, (fs < ft)); | 
|  | 2534 | break; | 
|  | 2535 | case C_ULT_D: | 
|  | 2536 | set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft))); | 
|  | 2537 | break; | 
|  | 2538 | case C_OLE_D: | 
|  | 2539 | set_fcsr_bit(fcsr_cc, (fs <= ft)); | 
|  | 2540 | break; | 
|  | 2541 | case C_ULE_D: | 
|  | 2542 | set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); | 
|  | 2543 | break; | 
|  | 2544 | case CVT_W_D: {  // Convert double to word. | 
|  | 2545 | double rounded; | 
|  | 2546 | int32_t result; | 
|  | 2547 | round_according_to_fcsr(fs, rounded, result, fs); | 
|  | 2548 | set_fpu_register_word(fd_reg(), result); | 
|  | 2549 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 2550 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 2551 | } | 
|  | 2552 | } break; | 
|  | 2553 | case ROUND_W_D:  // Round double to word (round half to even). | 
|  | 2554 | { | 
|  | 2555 | double rounded = std::floor(fs + 0.5); | 
|  | 2556 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 2557 | if ((result & 1) != 0 && result - fs == 0.5) { | 
|  | 2558 | // If the number is halfway between two integers, | 
|  | 2559 | // round to the even one. | 
|  | 2560 | result--; | 
|  | 2561 | } | 
|  | 2562 | set_fpu_register_word(fd_reg(), result); | 
|  | 2563 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 2564 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 2565 | } | 
|  | 2566 | } break; | 
|  | 2567 | case TRUNC_W_D:  // Truncate double to word (round towards 0). | 
|  | 2568 | { | 
|  | 2569 | double rounded = trunc(fs); | 
|  | 2570 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 2571 | set_fpu_register_word(fd_reg(), result); | 
|  | 2572 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 2573 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 2574 | } | 
|  | 2575 | } break; | 
|  | 2576 | case FLOOR_W_D:  // Round double to word towards negative infinity. | 
|  | 2577 | { | 
|  | 2578 | double rounded = std::floor(fs); | 
|  | 2579 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 2580 | set_fpu_register_word(fd_reg(), result); | 
|  | 2581 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 2582 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 2583 | } | 
|  | 2584 | } break; | 
|  | 2585 | case CEIL_W_D:  // Round double to word towards positive infinity. | 
|  | 2586 | { | 
|  | 2587 | double rounded = std::ceil(fs); | 
|  | 2588 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 2589 | set_fpu_register_word(fd_reg(), result); | 
|  | 2590 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 2591 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 2592 | } | 
|  | 2593 | } break; | 
|  | 2594 | case CVT_S_D:  // Convert double to float (single). | 
|  | 2595 | set_fpu_register_float(fd_reg(), static_cast<float>(fs)); | 
|  | 2596 | break; | 
|  | 2597 | case CVT_L_D: {  // Mips32r2: Truncate double to 64-bit long-word. | 
|  | 2598 | if (IsFp64Mode()) { | 
|  | 2599 | int64_t result; | 
|  | 2600 | double rounded; | 
|  | 2601 | round64_according_to_fcsr(fs, rounded, result, fs); | 
|  | 2602 | set_fpu_register(fd_reg(), result); | 
|  | 2603 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 2604 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 2605 | } | 
|  | 2606 | } else { | 
|  | 2607 | UNSUPPORTED(); | 
|  | 2608 | } | 
|  | 2609 | break; | 
|  | 2610 | break; | 
|  | 2611 | } | 
|  | 2612 | case TRUNC_L_D: {  // Mips32r2 instruction. | 
|  | 2613 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2614 | double rounded = trunc(fs); | 
|  | 2615 | i64 = static_cast<int64_t>(rounded); | 
|  | 2616 | if (IsFp64Mode()) { | 
|  | 2617 | set_fpu_register(fd_reg(), i64); | 
|  | 2618 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 2619 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 2620 | } | 
|  | 2621 | } else { | 
|  | 2622 | UNSUPPORTED(); | 
|  | 2623 | } | 
|  | 2624 | break; | 
|  | 2625 | } | 
|  | 2626 | case ROUND_L_D: {  // Mips32r2 instruction. | 
|  | 2627 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2628 | double rounded = std::floor(fs + 0.5); | 
|  | 2629 | int64_t result = static_cast<int64_t>(rounded); | 
|  | 2630 | if ((result & 1) != 0 && result - fs == 0.5) { | 
|  | 2631 | // If the number is halfway between two integers, | 
|  | 2632 | // round to the even one. | 
|  | 2633 | result--; | 
|  | 2634 | } | 
|  | 2635 | int64_t i64 = static_cast<int64_t>(result); | 
|  | 2636 | if (IsFp64Mode()) { | 
|  | 2637 | set_fpu_register(fd_reg(), i64); | 
|  | 2638 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 2639 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 2640 | } | 
|  | 2641 | } else { | 
|  | 2642 | UNSUPPORTED(); | 
|  | 2643 | } | 
|  | 2644 | break; | 
|  | 2645 | } | 
|  | 2646 | case FLOOR_L_D: {  // Mips32r2 instruction. | 
|  | 2647 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2648 | double rounded = std::floor(fs); | 
|  | 2649 | int64_t i64 = static_cast<int64_t>(rounded); | 
|  | 2650 | if (IsFp64Mode()) { | 
|  | 2651 | set_fpu_register(fd_reg(), i64); | 
|  | 2652 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 2653 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 2654 | } | 
|  | 2655 | } else { | 
|  | 2656 | UNSUPPORTED(); | 
|  | 2657 | } | 
|  | 2658 | break; | 
|  | 2659 | } | 
|  | 2660 | case CEIL_L_D: {  // Mips32r2 instruction. | 
|  | 2661 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2662 | double rounded = std::ceil(fs); | 
|  | 2663 | int64_t i64 = static_cast<int64_t>(rounded); | 
|  | 2664 | if (IsFp64Mode()) { | 
|  | 2665 | set_fpu_register(fd_reg(), i64); | 
|  | 2666 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 2667 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 2668 | } | 
|  | 2669 | } else { | 
|  | 2670 | UNSUPPORTED(); | 
|  | 2671 | } | 
|  | 2672 | break; | 
|  | 2673 | } | 
|  | 2674 | case CLASS_D: {  // Mips32r6 instruction | 
|  | 2675 | // Convert double input to uint64_t for easier bit manipulation | 
|  | 2676 | uint64_t classed = bit_cast<uint64_t>(fs); | 
|  | 2677 |  | 
|  | 2678 | // Extracting sign, exponent and mantissa from the input double | 
|  | 2679 | uint32_t sign = (classed >> 63) & 1; | 
|  | 2680 | uint32_t exponent = (classed >> 52) & 0x00000000000007ff; | 
|  | 2681 | uint64_t mantissa = classed & 0x000fffffffffffff; | 
|  | 2682 | uint64_t result; | 
|  | 2683 | double dResult; | 
|  | 2684 |  | 
|  | 2685 | // Setting flags if input double is negative infinity, | 
|  | 2686 | // positive infinity, negative zero or positive zero | 
|  | 2687 | bool negInf = (classed == 0xFFF0000000000000); | 
|  | 2688 | bool posInf = (classed == 0x7FF0000000000000); | 
|  | 2689 | bool negZero = (classed == 0x8000000000000000); | 
|  | 2690 | bool posZero = (classed == 0x0000000000000000); | 
|  | 2691 |  | 
|  | 2692 | bool signalingNan; | 
|  | 2693 | bool quietNan; | 
|  | 2694 | bool negSubnorm; | 
|  | 2695 | bool posSubnorm; | 
|  | 2696 | bool negNorm; | 
|  | 2697 | bool posNorm; | 
|  | 2698 |  | 
|  | 2699 | // Setting flags if double is NaN | 
|  | 2700 | signalingNan = false; | 
|  | 2701 | quietNan = false; | 
|  | 2702 | if (!negInf && !posInf && exponent == 0x7ff) { | 
|  | 2703 | quietNan = ((mantissa & 0x0008000000000000) != 0) && | 
|  | 2704 | ((mantissa & (0x0008000000000000 - 1)) == 0); | 
|  | 2705 | signalingNan = !quietNan; | 
|  | 2706 | } | 
|  | 2707 |  | 
|  | 2708 | // Setting flags if double is subnormal number | 
|  | 2709 | posSubnorm = false; | 
|  | 2710 | negSubnorm = false; | 
|  | 2711 | if ((exponent == 0) && (mantissa != 0)) { | 
|  | 2712 | DCHECK(sign == 0 || sign == 1); | 
|  | 2713 | posSubnorm = (sign == 0); | 
|  | 2714 | negSubnorm = (sign == 1); | 
|  | 2715 | } | 
|  | 2716 |  | 
|  | 2717 | // Setting flags if double is normal number | 
|  | 2718 | posNorm = false; | 
|  | 2719 | negNorm = false; | 
|  | 2720 | if (!posSubnorm && !negSubnorm && !posInf && !negInf && !signalingNan && | 
|  | 2721 | !quietNan && !negZero && !posZero) { | 
|  | 2722 | DCHECK(sign == 0 || sign == 1); | 
|  | 2723 | posNorm = (sign == 0); | 
|  | 2724 | negNorm = (sign == 1); | 
|  | 2725 | } | 
|  | 2726 |  | 
|  | 2727 | // Calculating result according to description of CLASS.D instruction | 
|  | 2728 | result = (posZero << 9) | (posSubnorm << 8) | (posNorm << 7) | | 
|  | 2729 | (posInf << 6) | (negZero << 5) | (negSubnorm << 4) | | 
|  | 2730 | (negNorm << 3) | (negInf << 2) | (quietNan << 1) | signalingNan; | 
|  | 2731 |  | 
|  | 2732 | DCHECK(result != 0); | 
|  | 2733 |  | 
|  | 2734 | dResult = bit_cast<double>(result); | 
|  | 2735 | set_fpu_register_double(fd_reg(), dResult); | 
|  | 2736 |  | 
|  | 2737 | break; | 
|  | 2738 | } | 
|  | 2739 | case C_F_D: { | 
|  | 2740 | set_fcsr_bit(fcsr_cc, false); | 
|  | 2741 | break; | 
|  | 2742 | } | 
|  | 2743 | default: | 
|  | 2744 | UNREACHABLE(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2745 | } | 
|  | 2746 | } | 
|  | 2747 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2748 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2749 | void Simulator::DecodeTypeRegisterWRsType() { | 
|  | 2750 | float fs = get_fpu_register_float(fs_reg()); | 
|  | 2751 | float ft = get_fpu_register_float(ft_reg()); | 
|  | 2752 | int32_t alu_out = 0x12345678; | 
|  | 2753 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 2754 | case CVT_S_W:  // Convert word to float (single). | 
|  | 2755 | alu_out = get_fpu_register_signed_word(fs_reg()); | 
|  | 2756 | set_fpu_register_float(fd_reg(), static_cast<float>(alu_out)); | 
|  | 2757 | break; | 
|  | 2758 | case CVT_D_W:  // Convert word to double. | 
|  | 2759 | alu_out = get_fpu_register_signed_word(fs_reg()); | 
|  | 2760 | set_fpu_register_double(fd_reg(), static_cast<double>(alu_out)); | 
|  | 2761 | break; | 
|  | 2762 | case CMP_AF: | 
|  | 2763 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2764 | break; | 
|  | 2765 | case CMP_UN: | 
|  | 2766 | if (std::isnan(fs) || std::isnan(ft)) { | 
|  | 2767 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2768 | } else { | 
|  | 2769 | set_fpu_register_word(fd_reg(), 0); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2770 | } | 
|  | 2771 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2772 | case CMP_EQ: | 
|  | 2773 | if (fs == ft) { | 
|  | 2774 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2775 | } else { | 
|  | 2776 | set_fpu_register_word(fd_reg(), 0); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2777 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2778 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2779 | case CMP_UEQ: | 
|  | 2780 | if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 2781 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2782 | } else { | 
|  | 2783 | set_fpu_register_word(fd_reg(), 0); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2784 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2785 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2786 | case CMP_LT: | 
|  | 2787 | if (fs < ft) { | 
|  | 2788 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2789 | } else { | 
|  | 2790 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2791 | } | 
|  | 2792 | break; | 
|  | 2793 | case CMP_ULT: | 
|  | 2794 | if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 2795 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2796 | } else { | 
|  | 2797 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2798 | } | 
|  | 2799 | break; | 
|  | 2800 | case CMP_LE: | 
|  | 2801 | if (fs <= ft) { | 
|  | 2802 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2803 | } else { | 
|  | 2804 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2805 | } | 
|  | 2806 | break; | 
|  | 2807 | case CMP_ULE: | 
|  | 2808 | if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 2809 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2810 | } else { | 
|  | 2811 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2812 | } | 
|  | 2813 | break; | 
|  | 2814 | case CMP_OR: | 
|  | 2815 | if (!std::isnan(fs) && !std::isnan(ft)) { | 
|  | 2816 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2817 | } else { | 
|  | 2818 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2819 | } | 
|  | 2820 | break; | 
|  | 2821 | case CMP_UNE: | 
|  | 2822 | if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 2823 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2824 | } else { | 
|  | 2825 | set_fpu_register_word(fd_reg(), 0); | 
|  | 2826 | } | 
|  | 2827 | break; | 
|  | 2828 | case CMP_NE: | 
|  | 2829 | if (fs != ft) { | 
|  | 2830 | set_fpu_register_word(fd_reg(), -1); | 
|  | 2831 | } else { | 
|  | 2832 | set_fpu_register_word(fd_reg(), 0); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2833 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2834 | break; | 
|  | 2835 | default: | 
|  | 2836 | UNREACHABLE(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2837 | } | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 2838 | } | 
|  | 2839 |  | 
|  | 2840 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2841 | void Simulator::DecodeTypeRegisterSRsType() { | 
|  | 2842 | float fs, ft, fd; | 
|  | 2843 | fs = get_fpu_register_float(fs_reg()); | 
|  | 2844 | ft = get_fpu_register_float(ft_reg()); | 
|  | 2845 | fd = get_fpu_register_float(fd_reg()); | 
|  | 2846 | int32_t ft_int = bit_cast<int32_t>(ft); | 
|  | 2847 | int32_t fd_int = bit_cast<int32_t>(fd); | 
|  | 2848 | uint32_t cc, fcsr_cc; | 
|  | 2849 | cc = get_instr()->FCccValue(); | 
|  | 2850 | fcsr_cc = get_fcsr_condition_bit(cc); | 
|  | 2851 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 2852 | case RINT: { | 
|  | 2853 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2854 | float result, temp_result; | 
|  | 2855 | double temp; | 
|  | 2856 | float upper = std::ceil(fs); | 
|  | 2857 | float lower = std::floor(fs); | 
|  | 2858 | switch (get_fcsr_rounding_mode()) { | 
|  | 2859 | case kRoundToNearest: | 
|  | 2860 | if (upper - fs < fs - lower) { | 
|  | 2861 | result = upper; | 
|  | 2862 | } else if (upper - fs > fs - lower) { | 
|  | 2863 | result = lower; | 
|  | 2864 | } else { | 
|  | 2865 | temp_result = upper / 2; | 
|  | 2866 | float reminder = modf(temp_result, &temp); | 
|  | 2867 | if (reminder == 0) { | 
|  | 2868 | result = upper; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2869 | } else { | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2870 | result = lower; | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2871 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 2872 | } | 
|  | 2873 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2874 | case kRoundToZero: | 
|  | 2875 | result = (fs > 0 ? lower : upper); | 
|  | 2876 | break; | 
|  | 2877 | case kRoundToPlusInf: | 
|  | 2878 | result = upper; | 
|  | 2879 | break; | 
|  | 2880 | case kRoundToMinusInf: | 
|  | 2881 | result = lower; | 
|  | 2882 | break; | 
|  | 2883 | } | 
|  | 2884 | set_fpu_register_float(fd_reg(), result); | 
|  | 2885 | if (result != fs) { | 
|  | 2886 | set_fcsr_bit(kFCSRInexactFlagBit, true); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 2887 | } | 
|  | 2888 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 2889 | } | 
|  | 2890 | case ADD_S: | 
|  | 2891 | set_fpu_register_float(fd_reg(), fs + ft); | 
|  | 2892 | break; | 
|  | 2893 | case SUB_S: | 
|  | 2894 | set_fpu_register_float(fd_reg(), fs - ft); | 
|  | 2895 | break; | 
|  | 2896 | case MUL_S: | 
|  | 2897 | set_fpu_register_float(fd_reg(), fs * ft); | 
|  | 2898 | break; | 
|  | 2899 | case DIV_S: | 
|  | 2900 | set_fpu_register_float(fd_reg(), fs / ft); | 
|  | 2901 | break; | 
|  | 2902 | case ABS_S: | 
|  | 2903 | set_fpu_register_float(fd_reg(), fabs(fs)); | 
|  | 2904 | break; | 
|  | 2905 | case MOV_S: | 
|  | 2906 | set_fpu_register_float(fd_reg(), fs); | 
|  | 2907 | break; | 
|  | 2908 | case NEG_S: | 
|  | 2909 | set_fpu_register_float(fd_reg(), -fs); | 
|  | 2910 | break; | 
|  | 2911 | case SQRT_S: | 
|  | 2912 | lazily_initialize_fast_sqrt(isolate_); | 
|  | 2913 | set_fpu_register_float(fd_reg(), fast_sqrt(fs, isolate_)); | 
|  | 2914 | break; | 
|  | 2915 | case RSQRT_S: { | 
|  | 2916 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2917 | lazily_initialize_fast_sqrt(isolate_); | 
|  | 2918 | float result = 1.0 / fast_sqrt(fs, isolate_); | 
|  | 2919 | set_fpu_register_float(fd_reg(), result); | 
|  | 2920 | break; | 
|  | 2921 | } | 
|  | 2922 | case RECIP_S: { | 
|  | 2923 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 2924 | float result = 1.0 / fs; | 
|  | 2925 | set_fpu_register_float(fd_reg(), result); | 
|  | 2926 | break; | 
|  | 2927 | } | 
|  | 2928 | case C_F_D: | 
|  | 2929 | set_fcsr_bit(fcsr_cc, false); | 
|  | 2930 | break; | 
|  | 2931 | case C_UN_D: | 
|  | 2932 | set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); | 
|  | 2933 | break; | 
|  | 2934 | case C_EQ_D: | 
|  | 2935 | set_fcsr_bit(fcsr_cc, (fs == ft)); | 
|  | 2936 | break; | 
|  | 2937 | case C_UEQ_D: | 
|  | 2938 | set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft))); | 
|  | 2939 | break; | 
|  | 2940 | case C_OLT_D: | 
|  | 2941 | set_fcsr_bit(fcsr_cc, (fs < ft)); | 
|  | 2942 | break; | 
|  | 2943 | case C_ULT_D: | 
|  | 2944 | set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft))); | 
|  | 2945 | break; | 
|  | 2946 | case C_OLE_D: | 
|  | 2947 | set_fcsr_bit(fcsr_cc, (fs <= ft)); | 
|  | 2948 | break; | 
|  | 2949 | case C_ULE_D: | 
|  | 2950 | set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); | 
|  | 2951 | break; | 
|  | 2952 | case CVT_D_S: | 
|  | 2953 | set_fpu_register_double(fd_reg(), static_cast<double>(fs)); | 
|  | 2954 | break; | 
|  | 2955 | case SEL: | 
|  | 2956 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 2957 | set_fpu_register_float(fd_reg(), (fd_int & 0x1) == 0 ? fs : ft); | 
|  | 2958 | break; | 
|  | 2959 | case CLASS_S: {  // Mips32r6 instruction | 
|  | 2960 | // Convert float input to uint32_t for easier bit manipulation | 
|  | 2961 | float fs = get_fpu_register_float(fs_reg()); | 
|  | 2962 | uint32_t classed = bit_cast<uint32_t>(fs); | 
|  | 2963 |  | 
|  | 2964 | // Extracting sign, exponent and mantissa from the input float | 
|  | 2965 | uint32_t sign = (classed >> 31) & 1; | 
|  | 2966 | uint32_t exponent = (classed >> 23) & 0x000000ff; | 
|  | 2967 | uint32_t mantissa = classed & 0x007fffff; | 
|  | 2968 | uint32_t result; | 
|  | 2969 | float fResult; | 
|  | 2970 |  | 
|  | 2971 | // Setting flags if input float is negative infinity, | 
|  | 2972 | // positive infinity, negative zero or positive zero | 
|  | 2973 | bool negInf = (classed == 0xFF800000); | 
|  | 2974 | bool posInf = (classed == 0x7F800000); | 
|  | 2975 | bool negZero = (classed == 0x80000000); | 
|  | 2976 | bool posZero = (classed == 0x00000000); | 
|  | 2977 |  | 
|  | 2978 | bool signalingNan; | 
|  | 2979 | bool quietNan; | 
|  | 2980 | bool negSubnorm; | 
|  | 2981 | bool posSubnorm; | 
|  | 2982 | bool negNorm; | 
|  | 2983 | bool posNorm; | 
|  | 2984 |  | 
|  | 2985 | // Setting flags if float is NaN | 
|  | 2986 | signalingNan = false; | 
|  | 2987 | quietNan = false; | 
|  | 2988 | if (!negInf && !posInf && (exponent == 0xff)) { | 
|  | 2989 | quietNan = ((mantissa & 0x00200000) == 0) && | 
|  | 2990 | ((mantissa & (0x00200000 - 1)) == 0); | 
|  | 2991 | signalingNan = !quietNan; | 
|  | 2992 | } | 
|  | 2993 |  | 
|  | 2994 | // Setting flags if float is subnormal number | 
|  | 2995 | posSubnorm = false; | 
|  | 2996 | negSubnorm = false; | 
|  | 2997 | if ((exponent == 0) && (mantissa != 0)) { | 
|  | 2998 | DCHECK(sign == 0 || sign == 1); | 
|  | 2999 | posSubnorm = (sign == 0); | 
|  | 3000 | negSubnorm = (sign == 1); | 
|  | 3001 | } | 
|  | 3002 |  | 
|  | 3003 | // Setting flags if float is normal number | 
|  | 3004 | posNorm = false; | 
|  | 3005 | negNorm = false; | 
|  | 3006 | if (!posSubnorm && !negSubnorm && !posInf && !negInf && !signalingNan && | 
|  | 3007 | !quietNan && !negZero && !posZero) { | 
|  | 3008 | DCHECK(sign == 0 || sign == 1); | 
|  | 3009 | posNorm = (sign == 0); | 
|  | 3010 | negNorm = (sign == 1); | 
|  | 3011 | } | 
|  | 3012 |  | 
|  | 3013 | // Calculating result according to description of CLASS.S instruction | 
|  | 3014 | result = (posZero << 9) | (posSubnorm << 8) | (posNorm << 7) | | 
|  | 3015 | (posInf << 6) | (negZero << 5) | (negSubnorm << 4) | | 
|  | 3016 | (negNorm << 3) | (negInf << 2) | (quietNan << 1) | signalingNan; | 
|  | 3017 |  | 
|  | 3018 | DCHECK(result != 0); | 
|  | 3019 |  | 
|  | 3020 | fResult = bit_cast<float>(result); | 
|  | 3021 | set_fpu_register_float(fd_reg(), fResult); | 
|  | 3022 |  | 
|  | 3023 | break; | 
|  | 3024 | } | 
|  | 3025 | case SELEQZ_C: | 
|  | 3026 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3027 | set_fpu_register_float(fd_reg(), (ft_int & 0x1) == 0 | 
|  | 3028 | ? get_fpu_register_float(fs_reg()) | 
|  | 3029 | : 0.0); | 
|  | 3030 | break; | 
|  | 3031 | case SELNEZ_C: | 
|  | 3032 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3033 | set_fpu_register_float(fd_reg(), (ft_int & 0x1) != 0 | 
|  | 3034 | ? get_fpu_register_float(fs_reg()) | 
|  | 3035 | : 0.0); | 
|  | 3036 | break; | 
|  | 3037 | case MOVZ_C: { | 
|  | 3038 | DCHECK(IsMipsArchVariant(kMips32r2)); | 
|  | 3039 | if (rt() == 0) { | 
|  | 3040 | set_fpu_register_float(fd_reg(), fs); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3041 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3042 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3043 | } | 
|  | 3044 | case MOVN_C: { | 
|  | 3045 | DCHECK(IsMipsArchVariant(kMips32r2)); | 
|  | 3046 | if (rt() != 0) { | 
|  | 3047 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3048 | } | 
|  | 3049 | break; | 
|  | 3050 | } | 
|  | 3051 | case MOVF: { | 
|  | 3052 | // Same function field for MOVT.D and MOVF.D | 
|  | 3053 | uint32_t ft_cc = (ft_reg() >> 2) & 0x7; | 
|  | 3054 | ft_cc = get_fcsr_condition_bit(ft_cc); | 
|  | 3055 |  | 
|  | 3056 | if (get_instr()->Bit(16)) {  // Read Tf bit. | 
|  | 3057 | // MOVT.D | 
|  | 3058 | if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg(), fs); | 
|  | 3059 | } else { | 
|  | 3060 | // MOVF.D | 
|  | 3061 | if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg(), fs); | 
|  | 3062 | } | 
|  | 3063 | break; | 
|  | 3064 | } | 
|  | 3065 | case TRUNC_W_S: {  // Truncate single to word (round towards 0). | 
|  | 3066 | float rounded = trunc(fs); | 
|  | 3067 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 3068 | set_fpu_register_word(fd_reg(), result); | 
|  | 3069 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 3070 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 3071 | } | 
|  | 3072 | } break; | 
|  | 3073 | case TRUNC_L_S: {  // Mips32r2 instruction. | 
|  | 3074 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 3075 | float rounded = trunc(fs); | 
|  | 3076 | int64_t i64 = static_cast<int64_t>(rounded); | 
|  | 3077 | if (IsFp64Mode()) { | 
|  | 3078 | set_fpu_register(fd_reg(), i64); | 
|  | 3079 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 3080 | set_fpu_register_invalid_result64(fs, rounded); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3081 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3082 | } else { | 
|  | 3083 | UNSUPPORTED(); | 
|  | 3084 | } | 
|  | 3085 | break; | 
|  | 3086 | } | 
|  | 3087 | case FLOOR_W_S:  // Round double to word towards negative infinity. | 
|  | 3088 | { | 
|  | 3089 | float rounded = std::floor(fs); | 
|  | 3090 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 3091 | set_fpu_register_word(fd_reg(), result); | 
|  | 3092 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 3093 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 3094 | } | 
|  | 3095 | } break; | 
|  | 3096 | case FLOOR_L_S: {  // Mips32r2 instruction. | 
|  | 3097 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 3098 | float rounded = std::floor(fs); | 
|  | 3099 | int64_t i64 = static_cast<int64_t>(rounded); | 
|  | 3100 | if (IsFp64Mode()) { | 
|  | 3101 | set_fpu_register(fd_reg(), i64); | 
|  | 3102 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 3103 | set_fpu_register_invalid_result64(fs, rounded); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3104 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3105 | } else { | 
|  | 3106 | UNSUPPORTED(); | 
|  | 3107 | } | 
|  | 3108 | break; | 
|  | 3109 | } | 
|  | 3110 | case ROUND_W_S: { | 
|  | 3111 | float rounded = std::floor(fs + 0.5); | 
|  | 3112 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 3113 | if ((result & 1) != 0 && result - fs == 0.5) { | 
|  | 3114 | // If the number is halfway between two integers, | 
|  | 3115 | // round to the even one. | 
|  | 3116 | result--; | 
|  | 3117 | } | 
|  | 3118 | set_fpu_register_word(fd_reg(), result); | 
|  | 3119 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 3120 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 3121 | } | 
|  | 3122 | break; | 
|  | 3123 | } | 
|  | 3124 | case ROUND_L_S: {  // Mips32r2 instruction. | 
|  | 3125 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 3126 | float rounded = std::floor(fs + 0.5); | 
|  | 3127 | int64_t result = static_cast<int64_t>(rounded); | 
|  | 3128 | if ((result & 1) != 0 && result - fs == 0.5) { | 
|  | 3129 | // If the number is halfway between two integers, | 
|  | 3130 | // round to the even one. | 
|  | 3131 | result--; | 
|  | 3132 | } | 
|  | 3133 | int64_t i64 = static_cast<int64_t>(result); | 
|  | 3134 | if (IsFp64Mode()) { | 
|  | 3135 | set_fpu_register(fd_reg(), i64); | 
|  | 3136 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 3137 | set_fpu_register_invalid_result64(fs, rounded); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3138 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3139 | } else { | 
|  | 3140 | UNSUPPORTED(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3141 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3142 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3143 | } | 
|  | 3144 | case CEIL_W_S:  // Round double to word towards positive infinity. | 
|  | 3145 | { | 
|  | 3146 | float rounded = std::ceil(fs); | 
|  | 3147 | int32_t result = static_cast<int32_t>(rounded); | 
|  | 3148 | set_fpu_register_word(fd_reg(), result); | 
|  | 3149 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 3150 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 3151 | } | 
|  | 3152 | } break; | 
|  | 3153 | case CEIL_L_S: {  // Mips32r2 instruction. | 
|  | 3154 | DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); | 
|  | 3155 | float rounded = std::ceil(fs); | 
|  | 3156 | int64_t i64 = static_cast<int64_t>(rounded); | 
|  | 3157 | if (IsFp64Mode()) { | 
|  | 3158 | set_fpu_register(fd_reg(), i64); | 
|  | 3159 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 3160 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 3161 | } | 
|  | 3162 | } else { | 
|  | 3163 | UNSUPPORTED(); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3164 | } | 
|  | 3165 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3166 | } | 
|  | 3167 | case MIN: | 
|  | 3168 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3169 | fs = get_fpu_register_float(fs_reg()); | 
|  | 3170 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 3171 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3172 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 3173 | set_fpu_register_float(fd_reg(), ft); | 
|  | 3174 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 3175 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3176 | } else { | 
|  | 3177 | set_fpu_register_float(fd_reg(), (fs >= ft) ? ft : fs); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3178 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3179 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3180 | case MAX: | 
|  | 3181 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3182 | fs = get_fpu_register_float(fs_reg()); | 
|  | 3183 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 3184 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3185 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 3186 | set_fpu_register_float(fd_reg(), ft); | 
|  | 3187 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 3188 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3189 | } else { | 
|  | 3190 | set_fpu_register_float(fd_reg(), (fs <= ft) ? ft : fs); | 
|  | 3191 | } | 
|  | 3192 | break; | 
|  | 3193 | case MINA: | 
|  | 3194 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3195 | fs = get_fpu_register_float(fs_reg()); | 
|  | 3196 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 3197 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3198 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 3199 | set_fpu_register_float(fd_reg(), ft); | 
|  | 3200 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 3201 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3202 | } else { | 
|  | 3203 | float result; | 
|  | 3204 | if (fabs(fs) > fabs(ft)) { | 
|  | 3205 | result = ft; | 
|  | 3206 | } else if (fabs(fs) < fabs(ft)) { | 
|  | 3207 | result = fs; | 
|  | 3208 | } else { | 
|  | 3209 | result = (fs < ft ? fs : ft); | 
|  | 3210 | } | 
|  | 3211 | set_fpu_register_float(fd_reg(), result); | 
|  | 3212 | } | 
|  | 3213 | break; | 
|  | 3214 | case MAXA: | 
|  | 3215 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3216 | fs = get_fpu_register_float(fs_reg()); | 
|  | 3217 | if (std::isnan(fs) && std::isnan(ft)) { | 
|  | 3218 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3219 | } else if (std::isnan(fs) && !std::isnan(ft)) { | 
|  | 3220 | set_fpu_register_float(fd_reg(), ft); | 
|  | 3221 | } else if (!std::isnan(fs) && std::isnan(ft)) { | 
|  | 3222 | set_fpu_register_float(fd_reg(), fs); | 
|  | 3223 | } else { | 
|  | 3224 | float result; | 
|  | 3225 | if (fabs(fs) < fabs(ft)) { | 
|  | 3226 | result = ft; | 
|  | 3227 | } else if (fabs(fs) > fabs(ft)) { | 
|  | 3228 | result = fs; | 
|  | 3229 | } else { | 
|  | 3230 | result = (fs > ft ? fs : ft); | 
|  | 3231 | } | 
|  | 3232 | set_fpu_register_float(fd_reg(), result); | 
|  | 3233 | } | 
|  | 3234 | break; | 
|  | 3235 | case CVT_L_S: { | 
|  | 3236 | if (IsFp64Mode()) { | 
|  | 3237 | int64_t result; | 
|  | 3238 | float rounded; | 
|  | 3239 | round64_according_to_fcsr(fs, rounded, result, fs); | 
|  | 3240 | set_fpu_register(fd_reg(), result); | 
|  | 3241 | if (set_fcsr_round64_error(fs, rounded)) { | 
|  | 3242 | set_fpu_register_invalid_result64(fs, rounded); | 
|  | 3243 | } | 
|  | 3244 | } else { | 
|  | 3245 | UNSUPPORTED(); | 
|  | 3246 | } | 
|  | 3247 | break; | 
|  | 3248 | } | 
|  | 3249 | case CVT_W_S: { | 
|  | 3250 | float rounded; | 
|  | 3251 | int32_t result; | 
|  | 3252 | round_according_to_fcsr(fs, rounded, result, fs); | 
|  | 3253 | set_fpu_register_word(fd_reg(), result); | 
|  | 3254 | if (set_fcsr_round_error(fs, rounded)) { | 
|  | 3255 | set_fpu_register_word_invalid_result(fs, rounded); | 
|  | 3256 | } | 
|  | 3257 | break; | 
|  | 3258 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3259 | default: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3260 | // CVT_W_S CVT_L_S  ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S | 
|  | 3261 | // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented. | 
|  | 3262 | UNREACHABLE(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3263 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3264 | } | 
|  | 3265 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3266 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3267 | void Simulator::DecodeTypeRegisterLRsType() { | 
|  | 3268 | double fs = get_fpu_register_double(fs_reg()); | 
|  | 3269 | double ft = get_fpu_register_double(ft_reg()); | 
|  | 3270 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 3271 | case CVT_D_L:  // Mips32r2 instruction. | 
|  | 3272 | // Watch the signs here, we want 2 32-bit vals | 
|  | 3273 | // to make a sign-64. | 
|  | 3274 | int64_t i64; | 
|  | 3275 | if (IsFp64Mode()) { | 
|  | 3276 | i64 = get_fpu_register(fs_reg()); | 
|  | 3277 | } else { | 
|  | 3278 | i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg())); | 
|  | 3279 | i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg() + 1)) << 32; | 
|  | 3280 | } | 
|  | 3281 | set_fpu_register_double(fd_reg(), static_cast<double>(i64)); | 
|  | 3282 | break; | 
|  | 3283 | case CVT_S_L: | 
|  | 3284 | if (IsFp64Mode()) { | 
|  | 3285 | i64 = get_fpu_register(fs_reg()); | 
|  | 3286 | } else { | 
|  | 3287 | i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg())); | 
|  | 3288 | i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg() + 1)) << 32; | 
|  | 3289 | } | 
|  | 3290 | set_fpu_register_float(fd_reg(), static_cast<float>(i64)); | 
|  | 3291 | break; | 
|  | 3292 | case CMP_AF:  // Mips64r6 CMP.D instructions. | 
|  | 3293 | set_fpu_register(fd_reg(), 0); | 
|  | 3294 | break; | 
|  | 3295 | case CMP_UN: | 
|  | 3296 | if (std::isnan(fs) || std::isnan(ft)) { | 
|  | 3297 | set_fpu_register(fd_reg(), -1); | 
|  | 3298 | } else { | 
|  | 3299 | set_fpu_register(fd_reg(), 0); | 
|  | 3300 | } | 
|  | 3301 | break; | 
|  | 3302 | case CMP_EQ: | 
|  | 3303 | if (fs == ft) { | 
|  | 3304 | set_fpu_register(fd_reg(), -1); | 
|  | 3305 | } else { | 
|  | 3306 | set_fpu_register(fd_reg(), 0); | 
|  | 3307 | } | 
|  | 3308 | break; | 
|  | 3309 | case CMP_UEQ: | 
|  | 3310 | if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 3311 | set_fpu_register(fd_reg(), -1); | 
|  | 3312 | } else { | 
|  | 3313 | set_fpu_register(fd_reg(), 0); | 
|  | 3314 | } | 
|  | 3315 | break; | 
|  | 3316 | case CMP_LT: | 
|  | 3317 | if (fs < ft) { | 
|  | 3318 | set_fpu_register(fd_reg(), -1); | 
|  | 3319 | } else { | 
|  | 3320 | set_fpu_register(fd_reg(), 0); | 
|  | 3321 | } | 
|  | 3322 | break; | 
|  | 3323 | case CMP_ULT: | 
|  | 3324 | if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 3325 | set_fpu_register(fd_reg(), -1); | 
|  | 3326 | } else { | 
|  | 3327 | set_fpu_register(fd_reg(), 0); | 
|  | 3328 | } | 
|  | 3329 | break; | 
|  | 3330 | case CMP_LE: | 
|  | 3331 | if (fs <= ft) { | 
|  | 3332 | set_fpu_register(fd_reg(), -1); | 
|  | 3333 | } else { | 
|  | 3334 | set_fpu_register(fd_reg(), 0); | 
|  | 3335 | } | 
|  | 3336 | break; | 
|  | 3337 | case CMP_ULE: | 
|  | 3338 | if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 3339 | set_fpu_register(fd_reg(), -1); | 
|  | 3340 | } else { | 
|  | 3341 | set_fpu_register(fd_reg(), 0); | 
|  | 3342 | } | 
|  | 3343 | break; | 
|  | 3344 | case CMP_OR: | 
|  | 3345 | if (!std::isnan(fs) && !std::isnan(ft)) { | 
|  | 3346 | set_fpu_register(fd_reg(), -1); | 
|  | 3347 | } else { | 
|  | 3348 | set_fpu_register(fd_reg(), 0); | 
|  | 3349 | } | 
|  | 3350 | break; | 
|  | 3351 | case CMP_UNE: | 
|  | 3352 | if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) { | 
|  | 3353 | set_fpu_register(fd_reg(), -1); | 
|  | 3354 | } else { | 
|  | 3355 | set_fpu_register(fd_reg(), 0); | 
|  | 3356 | } | 
|  | 3357 | break; | 
|  | 3358 | case CMP_NE: | 
|  | 3359 | if (fs != ft && (!std::isnan(fs) && !std::isnan(ft))) { | 
|  | 3360 | set_fpu_register(fd_reg(), -1); | 
|  | 3361 | } else { | 
|  | 3362 | set_fpu_register(fd_reg(), 0); | 
|  | 3363 | } | 
|  | 3364 | break; | 
|  | 3365 | default: | 
|  | 3366 | UNREACHABLE(); | 
|  | 3367 | } | 
|  | 3368 | } | 
|  | 3369 |  | 
|  | 3370 |  | 
|  | 3371 | void Simulator::DecodeTypeRegisterCOP1() { | 
|  | 3372 | switch (get_instr()->RsFieldRaw()) { | 
|  | 3373 | case CFC1: | 
|  | 3374 | // At the moment only FCSR is supported. | 
|  | 3375 | DCHECK(fs_reg() == kFCSRRegister); | 
|  | 3376 | set_register(rt_reg(), FCSR_); | 
|  | 3377 | break; | 
|  | 3378 | case MFC1: | 
|  | 3379 | set_register(rt_reg(), get_fpu_register_word(fs_reg())); | 
|  | 3380 | break; | 
|  | 3381 | case MFHC1: | 
|  | 3382 | set_register(rt_reg(), get_fpu_register_hi_word(fs_reg())); | 
|  | 3383 | break; | 
|  | 3384 | case CTC1: { | 
|  | 3385 | // At the moment only FCSR is supported. | 
|  | 3386 | DCHECK(fs_reg() == kFCSRRegister); | 
|  | 3387 | int32_t reg = registers_[rt_reg()]; | 
|  | 3388 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 3389 | FCSR_ = reg | kFCSRNaN2008FlagMask; | 
|  | 3390 | } else { | 
|  | 3391 | DCHECK(IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kMips32r2)); | 
|  | 3392 | FCSR_ = reg & ~kFCSRNaN2008FlagMask; | 
|  | 3393 | } | 
|  | 3394 | break; | 
|  | 3395 | } | 
|  | 3396 | case MTC1: | 
|  | 3397 | // Hardware writes upper 32-bits to zero on mtc1. | 
|  | 3398 | set_fpu_register_hi_word(fs_reg(), 0); | 
|  | 3399 | set_fpu_register_word(fs_reg(), registers_[rt_reg()]); | 
|  | 3400 | break; | 
|  | 3401 | case MTHC1: | 
|  | 3402 | set_fpu_register_hi_word(fs_reg(), registers_[rt_reg()]); | 
|  | 3403 | break; | 
|  | 3404 | case S: { | 
|  | 3405 | DecodeTypeRegisterSRsType(); | 
|  | 3406 | break; | 
|  | 3407 | } | 
|  | 3408 | case D: | 
|  | 3409 | DecodeTypeRegisterDRsType(); | 
|  | 3410 | break; | 
|  | 3411 | case W: | 
|  | 3412 | DecodeTypeRegisterWRsType(); | 
|  | 3413 | break; | 
|  | 3414 | case L: | 
|  | 3415 | DecodeTypeRegisterLRsType(); | 
|  | 3416 | break; | 
|  | 3417 | case PS: | 
|  | 3418 | // Not implemented. | 
|  | 3419 | UNREACHABLE(); | 
|  | 3420 | default: | 
|  | 3421 | UNREACHABLE(); | 
|  | 3422 | } | 
|  | 3423 | } | 
|  | 3424 |  | 
|  | 3425 |  | 
|  | 3426 | void Simulator::DecodeTypeRegisterCOP1X() { | 
|  | 3427 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 3428 | case MADD_D: | 
|  | 3429 | double fr, ft, fs; | 
|  | 3430 | fr = get_fpu_register_double(fr_reg()); | 
|  | 3431 | fs = get_fpu_register_double(fs_reg()); | 
|  | 3432 | ft = get_fpu_register_double(ft_reg()); | 
|  | 3433 | set_fpu_register_double(fd_reg(), fs * ft + fr); | 
|  | 3434 | break; | 
|  | 3435 | default: | 
|  | 3436 | UNREACHABLE(); | 
|  | 3437 | } | 
|  | 3438 | } | 
|  | 3439 |  | 
|  | 3440 |  | 
|  | 3441 | void Simulator::DecodeTypeRegisterSPECIAL() { | 
|  | 3442 | int64_t alu_out = 0x12345678; | 
|  | 3443 | int64_t i64hilo = 0; | 
|  | 3444 | uint64_t u64hilo = 0; | 
|  | 3445 | bool do_interrupt = false; | 
|  | 3446 |  | 
|  | 3447 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 3448 | case SELEQZ_S: | 
|  | 3449 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3450 | set_register(rd_reg(), rt() == 0 ? rs() : 0); | 
|  | 3451 | break; | 
|  | 3452 | case SELNEZ_S: | 
|  | 3453 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3454 | set_register(rd_reg(), rt() != 0 ? rs() : 0); | 
|  | 3455 | break; | 
|  | 3456 | case JR: { | 
|  | 3457 | int32_t next_pc = rs(); | 
|  | 3458 | int32_t current_pc = get_pc(); | 
|  | 3459 | Instruction* branch_delay_instr = | 
|  | 3460 | reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); | 
|  | 3461 | BranchDelayInstructionDecode(branch_delay_instr); | 
|  | 3462 | set_pc(next_pc); | 
|  | 3463 | pc_modified_ = true; | 
|  | 3464 | break; | 
|  | 3465 | } | 
|  | 3466 | case JALR: { | 
|  | 3467 | int32_t next_pc = rs(); | 
|  | 3468 | int32_t return_addr_reg = rd_reg(); | 
|  | 3469 | int32_t current_pc = get_pc(); | 
|  | 3470 | Instruction* branch_delay_instr = | 
|  | 3471 | reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); | 
|  | 3472 | BranchDelayInstructionDecode(branch_delay_instr); | 
|  | 3473 | set_register(return_addr_reg, current_pc + 2 * Instruction::kInstrSize); | 
|  | 3474 | set_pc(next_pc); | 
|  | 3475 | pc_modified_ = true; | 
|  | 3476 | break; | 
|  | 3477 | } | 
|  | 3478 | case SLL: | 
|  | 3479 | alu_out = rt() << sa(); | 
|  | 3480 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3481 | break; | 
|  | 3482 | case SRL: | 
|  | 3483 | if (rs_reg() == 0) { | 
|  | 3484 | // Regular logical right shift of a word by a fixed number of | 
|  | 3485 | // bits instruction. RS field is always equal to 0. | 
|  | 3486 | alu_out = rt_u() >> sa(); | 
|  | 3487 | } else { | 
|  | 3488 | // Logical right-rotate of a word by a fixed number of bits. This | 
|  | 3489 | // is special case of SRL instruction, added in MIPS32 Release 2. | 
|  | 3490 | // RS field is equal to 00001. | 
|  | 3491 | alu_out = base::bits::RotateRight32(rt_u(), sa()); | 
|  | 3492 | } | 
|  | 3493 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3494 | break; | 
|  | 3495 | case SRA: | 
|  | 3496 | alu_out = rt() >> sa(); | 
|  | 3497 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3498 | break; | 
|  | 3499 | case SLLV: | 
|  | 3500 | alu_out = rt() << rs(); | 
|  | 3501 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3502 | break; | 
|  | 3503 | case SRLV: | 
|  | 3504 | if (sa() == 0) { | 
|  | 3505 | // Regular logical right-shift of a word by a variable number of | 
|  | 3506 | // bits instruction. SA field is always equal to 0. | 
|  | 3507 | alu_out = rt_u() >> rs(); | 
|  | 3508 | } else { | 
|  | 3509 | // Logical right-rotate of a word by a variable number of bits. | 
|  | 3510 | // This is special case od SRLV instruction, added in MIPS32 | 
|  | 3511 | // Release 2. SA field is equal to 00001. | 
|  | 3512 | alu_out = base::bits::RotateRight32(rt_u(), rs_u()); | 
|  | 3513 | } | 
|  | 3514 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3515 | break; | 
|  | 3516 | case SRAV: | 
|  | 3517 | SetResult(rd_reg(), rt() >> rs()); | 
|  | 3518 | break; | 
|  | 3519 | case LSA: { | 
|  | 3520 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 3521 | int8_t sa = lsa_sa() + 1; | 
|  | 3522 | int32_t _rt = rt(); | 
|  | 3523 | int32_t _rs = rs(); | 
|  | 3524 | int32_t res = _rs << sa; | 
|  | 3525 | res += _rt; | 
|  | 3526 | DCHECK_EQ(res, (rs() << (lsa_sa() + 1)) + rt()); | 
|  | 3527 | SetResult(rd_reg(), (rs() << (lsa_sa() + 1)) + rt()); | 
|  | 3528 | break; | 
|  | 3529 | } | 
|  | 3530 | case MFHI:  // MFHI == CLZ on R6. | 
|  | 3531 | if (!IsMipsArchVariant(kMips32r6)) { | 
|  | 3532 | DCHECK(sa() == 0); | 
|  | 3533 | alu_out = get_register(HI); | 
|  | 3534 | } else { | 
|  | 3535 | // MIPS spec: If no bits were set in GPR rs, the result written to | 
|  | 3536 | // GPR rd is 32. | 
|  | 3537 | DCHECK(sa() == 1); | 
|  | 3538 | alu_out = base::bits::CountLeadingZeros32(rs_u()); | 
|  | 3539 | } | 
|  | 3540 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3541 | break; | 
|  | 3542 | case MFLO: | 
|  | 3543 | alu_out = get_register(LO); | 
|  | 3544 | SetResult(rd_reg(), static_cast<int32_t>(alu_out)); | 
|  | 3545 | break; | 
|  | 3546 | // Instructions using HI and LO registers. | 
|  | 3547 | case MULT: | 
|  | 3548 | i64hilo = static_cast<int64_t>(rs()) * static_cast<int64_t>(rt()); | 
|  | 3549 | if (!IsMipsArchVariant(kMips32r6)) { | 
|  | 3550 | set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff)); | 
|  | 3551 | set_register(HI, static_cast<int32_t>(i64hilo >> 32)); | 
|  | 3552 | } else { | 
|  | 3553 | switch (sa()) { | 
|  | 3554 | case MUL_OP: | 
|  | 3555 | set_register(rd_reg(), static_cast<int32_t>(i64hilo & 0xffffffff)); | 
|  | 3556 | break; | 
|  | 3557 | case MUH_OP: | 
|  | 3558 | set_register(rd_reg(), static_cast<int32_t>(i64hilo >> 32)); | 
|  | 3559 | break; | 
|  | 3560 | default: | 
|  | 3561 | UNIMPLEMENTED_MIPS(); | 
|  | 3562 | break; | 
|  | 3563 | } | 
|  | 3564 | } | 
|  | 3565 | break; | 
|  | 3566 | case MULTU: | 
|  | 3567 | u64hilo = static_cast<uint64_t>(rs_u()) * static_cast<uint64_t>(rt_u()); | 
|  | 3568 | if (!IsMipsArchVariant(kMips32r6)) { | 
|  | 3569 | set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff)); | 
|  | 3570 | set_register(HI, static_cast<int32_t>(u64hilo >> 32)); | 
|  | 3571 | } else { | 
|  | 3572 | switch (sa()) { | 
|  | 3573 | case MUL_OP: | 
|  | 3574 | set_register(rd_reg(), static_cast<int32_t>(u64hilo & 0xffffffff)); | 
|  | 3575 | break; | 
|  | 3576 | case MUH_OP: | 
|  | 3577 | set_register(rd_reg(), static_cast<int32_t>(u64hilo >> 32)); | 
|  | 3578 | break; | 
|  | 3579 | default: | 
|  | 3580 | UNIMPLEMENTED_MIPS(); | 
|  | 3581 | break; | 
|  | 3582 | } | 
|  | 3583 | } | 
|  | 3584 | break; | 
|  | 3585 | case DIV: | 
|  | 3586 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 3587 | switch (get_instr()->SaValue()) { | 
|  | 3588 | case DIV_OP: | 
|  | 3589 | if (rs() == INT_MIN && rt() == -1) { | 
|  | 3590 | set_register(rd_reg(), INT_MIN); | 
|  | 3591 | } else if (rt() != 0) { | 
|  | 3592 | set_register(rd_reg(), rs() / rt()); | 
|  | 3593 | } | 
|  | 3594 | break; | 
|  | 3595 | case MOD_OP: | 
|  | 3596 | if (rs() == INT_MIN && rt() == -1) { | 
|  | 3597 | set_register(rd_reg(), 0); | 
|  | 3598 | } else if (rt() != 0) { | 
|  | 3599 | set_register(rd_reg(), rs() % rt()); | 
|  | 3600 | } | 
|  | 3601 | break; | 
|  | 3602 | default: | 
|  | 3603 | UNIMPLEMENTED_MIPS(); | 
|  | 3604 | break; | 
|  | 3605 | } | 
|  | 3606 | } else { | 
|  | 3607 | // Divide by zero and overflow was not checked in the | 
|  | 3608 | // configuration step - div and divu do not raise exceptions. On | 
|  | 3609 | // division by 0 the result will be UNPREDICTABLE. On overflow | 
|  | 3610 | // (INT_MIN/-1), return INT_MIN which is what the hardware does. | 
|  | 3611 | if (rs() == INT_MIN && rt() == -1) { | 
|  | 3612 | set_register(LO, INT_MIN); | 
|  | 3613 | set_register(HI, 0); | 
|  | 3614 | } else if (rt() != 0) { | 
|  | 3615 | set_register(LO, rs() / rt()); | 
|  | 3616 | set_register(HI, rs() % rt()); | 
|  | 3617 | } | 
|  | 3618 | } | 
|  | 3619 | break; | 
|  | 3620 | case DIVU: | 
|  | 3621 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 3622 | switch (get_instr()->SaValue()) { | 
|  | 3623 | case DIV_OP: | 
|  | 3624 | if (rt_u() != 0) { | 
|  | 3625 | set_register(rd_reg(), rs_u() / rt_u()); | 
|  | 3626 | } | 
|  | 3627 | break; | 
|  | 3628 | case MOD_OP: | 
|  | 3629 | if (rt_u() != 0) { | 
|  | 3630 | set_register(rd_reg(), rs_u() % rt_u()); | 
|  | 3631 | } | 
|  | 3632 | break; | 
|  | 3633 | default: | 
|  | 3634 | UNIMPLEMENTED_MIPS(); | 
|  | 3635 | break; | 
|  | 3636 | } | 
|  | 3637 | } else { | 
|  | 3638 | if (rt_u() != 0) { | 
|  | 3639 | set_register(LO, rs_u() / rt_u()); | 
|  | 3640 | set_register(HI, rs_u() % rt_u()); | 
|  | 3641 | } | 
|  | 3642 | } | 
|  | 3643 | break; | 
|  | 3644 | case ADD: | 
|  | 3645 | if (HaveSameSign(rs(), rt())) { | 
|  | 3646 | if (rs() > 0) { | 
|  | 3647 | if (rs() <= (Registers::kMaxValue - rt())) { | 
|  | 3648 | SignalException(kIntegerOverflow); | 
|  | 3649 | } | 
|  | 3650 | } else if (rs() < 0) { | 
|  | 3651 | if (rs() >= (Registers::kMinValue - rt())) { | 
|  | 3652 | SignalException(kIntegerUnderflow); | 
|  | 3653 | } | 
|  | 3654 | } | 
|  | 3655 | } | 
|  | 3656 | SetResult(rd_reg(), rs() + rt()); | 
|  | 3657 | break; | 
|  | 3658 | case ADDU: | 
|  | 3659 | SetResult(rd_reg(), rs() + rt()); | 
|  | 3660 | break; | 
|  | 3661 | case SUB: | 
|  | 3662 | if (!HaveSameSign(rs(), rt())) { | 
|  | 3663 | if (rs() > 0) { | 
|  | 3664 | if (rs() <= (Registers::kMaxValue + rt())) { | 
|  | 3665 | SignalException(kIntegerOverflow); | 
|  | 3666 | } | 
|  | 3667 | } else if (rs() < 0) { | 
|  | 3668 | if (rs() >= (Registers::kMinValue + rt())) { | 
|  | 3669 | SignalException(kIntegerUnderflow); | 
|  | 3670 | } | 
|  | 3671 | } | 
|  | 3672 | } | 
|  | 3673 | SetResult(rd_reg(), rs() - rt()); | 
|  | 3674 | break; | 
|  | 3675 | case SUBU: | 
|  | 3676 | SetResult(rd_reg(), rs() - rt()); | 
|  | 3677 | break; | 
|  | 3678 | case AND: | 
|  | 3679 | SetResult(rd_reg(), rs() & rt()); | 
|  | 3680 | break; | 
|  | 3681 | case OR: | 
|  | 3682 | SetResult(rd_reg(), rs() | rt()); | 
|  | 3683 | break; | 
|  | 3684 | case XOR: | 
|  | 3685 | SetResult(rd_reg(), rs() ^ rt()); | 
|  | 3686 | break; | 
|  | 3687 | case NOR: | 
|  | 3688 | SetResult(rd_reg(), ~(rs() | rt())); | 
|  | 3689 | break; | 
|  | 3690 | case SLT: | 
|  | 3691 | SetResult(rd_reg(), rs() < rt() ? 1 : 0); | 
|  | 3692 | break; | 
|  | 3693 | case SLTU: | 
|  | 3694 | SetResult(rd_reg(), rs_u() < rt_u() ? 1 : 0); | 
|  | 3695 | break; | 
|  | 3696 | // Break and trap instructions. | 
|  | 3697 | case BREAK: | 
|  | 3698 | do_interrupt = true; | 
|  | 3699 | break; | 
|  | 3700 | case TGE: | 
|  | 3701 | do_interrupt = rs() >= rt(); | 
|  | 3702 | break; | 
|  | 3703 | case TGEU: | 
|  | 3704 | do_interrupt = rs_u() >= rt_u(); | 
|  | 3705 | break; | 
|  | 3706 | case TLT: | 
|  | 3707 | do_interrupt = rs() < rt(); | 
|  | 3708 | break; | 
|  | 3709 | case TLTU: | 
|  | 3710 | do_interrupt = rs_u() < rt_u(); | 
|  | 3711 | break; | 
|  | 3712 | case TEQ: | 
|  | 3713 | do_interrupt = rs() == rt(); | 
|  | 3714 | break; | 
|  | 3715 | case TNE: | 
|  | 3716 | do_interrupt = rs() != rt(); | 
|  | 3717 | break; | 
|  | 3718 | // Conditional moves. | 
|  | 3719 | case MOVN: | 
|  | 3720 | if (rt()) { | 
|  | 3721 | set_register(rd_reg(), rs()); | 
|  | 3722 | TraceRegWr(rs()); | 
|  | 3723 | } | 
|  | 3724 | break; | 
|  | 3725 | case MOVCI: { | 
|  | 3726 | uint32_t cc = get_instr()->FBccValue(); | 
|  | 3727 | uint32_t fcsr_cc = get_fcsr_condition_bit(cc); | 
|  | 3728 | if (get_instr()->Bit(16)) {  // Read Tf bit. | 
|  | 3729 | if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg(), rs()); | 
|  | 3730 | } else { | 
|  | 3731 | if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg(), rs()); | 
|  | 3732 | } | 
|  | 3733 | break; | 
|  | 3734 | } | 
|  | 3735 | case MOVZ: | 
|  | 3736 | if (!rt()) { | 
|  | 3737 | set_register(rd_reg(), rs()); | 
|  | 3738 | TraceRegWr(rs()); | 
|  | 3739 | } | 
|  | 3740 | break; | 
|  | 3741 | default: | 
|  | 3742 | UNREACHABLE(); | 
|  | 3743 | } | 
|  | 3744 | if (do_interrupt) { | 
|  | 3745 | SoftwareInterrupt(get_instr()); | 
|  | 3746 | } | 
|  | 3747 | } | 
|  | 3748 |  | 
|  | 3749 |  | 
|  | 3750 | void Simulator::DecodeTypeRegisterSPECIAL2() { | 
|  | 3751 | int32_t alu_out; | 
|  | 3752 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 3753 | case MUL: | 
|  | 3754 | // Only the lower 32 bits are kept. | 
|  | 3755 | alu_out = rs_u() * rt_u(); | 
|  | 3756 | // HI and LO are UNPREDICTABLE after the operation. | 
|  | 3757 | set_register(LO, Unpredictable); | 
|  | 3758 | set_register(HI, Unpredictable); | 
|  | 3759 | break; | 
|  | 3760 | case CLZ: | 
|  | 3761 | // MIPS32 spec: If no bits were set in GPR rs, the result written to | 
|  | 3762 | // GPR rd is 32. | 
|  | 3763 | alu_out = base::bits::CountLeadingZeros32(rs_u()); | 
|  | 3764 | break; | 
|  | 3765 | default: | 
|  | 3766 | alu_out = 0x12345678; | 
|  | 3767 | UNREACHABLE(); | 
|  | 3768 | } | 
|  | 3769 | SetResult(rd_reg(), alu_out); | 
|  | 3770 | } | 
|  | 3771 |  | 
|  | 3772 |  | 
|  | 3773 | void Simulator::DecodeTypeRegisterSPECIAL3() { | 
|  | 3774 | int32_t alu_out; | 
|  | 3775 | switch (get_instr()->FunctionFieldRaw()) { | 
|  | 3776 | case INS: {  // Mips32r2 instruction. | 
|  | 3777 | // Interpret rd field as 5-bit msb of insert. | 
|  | 3778 | uint16_t msb = rd_reg(); | 
|  | 3779 | // Interpret sa field as 5-bit lsb of insert. | 
|  | 3780 | uint16_t lsb = sa(); | 
|  | 3781 | uint16_t size = msb - lsb + 1; | 
|  | 3782 | uint32_t mask = (1 << size) - 1; | 
|  | 3783 | alu_out = (rt_u() & ~(mask << lsb)) | ((rs_u() & mask) << lsb); | 
|  | 3784 | // Ins instr leaves result in Rt, rather than Rd. | 
|  | 3785 | SetResult(rt_reg(), alu_out); | 
|  | 3786 | break; | 
|  | 3787 | } | 
|  | 3788 | case EXT: {  // Mips32r2 instruction. | 
|  | 3789 | // Interpret rd field as 5-bit msb of extract. | 
|  | 3790 | uint16_t msb = rd_reg(); | 
|  | 3791 | // Interpret sa field as 5-bit lsb of extract. | 
|  | 3792 | uint16_t lsb = sa(); | 
|  | 3793 | uint16_t size = msb + 1; | 
|  | 3794 | uint32_t mask = (1 << size) - 1; | 
|  | 3795 | alu_out = (rs_u() & (mask << lsb)) >> lsb; | 
|  | 3796 | SetResult(rt_reg(), alu_out); | 
|  | 3797 | break; | 
|  | 3798 | } | 
|  | 3799 | case BSHFL: { | 
|  | 3800 | int sa = get_instr()->SaFieldRaw() >> kSaShift; | 
|  | 3801 | switch (sa) { | 
|  | 3802 | case BITSWAP: { | 
|  | 3803 | uint32_t input = static_cast<uint32_t>(rt()); | 
|  | 3804 | uint32_t output = 0; | 
|  | 3805 | uint8_t i_byte, o_byte; | 
|  | 3806 |  | 
|  | 3807 | // Reverse the bit in byte for each individual byte | 
|  | 3808 | for (int i = 0; i < 4; i++) { | 
|  | 3809 | output = output >> 8; | 
|  | 3810 | i_byte = input & 0xff; | 
|  | 3811 |  | 
|  | 3812 | // Fast way to reverse bits in byte | 
|  | 3813 | // Devised by Sean Anderson, July 13, 2001 | 
|  | 3814 | o_byte = static_cast<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) | | 
|  | 3815 | (i_byte * 0x8020LU & 0x88440LU)) * | 
|  | 3816 | 0x10101LU >> | 
|  | 3817 | 16); | 
|  | 3818 |  | 
|  | 3819 | output = output | (static_cast<uint32_t>(o_byte << 24)); | 
|  | 3820 | input = input >> 8; | 
|  | 3821 | } | 
|  | 3822 |  | 
|  | 3823 | alu_out = static_cast<int32_t>(output); | 
|  | 3824 | break; | 
|  | 3825 | } | 
|  | 3826 | case SEB: | 
|  | 3827 | case SEH: | 
|  | 3828 | case WSBH: | 
|  | 3829 | alu_out = 0x12345678; | 
|  | 3830 | UNREACHABLE(); | 
|  | 3831 | break; | 
|  | 3832 | default: { | 
|  | 3833 | const uint8_t bp = get_instr()->Bp2Value(); | 
|  | 3834 | sa >>= kBp2Bits; | 
|  | 3835 | switch (sa) { | 
|  | 3836 | case ALIGN: { | 
|  | 3837 | if (bp == 0) { | 
|  | 3838 | alu_out = static_cast<int32_t>(rt()); | 
|  | 3839 | } else { | 
|  | 3840 | uint32_t rt_hi = rt() << (8 * bp); | 
|  | 3841 | uint32_t rs_lo = rs() >> (8 * (4 - bp)); | 
|  | 3842 | alu_out = static_cast<int32_t>(rt_hi | rs_lo); | 
|  | 3843 | } | 
|  | 3844 | break; | 
|  | 3845 | } | 
|  | 3846 | default: | 
|  | 3847 | alu_out = 0x12345678; | 
|  | 3848 | UNREACHABLE(); | 
|  | 3849 | break; | 
|  | 3850 | } | 
|  | 3851 | } | 
|  | 3852 | } | 
|  | 3853 | SetResult(rd_reg(), alu_out); | 
|  | 3854 | break; | 
|  | 3855 | } | 
|  | 3856 | default: | 
|  | 3857 | UNREACHABLE(); | 
|  | 3858 | } | 
|  | 3859 | } | 
|  | 3860 |  | 
|  | 3861 |  | 
|  | 3862 | void Simulator::DecodeTypeRegister(Instruction* instr) { | 
|  | 3863 | const Opcode op = instr->OpcodeFieldRaw(); | 
|  | 3864 |  | 
|  | 3865 | // Set up the variables if needed before executing the instruction. | 
|  | 3866 | //  ConfigureTypeRegister(instr); | 
|  | 3867 | set_instr(instr); | 
|  | 3868 |  | 
|  | 3869 | // ---------- Execution. | 
|  | 3870 | switch (op) { | 
|  | 3871 | case COP1: | 
|  | 3872 | DecodeTypeRegisterCOP1(); | 
|  | 3873 | break; | 
|  | 3874 | case COP1X: | 
|  | 3875 | DecodeTypeRegisterCOP1X(); | 
|  | 3876 | break; | 
|  | 3877 | case SPECIAL: | 
|  | 3878 | DecodeTypeRegisterSPECIAL(); | 
|  | 3879 | break; | 
|  | 3880 | case SPECIAL2: | 
|  | 3881 | DecodeTypeRegisterSPECIAL2(); | 
|  | 3882 | break; | 
|  | 3883 | case SPECIAL3: | 
|  | 3884 | DecodeTypeRegisterSPECIAL3(); | 
|  | 3885 | break; | 
|  | 3886 | default: | 
|  | 3887 | UNREACHABLE(); | 
|  | 3888 | } | 
|  | 3889 | } | 
|  | 3890 |  | 
|  | 3891 |  | 
|  | 3892 | // Type 2: instructions using a 16, 21 or 26 bits immediate. (e.g. beq, beqc). | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3893 | void Simulator::DecodeTypeImmediate(Instruction* instr) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3894 | // Instruction fields. | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3895 | Opcode op = instr->OpcodeFieldRaw(); | 
|  | 3896 | int32_t rs_reg = instr->RsValue(); | 
|  | 3897 | int32_t rs = get_register(instr->RsValue()); | 
|  | 3898 | uint32_t rs_u = static_cast<uint32_t>(rs); | 
|  | 3899 | int32_t rt_reg = instr->RtValue();  // Destination register. | 
|  | 3900 | int32_t rt = get_register(rt_reg); | 
|  | 3901 | int16_t imm16 = instr->Imm16Value(); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3902 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3903 | int32_t ft_reg = instr->FtValue();  // Destination register. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3904 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3905 | // Zero extended immediate. | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3906 | uint32_t oe_imm16 = 0xffff & imm16; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3907 | // Sign extended immediate. | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3908 | int32_t se_imm16 = imm16; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3909 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3910 | // Next pc. | 
|  | 3911 | int32_t next_pc = bad_ra; | 
|  | 3912 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3913 | // Used for conditional branch instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3914 | bool execute_branch_delay_instruction = false; | 
|  | 3915 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3916 | // Used for arithmetic instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3917 | int32_t alu_out = 0; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3918 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3919 | // Used for memory instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3920 | int32_t addr = 0x0; | 
|  | 3921 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3922 | // Branch instructions common part. | 
|  | 3923 | auto BranchAndLinkHelper = [this, instr, &next_pc, | 
|  | 3924 | &execute_branch_delay_instruction]( | 
|  | 3925 | bool do_branch) { | 
|  | 3926 | execute_branch_delay_instruction = true; | 
|  | 3927 | int32_t current_pc = get_pc(); | 
|  | 3928 | if (do_branch) { | 
|  | 3929 | int16_t imm16 = instr->Imm16Value(); | 
|  | 3930 | next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; | 
|  | 3931 | set_register(31, current_pc + 2 * Instruction::kInstrSize); | 
|  | 3932 | } else { | 
|  | 3933 | next_pc = current_pc + 2 * Instruction::kInstrSize; | 
|  | 3934 | } | 
|  | 3935 | }; | 
|  | 3936 |  | 
|  | 3937 | auto BranchHelper = [this, instr, &next_pc, | 
|  | 3938 | &execute_branch_delay_instruction](bool do_branch) { | 
|  | 3939 | execute_branch_delay_instruction = true; | 
|  | 3940 | int32_t current_pc = get_pc(); | 
|  | 3941 | if (do_branch) { | 
|  | 3942 | int16_t imm16 = instr->Imm16Value(); | 
|  | 3943 | next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; | 
|  | 3944 | } else { | 
|  | 3945 | next_pc = current_pc + 2 * Instruction::kInstrSize; | 
|  | 3946 | } | 
|  | 3947 | }; | 
|  | 3948 |  | 
|  | 3949 | auto BranchAndLinkCompactHelper = [this, instr, &next_pc](bool do_branch, | 
|  | 3950 | int bits) { | 
|  | 3951 | int32_t current_pc = get_pc(); | 
|  | 3952 | CheckForbiddenSlot(current_pc); | 
|  | 3953 | if (do_branch) { | 
|  | 3954 | int32_t imm = instr->ImmValue(bits); | 
|  | 3955 | imm <<= 32 - bits; | 
|  | 3956 | imm >>= 32 - bits; | 
|  | 3957 | next_pc = current_pc + (imm << 2) + Instruction::kInstrSize; | 
|  | 3958 | set_register(31, current_pc + Instruction::kInstrSize); | 
|  | 3959 | } | 
|  | 3960 | }; | 
|  | 3961 |  | 
|  | 3962 | auto BranchCompactHelper = [&next_pc, this, instr](bool do_branch, int bits) { | 
|  | 3963 | int32_t current_pc = get_pc(); | 
|  | 3964 | CheckForbiddenSlot(current_pc); | 
|  | 3965 | if (do_branch) { | 
|  | 3966 | int32_t imm = instr->ImmValue(bits); | 
|  | 3967 | imm <<= 32 - bits; | 
|  | 3968 | imm >>= 32 - bits; | 
|  | 3969 | next_pc = get_pc() + (imm << 2) + Instruction::kInstrSize; | 
|  | 3970 | } | 
|  | 3971 | }; | 
|  | 3972 |  | 
|  | 3973 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3974 | switch (op) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 3975 | // ------------- COP1. Coprocessor instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3976 | case COP1: | 
|  | 3977 | switch (instr->RsFieldRaw()) { | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3978 | case BC1: {  // Branch on coprocessor condition. | 
|  | 3979 | // Floating point. | 
|  | 3980 | uint32_t cc = instr->FBccValue(); | 
|  | 3981 | uint32_t fcsr_cc = get_fcsr_condition_bit(cc); | 
|  | 3982 | uint32_t cc_value = test_fcsr_bit(fcsr_cc); | 
|  | 3983 | bool do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value; | 
|  | 3984 | BranchHelper(do_branch); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3985 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3986 | } | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3987 | case BC1EQZ: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3988 | BranchHelper(!(get_fpu_register(ft_reg) & 0x1)); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3989 | break; | 
|  | 3990 | case BC1NEZ: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 3991 | BranchHelper(get_fpu_register(ft_reg) & 0x1); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3992 | break; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3993 | default: | 
|  | 3994 | UNREACHABLE(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 3995 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3996 | break; | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 3997 | // ------------- REGIMM class. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 3998 | case REGIMM: | 
|  | 3999 | switch (instr->RtFieldRaw()) { | 
|  | 4000 | case BLTZ: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4001 | BranchHelper(rs < 0); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4002 | break; | 
|  | 4003 | case BGEZ: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4004 | BranchHelper(rs >= 0); | 
|  | 4005 | break; | 
|  | 4006 | case BLTZAL: | 
|  | 4007 | BranchAndLinkHelper(rs < 0); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4008 | break; | 
|  | 4009 | case BGEZAL: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4010 | BranchAndLinkHelper(rs >= 0); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4011 | break; | 
|  | 4012 | default: | 
|  | 4013 | UNREACHABLE(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 4014 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4015 | break;  // case REGIMM. | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4016 | // ------------- Branch instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4017 | // When comparing to zero, the encoding of rt field is always 0, so we don't | 
|  | 4018 | // need to replace rt with zero. | 
|  | 4019 | case BEQ: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4020 | BranchHelper(rs == rt); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4021 | break; | 
|  | 4022 | case BNE: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4023 | BranchHelper(rs != rt); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4024 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4025 | case POP06:  // BLEZALC, BGEZALC, BGEUC, BLEZ (pre-r6) | 
|  | 4026 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 4027 | if (rt_reg != 0) { | 
|  | 4028 | if (rs_reg == 0) {  // BLEZALC | 
|  | 4029 | BranchAndLinkCompactHelper(rt <= 0, 16); | 
|  | 4030 | } else { | 
|  | 4031 | if (rs_reg == rt_reg) {  // BGEZALC | 
|  | 4032 | BranchAndLinkCompactHelper(rt >= 0, 16); | 
|  | 4033 | } else {  // BGEUC | 
|  | 4034 | BranchCompactHelper( | 
|  | 4035 | static_cast<uint32_t>(rs) >= static_cast<uint32_t>(rt), 16); | 
|  | 4036 | } | 
|  | 4037 | } | 
|  | 4038 | } else {  // BLEZ | 
|  | 4039 | BranchHelper(rs <= 0); | 
|  | 4040 | } | 
|  | 4041 | } else {  // BLEZ | 
|  | 4042 | BranchHelper(rs <= 0); | 
|  | 4043 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4044 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4045 | case POP07:  // BGTZALC, BLTZALC, BLTUC, BGTZ (pre-r6) | 
|  | 4046 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 4047 | if (rt_reg != 0) { | 
|  | 4048 | if (rs_reg == 0) {  // BGTZALC | 
|  | 4049 | BranchAndLinkCompactHelper(rt > 0, 16); | 
|  | 4050 | } else { | 
|  | 4051 | if (rt_reg == rs_reg) {  // BLTZALC | 
|  | 4052 | BranchAndLinkCompactHelper(rt < 0, 16); | 
|  | 4053 | } else {  // BLTUC | 
|  | 4054 | BranchCompactHelper( | 
|  | 4055 | static_cast<uint32_t>(rs) < static_cast<uint32_t>(rt), 16); | 
|  | 4056 | } | 
|  | 4057 | } | 
|  | 4058 | } else {  // BGTZ | 
|  | 4059 | BranchHelper(rs > 0); | 
|  | 4060 | } | 
|  | 4061 | } else {  // BGTZ | 
|  | 4062 | BranchHelper(rs > 0); | 
|  | 4063 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4064 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4065 | case POP26:  // BLEZC, BGEZC, BGEC/BLEC / BLEZL (pre-r6) | 
|  | 4066 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 4067 | if (rt_reg != 0) { | 
|  | 4068 | if (rs_reg == 0) {  // BLEZC | 
|  | 4069 | BranchCompactHelper(rt <= 0, 16); | 
|  | 4070 | } else { | 
|  | 4071 | if (rs_reg == rt_reg) {  // BGEZC | 
|  | 4072 | BranchCompactHelper(rt >= 0, 16); | 
|  | 4073 | } else {  // BGEC/BLEC | 
|  | 4074 | BranchCompactHelper(rs >= rt, 16); | 
|  | 4075 | } | 
|  | 4076 | } | 
|  | 4077 | } | 
|  | 4078 | } else {  // BLEZL | 
|  | 4079 | BranchAndLinkHelper(rs <= 0); | 
|  | 4080 | } | 
|  | 4081 | break; | 
|  | 4082 | case POP27:  // BGTZC, BLTZC, BLTC/BGTC / BGTZL (pre-r6) | 
|  | 4083 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 4084 | if (rt_reg != 0) { | 
|  | 4085 | if (rs_reg == 0) {  // BGTZC | 
|  | 4086 | BranchCompactHelper(rt > 0, 16); | 
|  | 4087 | } else { | 
|  | 4088 | if (rs_reg == rt_reg) {  // BLTZC | 
|  | 4089 | BranchCompactHelper(rt < 0, 16); | 
|  | 4090 | } else {  // BLTC/BGTC | 
|  | 4091 | BranchCompactHelper(rs < rt, 16); | 
|  | 4092 | } | 
|  | 4093 | } | 
|  | 4094 | } | 
|  | 4095 | } else {  // BGTZL | 
|  | 4096 | BranchAndLinkHelper(rs > 0); | 
|  | 4097 | } | 
|  | 4098 | break; | 
|  | 4099 | case POP66:           // BEQZC, JIC | 
|  | 4100 | if (rs_reg != 0) {  // BEQZC | 
|  | 4101 | BranchCompactHelper(rs == 0, 21); | 
|  | 4102 | } else {  // JIC | 
|  | 4103 | next_pc = rt + imm16; | 
|  | 4104 | } | 
|  | 4105 | break; | 
|  | 4106 | case POP76:           // BNEZC, JIALC | 
|  | 4107 | if (rs_reg != 0) {  // BNEZC | 
|  | 4108 | BranchCompactHelper(rs != 0, 21); | 
|  | 4109 | } else {  // JIALC | 
|  | 4110 | set_register(31, get_pc() + Instruction::kInstrSize); | 
|  | 4111 | next_pc = rt + imm16; | 
|  | 4112 | } | 
|  | 4113 | break; | 
|  | 4114 | case BC: | 
|  | 4115 | BranchCompactHelper(true, 26); | 
|  | 4116 | break; | 
|  | 4117 | case BALC: | 
|  | 4118 | BranchAndLinkCompactHelper(true, 26); | 
|  | 4119 | break; | 
|  | 4120 | case POP10:  // BOVC, BEQZALC, BEQC / ADDI (pre-r6) | 
|  | 4121 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 4122 | if (rs_reg >= rt_reg) {  // BOVC | 
|  | 4123 | if (HaveSameSign(rs, rt)) { | 
|  | 4124 | if (rs > 0) { | 
|  | 4125 | BranchCompactHelper(rs > Registers::kMaxValue - rt, 16); | 
|  | 4126 | } else if (rs < 0) { | 
|  | 4127 | BranchCompactHelper(rs < Registers::kMinValue - rt, 16); | 
|  | 4128 | } | 
|  | 4129 | } | 
|  | 4130 | } else { | 
|  | 4131 | if (rs_reg == 0) {  // BEQZALC | 
|  | 4132 | BranchAndLinkCompactHelper(rt == 0, 16); | 
|  | 4133 | } else {  // BEQC | 
|  | 4134 | BranchCompactHelper(rt == rs, 16); | 
|  | 4135 | } | 
|  | 4136 | } | 
|  | 4137 | } else {  // ADDI | 
|  | 4138 | if (HaveSameSign(rs, se_imm16)) { | 
|  | 4139 | if (rs > 0) { | 
|  | 4140 | if (rs <= Registers::kMaxValue - se_imm16) { | 
|  | 4141 | SignalException(kIntegerOverflow); | 
|  | 4142 | } | 
|  | 4143 | } else if (rs < 0) { | 
|  | 4144 | if (rs >= Registers::kMinValue - se_imm16) { | 
|  | 4145 | SignalException(kIntegerUnderflow); | 
|  | 4146 | } | 
|  | 4147 | } | 
|  | 4148 | } | 
|  | 4149 | SetResult(rt_reg, rs + se_imm16); | 
|  | 4150 | } | 
|  | 4151 | break; | 
|  | 4152 | case POP30:  // BNVC, BNEZALC, BNEC / DADDI (pre-r6) | 
|  | 4153 | if (IsMipsArchVariant(kMips32r6)) { | 
|  | 4154 | if (rs_reg >= rt_reg) {  // BNVC | 
|  | 4155 | if (!HaveSameSign(rs, rt) || rs == 0 || rt == 0) { | 
|  | 4156 | BranchCompactHelper(true, 16); | 
|  | 4157 | } else { | 
|  | 4158 | if (rs > 0) { | 
|  | 4159 | BranchCompactHelper(rs <= Registers::kMaxValue - rt, 16); | 
|  | 4160 | } else if (rs < 0) { | 
|  | 4161 | BranchCompactHelper(rs >= Registers::kMinValue - rt, 16); | 
|  | 4162 | } | 
|  | 4163 | } | 
|  | 4164 | } else { | 
|  | 4165 | if (rs_reg == 0) {  // BNEZALC | 
|  | 4166 | BranchAndLinkCompactHelper(rt != 0, 16); | 
|  | 4167 | } else {  // BNEC | 
|  | 4168 | BranchCompactHelper(rt != rs, 16); | 
|  | 4169 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4170 | } | 
|  | 4171 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4172 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4173 | // ------------- Arithmetic instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4174 | case ADDIU: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4175 | SetResult(rt_reg, rs + se_imm16); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4176 | break; | 
|  | 4177 | case SLTI: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4178 | SetResult(rt_reg, rs < se_imm16 ? 1 : 0); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4179 | break; | 
|  | 4180 | case SLTIU: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4181 | SetResult(rt_reg, rs_u < static_cast<uint32_t>(se_imm16) ? 1 : 0); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4182 | break; | 
|  | 4183 | case ANDI: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4184 | SetResult(rt_reg, rs & oe_imm16); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4185 | break; | 
|  | 4186 | case ORI: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4187 | SetResult(rt_reg, rs | oe_imm16); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4188 | break; | 
|  | 4189 | case XORI: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4190 | SetResult(rt_reg, rs ^ oe_imm16); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4191 | break; | 
|  | 4192 | case LUI: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4193 | if (rs_reg != 0) { | 
|  | 4194 | // AUI | 
|  | 4195 | DCHECK(IsMipsArchVariant(kMips32r6)); | 
|  | 4196 | SetResult(rt_reg, rs + (se_imm16 << 16)); | 
|  | 4197 | } else { | 
|  | 4198 | // LUI | 
|  | 4199 | SetResult(rt_reg, oe_imm16 << 16); | 
|  | 4200 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4201 | break; | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4202 | // ------------- Memory instructions. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4203 | case LB: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4204 | set_register(rt_reg, ReadB(rs + se_imm16)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4205 | break; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4206 | case LH: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4207 | set_register(rt_reg, ReadH(rs + se_imm16, instr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4208 | break; | 
|  | 4209 | case LWL: { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4210 | // al_offset is offset of the effective address within an aligned word. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4211 | uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask; | 
|  | 4212 | uint8_t byte_shift = kPointerAlignmentMask - al_offset; | 
|  | 4213 | uint32_t mask = (1 << byte_shift * 8) - 1; | 
|  | 4214 | addr = rs + se_imm16 - al_offset; | 
|  | 4215 | alu_out = ReadW(addr, instr); | 
|  | 4216 | alu_out <<= byte_shift * 8; | 
|  | 4217 | alu_out |= rt & mask; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4218 | set_register(rt_reg, alu_out); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4219 | break; | 
|  | 4220 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4221 | case LW: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4222 | set_register(rt_reg, ReadW(rs + se_imm16, instr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4223 | break; | 
|  | 4224 | case LBU: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4225 | set_register(rt_reg, ReadBU(rs + se_imm16)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4226 | break; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4227 | case LHU: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4228 | set_register(rt_reg, ReadHU(rs + se_imm16, instr)); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4229 | break; | 
|  | 4230 | case LWR: { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4231 | // al_offset is offset of the effective address within an aligned word. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4232 | uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask; | 
|  | 4233 | uint8_t byte_shift = kPointerAlignmentMask - al_offset; | 
|  | 4234 | uint32_t mask = al_offset ? (~0 << (byte_shift + 1) * 8) : 0; | 
|  | 4235 | addr = rs + se_imm16 - al_offset; | 
|  | 4236 | alu_out = ReadW(addr, instr); | 
|  | 4237 | alu_out = static_cast<uint32_t> (alu_out) >> al_offset * 8; | 
|  | 4238 | alu_out |= rt & mask; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4239 | set_register(rt_reg, alu_out); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4240 | break; | 
|  | 4241 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4242 | case SB: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4243 | WriteB(rs + se_imm16, static_cast<int8_t>(rt)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4244 | break; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4245 | case SH: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4246 | WriteH(rs + se_imm16, static_cast<uint16_t>(rt), instr); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4247 | break; | 
|  | 4248 | case SWL: { | 
|  | 4249 | uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask; | 
|  | 4250 | uint8_t byte_shift = kPointerAlignmentMask - al_offset; | 
|  | 4251 | uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0; | 
|  | 4252 | addr = rs + se_imm16 - al_offset; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4253 | // Value to be written in memory. | 
|  | 4254 | uint32_t mem_value = ReadW(addr, instr) & mask; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4255 | mem_value |= static_cast<uint32_t>(rt) >> byte_shift * 8; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4256 | WriteW(addr, mem_value, instr); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4257 | break; | 
|  | 4258 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4259 | case SW: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4260 | WriteW(rs + se_imm16, rt, instr); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4261 | break; | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4262 | case SWR: { | 
|  | 4263 | uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask; | 
|  | 4264 | uint32_t mask = (1 << al_offset * 8) - 1; | 
|  | 4265 | addr = rs + se_imm16 - al_offset; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4266 | uint32_t mem_value = ReadW(addr, instr); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4267 | mem_value = (rt << al_offset * 8) | (mem_value & mask); | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4268 | WriteW(addr, mem_value, instr); | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4269 | break; | 
|  | 4270 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4271 | case LWC1: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4272 | set_fpu_register_hi_word(ft_reg, 0); | 
|  | 4273 | set_fpu_register_word(ft_reg, ReadW(rs + se_imm16, instr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4274 | break; | 
|  | 4275 | case LDC1: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4276 | set_fpu_register_double(ft_reg, ReadD(rs + se_imm16, instr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4277 | break; | 
|  | 4278 | case SWC1: | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4279 | WriteW(rs + se_imm16, get_fpu_register_word(ft_reg), instr); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4280 | break; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4281 | case SDC1: | 
|  | 4282 | WriteD(rs + se_imm16, get_fpu_register_double(ft_reg), instr); | 
|  | 4283 | break; | 
|  | 4284 | // ------------- PC-Relative instructions. | 
|  | 4285 | case PCREL: { | 
|  | 4286 | // rt field: checking 5-bits. | 
|  | 4287 | int32_t imm21 = instr->Imm21Value(); | 
|  | 4288 | int32_t current_pc = get_pc(); | 
|  | 4289 | uint8_t rt = (imm21 >> kImm16Bits); | 
|  | 4290 | switch (rt) { | 
|  | 4291 | case ALUIPC: | 
|  | 4292 | addr = current_pc + (se_imm16 << 16); | 
|  | 4293 | alu_out = static_cast<int64_t>(~0x0FFFF) & addr; | 
|  | 4294 | break; | 
|  | 4295 | case AUIPC: | 
|  | 4296 | alu_out = current_pc + (se_imm16 << 16); | 
|  | 4297 | break; | 
|  | 4298 | default: { | 
|  | 4299 | int32_t imm19 = instr->Imm19Value(); | 
|  | 4300 | // rt field: checking the most significant 2-bits. | 
|  | 4301 | rt = (imm21 >> kImm19Bits); | 
|  | 4302 | switch (rt) { | 
|  | 4303 | case LWPC: { | 
|  | 4304 | // Set sign. | 
|  | 4305 | imm19 <<= (kOpcodeBits + kRsBits + 2); | 
|  | 4306 | imm19 >>= (kOpcodeBits + kRsBits + 2); | 
|  | 4307 | addr = current_pc + (imm19 << 2); | 
|  | 4308 | uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); | 
|  | 4309 | alu_out = *ptr; | 
|  | 4310 | break; | 
|  | 4311 | } | 
|  | 4312 | case ADDIUPC: { | 
|  | 4313 | int32_t se_imm19 = imm19 | ((imm19 & 0x40000) ? 0xfff80000 : 0); | 
|  | 4314 | alu_out = current_pc + (se_imm19 << 2); | 
|  | 4315 | break; | 
|  | 4316 | } | 
|  | 4317 | default: | 
|  | 4318 | UNREACHABLE(); | 
|  | 4319 | break; | 
|  | 4320 | } | 
|  | 4321 | } | 
|  | 4322 | } | 
|  | 4323 | set_register(rs_reg, alu_out); | 
|  | 4324 | break; | 
|  | 4325 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4326 | default: | 
|  | 4327 | UNREACHABLE(); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 4328 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4329 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4330 | if (execute_branch_delay_instruction) { | 
|  | 4331 | // Execute branch delay slot | 
|  | 4332 | // We don't check for end_sim_pc. First it should not be met as the current | 
|  | 4333 | // pc is valid. Secondly a jump should always execute its branch delay slot. | 
|  | 4334 | Instruction* branch_delay_instr = | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4335 | reinterpret_cast<Instruction*>(get_pc() + Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4336 | BranchDelayInstructionDecode(branch_delay_instr); | 
|  | 4337 | } | 
|  | 4338 |  | 
|  | 4339 | // If needed update pc after the branch delay execution. | 
|  | 4340 | if (next_pc != bad_ra) { | 
|  | 4341 | set_pc(next_pc); | 
|  | 4342 | } | 
|  | 4343 | } | 
|  | 4344 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4345 |  | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4346 | // Type 3: instructions using a 26 bytes immediate. (e.g. j, jal). | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4347 | void Simulator::DecodeTypeJump(Instruction* instr) { | 
|  | 4348 | // Get current pc. | 
|  | 4349 | int32_t current_pc = get_pc(); | 
|  | 4350 | // Get unchanged bits of pc. | 
|  | 4351 | int32_t pc_high_bits = current_pc & 0xf0000000; | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4352 | // Next pc. | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4353 | int32_t next_pc = pc_high_bits | (instr->Imm26Value() << 2); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4354 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4355 | // Execute branch delay slot. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4356 | // We don't check for end_sim_pc. First it should not be met as the current pc | 
|  | 4357 | // is valid. Secondly a jump should always execute its branch delay slot. | 
|  | 4358 | Instruction* branch_delay_instr = | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4359 | reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4360 | BranchDelayInstructionDecode(branch_delay_instr); | 
|  | 4361 |  | 
|  | 4362 | // Update pc and ra if necessary. | 
|  | 4363 | // Do this after the branch delay execution. | 
|  | 4364 | if (instr->IsLinkingInstruction()) { | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4365 | set_register(31, current_pc + 2 * Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4366 | } | 
|  | 4367 | set_pc(next_pc); | 
|  | 4368 | pc_modified_ = true; | 
|  | 4369 | } | 
|  | 4370 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4371 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4372 | // Executes the current instruction. | 
|  | 4373 | void Simulator::InstructionDecode(Instruction* instr) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4374 | if (v8::internal::FLAG_check_icache) { | 
|  | 4375 | CheckICache(isolate_->simulator_i_cache(), instr); | 
|  | 4376 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4377 | pc_modified_ = false; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4378 | v8::internal::EmbeddedVector<char, 256> buffer; | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4379 | if (::v8::internal::FLAG_trace_sim) { | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4380 | SNPrintF(trace_buf_, "%s", ""); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4381 | disasm::NameConverter converter; | 
|  | 4382 | disasm::Disassembler dasm(converter); | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4383 | dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr)); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4384 | } | 
|  | 4385 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4386 | switch (instr->InstructionType(Instruction::TypeChecks::EXTRA)) { | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4387 | case Instruction::kRegisterType: | 
|  | 4388 | DecodeTypeRegister(instr); | 
|  | 4389 | break; | 
|  | 4390 | case Instruction::kImmediateType: | 
|  | 4391 | DecodeTypeImmediate(instr); | 
|  | 4392 | break; | 
|  | 4393 | case Instruction::kJumpType: | 
|  | 4394 | DecodeTypeJump(instr); | 
|  | 4395 | break; | 
|  | 4396 | default: | 
|  | 4397 | UNSUPPORTED(); | 
|  | 4398 | } | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4399 | if (::v8::internal::FLAG_trace_sim) { | 
|  | 4400 | PrintF("  0x%08x  %-44s   %s\n", reinterpret_cast<intptr_t>(instr), | 
|  | 4401 | buffer.start(), trace_buf_.start()); | 
|  | 4402 | } | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4403 | if (!pc_modified_) { | 
|  | 4404 | set_register(pc, reinterpret_cast<int32_t>(instr) + | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4405 | Instruction::kInstrSize); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4406 | } | 
|  | 4407 | } | 
|  | 4408 |  | 
|  | 4409 |  | 
|  | 4410 |  | 
|  | 4411 | void Simulator::Execute() { | 
|  | 4412 | // Get the PC to simulate. Cannot use the accessor here as we need the | 
|  | 4413 | // raw PC value and not the one used as input to arithmetic instructions. | 
|  | 4414 | int program_counter = get_pc(); | 
|  | 4415 | if (::v8::internal::FLAG_stop_sim_at == 0) { | 
|  | 4416 | // Fast version of the dispatch loop without checking whether the simulator | 
|  | 4417 | // should be stopping at a particular executed instruction. | 
|  | 4418 | while (program_counter != end_sim_pc) { | 
|  | 4419 | Instruction* instr = reinterpret_cast<Instruction*>(program_counter); | 
|  | 4420 | icount_++; | 
|  | 4421 | InstructionDecode(instr); | 
|  | 4422 | program_counter = get_pc(); | 
|  | 4423 | } | 
|  | 4424 | } else { | 
|  | 4425 | // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when | 
|  | 4426 | // we reach the particular instuction count. | 
|  | 4427 | while (program_counter != end_sim_pc) { | 
|  | 4428 | Instruction* instr = reinterpret_cast<Instruction*>(program_counter); | 
|  | 4429 | icount_++; | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4430 | if (icount_ == static_cast<uint64_t>(::v8::internal::FLAG_stop_sim_at)) { | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4431 | MipsDebugger dbg(this); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4432 | dbg.Debug(); | 
|  | 4433 | } else { | 
|  | 4434 | InstructionDecode(instr); | 
|  | 4435 | } | 
|  | 4436 | program_counter = get_pc(); | 
|  | 4437 | } | 
|  | 4438 | } | 
|  | 4439 | } | 
|  | 4440 |  | 
|  | 4441 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 4442 | void Simulator::CallInternal(byte* entry) { | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4443 | // Adjust JS-based stack limit to C-based stack limit. | 
|  | 4444 | isolate_->stack_guard()->AdjustStackLimitForSimulator(); | 
|  | 4445 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4446 | // Prepare to execute the code at entry. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4447 | set_register(pc, reinterpret_cast<int32_t>(entry)); | 
|  | 4448 | // Put down marker for end of simulation. The simulator will stop simulation | 
|  | 4449 | // when the PC reaches this value. By saving the "end simulation" value into | 
|  | 4450 | // the LR the simulation stops when returning to this call point. | 
|  | 4451 | set_register(ra, end_sim_pc); | 
|  | 4452 |  | 
|  | 4453 | // Remember the values of callee-saved registers. | 
|  | 4454 | // The code below assumes that r9 is not used as sb (static base) in | 
|  | 4455 | // simulator code and therefore is regarded as a callee-saved register. | 
|  | 4456 | int32_t s0_val = get_register(s0); | 
|  | 4457 | int32_t s1_val = get_register(s1); | 
|  | 4458 | int32_t s2_val = get_register(s2); | 
|  | 4459 | int32_t s3_val = get_register(s3); | 
|  | 4460 | int32_t s4_val = get_register(s4); | 
|  | 4461 | int32_t s5_val = get_register(s5); | 
|  | 4462 | int32_t s6_val = get_register(s6); | 
|  | 4463 | int32_t s7_val = get_register(s7); | 
|  | 4464 | int32_t gp_val = get_register(gp); | 
|  | 4465 | int32_t sp_val = get_register(sp); | 
|  | 4466 | int32_t fp_val = get_register(fp); | 
|  | 4467 |  | 
| Ben Murdoch | 3ef787d | 2012-04-12 10:51:47 +0100 | [diff] [blame] | 4468 | // Set up the callee-saved registers with a known value. To be able to check | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4469 | // that they are preserved properly across JS execution. | 
|  | 4470 | int32_t callee_saved_value = icount_; | 
|  | 4471 | set_register(s0, callee_saved_value); | 
|  | 4472 | set_register(s1, callee_saved_value); | 
|  | 4473 | set_register(s2, callee_saved_value); | 
|  | 4474 | set_register(s3, callee_saved_value); | 
|  | 4475 | set_register(s4, callee_saved_value); | 
|  | 4476 | set_register(s5, callee_saved_value); | 
|  | 4477 | set_register(s6, callee_saved_value); | 
|  | 4478 | set_register(s7, callee_saved_value); | 
|  | 4479 | set_register(gp, callee_saved_value); | 
|  | 4480 | set_register(fp, callee_saved_value); | 
|  | 4481 |  | 
| Ben Murdoch | 257744e | 2011-11-30 15:57:28 +0000 | [diff] [blame] | 4482 | // Start the simulation. | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4483 | Execute(); | 
|  | 4484 |  | 
|  | 4485 | // Check that the callee-saved registers have been preserved. | 
|  | 4486 | CHECK_EQ(callee_saved_value, get_register(s0)); | 
|  | 4487 | CHECK_EQ(callee_saved_value, get_register(s1)); | 
|  | 4488 | CHECK_EQ(callee_saved_value, get_register(s2)); | 
|  | 4489 | CHECK_EQ(callee_saved_value, get_register(s3)); | 
|  | 4490 | CHECK_EQ(callee_saved_value, get_register(s4)); | 
|  | 4491 | CHECK_EQ(callee_saved_value, get_register(s5)); | 
|  | 4492 | CHECK_EQ(callee_saved_value, get_register(s6)); | 
|  | 4493 | CHECK_EQ(callee_saved_value, get_register(s7)); | 
|  | 4494 | CHECK_EQ(callee_saved_value, get_register(gp)); | 
|  | 4495 | CHECK_EQ(callee_saved_value, get_register(fp)); | 
|  | 4496 |  | 
|  | 4497 | // Restore callee-saved registers with the original value. | 
|  | 4498 | set_register(s0, s0_val); | 
|  | 4499 | set_register(s1, s1_val); | 
|  | 4500 | set_register(s2, s2_val); | 
|  | 4501 | set_register(s3, s3_val); | 
|  | 4502 | set_register(s4, s4_val); | 
|  | 4503 | set_register(s5, s5_val); | 
|  | 4504 | set_register(s6, s6_val); | 
|  | 4505 | set_register(s7, s7_val); | 
|  | 4506 | set_register(gp, gp_val); | 
|  | 4507 | set_register(sp, sp_val); | 
|  | 4508 | set_register(fp, fp_val); | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 4509 | } | 
|  | 4510 |  | 
|  | 4511 |  | 
|  | 4512 | int32_t Simulator::Call(byte* entry, int argument_count, ...) { | 
|  | 4513 | va_list parameters; | 
|  | 4514 | va_start(parameters, argument_count); | 
|  | 4515 | // Set up arguments. | 
|  | 4516 |  | 
|  | 4517 | // First four arguments passed in registers. | 
|  | 4518 | DCHECK(argument_count >= 4); | 
|  | 4519 | set_register(a0, va_arg(parameters, int32_t)); | 
|  | 4520 | set_register(a1, va_arg(parameters, int32_t)); | 
|  | 4521 | set_register(a2, va_arg(parameters, int32_t)); | 
|  | 4522 | set_register(a3, va_arg(parameters, int32_t)); | 
|  | 4523 |  | 
|  | 4524 | // Remaining arguments passed on stack. | 
|  | 4525 | int original_stack = get_register(sp); | 
|  | 4526 | // Compute position of stack on entry to generated code. | 
|  | 4527 | int entry_stack = (original_stack - (argument_count - 4) * sizeof(int32_t) | 
|  | 4528 | - kCArgsSlotsSize); | 
|  | 4529 | if (base::OS::ActivationFrameAlignment() != 0) { | 
|  | 4530 | entry_stack &= -base::OS::ActivationFrameAlignment(); | 
|  | 4531 | } | 
|  | 4532 | // Store remaining arguments on stack, from low to high memory. | 
|  | 4533 | intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack); | 
|  | 4534 | for (int i = 4; i < argument_count; i++) { | 
|  | 4535 | stack_argument[i - 4 + kCArgSlotCount] = va_arg(parameters, int32_t); | 
|  | 4536 | } | 
|  | 4537 | va_end(parameters); | 
|  | 4538 | set_register(sp, entry_stack); | 
|  | 4539 |  | 
|  | 4540 | CallInternal(entry); | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4541 |  | 
|  | 4542 | // Pop stack passed arguments. | 
|  | 4543 | CHECK_EQ(entry_stack, get_register(sp)); | 
|  | 4544 | set_register(sp, original_stack); | 
|  | 4545 |  | 
|  | 4546 | int32_t result = get_register(v0); | 
|  | 4547 | return result; | 
|  | 4548 | } | 
|  | 4549 |  | 
|  | 4550 |  | 
| Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 4551 | double Simulator::CallFP(byte* entry, double d0, double d1) { | 
|  | 4552 | if (!IsMipsSoftFloatABI) { | 
|  | 4553 | set_fpu_register_double(f12, d0); | 
|  | 4554 | set_fpu_register_double(f14, d1); | 
|  | 4555 | } else { | 
|  | 4556 | int buffer[2]; | 
|  | 4557 | DCHECK(sizeof(buffer[0]) * 2 == sizeof(d0)); | 
|  | 4558 | memcpy(buffer, &d0, sizeof(d0)); | 
|  | 4559 | set_dw_register(a0, buffer); | 
|  | 4560 | memcpy(buffer, &d1, sizeof(d1)); | 
|  | 4561 | set_dw_register(a2, buffer); | 
|  | 4562 | } | 
|  | 4563 | CallInternal(entry); | 
|  | 4564 | if (!IsMipsSoftFloatABI) { | 
|  | 4565 | return get_fpu_register_double(f0); | 
|  | 4566 | } else { | 
|  | 4567 | return get_double_from_register_pair(v0); | 
|  | 4568 | } | 
|  | 4569 | } | 
|  | 4570 |  | 
|  | 4571 |  | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4572 | uintptr_t Simulator::PushAddress(uintptr_t address) { | 
|  | 4573 | int new_sp = get_register(sp) - sizeof(uintptr_t); | 
|  | 4574 | uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(new_sp); | 
|  | 4575 | *stack_slot = address; | 
|  | 4576 | set_register(sp, new_sp); | 
|  | 4577 | return new_sp; | 
|  | 4578 | } | 
|  | 4579 |  | 
|  | 4580 |  | 
|  | 4581 | uintptr_t Simulator::PopAddress() { | 
|  | 4582 | int current_sp = get_register(sp); | 
|  | 4583 | uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(current_sp); | 
|  | 4584 | uintptr_t address = *stack_slot; | 
|  | 4585 | set_register(sp, current_sp + sizeof(uintptr_t)); | 
|  | 4586 | return address; | 
|  | 4587 | } | 
|  | 4588 |  | 
|  | 4589 |  | 
|  | 4590 | #undef UNSUPPORTED | 
|  | 4591 |  | 
| Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 4592 | }  // namespace internal | 
|  | 4593 | }  // namespace v8 | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4594 |  | 
| Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 4595 | #endif  // USE_SIMULATOR | 
| Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 4596 |  | 
| Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 4597 | #endif  // V8_TARGET_ARCH_MIPS |