Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | // Copyright 2012 the V8 project authors. All rights reserved. |
michael@0 | 3 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 4 | // modification, are permitted provided that the following conditions are |
michael@0 | 5 | // met: |
michael@0 | 6 | // |
michael@0 | 7 | // * Redistributions of source code must retain the above copyright |
michael@0 | 8 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 9 | // * Redistributions in binary form must reproduce the above |
michael@0 | 10 | // copyright notice, this list of conditions and the following |
michael@0 | 11 | // disclaimer in the documentation and/or other materials provided |
michael@0 | 12 | // with the distribution. |
michael@0 | 13 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 14 | // contributors may be used to endorse or promote products derived |
michael@0 | 15 | // from this software without specific prior written permission. |
michael@0 | 16 | // |
michael@0 | 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 28 | |
michael@0 | 29 | #include "jit/arm/Simulator-arm.h" |
michael@0 | 30 | |
michael@0 | 31 | #include "mozilla/Casting.h" |
michael@0 | 32 | #include "mozilla/FloatingPoint.h" |
michael@0 | 33 | #include "mozilla/Likely.h" |
michael@0 | 34 | #include "mozilla/MathAlgorithms.h" |
michael@0 | 35 | |
michael@0 | 36 | #include "jit/arm/Assembler-arm.h" |
michael@0 | 37 | #include "jit/AsmJS.h" |
michael@0 | 38 | #include "vm/Runtime.h" |
michael@0 | 39 | |
michael@0 | 40 | extern "C" { |
michael@0 | 41 | |
michael@0 | 42 | int64_t |
michael@0 | 43 | __aeabi_idivmod(int x, int y) |
michael@0 | 44 | { |
michael@0 | 45 | uint32_t lo = uint32_t(x / y); |
michael@0 | 46 | uint32_t hi = uint32_t(x % y); |
michael@0 | 47 | return (int64_t(hi) << 32) | lo; |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | int64_t |
michael@0 | 51 | __aeabi_uidivmod(int x, int y) |
michael@0 | 52 | { |
michael@0 | 53 | uint32_t lo = uint32_t(x) / uint32_t(y); |
michael@0 | 54 | uint32_t hi = uint32_t(x) % uint32_t(y); |
michael@0 | 55 | return (int64_t(hi) << 32) | lo; |
michael@0 | 56 | } |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | namespace js { |
michael@0 | 60 | namespace jit { |
michael@0 | 61 | |
michael@0 | 62 | // Load/store multiple addressing mode. |
michael@0 | 63 | enum BlockAddrMode { |
michael@0 | 64 | // Alias modes for comparison when writeback does not matter. |
michael@0 | 65 | da_x = (0|0|0) << 21, // Decrement after. |
michael@0 | 66 | ia_x = (0|4|0) << 21, // Increment after. |
michael@0 | 67 | db_x = (8|0|0) << 21, // Decrement before. |
michael@0 | 68 | ib_x = (8|4|0) << 21, // Increment before. |
michael@0 | 69 | }; |
michael@0 | 70 | |
michael@0 | 71 | // Type of VFP register. Determines register encoding. |
michael@0 | 72 | enum VFPRegPrecision { |
michael@0 | 73 | kSinglePrecision = 0, |
michael@0 | 74 | kDoublePrecision = 1 |
michael@0 | 75 | }; |
michael@0 | 76 | |
michael@0 | 77 | enum NeonListType { |
michael@0 | 78 | nlt_1 = 0x7, |
michael@0 | 79 | nlt_2 = 0xA, |
michael@0 | 80 | nlt_3 = 0x6, |
michael@0 | 81 | nlt_4 = 0x2 |
michael@0 | 82 | }; |
michael@0 | 83 | |
michael@0 | 84 | // Supervisor Call (svc) specific support. |
michael@0 | 85 | |
michael@0 | 86 | // Special Software Interrupt codes when used in the presence of the ARM |
michael@0 | 87 | // simulator. |
michael@0 | 88 | // svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for |
michael@0 | 89 | // standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature. |
michael@0 | 90 | enum SoftwareInterruptCodes { |
michael@0 | 91 | kCallRtRedirected = 0x10, // Transition to C code. |
michael@0 | 92 | kBreakpoint= 0x20, // Breakpoint. |
michael@0 | 93 | kStopCode = 1 << 23 // Stop. |
michael@0 | 94 | }; |
michael@0 | 95 | |
michael@0 | 96 | const uint32_t kStopCodeMask = kStopCode - 1; |
michael@0 | 97 | const uint32_t kMaxStopCode = kStopCode - 1; |
michael@0 | 98 | |
michael@0 | 99 | // ----------------------------------------------------------------------------- |
michael@0 | 100 | // Instruction abstraction. |
michael@0 | 101 | |
michael@0 | 102 | // The class Instruction enables access to individual fields defined in the ARM |
michael@0 | 103 | // architecture instruction set encoding as described in figure A3-1. |
michael@0 | 104 | // Note that the Assembler uses typedef int32_t Instr. |
michael@0 | 105 | // |
michael@0 | 106 | // Example: Test whether the instruction at ptr does set the condition code |
michael@0 | 107 | // bits. |
michael@0 | 108 | // |
michael@0 | 109 | // bool InstructionSetsConditionCodes(byte* ptr) { |
michael@0 | 110 | // Instruction* instr = Instruction::At(ptr); |
michael@0 | 111 | // int type = instr->TypeValue(); |
michael@0 | 112 | // return ((type == 0) || (type == 1)) && instr->hasS(); |
michael@0 | 113 | // } |
michael@0 | 114 | // |
michael@0 | 115 | class SimInstruction { |
michael@0 | 116 | public: |
michael@0 | 117 | enum { |
michael@0 | 118 | kInstrSize = 4, |
michael@0 | 119 | kPCReadOffset = 8 |
michael@0 | 120 | }; |
michael@0 | 121 | |
michael@0 | 122 | // Get the raw instruction bits. |
michael@0 | 123 | inline Instr instructionBits() const { |
michael@0 | 124 | return *reinterpret_cast<const Instr*>(this); |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | // Set the raw instruction bits to value. |
michael@0 | 128 | inline void setInstructionBits(Instr value) { |
michael@0 | 129 | *reinterpret_cast<Instr*>(this) = value; |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | // Read one particular bit out of the instruction bits. |
michael@0 | 133 | inline int bit(int nr) const { |
michael@0 | 134 | return (instructionBits() >> nr) & 1; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | // Read a bit field's value out of the instruction bits. |
michael@0 | 138 | inline int bits(int hi, int lo) const { |
michael@0 | 139 | return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1); |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | // Read a bit field out of the instruction bits. |
michael@0 | 143 | inline int bitField(int hi, int lo) const { |
michael@0 | 144 | return instructionBits() & (((2 << (hi - lo)) - 1) << lo); |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | // Accessors for the different named fields used in the ARM encoding. |
michael@0 | 148 | // The naming of these accessor corresponds to figure A3-1. |
michael@0 | 149 | // |
michael@0 | 150 | // Two kind of accessors are declared: |
michael@0 | 151 | // - <Name>Field() will return the raw field, i.e. the field's bits at their |
michael@0 | 152 | // original place in the instruction encoding. |
michael@0 | 153 | // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as |
michael@0 | 154 | // 0xC0810002 conditionField(instr) will return 0xC0000000. |
michael@0 | 155 | // - <Name>Value() will return the field value, shifted back to bit 0. |
michael@0 | 156 | // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as |
michael@0 | 157 | // 0xC0810002 conditionField(instr) will return 0xC. |
michael@0 | 158 | |
michael@0 | 159 | // Generally applicable fields |
michael@0 | 160 | inline Assembler::ARMCondition conditionField() const { |
michael@0 | 161 | return static_cast<Assembler::ARMCondition>(bitField(31, 28)); |
michael@0 | 162 | } |
michael@0 | 163 | inline int typeValue() const { return bits(27, 25); } |
michael@0 | 164 | inline int specialValue() const { return bits(27, 23); } |
michael@0 | 165 | |
michael@0 | 166 | inline int rnValue() const { return bits(19, 16); } |
michael@0 | 167 | inline int rdValue() const { return bits(15, 12); } |
michael@0 | 168 | |
michael@0 | 169 | inline int coprocessorValue() const { return bits(11, 8); } |
michael@0 | 170 | |
michael@0 | 171 | // Support for VFP. |
michael@0 | 172 | // Vn(19-16) | Vd(15-12) | Vm(3-0) |
michael@0 | 173 | inline int vnValue() const { return bits(19, 16); } |
michael@0 | 174 | inline int vmValue() const { return bits(3, 0); } |
michael@0 | 175 | inline int vdValue() const { return bits(15, 12); } |
michael@0 | 176 | inline int nValue() const { return bit(7); } |
michael@0 | 177 | inline int mValue() const { return bit(5); } |
michael@0 | 178 | inline int dValue() const { return bit(22); } |
michael@0 | 179 | inline int rtValue() const { return bits(15, 12); } |
michael@0 | 180 | inline int pValue() const { return bit(24); } |
michael@0 | 181 | inline int uValue() const { return bit(23); } |
michael@0 | 182 | inline int opc1Value() const { return (bit(23) << 2) | bits(21, 20); } |
michael@0 | 183 | inline int opc2Value() const { return bits(19, 16); } |
michael@0 | 184 | inline int opc3Value() const { return bits(7, 6); } |
michael@0 | 185 | inline int szValue() const { return bit(8); } |
michael@0 | 186 | inline int VLValue() const { return bit(20); } |
michael@0 | 187 | inline int VCValue() const { return bit(8); } |
michael@0 | 188 | inline int VAValue() const { return bits(23, 21); } |
michael@0 | 189 | inline int VBValue() const { return bits(6, 5); } |
michael@0 | 190 | inline int VFPNRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 16, 7); } |
michael@0 | 191 | inline int VFPMRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 0, 5); } |
michael@0 | 192 | inline int VFPDRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 12, 22); } |
michael@0 | 193 | |
michael@0 | 194 | // Fields used in Data processing instructions |
michael@0 | 195 | inline int opcodeValue() const { return static_cast<ALUOp>(bits(24, 21)); } |
michael@0 | 196 | inline ALUOp opcodeField() const { return static_cast<ALUOp>(bitField(24, 21)); } |
michael@0 | 197 | inline int sValue() const { return bit(20); } |
michael@0 | 198 | |
michael@0 | 199 | // with register |
michael@0 | 200 | inline int rmValue() const { return bits(3, 0); } |
michael@0 | 201 | inline ShiftType shifttypeValue() const { return static_cast<ShiftType>(bits(6, 5)); } |
michael@0 | 202 | inline int rsValue() const { return bits(11, 8); } |
michael@0 | 203 | inline int shiftAmountValue() const { return bits(11, 7); } |
michael@0 | 204 | |
michael@0 | 205 | // with immediate |
michael@0 | 206 | inline int rotateValue() const { return bits(11, 8); } |
michael@0 | 207 | inline int immed8Value() const { return bits(7, 0); } |
michael@0 | 208 | inline int immed4Value() const { return bits(19, 16); } |
michael@0 | 209 | inline int immedMovwMovtValue() const { return immed4Value() << 12 | offset12Value(); } |
michael@0 | 210 | |
michael@0 | 211 | // Fields used in Load/Store instructions |
michael@0 | 212 | inline int PUValue() const { return bits(24, 23); } |
michael@0 | 213 | inline int PUField() const { return bitField(24, 23); } |
michael@0 | 214 | inline int bValue() const { return bit(22); } |
michael@0 | 215 | inline int wValue() const { return bit(21); } |
michael@0 | 216 | inline int lValue() const { return bit(20); } |
michael@0 | 217 | |
michael@0 | 218 | // with register uses same fields as Data processing instructions above |
michael@0 | 219 | // with immediate |
michael@0 | 220 | inline int offset12Value() const { return bits(11, 0); } |
michael@0 | 221 | |
michael@0 | 222 | // multiple |
michael@0 | 223 | inline int rlistValue() const { return bits(15, 0); } |
michael@0 | 224 | |
michael@0 | 225 | // extra loads and stores |
michael@0 | 226 | inline int signValue() const { return bit(6); } |
michael@0 | 227 | inline int hValue() const { return bit(5); } |
michael@0 | 228 | inline int immedHValue() const { return bits(11, 8); } |
michael@0 | 229 | inline int immedLValue() const { return bits(3, 0); } |
michael@0 | 230 | |
michael@0 | 231 | // Fields used in Branch instructions |
michael@0 | 232 | inline int linkValue() const { return bit(24); } |
michael@0 | 233 | inline int sImmed24Value() const { return ((instructionBits() << 8) >> 8); } |
michael@0 | 234 | |
michael@0 | 235 | // Fields used in Software interrupt instructions |
michael@0 | 236 | inline SoftwareInterruptCodes svcValue() const { |
michael@0 | 237 | return static_cast<SoftwareInterruptCodes>(bits(23, 0)); |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | // Test for special encodings of type 0 instructions (extra loads and stores, |
michael@0 | 241 | // as well as multiplications). |
michael@0 | 242 | inline bool isSpecialType0() const { return (bit(7) == 1) && (bit(4) == 1); } |
michael@0 | 243 | |
michael@0 | 244 | // Test for miscellaneous instructions encodings of type 0 instructions. |
michael@0 | 245 | inline bool isMiscType0() const { |
michael@0 | 246 | return bit(24) == 1 && bit(23) == 0 && bit(20) == 0 && (bit(7) == 0); |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | // Test for a nop instruction, which falls under type 1. |
michael@0 | 250 | inline bool isNopType1() const { return bits(24, 0) == 0x0120F000; } |
michael@0 | 251 | |
michael@0 | 252 | // Test for a stop instruction. |
michael@0 | 253 | inline bool isStop() const { |
michael@0 | 254 | return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | // Special accessors that test for existence of a value. |
michael@0 | 258 | inline bool hasS() const { return sValue() == 1; } |
michael@0 | 259 | inline bool hasB() const { return bValue() == 1; } |
michael@0 | 260 | inline bool hasW() const { return wValue() == 1; } |
michael@0 | 261 | inline bool hasL() const { return lValue() == 1; } |
michael@0 | 262 | inline bool hasU() const { return uValue() == 1; } |
michael@0 | 263 | inline bool hasSign() const { return signValue() == 1; } |
michael@0 | 264 | inline bool hasH() const { return hValue() == 1; } |
michael@0 | 265 | inline bool hasLink() const { return linkValue() == 1; } |
michael@0 | 266 | |
michael@0 | 267 | // Decoding the double immediate in the vmov instruction. |
michael@0 | 268 | double doubleImmedVmov() const; |
michael@0 | 269 | // Decoding the float32 immediate in the vmov.f32 instruction. |
michael@0 | 270 | float float32ImmedVmov() const; |
michael@0 | 271 | |
michael@0 | 272 | private: |
michael@0 | 273 | // Join split register codes, depending on single or double precision. |
michael@0 | 274 | // four_bit is the position of the least-significant bit of the four |
michael@0 | 275 | // bit specifier. one_bit is the position of the additional single bit |
michael@0 | 276 | // specifier. |
michael@0 | 277 | inline int VFPGlueRegValue(VFPRegPrecision pre, int four_bit, int one_bit) { |
michael@0 | 278 | if (pre == kSinglePrecision) |
michael@0 | 279 | return (bits(four_bit + 3, four_bit) << 1) | bit(one_bit); |
michael@0 | 280 | return (bit(one_bit) << 4) | bits(four_bit + 3, four_bit); |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | SimInstruction() MOZ_DELETE; |
michael@0 | 284 | SimInstruction(const SimInstruction &other) MOZ_DELETE; |
michael@0 | 285 | void operator=(const SimInstruction &other) MOZ_DELETE; |
michael@0 | 286 | }; |
michael@0 | 287 | |
michael@0 | 288 | double |
michael@0 | 289 | SimInstruction::doubleImmedVmov() const |
michael@0 | 290 | { |
michael@0 | 291 | // Reconstruct a double from the immediate encoded in the vmov instruction. |
michael@0 | 292 | // |
michael@0 | 293 | // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh] |
michael@0 | 294 | // double: [aBbbbbbb,bbcdefgh,00000000,00000000, |
michael@0 | 295 | // 00000000,00000000,00000000,00000000] |
michael@0 | 296 | // |
michael@0 | 297 | // where B = ~b. Only the high 16 bits are affected. |
michael@0 | 298 | uint64_t high16; |
michael@0 | 299 | high16 = (bits(17, 16) << 4) | bits(3, 0); // xxxxxxxx,xxcdefgh. |
michael@0 | 300 | high16 |= (0xff * bit(18)) << 6; // xxbbbbbb,bbxxxxxx. |
michael@0 | 301 | high16 |= (bit(18) ^ 1) << 14; // xBxxxxxx,xxxxxxxx. |
michael@0 | 302 | high16 |= bit(19) << 15; // axxxxxxx,xxxxxxxx. |
michael@0 | 303 | |
michael@0 | 304 | uint64_t imm = high16 << 48; |
michael@0 | 305 | return mozilla::BitwiseCast<double>(imm); |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | float |
michael@0 | 309 | SimInstruction::float32ImmedVmov() const |
michael@0 | 310 | { |
michael@0 | 311 | // Reconstruct a float32 from the immediate encoded in the vmov instruction. |
michael@0 | 312 | // |
michael@0 | 313 | // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh] |
michael@0 | 314 | // float32: [aBbbbbbc, defgh000, 00000000, 00000000] |
michael@0 | 315 | // |
michael@0 | 316 | // where B = ~b. Only the high 16 bits are affected. |
michael@0 | 317 | uint32_t imm; |
michael@0 | 318 | imm = (bits(17, 16) << 23) | (bits(3, 0) << 19); // xxxxxxxc,defgh000.0.0 |
michael@0 | 319 | imm |= (0x1f * bit(18)) << 25; // xxbbbbbx,xxxxxxxx.0.0 |
michael@0 | 320 | imm |= (bit(18) ^ 1) << 30; // xBxxxxxx,xxxxxxxx.0.0 |
michael@0 | 321 | imm |= bit(19) << 31; // axxxxxxx,xxxxxxxx.0.0 |
michael@0 | 322 | |
michael@0 | 323 | return mozilla::BitwiseCast<float>(imm); |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | class CachePage |
michael@0 | 327 | { |
michael@0 | 328 | public: |
michael@0 | 329 | static const int LINE_VALID = 0; |
michael@0 | 330 | static const int LINE_INVALID = 1; |
michael@0 | 331 | static const int kPageShift = 12; |
michael@0 | 332 | static const int kPageSize = 1 << kPageShift; |
michael@0 | 333 | static const int kPageMask = kPageSize - 1; |
michael@0 | 334 | static const int kLineShift = 2; // The cache line is only 4 bytes right now. |
michael@0 | 335 | static const int kLineLength = 1 << kLineShift; |
michael@0 | 336 | static const int kLineMask = kLineLength - 1; |
michael@0 | 337 | |
michael@0 | 338 | CachePage() { |
michael@0 | 339 | memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); |
michael@0 | 340 | } |
michael@0 | 341 | char *validityByte(int offset) { |
michael@0 | 342 | return &validity_map_[offset >> kLineShift]; |
michael@0 | 343 | } |
michael@0 | 344 | char *cachedData(int offset) { |
michael@0 | 345 | return &data_[offset]; |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | private: |
michael@0 | 349 | char data_[kPageSize]; // The cached data. |
michael@0 | 350 | static const int kValidityMapSize = kPageSize >> kLineShift; |
michael@0 | 351 | char validity_map_[kValidityMapSize]; // One byte per line. |
michael@0 | 352 | }; |
michael@0 | 353 | |
michael@0 | 354 | class Redirection; |
michael@0 | 355 | |
michael@0 | 356 | class SimulatorRuntime |
michael@0 | 357 | { |
michael@0 | 358 | friend class AutoLockSimulatorRuntime; |
michael@0 | 359 | |
michael@0 | 360 | Redirection *redirection_; |
michael@0 | 361 | |
michael@0 | 362 | // ICache checking. |
michael@0 | 363 | struct ICacheHasher { |
michael@0 | 364 | typedef void *Key; |
michael@0 | 365 | typedef void *Lookup; |
michael@0 | 366 | static HashNumber hash(const Lookup &l); |
michael@0 | 367 | static bool match(const Key &k, const Lookup &l); |
michael@0 | 368 | }; |
michael@0 | 369 | |
michael@0 | 370 | public: |
michael@0 | 371 | typedef HashMap<void *, CachePage *, ICacheHasher, SystemAllocPolicy> ICacheMap; |
michael@0 | 372 | |
michael@0 | 373 | protected: |
michael@0 | 374 | ICacheMap icache_; |
michael@0 | 375 | |
michael@0 | 376 | // Synchronize access between main thread and compilation/PJS threads. |
michael@0 | 377 | PRLock *lock_; |
michael@0 | 378 | mozilla::DebugOnly<PRThread *> lockOwner_; |
michael@0 | 379 | |
michael@0 | 380 | public: |
michael@0 | 381 | SimulatorRuntime() |
michael@0 | 382 | : redirection_(nullptr), |
michael@0 | 383 | lock_(nullptr), |
michael@0 | 384 | lockOwner_(nullptr) |
michael@0 | 385 | {} |
michael@0 | 386 | ~SimulatorRuntime(); |
michael@0 | 387 | bool init() { |
michael@0 | 388 | #ifdef JS_THREADSAFE |
michael@0 | 389 | lock_ = PR_NewLock(); |
michael@0 | 390 | if (!lock_) |
michael@0 | 391 | return false; |
michael@0 | 392 | #endif |
michael@0 | 393 | if (!icache_.init()) |
michael@0 | 394 | return false; |
michael@0 | 395 | return true; |
michael@0 | 396 | } |
michael@0 | 397 | ICacheMap &icache() { |
michael@0 | 398 | #ifdef JS_THREADSAFE |
michael@0 | 399 | MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); |
michael@0 | 400 | #endif |
michael@0 | 401 | return icache_; |
michael@0 | 402 | } |
michael@0 | 403 | Redirection *redirection() const { |
michael@0 | 404 | #ifdef JS_THREADSAFE |
michael@0 | 405 | MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); |
michael@0 | 406 | #endif |
michael@0 | 407 | return redirection_; |
michael@0 | 408 | } |
michael@0 | 409 | void setRedirection(js::jit::Redirection *redirection) { |
michael@0 | 410 | #ifdef JS_THREADSAFE |
michael@0 | 411 | MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); |
michael@0 | 412 | #endif |
michael@0 | 413 | redirection_ = redirection; |
michael@0 | 414 | } |
michael@0 | 415 | }; |
michael@0 | 416 | |
michael@0 | 417 | class AutoLockSimulatorRuntime |
michael@0 | 418 | { |
michael@0 | 419 | protected: |
michael@0 | 420 | SimulatorRuntime *srt_; |
michael@0 | 421 | |
michael@0 | 422 | public: |
michael@0 | 423 | AutoLockSimulatorRuntime(SimulatorRuntime *srt) |
michael@0 | 424 | : srt_(srt) |
michael@0 | 425 | { |
michael@0 | 426 | #ifdef JS_THREADSAFE |
michael@0 | 427 | PR_Lock(srt_->lock_); |
michael@0 | 428 | MOZ_ASSERT(!srt_->lockOwner_); |
michael@0 | 429 | #ifdef DEBUG |
michael@0 | 430 | srt_->lockOwner_ = PR_GetCurrentThread(); |
michael@0 | 431 | #endif |
michael@0 | 432 | #endif |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | ~AutoLockSimulatorRuntime() { |
michael@0 | 436 | #ifdef JS_THREADSAFE |
michael@0 | 437 | MOZ_ASSERT(srt_->lockOwner_ == PR_GetCurrentThread()); |
michael@0 | 438 | srt_->lockOwner_ = nullptr; |
michael@0 | 439 | PR_Unlock(srt_->lock_); |
michael@0 | 440 | #endif |
michael@0 | 441 | } |
michael@0 | 442 | }; |
michael@0 | 443 | |
michael@0 | 444 | bool Simulator::ICacheCheckingEnabled = false; |
michael@0 | 445 | |
michael@0 | 446 | int64_t Simulator::StopSimAt = -1L; |
michael@0 | 447 | |
michael@0 | 448 | SimulatorRuntime * |
michael@0 | 449 | CreateSimulatorRuntime() |
michael@0 | 450 | { |
michael@0 | 451 | SimulatorRuntime *srt = js_new<SimulatorRuntime>(); |
michael@0 | 452 | if (!srt) |
michael@0 | 453 | return nullptr; |
michael@0 | 454 | |
michael@0 | 455 | if (!srt->init()) { |
michael@0 | 456 | js_delete(srt); |
michael@0 | 457 | return nullptr; |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | if (getenv("ARM_SIM_ICACHE_CHECKS")) |
michael@0 | 461 | Simulator::ICacheCheckingEnabled = true; |
michael@0 | 462 | |
michael@0 | 463 | char *stopAtStr = getenv("ARM_SIM_STOP_AT"); |
michael@0 | 464 | int64_t stopAt; |
michael@0 | 465 | if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) { |
michael@0 | 466 | fprintf(stderr, "\nStopping simulation at icount %lld\n", stopAt); |
michael@0 | 467 | Simulator::StopSimAt = stopAt; |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | return srt; |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | void |
michael@0 | 474 | DestroySimulatorRuntime(SimulatorRuntime *srt) |
michael@0 | 475 | { |
michael@0 | 476 | js_delete(srt); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | // The ArmDebugger class is used by the simulator while debugging simulated ARM |
michael@0 | 480 | // code. |
michael@0 | 481 | class ArmDebugger { |
michael@0 | 482 | public: |
michael@0 | 483 | explicit ArmDebugger(Simulator *sim) : sim_(sim) { } |
michael@0 | 484 | |
michael@0 | 485 | void stop(SimInstruction *instr); |
michael@0 | 486 | void debug(); |
michael@0 | 487 | |
michael@0 | 488 | private: |
michael@0 | 489 | static const Instr kBreakpointInstr = (Assembler::AL | (7 * (1 << 25)) | (1* (1 << 24)) | kBreakpoint); |
michael@0 | 490 | static const Instr kNopInstr = (Assembler::AL | (13 * (1 << 21))); |
michael@0 | 491 | |
michael@0 | 492 | Simulator* sim_; |
michael@0 | 493 | |
michael@0 | 494 | int32_t getRegisterValue(int regnum); |
michael@0 | 495 | double getRegisterPairDoubleValue(int regnum); |
michael@0 | 496 | double getVFPDoubleRegisterValue(int regnum); |
michael@0 | 497 | bool getValue(const char *desc, int32_t *value); |
michael@0 | 498 | bool getVFPDoubleValue(const char *desc, double *value); |
michael@0 | 499 | |
michael@0 | 500 | // Set or delete a breakpoint. Returns true if successful. |
michael@0 | 501 | bool setBreakpoint(SimInstruction *breakpc); |
michael@0 | 502 | bool deleteBreakpoint(SimInstruction *breakpc); |
michael@0 | 503 | |
michael@0 | 504 | // Undo and redo all breakpoints. This is needed to bracket disassembly and |
michael@0 | 505 | // execution to skip past breakpoints when run from the debugger. |
michael@0 | 506 | void undoBreakpoints(); |
michael@0 | 507 | void redoBreakpoints(); |
michael@0 | 508 | }; |
michael@0 | 509 | |
michael@0 | 510 | void |
michael@0 | 511 | ArmDebugger::stop(SimInstruction * instr) |
michael@0 | 512 | { |
michael@0 | 513 | // Get the stop code. |
michael@0 | 514 | uint32_t code = instr->svcValue() & kStopCodeMask; |
michael@0 | 515 | // Retrieve the encoded address, which comes just after this stop. |
michael@0 | 516 | char* msg = *reinterpret_cast<char**>(sim_->get_pc() |
michael@0 | 517 | + SimInstruction::kInstrSize); |
michael@0 | 518 | // Update this stop description. |
michael@0 | 519 | if (sim_->isWatchedStop(code) && !sim_->watched_stops_[code].desc) { |
michael@0 | 520 | sim_->watched_stops_[code].desc = msg; |
michael@0 | 521 | } |
michael@0 | 522 | // Print the stop message and code if it is not the default code. |
michael@0 | 523 | if (code != kMaxStopCode) { |
michael@0 | 524 | printf("Simulator hit stop %u: %s\n", code, msg); |
michael@0 | 525 | } else { |
michael@0 | 526 | printf("Simulator hit %s\n", msg); |
michael@0 | 527 | } |
michael@0 | 528 | sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize); |
michael@0 | 529 | debug(); |
michael@0 | 530 | } |
michael@0 | 531 | |
michael@0 | 532 | int32_t |
michael@0 | 533 | ArmDebugger::getRegisterValue(int regnum) |
michael@0 | 534 | { |
michael@0 | 535 | if (regnum == Registers::pc) |
michael@0 | 536 | return sim_->get_pc(); |
michael@0 | 537 | return sim_->get_register(regnum); |
michael@0 | 538 | } |
michael@0 | 539 | |
michael@0 | 540 | double |
michael@0 | 541 | ArmDebugger::getRegisterPairDoubleValue(int regnum) |
michael@0 | 542 | { |
michael@0 | 543 | return sim_->get_double_from_register_pair(regnum); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | double |
michael@0 | 547 | ArmDebugger::getVFPDoubleRegisterValue(int regnum) |
michael@0 | 548 | { |
michael@0 | 549 | return sim_->get_double_from_d_register(regnum); |
michael@0 | 550 | } |
michael@0 | 551 | |
michael@0 | 552 | bool |
michael@0 | 553 | ArmDebugger::getValue(const char *desc, int32_t *value) |
michael@0 | 554 | { |
michael@0 | 555 | Register reg = Register::FromName(desc); |
michael@0 | 556 | if (reg != InvalidReg) { |
michael@0 | 557 | *value = getRegisterValue(reg.code()); |
michael@0 | 558 | return true; |
michael@0 | 559 | } |
michael@0 | 560 | if (strncmp(desc, "0x", 2) == 0) |
michael@0 | 561 | return sscanf(desc + 2, "%x", reinterpret_cast<uint32_t*>(value)) == 1; |
michael@0 | 562 | return sscanf(desc, "%u", reinterpret_cast<uint32_t*>(value)) == 1; |
michael@0 | 563 | } |
michael@0 | 564 | |
michael@0 | 565 | bool |
michael@0 | 566 | ArmDebugger::getVFPDoubleValue(const char *desc, double *value) |
michael@0 | 567 | { |
michael@0 | 568 | FloatRegister reg = FloatRegister::FromName(desc); |
michael@0 | 569 | if (reg != InvalidFloatReg) { |
michael@0 | 570 | *value = sim_->get_double_from_d_register(reg.code()); |
michael@0 | 571 | return true; |
michael@0 | 572 | } |
michael@0 | 573 | return false; |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | bool |
michael@0 | 577 | ArmDebugger::setBreakpoint(SimInstruction *breakpc) |
michael@0 | 578 | { |
michael@0 | 579 | // Check if a breakpoint can be set. If not return without any side-effects. |
michael@0 | 580 | if (sim_->break_pc_) |
michael@0 | 581 | return false; |
michael@0 | 582 | |
michael@0 | 583 | // Set the breakpoint. |
michael@0 | 584 | sim_->break_pc_ = breakpc; |
michael@0 | 585 | sim_->break_instr_ = breakpc->instructionBits(); |
michael@0 | 586 | // Not setting the breakpoint instruction in the code itself. It will be set |
michael@0 | 587 | // when the debugger shell continues. |
michael@0 | 588 | return true; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | bool |
michael@0 | 592 | ArmDebugger::deleteBreakpoint(SimInstruction *breakpc) |
michael@0 | 593 | { |
michael@0 | 594 | if (sim_->break_pc_ != nullptr) |
michael@0 | 595 | sim_->break_pc_->setInstructionBits(sim_->break_instr_); |
michael@0 | 596 | |
michael@0 | 597 | sim_->break_pc_ = nullptr; |
michael@0 | 598 | sim_->break_instr_ = 0; |
michael@0 | 599 | return true; |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | void |
michael@0 | 603 | ArmDebugger::undoBreakpoints() |
michael@0 | 604 | { |
michael@0 | 605 | if (sim_->break_pc_) |
michael@0 | 606 | sim_->break_pc_->setInstructionBits(sim_->break_instr_); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | void |
michael@0 | 610 | ArmDebugger::redoBreakpoints() |
michael@0 | 611 | { |
michael@0 | 612 | if (sim_->break_pc_) |
michael@0 | 613 | sim_->break_pc_->setInstructionBits(kBreakpointInstr); |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | static char * |
michael@0 | 617 | ReadLine(const char *prompt) |
michael@0 | 618 | { |
michael@0 | 619 | char *result = nullptr; |
michael@0 | 620 | char line_buf[256]; |
michael@0 | 621 | int offset = 0; |
michael@0 | 622 | bool keep_going = true; |
michael@0 | 623 | fprintf(stdout, "%s", prompt); |
michael@0 | 624 | fflush(stdout); |
michael@0 | 625 | while (keep_going) { |
michael@0 | 626 | if (fgets(line_buf, sizeof(line_buf), stdin) == nullptr) { |
michael@0 | 627 | // fgets got an error. Just give up. |
michael@0 | 628 | if (result) |
michael@0 | 629 | js_delete(result); |
michael@0 | 630 | return nullptr; |
michael@0 | 631 | } |
michael@0 | 632 | int len = strlen(line_buf); |
michael@0 | 633 | if (len > 0 && line_buf[len - 1] == '\n') { |
michael@0 | 634 | // Since we read a new line we are done reading the line. This |
michael@0 | 635 | // will exit the loop after copying this buffer into the result. |
michael@0 | 636 | keep_going = false; |
michael@0 | 637 | } |
michael@0 | 638 | if (!result) { |
michael@0 | 639 | // Allocate the initial result and make room for the terminating '\0' |
michael@0 | 640 | result = (char *)js_malloc(len + 1); |
michael@0 | 641 | if (!result) |
michael@0 | 642 | return nullptr; |
michael@0 | 643 | } else { |
michael@0 | 644 | // Allocate a new result with enough room for the new addition. |
michael@0 | 645 | int new_len = offset + len + 1; |
michael@0 | 646 | char *new_result = (char *)js_malloc(new_len); |
michael@0 | 647 | if (!new_result) |
michael@0 | 648 | return nullptr; |
michael@0 | 649 | // Copy the existing input into the new array and set the new |
michael@0 | 650 | // array as the result. |
michael@0 | 651 | memcpy(new_result, result, offset * sizeof(char)); |
michael@0 | 652 | js_free(result); |
michael@0 | 653 | result = new_result; |
michael@0 | 654 | } |
michael@0 | 655 | // Copy the newly read line into the result. |
michael@0 | 656 | memcpy(result + offset, line_buf, len * sizeof(char)); |
michael@0 | 657 | offset += len; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | MOZ_ASSERT(result); |
michael@0 | 661 | result[offset] = '\0'; |
michael@0 | 662 | return result; |
michael@0 | 663 | } |
michael@0 | 664 | |
michael@0 | 665 | static void |
michael@0 | 666 | DisassembleInstruction(uint32_t pc) |
michael@0 | 667 | { |
michael@0 | 668 | uint8_t *bytes = reinterpret_cast<uint8_t *>(pc); |
michael@0 | 669 | char hexbytes[256]; |
michael@0 | 670 | sprintf(hexbytes, "0x%x 0x%x 0x%x 0x%x", bytes[0], bytes[1], bytes[2], bytes[3]); |
michael@0 | 671 | char llvmcmd[1024]; |
michael@0 | 672 | sprintf(llvmcmd, "bash -c \"echo -n '%p'; echo '%s' | " |
michael@0 | 673 | "llvm-mc -disassemble -arch=arm -mcpu=cortex-a9 | " |
michael@0 | 674 | "grep -v pure_instructions | grep -v .text\"", |
michael@0 | 675 | reinterpret_cast<void*>(pc), hexbytes); |
michael@0 | 676 | system(llvmcmd); |
michael@0 | 677 | } |
michael@0 | 678 | |
michael@0 | 679 | void |
michael@0 | 680 | ArmDebugger::debug() |
michael@0 | 681 | { |
michael@0 | 682 | intptr_t last_pc = -1; |
michael@0 | 683 | bool done = false; |
michael@0 | 684 | |
michael@0 | 685 | #define COMMAND_SIZE 63 |
michael@0 | 686 | #define ARG_SIZE 255 |
michael@0 | 687 | |
michael@0 | 688 | #define STR(a) #a |
michael@0 | 689 | #define XSTR(a) STR(a) |
michael@0 | 690 | |
michael@0 | 691 | char cmd[COMMAND_SIZE + 1]; |
michael@0 | 692 | char arg1[ARG_SIZE + 1]; |
michael@0 | 693 | char arg2[ARG_SIZE + 1]; |
michael@0 | 694 | char *argv[3] = { cmd, arg1, arg2 }; |
michael@0 | 695 | |
michael@0 | 696 | // make sure to have a proper terminating character if reaching the limit |
michael@0 | 697 | cmd[COMMAND_SIZE] = 0; |
michael@0 | 698 | arg1[ARG_SIZE] = 0; |
michael@0 | 699 | arg2[ARG_SIZE] = 0; |
michael@0 | 700 | |
michael@0 | 701 | // Undo all set breakpoints while running in the debugger shell. This will |
michael@0 | 702 | // make them invisible to all commands. |
michael@0 | 703 | undoBreakpoints(); |
michael@0 | 704 | |
michael@0 | 705 | while (!done && !sim_->has_bad_pc()) { |
michael@0 | 706 | if (last_pc != sim_->get_pc()) { |
michael@0 | 707 | DisassembleInstruction(sim_->get_pc()); |
michael@0 | 708 | last_pc = sim_->get_pc(); |
michael@0 | 709 | } |
michael@0 | 710 | char *line = ReadLine("sim> "); |
michael@0 | 711 | if (line == nullptr) { |
michael@0 | 712 | break; |
michael@0 | 713 | } else { |
michael@0 | 714 | char *last_input = sim_->lastDebuggerInput(); |
michael@0 | 715 | if (strcmp(line, "\n") == 0 && last_input != nullptr) { |
michael@0 | 716 | line = last_input; |
michael@0 | 717 | } else { |
michael@0 | 718 | // Ownership is transferred to sim_; |
michael@0 | 719 | sim_->setLastDebuggerInput(line); |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | // Use sscanf to parse the individual parts of the command line. At the |
michael@0 | 723 | // moment no command expects more than two parameters. |
michael@0 | 724 | int argc = sscanf(line, |
michael@0 | 725 | "%" XSTR(COMMAND_SIZE) "s " |
michael@0 | 726 | "%" XSTR(ARG_SIZE) "s " |
michael@0 | 727 | "%" XSTR(ARG_SIZE) "s", |
michael@0 | 728 | cmd, arg1, arg2); |
michael@0 | 729 | if (argc < 0) { |
michael@0 | 730 | continue; |
michael@0 | 731 | } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { |
michael@0 | 732 | sim_->instructionDecode(reinterpret_cast<SimInstruction *>(sim_->get_pc())); |
michael@0 | 733 | sim_->icount_++; |
michael@0 | 734 | } else if ((strcmp(cmd, "skip") == 0)) { |
michael@0 | 735 | sim_->set_pc(sim_->get_pc() + 4); |
michael@0 | 736 | sim_->icount_++; |
michael@0 | 737 | } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { |
michael@0 | 738 | // Execute the one instruction we broke at with breakpoints disabled. |
michael@0 | 739 | sim_->instructionDecode(reinterpret_cast<SimInstruction *>(sim_->get_pc())); |
michael@0 | 740 | sim_->icount_++; |
michael@0 | 741 | // Leave the debugger shell. |
michael@0 | 742 | done = true; |
michael@0 | 743 | } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { |
michael@0 | 744 | if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) { |
michael@0 | 745 | int32_t value; |
michael@0 | 746 | double dvalue; |
michael@0 | 747 | if (strcmp(arg1, "all") == 0) { |
michael@0 | 748 | for (uint32_t i = 0; i < Registers::Total; i++) { |
michael@0 | 749 | value = getRegisterValue(i); |
michael@0 | 750 | printf("%3s: 0x%08x %10d", Registers::GetName(i), value, value); |
michael@0 | 751 | if ((argc == 3 && strcmp(arg2, "fp") == 0) && |
michael@0 | 752 | i < 8 && |
michael@0 | 753 | (i % 2) == 0) { |
michael@0 | 754 | dvalue = getRegisterPairDoubleValue(i); |
michael@0 | 755 | printf(" (%f)\n", dvalue); |
michael@0 | 756 | } else { |
michael@0 | 757 | printf("\n"); |
michael@0 | 758 | } |
michael@0 | 759 | } |
michael@0 | 760 | for (uint32_t i = 0; i < FloatRegisters::Total; i++) { |
michael@0 | 761 | dvalue = getVFPDoubleRegisterValue(i); |
michael@0 | 762 | uint64_t as_words = mozilla::BitwiseCast<uint64_t>(dvalue); |
michael@0 | 763 | printf("%3s: %f 0x%08x %08x\n", |
michael@0 | 764 | FloatRegister::FromCode(i).name(), |
michael@0 | 765 | dvalue, |
michael@0 | 766 | static_cast<uint32_t>(as_words >> 32), |
michael@0 | 767 | static_cast<uint32_t>(as_words & 0xffffffff)); |
michael@0 | 768 | } |
michael@0 | 769 | } else { |
michael@0 | 770 | if (getValue(arg1, &value)) { |
michael@0 | 771 | printf("%s: 0x%08x %d \n", arg1, value, value); |
michael@0 | 772 | } else if (getVFPDoubleValue(arg1, &dvalue)) { |
michael@0 | 773 | uint64_t as_words = mozilla::BitwiseCast<uint64_t>(dvalue); |
michael@0 | 774 | printf("%s: %f 0x%08x %08x\n", |
michael@0 | 775 | arg1, |
michael@0 | 776 | dvalue, |
michael@0 | 777 | static_cast<uint32_t>(as_words >> 32), |
michael@0 | 778 | static_cast<uint32_t>(as_words & 0xffffffff)); |
michael@0 | 779 | } else { |
michael@0 | 780 | printf("%s unrecognized\n", arg1); |
michael@0 | 781 | } |
michael@0 | 782 | } |
michael@0 | 783 | } else { |
michael@0 | 784 | printf("print <register>\n"); |
michael@0 | 785 | } |
michael@0 | 786 | } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { |
michael@0 | 787 | int32_t *cur = nullptr; |
michael@0 | 788 | int32_t *end = nullptr; |
michael@0 | 789 | int next_arg = 1; |
michael@0 | 790 | |
michael@0 | 791 | if (strcmp(cmd, "stack") == 0) { |
michael@0 | 792 | cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp)); |
michael@0 | 793 | } else { // "mem" |
michael@0 | 794 | int32_t value; |
michael@0 | 795 | if (!getValue(arg1, &value)) { |
michael@0 | 796 | printf("%s unrecognized\n", arg1); |
michael@0 | 797 | continue; |
michael@0 | 798 | } |
michael@0 | 799 | cur = reinterpret_cast<int32_t*>(value); |
michael@0 | 800 | next_arg++; |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | int32_t words; |
michael@0 | 804 | if (argc == next_arg) { |
michael@0 | 805 | words = 10; |
michael@0 | 806 | } else { |
michael@0 | 807 | if (!getValue(argv[next_arg], &words)) { |
michael@0 | 808 | words = 10; |
michael@0 | 809 | } |
michael@0 | 810 | } |
michael@0 | 811 | end = cur + words; |
michael@0 | 812 | |
michael@0 | 813 | while (cur < end) { |
michael@0 | 814 | printf(" %p: 0x%08x %10d", cur, *cur, *cur); |
michael@0 | 815 | printf("\n"); |
michael@0 | 816 | cur++; |
michael@0 | 817 | } |
michael@0 | 818 | } else if (strcmp(cmd, "disasm") == 0 || strcmp(cmd, "di") == 0) { |
michael@0 | 819 | uint8_t *cur = nullptr; |
michael@0 | 820 | uint8_t *end = nullptr; |
michael@0 | 821 | if (argc == 1) { |
michael@0 | 822 | cur = reinterpret_cast<uint8_t *>(sim_->get_pc()); |
michael@0 | 823 | end = cur + (10 * SimInstruction::kInstrSize); |
michael@0 | 824 | } else if (argc == 2) { |
michael@0 | 825 | Register reg = Register::FromName(arg1); |
michael@0 | 826 | if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) { |
michael@0 | 827 | // The argument is an address or a register name. |
michael@0 | 828 | int32_t value; |
michael@0 | 829 | if (getValue(arg1, &value)) { |
michael@0 | 830 | cur = reinterpret_cast<uint8_t *>(value); |
michael@0 | 831 | // Disassemble 10 instructions at <arg1>. |
michael@0 | 832 | end = cur + (10 * SimInstruction::kInstrSize); |
michael@0 | 833 | } |
michael@0 | 834 | } else { |
michael@0 | 835 | // The argument is the number of instructions. |
michael@0 | 836 | int32_t value; |
michael@0 | 837 | if (getValue(arg1, &value)) { |
michael@0 | 838 | cur = reinterpret_cast<uint8_t *>(sim_->get_pc()); |
michael@0 | 839 | // Disassemble <arg1> instructions. |
michael@0 | 840 | end = cur + (value * SimInstruction::kInstrSize); |
michael@0 | 841 | } |
michael@0 | 842 | } |
michael@0 | 843 | } else { |
michael@0 | 844 | int32_t value1; |
michael@0 | 845 | int32_t value2; |
michael@0 | 846 | if (getValue(arg1, &value1) && getValue(arg2, &value2)) { |
michael@0 | 847 | cur = reinterpret_cast<uint8_t *>(value1); |
michael@0 | 848 | end = cur + (value2 * SimInstruction::kInstrSize); |
michael@0 | 849 | } |
michael@0 | 850 | } |
michael@0 | 851 | while (cur < end) { |
michael@0 | 852 | DisassembleInstruction(uint32_t(cur)); |
michael@0 | 853 | cur += SimInstruction::kInstrSize; |
michael@0 | 854 | } |
michael@0 | 855 | } else if (strcmp(cmd, "gdb") == 0) { |
michael@0 | 856 | printf("relinquishing control to gdb\n"); |
michael@0 | 857 | asm("int $3"); |
michael@0 | 858 | printf("regaining control from gdb\n"); |
michael@0 | 859 | } else if (strcmp(cmd, "break") == 0) { |
michael@0 | 860 | if (argc == 2) { |
michael@0 | 861 | int32_t value; |
michael@0 | 862 | if (getValue(arg1, &value)) { |
michael@0 | 863 | if (!setBreakpoint(reinterpret_cast<SimInstruction *>(value))) |
michael@0 | 864 | printf("setting breakpoint failed\n"); |
michael@0 | 865 | } else { |
michael@0 | 866 | printf("%s unrecognized\n", arg1); |
michael@0 | 867 | } |
michael@0 | 868 | } else { |
michael@0 | 869 | printf("break <address>\n"); |
michael@0 | 870 | } |
michael@0 | 871 | } else if (strcmp(cmd, "del") == 0) { |
michael@0 | 872 | if (!deleteBreakpoint(nullptr)) { |
michael@0 | 873 | printf("deleting breakpoint failed\n"); |
michael@0 | 874 | } |
michael@0 | 875 | } else if (strcmp(cmd, "flags") == 0) { |
michael@0 | 876 | printf("N flag: %d; ", sim_->n_flag_); |
michael@0 | 877 | printf("Z flag: %d; ", sim_->z_flag_); |
michael@0 | 878 | printf("C flag: %d; ", sim_->c_flag_); |
michael@0 | 879 | printf("V flag: %d\n", sim_->v_flag_); |
michael@0 | 880 | printf("INVALID OP flag: %d; ", sim_->inv_op_vfp_flag_); |
michael@0 | 881 | printf("DIV BY ZERO flag: %d; ", sim_->div_zero_vfp_flag_); |
michael@0 | 882 | printf("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_); |
michael@0 | 883 | printf("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_); |
michael@0 | 884 | printf("INEXACT flag: %d;\n", sim_->inexact_vfp_flag_); |
michael@0 | 885 | } else if (strcmp(cmd, "stop") == 0) { |
michael@0 | 886 | int32_t value; |
michael@0 | 887 | intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize; |
michael@0 | 888 | SimInstruction *stop_instr = reinterpret_cast<SimInstruction *>(stop_pc); |
michael@0 | 889 | SimInstruction *msg_address = |
michael@0 | 890 | reinterpret_cast<SimInstruction *>(stop_pc + SimInstruction::kInstrSize); |
michael@0 | 891 | if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { |
michael@0 | 892 | // Remove the current stop. |
michael@0 | 893 | if (sim_->isStopInstruction(stop_instr)) { |
michael@0 | 894 | stop_instr->setInstructionBits(kNopInstr); |
michael@0 | 895 | msg_address->setInstructionBits(kNopInstr); |
michael@0 | 896 | } else { |
michael@0 | 897 | printf("Not at debugger stop.\n"); |
michael@0 | 898 | } |
michael@0 | 899 | } else if (argc == 3) { |
michael@0 | 900 | // Print information about all/the specified breakpoint(s). |
michael@0 | 901 | if (strcmp(arg1, "info") == 0) { |
michael@0 | 902 | if (strcmp(arg2, "all") == 0) { |
michael@0 | 903 | printf("Stop information:\n"); |
michael@0 | 904 | for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) |
michael@0 | 905 | sim_->printStopInfo(i); |
michael@0 | 906 | } else if (getValue(arg2, &value)) { |
michael@0 | 907 | sim_->printStopInfo(value); |
michael@0 | 908 | } else { |
michael@0 | 909 | printf("Unrecognized argument.\n"); |
michael@0 | 910 | } |
michael@0 | 911 | } else if (strcmp(arg1, "enable") == 0) { |
michael@0 | 912 | // Enable all/the specified breakpoint(s). |
michael@0 | 913 | if (strcmp(arg2, "all") == 0) { |
michael@0 | 914 | for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) |
michael@0 | 915 | sim_->enableStop(i); |
michael@0 | 916 | } else if (getValue(arg2, &value)) { |
michael@0 | 917 | sim_->enableStop(value); |
michael@0 | 918 | } else { |
michael@0 | 919 | printf("Unrecognized argument.\n"); |
michael@0 | 920 | } |
michael@0 | 921 | } else if (strcmp(arg1, "disable") == 0) { |
michael@0 | 922 | // Disable all/the specified breakpoint(s). |
michael@0 | 923 | if (strcmp(arg2, "all") == 0) { |
michael@0 | 924 | for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) { |
michael@0 | 925 | sim_->disableStop(i); |
michael@0 | 926 | } |
michael@0 | 927 | } else if (getValue(arg2, &value)) { |
michael@0 | 928 | sim_->disableStop(value); |
michael@0 | 929 | } else { |
michael@0 | 930 | printf("Unrecognized argument.\n"); |
michael@0 | 931 | } |
michael@0 | 932 | } |
michael@0 | 933 | } else { |
michael@0 | 934 | printf("Wrong usage. Use help command for more information.\n"); |
michael@0 | 935 | } |
michael@0 | 936 | } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { |
michael@0 | 937 | printf("cont\n"); |
michael@0 | 938 | printf(" continue execution (alias 'c')\n"); |
michael@0 | 939 | printf("skip\n"); |
michael@0 | 940 | printf(" skip one instruction (set pc to next instruction)\n"); |
michael@0 | 941 | printf("stepi\n"); |
michael@0 | 942 | printf(" step one instruction (alias 'si')\n"); |
michael@0 | 943 | printf("print <register>\n"); |
michael@0 | 944 | printf(" print register content (alias 'p')\n"); |
michael@0 | 945 | printf(" use register name 'all' to print all registers\n"); |
michael@0 | 946 | printf(" add argument 'fp' to print register pair double values\n"); |
michael@0 | 947 | printf("flags\n"); |
michael@0 | 948 | printf(" print flags\n"); |
michael@0 | 949 | printf("stack [<words>]\n"); |
michael@0 | 950 | printf(" dump stack content, default dump 10 words)\n"); |
michael@0 | 951 | printf("mem <address> [<words>]\n"); |
michael@0 | 952 | printf(" dump memory content, default dump 10 words)\n"); |
michael@0 | 953 | printf("disasm [<instructions>]\n"); |
michael@0 | 954 | printf("disasm [<address/register>]\n"); |
michael@0 | 955 | printf("disasm [[<address/register>] <instructions>]\n"); |
michael@0 | 956 | printf(" disassemble code, default is 10 instructions\n"); |
michael@0 | 957 | printf(" from pc (alias 'di')\n"); |
michael@0 | 958 | printf("gdb\n"); |
michael@0 | 959 | printf(" enter gdb\n"); |
michael@0 | 960 | printf("break <address>\n"); |
michael@0 | 961 | printf(" set a break point on the address\n"); |
michael@0 | 962 | printf("del\n"); |
michael@0 | 963 | printf(" delete the breakpoint\n"); |
michael@0 | 964 | printf("stop feature:\n"); |
michael@0 | 965 | printf(" Description:\n"); |
michael@0 | 966 | printf(" Stops are debug instructions inserted by\n"); |
michael@0 | 967 | printf(" the Assembler::stop() function.\n"); |
michael@0 | 968 | printf(" When hitting a stop, the Simulator will\n"); |
michael@0 | 969 | printf(" stop and and give control to the ArmDebugger.\n"); |
michael@0 | 970 | printf(" The first %d stop codes are watched:\n", |
michael@0 | 971 | Simulator::kNumOfWatchedStops); |
michael@0 | 972 | printf(" - They can be enabled / disabled: the Simulator\n"); |
michael@0 | 973 | printf(" will / won't stop when hitting them.\n"); |
michael@0 | 974 | printf(" - The Simulator keeps track of how many times they \n"); |
michael@0 | 975 | printf(" are met. (See the info command.) Going over a\n"); |
michael@0 | 976 | printf(" disabled stop still increases its counter. \n"); |
michael@0 | 977 | printf(" Commands:\n"); |
michael@0 | 978 | printf(" stop info all/<code> : print infos about number <code>\n"); |
michael@0 | 979 | printf(" or all stop(s).\n"); |
michael@0 | 980 | printf(" stop enable/disable all/<code> : enables / disables\n"); |
michael@0 | 981 | printf(" all or number <code> stop(s)\n"); |
michael@0 | 982 | printf(" stop unstop\n"); |
michael@0 | 983 | printf(" ignore the stop instruction at the current location\n"); |
michael@0 | 984 | printf(" from now on\n"); |
michael@0 | 985 | } else { |
michael@0 | 986 | printf("Unknown command: %s\n", cmd); |
michael@0 | 987 | } |
michael@0 | 988 | } |
michael@0 | 989 | } |
michael@0 | 990 | |
michael@0 | 991 | // Add all the breakpoints back to stop execution and enter the debugger |
michael@0 | 992 | // shell when hit. |
michael@0 | 993 | redoBreakpoints(); |
michael@0 | 994 | |
michael@0 | 995 | #undef COMMAND_SIZE |
michael@0 | 996 | #undef ARG_SIZE |
michael@0 | 997 | |
michael@0 | 998 | #undef STR |
michael@0 | 999 | #undef XSTR |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | static bool |
michael@0 | 1003 | AllOnOnePage(uintptr_t start, int size) |
michael@0 | 1004 | { |
michael@0 | 1005 | intptr_t start_page = (start & ~CachePage::kPageMask); |
michael@0 | 1006 | intptr_t end_page = ((start + size) & ~CachePage::kPageMask); |
michael@0 | 1007 | return start_page == end_page; |
michael@0 | 1008 | } |
michael@0 | 1009 | |
michael@0 | 1010 | static CachePage * |
michael@0 | 1011 | GetCachePage(SimulatorRuntime::ICacheMap &i_cache, void *page) |
michael@0 | 1012 | { |
michael@0 | 1013 | MOZ_ASSERT(Simulator::ICacheCheckingEnabled); |
michael@0 | 1014 | |
michael@0 | 1015 | SimulatorRuntime::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); |
michael@0 | 1016 | if (p) |
michael@0 | 1017 | return p->value(); |
michael@0 | 1018 | |
michael@0 | 1019 | CachePage *new_page = js_new<CachePage>(); |
michael@0 | 1020 | if (!i_cache.add(p, page, new_page)) |
michael@0 | 1021 | return nullptr; |
michael@0 | 1022 | return new_page; |
michael@0 | 1023 | } |
michael@0 | 1024 | |
michael@0 | 1025 | // Flush from start up to and not including start + size. |
michael@0 | 1026 | static void |
michael@0 | 1027 | FlushOnePage(SimulatorRuntime::ICacheMap &i_cache, intptr_t start, int size) |
michael@0 | 1028 | { |
michael@0 | 1029 | MOZ_ASSERT(size <= CachePage::kPageSize); |
michael@0 | 1030 | MOZ_ASSERT(AllOnOnePage(start, size - 1)); |
michael@0 | 1031 | MOZ_ASSERT((start & CachePage::kLineMask) == 0); |
michael@0 | 1032 | MOZ_ASSERT((size & CachePage::kLineMask) == 0); |
michael@0 | 1033 | |
michael@0 | 1034 | void *page = reinterpret_cast<void*>(start & (~CachePage::kPageMask)); |
michael@0 | 1035 | int offset = (start & CachePage::kPageMask); |
michael@0 | 1036 | CachePage *cache_page = GetCachePage(i_cache, page); |
michael@0 | 1037 | char *valid_bytemap = cache_page->validityByte(offset); |
michael@0 | 1038 | memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); |
michael@0 | 1039 | } |
michael@0 | 1040 | |
michael@0 | 1041 | static void |
michael@0 | 1042 | FlushICache(SimulatorRuntime::ICacheMap &i_cache, void *start_addr, size_t size) |
michael@0 | 1043 | { |
michael@0 | 1044 | intptr_t start = reinterpret_cast<intptr_t>(start_addr); |
michael@0 | 1045 | int intra_line = (start & CachePage::kLineMask); |
michael@0 | 1046 | start -= intra_line; |
michael@0 | 1047 | size += intra_line; |
michael@0 | 1048 | size = ((size - 1) | CachePage::kLineMask) + 1; |
michael@0 | 1049 | int offset = (start & CachePage::kPageMask); |
michael@0 | 1050 | while (!AllOnOnePage(start, size - 1)) { |
michael@0 | 1051 | int bytes_to_flush = CachePage::kPageSize - offset; |
michael@0 | 1052 | FlushOnePage(i_cache, start, bytes_to_flush); |
michael@0 | 1053 | start += bytes_to_flush; |
michael@0 | 1054 | size -= bytes_to_flush; |
michael@0 | 1055 | MOZ_ASSERT((start & CachePage::kPageMask) == 0); |
michael@0 | 1056 | offset = 0; |
michael@0 | 1057 | } |
michael@0 | 1058 | if (size != 0) |
michael@0 | 1059 | FlushOnePage(i_cache, start, size); |
michael@0 | 1060 | } |
michael@0 | 1061 | |
michael@0 | 1062 | static void |
michael@0 | 1063 | CheckICache(SimulatorRuntime::ICacheMap &i_cache, SimInstruction *instr) |
michael@0 | 1064 | { |
michael@0 | 1065 | intptr_t address = reinterpret_cast<intptr_t>(instr); |
michael@0 | 1066 | void *page = reinterpret_cast<void*>(address & (~CachePage::kPageMask)); |
michael@0 | 1067 | void *line = reinterpret_cast<void*>(address & (~CachePage::kLineMask)); |
michael@0 | 1068 | int offset = (address & CachePage::kPageMask); |
michael@0 | 1069 | CachePage* cache_page = GetCachePage(i_cache, page); |
michael@0 | 1070 | char *cache_valid_byte = cache_page->validityByte(offset); |
michael@0 | 1071 | bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); |
michael@0 | 1072 | char *cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask); |
michael@0 | 1073 | if (cache_hit) { |
michael@0 | 1074 | // Check that the data in memory matches the contents of the I-cache. |
michael@0 | 1075 | MOZ_ASSERT(memcmp(reinterpret_cast<void*>(instr), |
michael@0 | 1076 | cache_page->cachedData(offset), |
michael@0 | 1077 | SimInstruction::kInstrSize) == 0); |
michael@0 | 1078 | } else { |
michael@0 | 1079 | // Cache miss. Load memory into the cache. |
michael@0 | 1080 | memcpy(cached_line, line, CachePage::kLineLength); |
michael@0 | 1081 | *cache_valid_byte = CachePage::LINE_VALID; |
michael@0 | 1082 | } |
michael@0 | 1083 | } |
michael@0 | 1084 | |
michael@0 | 1085 | HashNumber |
michael@0 | 1086 | SimulatorRuntime::ICacheHasher::hash(const Lookup &l) |
michael@0 | 1087 | { |
michael@0 | 1088 | return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2; |
michael@0 | 1089 | } |
michael@0 | 1090 | |
michael@0 | 1091 | bool |
michael@0 | 1092 | SimulatorRuntime::ICacheHasher::match(const Key &k, const Lookup &l) |
michael@0 | 1093 | { |
michael@0 | 1094 | MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0); |
michael@0 | 1095 | MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0); |
michael@0 | 1096 | return k == l; |
michael@0 | 1097 | } |
michael@0 | 1098 | |
michael@0 | 1099 | void |
michael@0 | 1100 | Simulator::setLastDebuggerInput(char *input) |
michael@0 | 1101 | { |
michael@0 | 1102 | js_free(lastDebuggerInput_); |
michael@0 | 1103 | lastDebuggerInput_ = input; |
michael@0 | 1104 | } |
michael@0 | 1105 | |
michael@0 | 1106 | void |
michael@0 | 1107 | Simulator::FlushICache(void *start_addr, size_t size) |
michael@0 | 1108 | { |
michael@0 | 1109 | IonSpewCont(IonSpew_CacheFlush, "[%p %zx]", start_addr, size); |
michael@0 | 1110 | if (!Simulator::ICacheCheckingEnabled) |
michael@0 | 1111 | return; |
michael@0 | 1112 | SimulatorRuntime *srt = Simulator::Current()->srt_; |
michael@0 | 1113 | AutoLockSimulatorRuntime alsr(srt); |
michael@0 | 1114 | js::jit::FlushICache(srt->icache(), start_addr, size); |
michael@0 | 1115 | } |
michael@0 | 1116 | |
michael@0 | 1117 | Simulator::~Simulator() |
michael@0 | 1118 | { |
michael@0 | 1119 | js_free(stack_); |
michael@0 | 1120 | } |
michael@0 | 1121 | |
michael@0 | 1122 | Simulator::Simulator(SimulatorRuntime *srt) |
michael@0 | 1123 | : srt_(srt) |
michael@0 | 1124 | { |
michael@0 | 1125 | // Set up simulator support first. Some of this information is needed to |
michael@0 | 1126 | // setup the architecture state. |
michael@0 | 1127 | |
michael@0 | 1128 | // Allocate 2MB for the stack. Note that we will only use 1MB, see also |
michael@0 | 1129 | // Simulator::stackLimit(). |
michael@0 | 1130 | static const size_t stackSize = 2 * 1024*1024; |
michael@0 | 1131 | stack_ = reinterpret_cast<char*>(js_malloc(stackSize)); |
michael@0 | 1132 | if (!stack_) { |
michael@0 | 1133 | MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__); |
michael@0 | 1134 | MOZ_CRASH(); |
michael@0 | 1135 | } |
michael@0 | 1136 | pc_modified_ = false; |
michael@0 | 1137 | icount_ = 0L; |
michael@0 | 1138 | resume_pc_ = 0; |
michael@0 | 1139 | break_pc_ = nullptr; |
michael@0 | 1140 | break_instr_ = 0; |
michael@0 | 1141 | |
michael@0 | 1142 | // Set up architecture state. |
michael@0 | 1143 | // All registers are initialized to zero to start with. |
michael@0 | 1144 | for (int i = 0; i < num_registers; i++) |
michael@0 | 1145 | registers_[i] = 0; |
michael@0 | 1146 | |
michael@0 | 1147 | n_flag_ = false; |
michael@0 | 1148 | z_flag_ = false; |
michael@0 | 1149 | c_flag_ = false; |
michael@0 | 1150 | v_flag_ = false; |
michael@0 | 1151 | |
michael@0 | 1152 | for (int i = 0; i < num_d_registers * 2; i++) |
michael@0 | 1153 | vfp_registers_[i] = 0; |
michael@0 | 1154 | |
michael@0 | 1155 | n_flag_FPSCR_ = false; |
michael@0 | 1156 | z_flag_FPSCR_ = false; |
michael@0 | 1157 | c_flag_FPSCR_ = false; |
michael@0 | 1158 | v_flag_FPSCR_ = false; |
michael@0 | 1159 | FPSCR_rounding_mode_ = SimRZ; |
michael@0 | 1160 | FPSCR_default_NaN_mode_ = true; |
michael@0 | 1161 | |
michael@0 | 1162 | inv_op_vfp_flag_ = false; |
michael@0 | 1163 | div_zero_vfp_flag_ = false; |
michael@0 | 1164 | overflow_vfp_flag_ = false; |
michael@0 | 1165 | underflow_vfp_flag_ = false; |
michael@0 | 1166 | inexact_vfp_flag_ = false; |
michael@0 | 1167 | |
michael@0 | 1168 | // The sp is initialized to point to the bottom (high address) of the |
michael@0 | 1169 | // allocated stack area. To be safe in potential stack underflows we leave |
michael@0 | 1170 | // some buffer below. |
michael@0 | 1171 | registers_[sp] = reinterpret_cast<int32_t>(stack_) + stackSize - 64; |
michael@0 | 1172 | |
michael@0 | 1173 | // The lr and pc are initialized to a known bad value that will cause an |
michael@0 | 1174 | // access violation if the simulator ever tries to execute it. |
michael@0 | 1175 | registers_[pc] = bad_lr; |
michael@0 | 1176 | registers_[lr] = bad_lr; |
michael@0 | 1177 | |
michael@0 | 1178 | lastDebuggerInput_ = nullptr; |
michael@0 | 1179 | } |
michael@0 | 1180 | |
michael@0 | 1181 | // When the generated code calls a VM function (masm.callWithABI) we need to |
michael@0 | 1182 | // call that function instead of trying to execute it with the simulator |
michael@0 | 1183 | // (because it's x86 code instead of arm code). We do that by redirecting the |
michael@0 | 1184 | // VM call to a svc (Supervisor Call) instruction that is handled by the |
michael@0 | 1185 | // simulator. We write the original destination of the jump just at a known |
michael@0 | 1186 | // offset from the svc instruction so the simulator knows what to call. |
michael@0 | 1187 | class Redirection |
michael@0 | 1188 | { |
michael@0 | 1189 | friend class SimulatorRuntime; |
michael@0 | 1190 | |
michael@0 | 1191 | Redirection(void *nativeFunction, ABIFunctionType type) |
michael@0 | 1192 | : nativeFunction_(nativeFunction), |
michael@0 | 1193 | swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected), |
michael@0 | 1194 | type_(type), |
michael@0 | 1195 | next_(nullptr) |
michael@0 | 1196 | { |
michael@0 | 1197 | Simulator *sim = Simulator::Current(); |
michael@0 | 1198 | SimulatorRuntime *srt = sim->srt_; |
michael@0 | 1199 | next_ = srt->redirection(); |
michael@0 | 1200 | if (Simulator::ICacheCheckingEnabled) |
michael@0 | 1201 | FlushICache(srt->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); |
michael@0 | 1202 | srt->setRedirection(this); |
michael@0 | 1203 | } |
michael@0 | 1204 | |
michael@0 | 1205 | public: |
michael@0 | 1206 | void *addressOfSwiInstruction() { return &swiInstruction_; } |
michael@0 | 1207 | void *nativeFunction() const { return nativeFunction_; } |
michael@0 | 1208 | ABIFunctionType type() const { return type_; } |
michael@0 | 1209 | |
michael@0 | 1210 | static Redirection *Get(void *nativeFunction, ABIFunctionType type) { |
michael@0 | 1211 | Simulator *sim = Simulator::Current(); |
michael@0 | 1212 | AutoLockSimulatorRuntime alsr(sim->srt_); |
michael@0 | 1213 | |
michael@0 | 1214 | Redirection *current = sim->srt_->redirection(); |
michael@0 | 1215 | for (; current != nullptr; current = current->next_) { |
michael@0 | 1216 | if (current->nativeFunction_ == nativeFunction) { |
michael@0 | 1217 | MOZ_ASSERT(current->type() == type); |
michael@0 | 1218 | return current; |
michael@0 | 1219 | } |
michael@0 | 1220 | } |
michael@0 | 1221 | |
michael@0 | 1222 | Redirection *redir = (Redirection *)js_malloc(sizeof(Redirection)); |
michael@0 | 1223 | if (!redir) { |
michael@0 | 1224 | MOZ_ReportAssertionFailure("[unhandlable oom] Simulator redirection", |
michael@0 | 1225 | __FILE__, __LINE__); |
michael@0 | 1226 | MOZ_CRASH(); |
michael@0 | 1227 | } |
michael@0 | 1228 | new(redir) Redirection(nativeFunction, type); |
michael@0 | 1229 | return redir; |
michael@0 | 1230 | } |
michael@0 | 1231 | |
michael@0 | 1232 | static Redirection *FromSwiInstruction(SimInstruction *swiInstruction) { |
michael@0 | 1233 | uint8_t *addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction); |
michael@0 | 1234 | uint8_t *addrOfRedirection = addrOfSwi - offsetof(Redirection, swiInstruction_); |
michael@0 | 1235 | return reinterpret_cast<Redirection*>(addrOfRedirection); |
michael@0 | 1236 | } |
michael@0 | 1237 | |
michael@0 | 1238 | private: |
michael@0 | 1239 | void *nativeFunction_; |
michael@0 | 1240 | uint32_t swiInstruction_; |
michael@0 | 1241 | ABIFunctionType type_; |
michael@0 | 1242 | Redirection *next_; |
michael@0 | 1243 | }; |
michael@0 | 1244 | |
michael@0 | 1245 | /* static */ void * |
michael@0 | 1246 | Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) |
michael@0 | 1247 | { |
michael@0 | 1248 | Redirection *redirection = Redirection::Get(nativeFunction, type); |
michael@0 | 1249 | return redirection->addressOfSwiInstruction(); |
michael@0 | 1250 | } |
michael@0 | 1251 | |
michael@0 | 1252 | SimulatorRuntime::~SimulatorRuntime() |
michael@0 | 1253 | { |
michael@0 | 1254 | Redirection *r = redirection_; |
michael@0 | 1255 | while (r) { |
michael@0 | 1256 | Redirection *next = r->next_; |
michael@0 | 1257 | js_delete(r); |
michael@0 | 1258 | r = next; |
michael@0 | 1259 | } |
michael@0 | 1260 | #ifdef JS_THREADSAFE |
michael@0 | 1261 | if (lock_) |
michael@0 | 1262 | PR_DestroyLock(lock_); |
michael@0 | 1263 | #endif |
michael@0 | 1264 | } |
michael@0 | 1265 | |
michael@0 | 1266 | // Sets the register in the architecture state. It will also deal with updating |
michael@0 | 1267 | // Simulator internal state for special registers such as PC. |
michael@0 | 1268 | void |
michael@0 | 1269 | Simulator::set_register(int reg, int32_t value) |
michael@0 | 1270 | { |
michael@0 | 1271 | MOZ_ASSERT(reg >= 0 && reg < num_registers); |
michael@0 | 1272 | if (reg == pc) |
michael@0 | 1273 | pc_modified_ = true; |
michael@0 | 1274 | registers_[reg] = value; |
michael@0 | 1275 | } |
michael@0 | 1276 | |
michael@0 | 1277 | // Get the register from the architecture state. This function does handle |
michael@0 | 1278 | // the special case of accessing the PC register. |
michael@0 | 1279 | int32_t |
michael@0 | 1280 | Simulator::get_register(int reg) const |
michael@0 | 1281 | { |
michael@0 | 1282 | MOZ_ASSERT(reg >= 0 && reg < num_registers); |
michael@0 | 1283 | // Work around GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 |
michael@0 | 1284 | if (reg >= num_registers) return 0; |
michael@0 | 1285 | return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0); |
michael@0 | 1286 | } |
michael@0 | 1287 | |
michael@0 | 1288 | double |
michael@0 | 1289 | Simulator::get_double_from_register_pair(int reg) |
michael@0 | 1290 | { |
michael@0 | 1291 | MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0); |
michael@0 | 1292 | |
michael@0 | 1293 | // Read the bits from the unsigned integer register_[] array |
michael@0 | 1294 | // into the double precision floating point value and return it. |
michael@0 | 1295 | double dm_val = 0.0; |
michael@0 | 1296 | char buffer[2 * sizeof(vfp_registers_[0])]; |
michael@0 | 1297 | memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); |
michael@0 | 1298 | memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); |
michael@0 | 1299 | return dm_val; |
michael@0 | 1300 | } |
michael@0 | 1301 | |
michael@0 | 1302 | void |
michael@0 | 1303 | Simulator::set_register_pair_from_double(int reg, double *value) |
michael@0 | 1304 | { |
michael@0 | 1305 | MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0); |
michael@0 | 1306 | memcpy(registers_ + reg, value, sizeof(*value)); |
michael@0 | 1307 | } |
michael@0 | 1308 | |
michael@0 | 1309 | void |
michael@0 | 1310 | Simulator::set_dw_register(int dreg, const int *dbl) |
michael@0 | 1311 | { |
michael@0 | 1312 | MOZ_ASSERT(dreg >= 0 && dreg < num_d_registers); |
michael@0 | 1313 | registers_[dreg] = dbl[0]; |
michael@0 | 1314 | registers_[dreg + 1] = dbl[1]; |
michael@0 | 1315 | } |
michael@0 | 1316 | |
michael@0 | 1317 | void |
michael@0 | 1318 | Simulator::get_d_register(int dreg, uint64_t *value) |
michael@0 | 1319 | { |
michael@0 | 1320 | MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
michael@0 | 1321 | memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); |
michael@0 | 1322 | } |
michael@0 | 1323 | |
michael@0 | 1324 | void |
michael@0 | 1325 | Simulator::set_d_register(int dreg, const uint64_t *value) |
michael@0 | 1326 | { |
michael@0 | 1327 | MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
michael@0 | 1328 | memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | void |
michael@0 | 1332 | Simulator::get_d_register(int dreg, uint32_t *value) |
michael@0 | 1333 | { |
michael@0 | 1334 | MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
michael@0 | 1335 | memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); |
michael@0 | 1336 | } |
michael@0 | 1337 | |
michael@0 | 1338 | void |
michael@0 | 1339 | Simulator::set_d_register(int dreg, const uint32_t *value) |
michael@0 | 1340 | { |
michael@0 | 1341 | MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
michael@0 | 1342 | memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); |
michael@0 | 1343 | } |
michael@0 | 1344 | |
michael@0 | 1345 | void |
michael@0 | 1346 | Simulator::get_q_register(int qreg, uint64_t *value) |
michael@0 | 1347 | { |
michael@0 | 1348 | MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); |
michael@0 | 1349 | memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 2); |
michael@0 | 1350 | } |
michael@0 | 1351 | |
michael@0 | 1352 | void |
michael@0 | 1353 | Simulator::set_q_register(int qreg, const uint64_t *value) |
michael@0 | 1354 | { |
michael@0 | 1355 | MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); |
michael@0 | 1356 | memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 2); |
michael@0 | 1357 | } |
michael@0 | 1358 | |
michael@0 | 1359 | void |
michael@0 | 1360 | Simulator::get_q_register(int qreg, uint32_t *value) |
michael@0 | 1361 | { |
michael@0 | 1362 | MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); |
michael@0 | 1363 | memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 4); |
michael@0 | 1364 | } |
michael@0 | 1365 | |
michael@0 | 1366 | void |
michael@0 | 1367 | Simulator::set_q_register(int qreg, const uint32_t *value) |
michael@0 | 1368 | { |
michael@0 | 1369 | MOZ_ASSERT((qreg >= 0) && (qreg < num_q_registers)); |
michael@0 | 1370 | memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 4); |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | void |
michael@0 | 1374 | Simulator::set_pc(int32_t value) |
michael@0 | 1375 | { |
michael@0 | 1376 | pc_modified_ = true; |
michael@0 | 1377 | registers_[pc] = value; |
michael@0 | 1378 | } |
michael@0 | 1379 | |
michael@0 | 1380 | bool |
michael@0 | 1381 | Simulator::has_bad_pc() const |
michael@0 | 1382 | { |
michael@0 | 1383 | return registers_[pc] == bad_lr || registers_[pc] == end_sim_pc; |
michael@0 | 1384 | } |
michael@0 | 1385 | |
michael@0 | 1386 | // Raw access to the PC register without the special adjustment when reading. |
michael@0 | 1387 | int32_t |
michael@0 | 1388 | Simulator::get_pc() const |
michael@0 | 1389 | { |
michael@0 | 1390 | return registers_[pc]; |
michael@0 | 1391 | } |
michael@0 | 1392 | |
michael@0 | 1393 | void |
michael@0 | 1394 | Simulator::set_s_register(int sreg, unsigned int value) |
michael@0 | 1395 | { |
michael@0 | 1396 | MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers); |
michael@0 | 1397 | vfp_registers_[sreg] = value; |
michael@0 | 1398 | } |
michael@0 | 1399 | |
michael@0 | 1400 | unsigned |
michael@0 | 1401 | Simulator::get_s_register(int sreg) const |
michael@0 | 1402 | { |
michael@0 | 1403 | MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers); |
michael@0 | 1404 | return vfp_registers_[sreg]; |
michael@0 | 1405 | } |
michael@0 | 1406 | |
michael@0 | 1407 | template<class InputType, int register_size> |
michael@0 | 1408 | void |
michael@0 | 1409 | Simulator::setVFPRegister(int reg_index, const InputType &value) |
michael@0 | 1410 | { |
michael@0 | 1411 | MOZ_ASSERT(reg_index >= 0); |
michael@0 | 1412 | MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers); |
michael@0 | 1413 | MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::Total)); |
michael@0 | 1414 | |
michael@0 | 1415 | char buffer[register_size * sizeof(vfp_registers_[0])]; |
michael@0 | 1416 | memcpy(buffer, &value, register_size * sizeof(vfp_registers_[0])); |
michael@0 | 1417 | memcpy(&vfp_registers_[reg_index * register_size], buffer, |
michael@0 | 1418 | register_size * sizeof(vfp_registers_[0])); |
michael@0 | 1419 | } |
michael@0 | 1420 | |
michael@0 | 1421 | template<class ReturnType, int register_size> |
michael@0 | 1422 | ReturnType Simulator::getFromVFPRegister(int reg_index) |
michael@0 | 1423 | { |
michael@0 | 1424 | MOZ_ASSERT(reg_index >= 0); |
michael@0 | 1425 | MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers); |
michael@0 | 1426 | MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::Total)); |
michael@0 | 1427 | |
michael@0 | 1428 | ReturnType value = 0; |
michael@0 | 1429 | char buffer[register_size * sizeof(vfp_registers_[0])]; |
michael@0 | 1430 | memcpy(buffer, &vfp_registers_[register_size * reg_index], |
michael@0 | 1431 | register_size * sizeof(vfp_registers_[0])); |
michael@0 | 1432 | memcpy(&value, buffer, register_size * sizeof(vfp_registers_[0])); |
michael@0 | 1433 | return value; |
michael@0 | 1434 | } |
michael@0 | 1435 | |
michael@0 | 1436 | void |
michael@0 | 1437 | Simulator::getFpArgs(double *x, double *y, int32_t *z) |
michael@0 | 1438 | { |
michael@0 | 1439 | if (useHardFpABI()) { |
michael@0 | 1440 | *x = get_double_from_d_register(0); |
michael@0 | 1441 | *y = get_double_from_d_register(1); |
michael@0 | 1442 | *z = get_register(0); |
michael@0 | 1443 | } else { |
michael@0 | 1444 | *x = get_double_from_register_pair(0); |
michael@0 | 1445 | *y = get_double_from_register_pair(2); |
michael@0 | 1446 | *z = get_register(2); |
michael@0 | 1447 | } |
michael@0 | 1448 | } |
michael@0 | 1449 | |
michael@0 | 1450 | void |
michael@0 | 1451 | Simulator::setCallResultDouble(double result) |
michael@0 | 1452 | { |
michael@0 | 1453 | // The return value is either in r0/r1 or d0. |
michael@0 | 1454 | if (useHardFpABI()) { |
michael@0 | 1455 | char buffer[2 * sizeof(vfp_registers_[0])]; |
michael@0 | 1456 | memcpy(buffer, &result, sizeof(buffer)); |
michael@0 | 1457 | // Copy result to d0. |
michael@0 | 1458 | memcpy(vfp_registers_, buffer, sizeof(buffer)); |
michael@0 | 1459 | } else { |
michael@0 | 1460 | char buffer[2 * sizeof(registers_[0])]; |
michael@0 | 1461 | memcpy(buffer, &result, sizeof(buffer)); |
michael@0 | 1462 | // Copy result to r0 and r1. |
michael@0 | 1463 | memcpy(registers_, buffer, sizeof(buffer)); |
michael@0 | 1464 | } |
michael@0 | 1465 | } |
michael@0 | 1466 | |
michael@0 | 1467 | void |
michael@0 | 1468 | Simulator::setCallResultFloat(float result) |
michael@0 | 1469 | { |
michael@0 | 1470 | if (useHardFpABI()) { |
michael@0 | 1471 | char buffer[sizeof(registers_[0])]; |
michael@0 | 1472 | memcpy(buffer, &result, sizeof(buffer)); |
michael@0 | 1473 | // Copy result to s0. |
michael@0 | 1474 | memcpy(vfp_registers_, buffer, sizeof(buffer)); |
michael@0 | 1475 | } else { |
michael@0 | 1476 | char buffer[sizeof(registers_[0])]; |
michael@0 | 1477 | memcpy(buffer, &result, sizeof(buffer)); |
michael@0 | 1478 | // Copy result to r0. |
michael@0 | 1479 | memcpy(registers_, buffer, sizeof(buffer)); |
michael@0 | 1480 | } |
michael@0 | 1481 | } |
michael@0 | 1482 | |
michael@0 | 1483 | void |
michael@0 | 1484 | Simulator::setCallResult(int64_t res) |
michael@0 | 1485 | { |
michael@0 | 1486 | set_register(r0, static_cast<int32_t>(res)); |
michael@0 | 1487 | set_register(r1, static_cast<int32_t>(res >> 32)); |
michael@0 | 1488 | } |
michael@0 | 1489 | |
michael@0 | 1490 | int |
michael@0 | 1491 | Simulator::readW(int32_t addr, SimInstruction *instr) |
michael@0 | 1492 | { |
michael@0 | 1493 | // YARR emits unaligned loads, so we don't check for them here like the |
michael@0 | 1494 | // other methods below. |
michael@0 | 1495 | intptr_t *ptr = reinterpret_cast<intptr_t*>(addr); |
michael@0 | 1496 | return *ptr; |
michael@0 | 1497 | } |
michael@0 | 1498 | |
michael@0 | 1499 | void |
michael@0 | 1500 | Simulator::writeW(int32_t addr, int value, SimInstruction *instr) |
michael@0 | 1501 | { |
michael@0 | 1502 | if ((addr & 3) == 0) { |
michael@0 | 1503 | intptr_t *ptr = reinterpret_cast<intptr_t*>(addr); |
michael@0 | 1504 | *ptr = value; |
michael@0 | 1505 | } else { |
michael@0 | 1506 | printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr); |
michael@0 | 1507 | MOZ_CRASH(); |
michael@0 | 1508 | } |
michael@0 | 1509 | } |
michael@0 | 1510 | |
michael@0 | 1511 | uint16_t |
michael@0 | 1512 | Simulator::readHU(int32_t addr, SimInstruction *instr) |
michael@0 | 1513 | { |
michael@0 | 1514 | if ((addr & 1) == 0) { |
michael@0 | 1515 | uint16_t *ptr = reinterpret_cast<uint16_t*>(addr); |
michael@0 | 1516 | return *ptr; |
michael@0 | 1517 | } |
michael@0 | 1518 | printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr); |
michael@0 | 1519 | MOZ_CRASH(); |
michael@0 | 1520 | return 0; |
michael@0 | 1521 | } |
michael@0 | 1522 | |
michael@0 | 1523 | int16_t |
michael@0 | 1524 | Simulator::readH(int32_t addr, SimInstruction *instr) |
michael@0 | 1525 | { |
michael@0 | 1526 | if ((addr & 1) == 0) { |
michael@0 | 1527 | int16_t *ptr = reinterpret_cast<int16_t*>(addr); |
michael@0 | 1528 | return *ptr; |
michael@0 | 1529 | } |
michael@0 | 1530 | printf("Unaligned signed halfword read at 0x%08x\n", addr); |
michael@0 | 1531 | MOZ_CRASH(); |
michael@0 | 1532 | return 0; |
michael@0 | 1533 | } |
michael@0 | 1534 | |
michael@0 | 1535 | void |
michael@0 | 1536 | Simulator::writeH(int32_t addr, uint16_t value, SimInstruction *instr) |
michael@0 | 1537 | { |
michael@0 | 1538 | if ((addr & 1) == 0) { |
michael@0 | 1539 | uint16_t *ptr = reinterpret_cast<uint16_t*>(addr); |
michael@0 | 1540 | *ptr = value; |
michael@0 | 1541 | } else { |
michael@0 | 1542 | printf("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr); |
michael@0 | 1543 | MOZ_CRASH(); |
michael@0 | 1544 | } |
michael@0 | 1545 | } |
michael@0 | 1546 | |
michael@0 | 1547 | void |
michael@0 | 1548 | Simulator::writeH(int32_t addr, int16_t value, SimInstruction *instr) |
michael@0 | 1549 | { |
michael@0 | 1550 | if ((addr & 1) == 0) { |
michael@0 | 1551 | int16_t *ptr = reinterpret_cast<int16_t*>(addr); |
michael@0 | 1552 | *ptr = value; |
michael@0 | 1553 | } else { |
michael@0 | 1554 | printf("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr); |
michael@0 | 1555 | MOZ_CRASH(); |
michael@0 | 1556 | } |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | uint8_t |
michael@0 | 1560 | Simulator::readBU(int32_t addr) |
michael@0 | 1561 | { |
michael@0 | 1562 | uint8_t *ptr = reinterpret_cast<uint8_t*>(addr); |
michael@0 | 1563 | return *ptr; |
michael@0 | 1564 | } |
michael@0 | 1565 | |
michael@0 | 1566 | int8_t |
michael@0 | 1567 | Simulator::readB(int32_t addr) |
michael@0 | 1568 | { |
michael@0 | 1569 | int8_t *ptr = reinterpret_cast<int8_t*>(addr); |
michael@0 | 1570 | return *ptr; |
michael@0 | 1571 | } |
michael@0 | 1572 | |
michael@0 | 1573 | void |
michael@0 | 1574 | Simulator::writeB(int32_t addr, uint8_t value) |
michael@0 | 1575 | { |
michael@0 | 1576 | uint8_t *ptr = reinterpret_cast<uint8_t*>(addr); |
michael@0 | 1577 | *ptr = value; |
michael@0 | 1578 | } |
michael@0 | 1579 | |
michael@0 | 1580 | void |
michael@0 | 1581 | Simulator::writeB(int32_t addr, int8_t value) |
michael@0 | 1582 | { |
michael@0 | 1583 | int8_t *ptr = reinterpret_cast<int8_t*>(addr); |
michael@0 | 1584 | *ptr = value; |
michael@0 | 1585 | } |
michael@0 | 1586 | |
michael@0 | 1587 | int32_t * |
michael@0 | 1588 | Simulator::readDW(int32_t addr) |
michael@0 | 1589 | { |
michael@0 | 1590 | if ((addr & 3) == 0) { |
michael@0 | 1591 | int32_t *ptr = reinterpret_cast<int32_t*>(addr); |
michael@0 | 1592 | return ptr; |
michael@0 | 1593 | } |
michael@0 | 1594 | printf("Unaligned read at 0x%08x\n", addr); |
michael@0 | 1595 | MOZ_CRASH(); |
michael@0 | 1596 | return 0; |
michael@0 | 1597 | } |
michael@0 | 1598 | |
michael@0 | 1599 | void |
michael@0 | 1600 | Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2) |
michael@0 | 1601 | { |
michael@0 | 1602 | if ((addr & 3) == 0) { |
michael@0 | 1603 | int32_t *ptr = reinterpret_cast<int32_t*>(addr); |
michael@0 | 1604 | *ptr++ = value1; |
michael@0 | 1605 | *ptr = value2; |
michael@0 | 1606 | } else { |
michael@0 | 1607 | printf("Unaligned write at 0x%08x\n", addr); |
michael@0 | 1608 | MOZ_CRASH(); |
michael@0 | 1609 | } |
michael@0 | 1610 | } |
michael@0 | 1611 | |
michael@0 | 1612 | uintptr_t |
michael@0 | 1613 | Simulator::stackLimit() const |
michael@0 | 1614 | { |
michael@0 | 1615 | // Leave a safety margin of 1MB to prevent overrunning the stack when |
michael@0 | 1616 | // pushing values (total stack size is 2MB). |
michael@0 | 1617 | return reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024; |
michael@0 | 1618 | } |
michael@0 | 1619 | |
michael@0 | 1620 | bool |
michael@0 | 1621 | Simulator::overRecursed(uintptr_t newsp) const |
michael@0 | 1622 | { |
michael@0 | 1623 | if (newsp == 0) |
michael@0 | 1624 | newsp = get_register(sp); |
michael@0 | 1625 | return newsp <= stackLimit(); |
michael@0 | 1626 | } |
michael@0 | 1627 | |
michael@0 | 1628 | bool |
michael@0 | 1629 | Simulator::overRecursedWithExtra(uint32_t extra) const |
michael@0 | 1630 | { |
michael@0 | 1631 | uintptr_t newsp = get_register(sp) - extra; |
michael@0 | 1632 | return newsp <= stackLimit(); |
michael@0 | 1633 | } |
michael@0 | 1634 | |
michael@0 | 1635 | // Checks if the current instruction should be executed based on its |
michael@0 | 1636 | // condition bits. |
michael@0 | 1637 | bool |
michael@0 | 1638 | Simulator::conditionallyExecute(SimInstruction *instr) |
michael@0 | 1639 | { |
michael@0 | 1640 | switch (instr->conditionField()) { |
michael@0 | 1641 | case Assembler::EQ: return z_flag_; |
michael@0 | 1642 | case Assembler::NE: return !z_flag_; |
michael@0 | 1643 | case Assembler::CS: return c_flag_; |
michael@0 | 1644 | case Assembler::CC: return !c_flag_; |
michael@0 | 1645 | case Assembler::MI: return n_flag_; |
michael@0 | 1646 | case Assembler::PL: return !n_flag_; |
michael@0 | 1647 | case Assembler::VS: return v_flag_; |
michael@0 | 1648 | case Assembler::VC: return !v_flag_; |
michael@0 | 1649 | case Assembler::HI: return c_flag_ && !z_flag_; |
michael@0 | 1650 | case Assembler::LS: return !c_flag_ || z_flag_; |
michael@0 | 1651 | case Assembler::GE: return n_flag_ == v_flag_; |
michael@0 | 1652 | case Assembler::LT: return n_flag_ != v_flag_; |
michael@0 | 1653 | case Assembler::GT: return !z_flag_ && (n_flag_ == v_flag_); |
michael@0 | 1654 | case Assembler::LE: return z_flag_ || (n_flag_ != v_flag_); |
michael@0 | 1655 | case Assembler::AL: return true; |
michael@0 | 1656 | default: MOZ_ASSUME_UNREACHABLE(); |
michael@0 | 1657 | } |
michael@0 | 1658 | return false; |
michael@0 | 1659 | } |
michael@0 | 1660 | |
michael@0 | 1661 | // Calculate and set the Negative and Zero flags. |
michael@0 | 1662 | void |
michael@0 | 1663 | Simulator::setNZFlags(int32_t val) |
michael@0 | 1664 | { |
michael@0 | 1665 | n_flag_ = (val < 0); |
michael@0 | 1666 | z_flag_ = (val == 0); |
michael@0 | 1667 | } |
michael@0 | 1668 | |
michael@0 | 1669 | // Set the Carry flag. |
michael@0 | 1670 | void |
michael@0 | 1671 | Simulator::setCFlag(bool val) |
michael@0 | 1672 | { |
michael@0 | 1673 | c_flag_ = val; |
michael@0 | 1674 | } |
michael@0 | 1675 | |
michael@0 | 1676 | // Set the oVerflow flag. |
michael@0 | 1677 | void |
michael@0 | 1678 | Simulator::setVFlag(bool val) |
michael@0 | 1679 | { |
michael@0 | 1680 | v_flag_ = val; |
michael@0 | 1681 | } |
michael@0 | 1682 | |
michael@0 | 1683 | // Calculate C flag value for additions. |
michael@0 | 1684 | bool |
michael@0 | 1685 | Simulator::carryFrom(int32_t left, int32_t right, int32_t carry) |
michael@0 | 1686 | { |
michael@0 | 1687 | uint32_t uleft = static_cast<uint32_t>(left); |
michael@0 | 1688 | uint32_t uright = static_cast<uint32_t>(right); |
michael@0 | 1689 | uint32_t urest = 0xffffffffU - uleft; |
michael@0 | 1690 | return (uright > urest) || |
michael@0 | 1691 | (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); |
michael@0 | 1692 | } |
michael@0 | 1693 | |
michael@0 | 1694 | // Calculate C flag value for subtractions. |
michael@0 | 1695 | bool |
michael@0 | 1696 | Simulator::borrowFrom(int32_t left, int32_t right) |
michael@0 | 1697 | { |
michael@0 | 1698 | uint32_t uleft = static_cast<uint32_t>(left); |
michael@0 | 1699 | uint32_t uright = static_cast<uint32_t>(right); |
michael@0 | 1700 | return (uright > uleft); |
michael@0 | 1701 | } |
michael@0 | 1702 | |
michael@0 | 1703 | // Calculate V flag value for additions and subtractions. |
michael@0 | 1704 | bool |
michael@0 | 1705 | Simulator::overflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) |
michael@0 | 1706 | { |
michael@0 | 1707 | bool overflow; |
michael@0 | 1708 | if (addition) { |
michael@0 | 1709 | // operands have the same sign |
michael@0 | 1710 | overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) |
michael@0 | 1711 | // and operands and result have different sign |
michael@0 | 1712 | && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
michael@0 | 1713 | } else { |
michael@0 | 1714 | // operands have different signs |
michael@0 | 1715 | overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) |
michael@0 | 1716 | // and first operand and result have different signs |
michael@0 | 1717 | && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
michael@0 | 1718 | } |
michael@0 | 1719 | return overflow; |
michael@0 | 1720 | } |
michael@0 | 1721 | |
michael@0 | 1722 | // Support for VFP comparisons. |
michael@0 | 1723 | void |
michael@0 | 1724 | Simulator::compute_FPSCR_Flags(double val1, double val2) |
michael@0 | 1725 | { |
michael@0 | 1726 | if (mozilla::IsNaN(val1) || mozilla::IsNaN(val2)) { |
michael@0 | 1727 | n_flag_FPSCR_ = false; |
michael@0 | 1728 | z_flag_FPSCR_ = false; |
michael@0 | 1729 | c_flag_FPSCR_ = true; |
michael@0 | 1730 | v_flag_FPSCR_ = true; |
michael@0 | 1731 | // All non-NaN cases. |
michael@0 | 1732 | } else if (val1 == val2) { |
michael@0 | 1733 | n_flag_FPSCR_ = false; |
michael@0 | 1734 | z_flag_FPSCR_ = true; |
michael@0 | 1735 | c_flag_FPSCR_ = true; |
michael@0 | 1736 | v_flag_FPSCR_ = false; |
michael@0 | 1737 | } else if (val1 < val2) { |
michael@0 | 1738 | n_flag_FPSCR_ = true; |
michael@0 | 1739 | z_flag_FPSCR_ = false; |
michael@0 | 1740 | c_flag_FPSCR_ = false; |
michael@0 | 1741 | v_flag_FPSCR_ = false; |
michael@0 | 1742 | } else { |
michael@0 | 1743 | // Case when (val1 > val2). |
michael@0 | 1744 | n_flag_FPSCR_ = false; |
michael@0 | 1745 | z_flag_FPSCR_ = false; |
michael@0 | 1746 | c_flag_FPSCR_ = true; |
michael@0 | 1747 | v_flag_FPSCR_ = false; |
michael@0 | 1748 | } |
michael@0 | 1749 | } |
michael@0 | 1750 | |
michael@0 | 1751 | void |
michael@0 | 1752 | Simulator::copy_FPSCR_to_APSR() |
michael@0 | 1753 | { |
michael@0 | 1754 | n_flag_ = n_flag_FPSCR_; |
michael@0 | 1755 | z_flag_ = z_flag_FPSCR_; |
michael@0 | 1756 | c_flag_ = c_flag_FPSCR_; |
michael@0 | 1757 | v_flag_ = v_flag_FPSCR_; |
michael@0 | 1758 | } |
michael@0 | 1759 | |
michael@0 | 1760 | // Addressing Mode 1 - Data-processing operands: |
michael@0 | 1761 | // Get the value based on the shifter_operand with register. |
michael@0 | 1762 | int32_t |
michael@0 | 1763 | Simulator::getShiftRm(SimInstruction *instr, bool *carry_out) |
michael@0 | 1764 | { |
michael@0 | 1765 | ShiftType shift = instr->shifttypeValue(); |
michael@0 | 1766 | int shift_amount = instr->shiftAmountValue(); |
michael@0 | 1767 | int32_t result = get_register(instr->rmValue()); |
michael@0 | 1768 | if (instr->bit(4) == 0) { |
michael@0 | 1769 | // By immediate. |
michael@0 | 1770 | if (shift == ROR && shift_amount == 0) { |
michael@0 | 1771 | MOZ_ASSUME_UNREACHABLE("NYI"); |
michael@0 | 1772 | return result; |
michael@0 | 1773 | } |
michael@0 | 1774 | if ((shift == LSR || shift == ASR) && shift_amount == 0) |
michael@0 | 1775 | shift_amount = 32; |
michael@0 | 1776 | switch (shift) { |
michael@0 | 1777 | case ASR: { |
michael@0 | 1778 | if (shift_amount == 0) { |
michael@0 | 1779 | if (result < 0) { |
michael@0 | 1780 | result = 0xffffffff; |
michael@0 | 1781 | *carry_out = true; |
michael@0 | 1782 | } else { |
michael@0 | 1783 | result = 0; |
michael@0 | 1784 | *carry_out = false; |
michael@0 | 1785 | } |
michael@0 | 1786 | } else { |
michael@0 | 1787 | result >>= (shift_amount - 1); |
michael@0 | 1788 | *carry_out = (result & 1) == 1; |
michael@0 | 1789 | result >>= 1; |
michael@0 | 1790 | } |
michael@0 | 1791 | break; |
michael@0 | 1792 | } |
michael@0 | 1793 | |
michael@0 | 1794 | case LSL: { |
michael@0 | 1795 | if (shift_amount == 0) { |
michael@0 | 1796 | *carry_out = c_flag_; |
michael@0 | 1797 | } else { |
michael@0 | 1798 | result <<= (shift_amount - 1); |
michael@0 | 1799 | *carry_out = (result < 0); |
michael@0 | 1800 | result <<= 1; |
michael@0 | 1801 | } |
michael@0 | 1802 | break; |
michael@0 | 1803 | } |
michael@0 | 1804 | |
michael@0 | 1805 | case LSR: { |
michael@0 | 1806 | if (shift_amount == 0) { |
michael@0 | 1807 | result = 0; |
michael@0 | 1808 | *carry_out = c_flag_; |
michael@0 | 1809 | } else { |
michael@0 | 1810 | uint32_t uresult = static_cast<uint32_t>(result); |
michael@0 | 1811 | uresult >>= (shift_amount - 1); |
michael@0 | 1812 | *carry_out = (uresult & 1) == 1; |
michael@0 | 1813 | uresult >>= 1; |
michael@0 | 1814 | result = static_cast<int32_t>(uresult); |
michael@0 | 1815 | } |
michael@0 | 1816 | break; |
michael@0 | 1817 | } |
michael@0 | 1818 | |
michael@0 | 1819 | case ROR: { |
michael@0 | 1820 | if (shift_amount == 0) { |
michael@0 | 1821 | *carry_out = c_flag_; |
michael@0 | 1822 | } else { |
michael@0 | 1823 | uint32_t left = static_cast<uint32_t>(result) >> shift_amount; |
michael@0 | 1824 | uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount); |
michael@0 | 1825 | result = right | left; |
michael@0 | 1826 | *carry_out = (static_cast<uint32_t>(result) >> 31) != 0; |
michael@0 | 1827 | } |
michael@0 | 1828 | break; |
michael@0 | 1829 | } |
michael@0 | 1830 | |
michael@0 | 1831 | default: |
michael@0 | 1832 | MOZ_ASSUME_UNREACHABLE(); |
michael@0 | 1833 | break; |
michael@0 | 1834 | } |
michael@0 | 1835 | } else { |
michael@0 | 1836 | // By register. |
michael@0 | 1837 | int rs = instr->rsValue(); |
michael@0 | 1838 | shift_amount = get_register(rs) &0xff; |
michael@0 | 1839 | switch (shift) { |
michael@0 | 1840 | case ASR: { |
michael@0 | 1841 | if (shift_amount == 0) { |
michael@0 | 1842 | *carry_out = c_flag_; |
michael@0 | 1843 | } else if (shift_amount < 32) { |
michael@0 | 1844 | result >>= (shift_amount - 1); |
michael@0 | 1845 | *carry_out = (result & 1) == 1; |
michael@0 | 1846 | result >>= 1; |
michael@0 | 1847 | } else { |
michael@0 | 1848 | MOZ_ASSERT(shift_amount >= 32); |
michael@0 | 1849 | if (result < 0) { |
michael@0 | 1850 | *carry_out = true; |
michael@0 | 1851 | result = 0xffffffff; |
michael@0 | 1852 | } else { |
michael@0 | 1853 | *carry_out = false; |
michael@0 | 1854 | result = 0; |
michael@0 | 1855 | } |
michael@0 | 1856 | } |
michael@0 | 1857 | break; |
michael@0 | 1858 | } |
michael@0 | 1859 | |
michael@0 | 1860 | case LSL: { |
michael@0 | 1861 | if (shift_amount == 0) { |
michael@0 | 1862 | *carry_out = c_flag_; |
michael@0 | 1863 | } else if (shift_amount < 32) { |
michael@0 | 1864 | result <<= (shift_amount - 1); |
michael@0 | 1865 | *carry_out = (result < 0); |
michael@0 | 1866 | result <<= 1; |
michael@0 | 1867 | } else if (shift_amount == 32) { |
michael@0 | 1868 | *carry_out = (result & 1) == 1; |
michael@0 | 1869 | result = 0; |
michael@0 | 1870 | } else { |
michael@0 | 1871 | MOZ_ASSERT(shift_amount > 32); |
michael@0 | 1872 | *carry_out = false; |
michael@0 | 1873 | result = 0; |
michael@0 | 1874 | } |
michael@0 | 1875 | break; |
michael@0 | 1876 | } |
michael@0 | 1877 | |
michael@0 | 1878 | case LSR: { |
michael@0 | 1879 | if (shift_amount == 0) { |
michael@0 | 1880 | *carry_out = c_flag_; |
michael@0 | 1881 | } else if (shift_amount < 32) { |
michael@0 | 1882 | uint32_t uresult = static_cast<uint32_t>(result); |
michael@0 | 1883 | uresult >>= (shift_amount - 1); |
michael@0 | 1884 | *carry_out = (uresult & 1) == 1; |
michael@0 | 1885 | uresult >>= 1; |
michael@0 | 1886 | result = static_cast<int32_t>(uresult); |
michael@0 | 1887 | } else if (shift_amount == 32) { |
michael@0 | 1888 | *carry_out = (result < 0); |
michael@0 | 1889 | result = 0; |
michael@0 | 1890 | } else { |
michael@0 | 1891 | *carry_out = false; |
michael@0 | 1892 | result = 0; |
michael@0 | 1893 | } |
michael@0 | 1894 | break; |
michael@0 | 1895 | } |
michael@0 | 1896 | |
michael@0 | 1897 | case ROR: { |
michael@0 | 1898 | if (shift_amount == 0) { |
michael@0 | 1899 | *carry_out = c_flag_; |
michael@0 | 1900 | } else { |
michael@0 | 1901 | uint32_t left = static_cast<uint32_t>(result) >> shift_amount; |
michael@0 | 1902 | uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount); |
michael@0 | 1903 | result = right | left; |
michael@0 | 1904 | *carry_out = (static_cast<uint32_t>(result) >> 31) != 0; |
michael@0 | 1905 | } |
michael@0 | 1906 | break; |
michael@0 | 1907 | } |
michael@0 | 1908 | |
michael@0 | 1909 | default: |
michael@0 | 1910 | MOZ_ASSUME_UNREACHABLE(); |
michael@0 | 1911 | break; |
michael@0 | 1912 | } |
michael@0 | 1913 | } |
michael@0 | 1914 | return result; |
michael@0 | 1915 | } |
michael@0 | 1916 | |
michael@0 | 1917 | // Addressing Mode 1 - Data-processing operands: |
michael@0 | 1918 | // Get the value based on the shifter_operand with immediate. |
michael@0 | 1919 | int32_t |
michael@0 | 1920 | Simulator::getImm(SimInstruction *instr, bool *carry_out) |
michael@0 | 1921 | { |
michael@0 | 1922 | int rotate = instr->rotateValue() * 2; |
michael@0 | 1923 | int immed8 = instr->immed8Value(); |
michael@0 | 1924 | int imm = (immed8 >> rotate) | (immed8 << (32 - rotate)); |
michael@0 | 1925 | *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); |
michael@0 | 1926 | return imm; |
michael@0 | 1927 | } |
michael@0 | 1928 | |
michael@0 | 1929 | int32_t |
michael@0 | 1930 | Simulator::processPU(SimInstruction *instr, int num_regs, int reg_size, |
michael@0 | 1931 | intptr_t *start_address, intptr_t *end_address) |
michael@0 | 1932 | { |
michael@0 | 1933 | int rn = instr->rnValue(); |
michael@0 | 1934 | int32_t rn_val = get_register(rn); |
michael@0 | 1935 | switch (instr->PUField()) { |
michael@0 | 1936 | case da_x: |
michael@0 | 1937 | MOZ_CRASH(); |
michael@0 | 1938 | break; |
michael@0 | 1939 | case ia_x: |
michael@0 | 1940 | *start_address = rn_val; |
michael@0 | 1941 | *end_address = rn_val + (num_regs * reg_size) - reg_size; |
michael@0 | 1942 | rn_val = rn_val + (num_regs * reg_size); |
michael@0 | 1943 | break; |
michael@0 | 1944 | case db_x: |
michael@0 | 1945 | *start_address = rn_val - (num_regs * reg_size); |
michael@0 | 1946 | *end_address = rn_val - reg_size; |
michael@0 | 1947 | rn_val = *start_address; |
michael@0 | 1948 | break; |
michael@0 | 1949 | case ib_x: |
michael@0 | 1950 | *start_address = rn_val + reg_size; |
michael@0 | 1951 | *end_address = rn_val + (num_regs * reg_size); |
michael@0 | 1952 | rn_val = *end_address; |
michael@0 | 1953 | break; |
michael@0 | 1954 | default: |
michael@0 | 1955 | MOZ_ASSUME_UNREACHABLE(); |
michael@0 | 1956 | break; |
michael@0 | 1957 | } |
michael@0 | 1958 | return rn_val; |
michael@0 | 1959 | } |
michael@0 | 1960 | |
michael@0 | 1961 | // Addressing Mode 4 - Load and Store Multiple |
michael@0 | 1962 | void |
michael@0 | 1963 | Simulator::handleRList(SimInstruction *instr, bool load) |
michael@0 | 1964 | { |
michael@0 | 1965 | int rlist = instr->rlistValue(); |
michael@0 | 1966 | int num_regs = mozilla::CountPopulation32(rlist); |
michael@0 | 1967 | |
michael@0 | 1968 | intptr_t start_address = 0; |
michael@0 | 1969 | intptr_t end_address = 0; |
michael@0 | 1970 | int32_t rn_val = processPU(instr, num_regs, sizeof(void *), &start_address, &end_address); |
michael@0 | 1971 | intptr_t *address = reinterpret_cast<intptr_t*>(start_address); |
michael@0 | 1972 | |
michael@0 | 1973 | // Catch null pointers a little earlier. |
michael@0 | 1974 | MOZ_ASSERT(start_address > 8191 || start_address < 0); |
michael@0 | 1975 | |
michael@0 | 1976 | int reg = 0; |
michael@0 | 1977 | while (rlist != 0) { |
michael@0 | 1978 | if ((rlist & 1) != 0) { |
michael@0 | 1979 | if (load) { |
michael@0 | 1980 | set_register(reg, *address); |
michael@0 | 1981 | } else { |
michael@0 | 1982 | *address = get_register(reg); |
michael@0 | 1983 | } |
michael@0 | 1984 | address += 1; |
michael@0 | 1985 | } |
michael@0 | 1986 | reg++; |
michael@0 | 1987 | rlist >>= 1; |
michael@0 | 1988 | } |
michael@0 | 1989 | MOZ_ASSERT(end_address == ((intptr_t)address) - 4); |
michael@0 | 1990 | if (instr->hasW()) |
michael@0 | 1991 | set_register(instr->rnValue(), rn_val); |
michael@0 | 1992 | } |
michael@0 | 1993 | |
michael@0 | 1994 | // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. |
michael@0 | 1995 | void |
michael@0 | 1996 | Simulator::handleVList(SimInstruction *instr) |
michael@0 | 1997 | { |
michael@0 | 1998 | VFPRegPrecision precision = (instr->szValue() == 0) ? kSinglePrecision : kDoublePrecision; |
michael@0 | 1999 | int operand_size = (precision == kSinglePrecision) ? 4 : 8; |
michael@0 | 2000 | bool load = (instr->VLValue() == 0x1); |
michael@0 | 2001 | |
michael@0 | 2002 | int vd; |
michael@0 | 2003 | int num_regs; |
michael@0 | 2004 | vd = instr->VFPDRegValue(precision); |
michael@0 | 2005 | if (precision == kSinglePrecision) |
michael@0 | 2006 | num_regs = instr->immed8Value(); |
michael@0 | 2007 | else |
michael@0 | 2008 | num_regs = instr->immed8Value() / 2; |
michael@0 | 2009 | |
michael@0 | 2010 | intptr_t start_address = 0; |
michael@0 | 2011 | intptr_t end_address = 0; |
michael@0 | 2012 | int32_t rn_val = processPU(instr, num_regs, operand_size, &start_address, &end_address); |
michael@0 | 2013 | |
michael@0 | 2014 | intptr_t *address = reinterpret_cast<intptr_t*>(start_address); |
michael@0 | 2015 | for (int reg = vd; reg < vd + num_regs; reg++) { |
michael@0 | 2016 | if (precision == kSinglePrecision) { |
michael@0 | 2017 | if (load) |
michael@0 | 2018 | set_s_register_from_sinteger(reg, readW(reinterpret_cast<int32_t>(address), instr)); |
michael@0 | 2019 | else |
michael@0 | 2020 | writeW(reinterpret_cast<int32_t>(address), get_sinteger_from_s_register(reg), instr); |
michael@0 | 2021 | address += 1; |
michael@0 | 2022 | } else { |
michael@0 | 2023 | if (load) { |
michael@0 | 2024 | int32_t data[] = { |
michael@0 | 2025 | readW(reinterpret_cast<int32_t>(address), instr), |
michael@0 | 2026 | readW(reinterpret_cast<int32_t>(address + 1), instr) |
michael@0 | 2027 | }; |
michael@0 | 2028 | double d; |
michael@0 | 2029 | memcpy(&d, data, 8); |
michael@0 | 2030 | set_d_register_from_double(reg, d); |
michael@0 | 2031 | } else { |
michael@0 | 2032 | int32_t data[2]; |
michael@0 | 2033 | double d = get_double_from_d_register(reg); |
michael@0 | 2034 | memcpy(data, &d, 8); |
michael@0 | 2035 | writeW(reinterpret_cast<int32_t>(address), data[0], instr); |
michael@0 | 2036 | writeW(reinterpret_cast<int32_t>(address + 1), data[1], instr); |
michael@0 | 2037 | } |
michael@0 | 2038 | address += 2; |
michael@0 | 2039 | } |
michael@0 | 2040 | } |
michael@0 | 2041 | MOZ_ASSERT(reinterpret_cast<intptr_t>(address) - operand_size == end_address); |
michael@0 | 2042 | if (instr->hasW()) |
michael@0 | 2043 | set_register(instr->rnValue(), rn_val); |
michael@0 | 2044 | } |
michael@0 | 2045 | |
michael@0 | 2046 | |
michael@0 | 2047 | // Note: With the code below we assume that all runtime calls return a 64 bits |
michael@0 | 2048 | // result. If they don't, the r1 result register contains a bogus value, which |
michael@0 | 2049 | // is fine because it is caller-saved. |
michael@0 | 2050 | typedef int64_t (*Prototype_General0)(); |
michael@0 | 2051 | typedef int64_t (*Prototype_General1)(int32_t arg0); |
michael@0 | 2052 | typedef int64_t (*Prototype_General2)(int32_t arg0, int32_t arg1); |
michael@0 | 2053 | typedef int64_t (*Prototype_General3)(int32_t arg0, int32_t arg1, int32_t arg2); |
michael@0 | 2054 | typedef int64_t (*Prototype_General4)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); |
michael@0 | 2055 | typedef int64_t (*Prototype_General5)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
michael@0 | 2056 | int32_t arg4); |
michael@0 | 2057 | typedef int64_t (*Prototype_General6)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
michael@0 | 2058 | int32_t arg4, int32_t arg5); |
michael@0 | 2059 | typedef int64_t (*Prototype_General7)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
michael@0 | 2060 | int32_t arg4, int32_t arg5, int32_t arg6); |
michael@0 | 2061 | typedef int64_t (*Prototype_General8)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
michael@0 | 2062 | int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7); |
michael@0 | 2063 | |
michael@0 | 2064 | typedef double (*Prototype_Double_None)(); |
michael@0 | 2065 | typedef double (*Prototype_Double_Double)(double arg0); |
michael@0 | 2066 | typedef double (*Prototype_Double_Int)(int32_t arg0); |
michael@0 | 2067 | typedef int32_t (*Prototype_Int_Double)(double arg0); |
michael@0 | 2068 | typedef float (*Prototype_Float32_Float32)(float arg0); |
michael@0 | 2069 | |
michael@0 | 2070 | typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1); |
michael@0 | 2071 | typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1); |
michael@0 | 2072 | typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); |
michael@0 | 2073 | typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1); |
michael@0 | 2074 | |
michael@0 | 2075 | |
michael@0 | 2076 | // Fill the volatile registers with scratch values. |
michael@0 | 2077 | // |
michael@0 | 2078 | // Some of the ABI calls assume that the float registers are not scratched, even |
michael@0 | 2079 | // though the ABI defines them as volatile - a performance optimization. These are |
michael@0 | 2080 | // all calls passing operands in integer registers, so for now the simulator does not |
michael@0 | 2081 | // scratch any float registers for these calls. Should try to narrow it further in |
michael@0 | 2082 | // future. |
michael@0 | 2083 | // |
michael@0 | 2084 | void |
michael@0 | 2085 | Simulator::scratchVolatileRegisters(bool scratchFloat) |
michael@0 | 2086 | { |
michael@0 | 2087 | int32_t scratch_value = 0xa5a5a5a5 ^ uint32_t(icount_); |
michael@0 | 2088 | set_register(r0, scratch_value); |
michael@0 | 2089 | set_register(r1, scratch_value); |
michael@0 | 2090 | set_register(r2, scratch_value); |
michael@0 | 2091 | set_register(r3, scratch_value); |
michael@0 | 2092 | set_register(r12, scratch_value); // Intra-Procedure-call scratch register |
michael@0 | 2093 | set_register(r14, scratch_value); // Link register |
michael@0 | 2094 | |
michael@0 | 2095 | if (scratchFloat) { |
michael@0 | 2096 | uint64_t scratch_value_d = 0x5a5a5a5a5a5a5a5aLU ^ uint64_t(icount_) ^ (uint64_t(icount_) << 30); |
michael@0 | 2097 | for (uint32_t i = d0; i < d8; i++) |
michael@0 | 2098 | set_d_register(i, &scratch_value_d); |
michael@0 | 2099 | for (uint32_t i = d16; i < FloatRegisters::Total; i++) |
michael@0 | 2100 | set_d_register(i, &scratch_value_d); |
michael@0 | 2101 | } |
michael@0 | 2102 | } |
michael@0 | 2103 | |
michael@0 | 2104 | // Software interrupt instructions are used by the simulator to call into C++. |
michael@0 | 2105 | void |
michael@0 | 2106 | Simulator::softwareInterrupt(SimInstruction *instr) |
michael@0 | 2107 | { |
michael@0 | 2108 | int svc = instr->svcValue(); |
michael@0 | 2109 | switch (svc) { |
michael@0 | 2110 | case kCallRtRedirected: { |
michael@0 | 2111 | Redirection *redirection = Redirection::FromSwiInstruction(instr); |
michael@0 | 2112 | int32_t arg0 = get_register(r0); |
michael@0 | 2113 | int32_t arg1 = get_register(r1); |
michael@0 | 2114 | int32_t arg2 = get_register(r2); |
michael@0 | 2115 | int32_t arg3 = get_register(r3); |
michael@0 | 2116 | int32_t *stack_pointer = reinterpret_cast<int32_t*>(get_register(sp)); |
michael@0 | 2117 | int32_t arg4 = stack_pointer[0]; |
michael@0 | 2118 | int32_t arg5 = stack_pointer[1]; |
michael@0 | 2119 | |
michael@0 | 2120 | int32_t saved_lr = get_register(lr); |
michael@0 | 2121 | intptr_t external = reinterpret_cast<intptr_t>(redirection->nativeFunction()); |
michael@0 | 2122 | |
michael@0 | 2123 | bool stack_aligned = (get_register(sp) & (StackAlignment - 1)) == 0; |
michael@0 | 2124 | if (!stack_aligned) { |
michael@0 | 2125 | fprintf(stderr, "Runtime call with unaligned stack!\n"); |
michael@0 | 2126 | MOZ_CRASH(); |
michael@0 | 2127 | } |
michael@0 | 2128 | |
michael@0 | 2129 | switch (redirection->type()) { |
michael@0 | 2130 | case Args_General0: { |
michael@0 | 2131 | Prototype_General0 target = reinterpret_cast<Prototype_General0>(external); |
michael@0 | 2132 | int64_t result = target(); |
michael@0 | 2133 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2134 | setCallResult(result); |
michael@0 | 2135 | break; |
michael@0 | 2136 | } |
michael@0 | 2137 | case Args_General1: { |
michael@0 | 2138 | Prototype_General1 target = reinterpret_cast<Prototype_General1>(external); |
michael@0 | 2139 | int64_t result = target(arg0); |
michael@0 | 2140 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2141 | setCallResult(result); |
michael@0 | 2142 | break; |
michael@0 | 2143 | } |
michael@0 | 2144 | case Args_General2: { |
michael@0 | 2145 | Prototype_General2 target = reinterpret_cast<Prototype_General2>(external); |
michael@0 | 2146 | int64_t result = target(arg0, arg1); |
michael@0 | 2147 | // The ARM backend makes calls to __aeabi_idivmod and __aeabi_uidivmod assuming |
michael@0 | 2148 | // that the float registers are non-volatile as a performance optimization, so the |
michael@0 | 2149 | // float registers must not be scratch when calling these. |
michael@0 | 2150 | bool scratchFloat = target != __aeabi_idivmod && target != __aeabi_uidivmod; |
michael@0 | 2151 | scratchVolatileRegisters(/* scratchFloat = */ scratchFloat); |
michael@0 | 2152 | setCallResult(result); |
michael@0 | 2153 | break; |
michael@0 | 2154 | } |
michael@0 | 2155 | case Args_General3: { |
michael@0 | 2156 | Prototype_General3 target = reinterpret_cast<Prototype_General3>(external); |
michael@0 | 2157 | int64_t result = target(arg0, arg1, arg2); |
michael@0 | 2158 | scratchVolatileRegisters(/* scratchFloat = true*/); |
michael@0 | 2159 | setCallResult(result); |
michael@0 | 2160 | break; |
michael@0 | 2161 | } |
michael@0 | 2162 | case Args_General4: { |
michael@0 | 2163 | Prototype_General4 target = reinterpret_cast<Prototype_General4>(external); |
michael@0 | 2164 | int64_t result = target(arg0, arg1, arg2, arg3); |
michael@0 | 2165 | scratchVolatileRegisters(/* scratchFloat = true*/); |
michael@0 | 2166 | setCallResult(result); |
michael@0 | 2167 | break; |
michael@0 | 2168 | } |
michael@0 | 2169 | case Args_General5: { |
michael@0 | 2170 | Prototype_General5 target = reinterpret_cast<Prototype_General5>(external); |
michael@0 | 2171 | int64_t result = target(arg0, arg1, arg2, arg3, arg4); |
michael@0 | 2172 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2173 | setCallResult(result); |
michael@0 | 2174 | break; |
michael@0 | 2175 | } |
michael@0 | 2176 | case Args_General6: { |
michael@0 | 2177 | Prototype_General6 target = reinterpret_cast<Prototype_General6>(external); |
michael@0 | 2178 | int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5); |
michael@0 | 2179 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2180 | setCallResult(result); |
michael@0 | 2181 | break; |
michael@0 | 2182 | } |
michael@0 | 2183 | case Args_General7: { |
michael@0 | 2184 | Prototype_General7 target = reinterpret_cast<Prototype_General7>(external); |
michael@0 | 2185 | int32_t arg6 = stack_pointer[2]; |
michael@0 | 2186 | int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6); |
michael@0 | 2187 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2188 | setCallResult(result); |
michael@0 | 2189 | break; |
michael@0 | 2190 | } |
michael@0 | 2191 | case Args_General8: { |
michael@0 | 2192 | Prototype_General8 target = reinterpret_cast<Prototype_General8>(external); |
michael@0 | 2193 | int32_t arg6 = stack_pointer[2]; |
michael@0 | 2194 | int32_t arg7 = stack_pointer[3]; |
michael@0 | 2195 | int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); |
michael@0 | 2196 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2197 | setCallResult(result); |
michael@0 | 2198 | break; |
michael@0 | 2199 | } |
michael@0 | 2200 | case Args_Double_None: { |
michael@0 | 2201 | Prototype_Double_None target = reinterpret_cast<Prototype_Double_None>(external); |
michael@0 | 2202 | double dresult = target(); |
michael@0 | 2203 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2204 | setCallResultDouble(dresult); |
michael@0 | 2205 | break; |
michael@0 | 2206 | } |
michael@0 | 2207 | case Args_Int_Double: { |
michael@0 | 2208 | double dval0, dval1; |
michael@0 | 2209 | int32_t ival; |
michael@0 | 2210 | getFpArgs(&dval0, &dval1, &ival); |
michael@0 | 2211 | Prototype_Int_Double target = reinterpret_cast<Prototype_Int_Double>(external); |
michael@0 | 2212 | int32_t res = target(dval0); |
michael@0 | 2213 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2214 | set_register(r0, res); |
michael@0 | 2215 | break; |
michael@0 | 2216 | } |
michael@0 | 2217 | case Args_Double_Double: { |
michael@0 | 2218 | double dval0, dval1; |
michael@0 | 2219 | int32_t ival; |
michael@0 | 2220 | getFpArgs(&dval0, &dval1, &ival); |
michael@0 | 2221 | Prototype_Double_Double target = reinterpret_cast<Prototype_Double_Double>(external); |
michael@0 | 2222 | double dresult = target(dval0); |
michael@0 | 2223 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2224 | setCallResultDouble(dresult); |
michael@0 | 2225 | break; |
michael@0 | 2226 | } |
michael@0 | 2227 | case Args_Float32_Float32: { |
michael@0 | 2228 | float fval0; |
michael@0 | 2229 | if (useHardFpABI()) |
michael@0 | 2230 | fval0 = get_float_from_s_register(0); |
michael@0 | 2231 | else |
michael@0 | 2232 | fval0 = mozilla::BitwiseCast<float>(arg0); |
michael@0 | 2233 | Prototype_Float32_Float32 target = reinterpret_cast<Prototype_Float32_Float32>(external); |
michael@0 | 2234 | float fresult = target(fval0); |
michael@0 | 2235 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2236 | setCallResultFloat(fresult); |
michael@0 | 2237 | break; |
michael@0 | 2238 | } |
michael@0 | 2239 | case Args_Double_Int: { |
michael@0 | 2240 | Prototype_Double_Int target = reinterpret_cast<Prototype_Double_Int>(external); |
michael@0 | 2241 | double dresult = target(arg0); |
michael@0 | 2242 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2243 | setCallResultDouble(dresult); |
michael@0 | 2244 | break; |
michael@0 | 2245 | } |
michael@0 | 2246 | case Args_Double_DoubleInt: { |
michael@0 | 2247 | double dval0, dval1; |
michael@0 | 2248 | int32_t ival; |
michael@0 | 2249 | getFpArgs(&dval0, &dval1, &ival); |
michael@0 | 2250 | Prototype_DoubleInt target = reinterpret_cast<Prototype_DoubleInt>(external); |
michael@0 | 2251 | double dresult = target(dval0, ival); |
michael@0 | 2252 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2253 | setCallResultDouble(dresult); |
michael@0 | 2254 | break; |
michael@0 | 2255 | } |
michael@0 | 2256 | case Args_Double_DoubleDouble: { |
michael@0 | 2257 | double dval0, dval1; |
michael@0 | 2258 | int32_t ival; |
michael@0 | 2259 | getFpArgs(&dval0, &dval1, &ival); |
michael@0 | 2260 | Prototype_Double_DoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDouble>(external); |
michael@0 | 2261 | double dresult = target(dval0, dval1); |
michael@0 | 2262 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2263 | setCallResultDouble(dresult); |
michael@0 | 2264 | break; |
michael@0 | 2265 | } |
michael@0 | 2266 | case Args_Double_IntDouble: { |
michael@0 | 2267 | int32_t ival = get_register(0); |
michael@0 | 2268 | double dval0; |
michael@0 | 2269 | if (useHardFpABI()) |
michael@0 | 2270 | dval0 = get_double_from_d_register(0); |
michael@0 | 2271 | else |
michael@0 | 2272 | dval0 = get_double_from_register_pair(2); |
michael@0 | 2273 | Prototype_Double_IntDouble target = reinterpret_cast<Prototype_Double_IntDouble>(external); |
michael@0 | 2274 | double dresult = target(ival, dval0); |
michael@0 | 2275 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2276 | setCallResultDouble(dresult); |
michael@0 | 2277 | break; |
michael@0 | 2278 | } |
michael@0 | 2279 | case Args_Int_IntDouble: { |
michael@0 | 2280 | int32_t ival = get_register(0); |
michael@0 | 2281 | double dval0; |
michael@0 | 2282 | if (useHardFpABI()) |
michael@0 | 2283 | dval0 = get_double_from_d_register(0); |
michael@0 | 2284 | else |
michael@0 | 2285 | dval0 = get_double_from_register_pair(2); |
michael@0 | 2286 | Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external); |
michael@0 | 2287 | int32_t result = target(ival, dval0); |
michael@0 | 2288 | scratchVolatileRegisters(/* scratchFloat = true */); |
michael@0 | 2289 | set_register(r0, result); |
michael@0 | 2290 | break; |
michael@0 | 2291 | } |
michael@0 | 2292 | default: |
michael@0 | 2293 | MOZ_ASSUME_UNREACHABLE("call"); |
michael@0 | 2294 | } |
michael@0 | 2295 | |
michael@0 | 2296 | set_register(lr, saved_lr); |
michael@0 | 2297 | set_pc(get_register(lr)); |
michael@0 | 2298 | break; |
michael@0 | 2299 | } |
michael@0 | 2300 | case kBreakpoint: { |
michael@0 | 2301 | ArmDebugger dbg(this); |
michael@0 | 2302 | dbg.debug(); |
michael@0 | 2303 | break; |
michael@0 | 2304 | } |
michael@0 | 2305 | default: { // Stop uses all codes greater than 1 << 23. |
michael@0 | 2306 | if (svc >= (1 << 23)) { |
michael@0 | 2307 | uint32_t code = svc & kStopCodeMask; |
michael@0 | 2308 | if (isWatchedStop(code)) |
michael@0 | 2309 | increaseStopCounter(code); |
michael@0 | 2310 | |
michael@0 | 2311 | // Stop if it is enabled, otherwise go on jumping over the stop |
michael@0 | 2312 | // and the message address. |
michael@0 | 2313 | if (isEnabledStop(code)) { |
michael@0 | 2314 | ArmDebugger dbg(this); |
michael@0 | 2315 | dbg.stop(instr); |
michael@0 | 2316 | } else { |
michael@0 | 2317 | set_pc(get_pc() + 2 * SimInstruction::kInstrSize); |
michael@0 | 2318 | } |
michael@0 | 2319 | } else { |
michael@0 | 2320 | // This is not a valid svc code. |
michael@0 | 2321 | MOZ_CRASH(); |
michael@0 | 2322 | break; |
michael@0 | 2323 | } |
michael@0 | 2324 | } |
michael@0 | 2325 | } |
michael@0 | 2326 | } |
michael@0 | 2327 | |
michael@0 | 2328 | double |
michael@0 | 2329 | Simulator::canonicalizeNaN(double value) |
michael@0 | 2330 | { |
michael@0 | 2331 | return FPSCR_default_NaN_mode_ ? JS::CanonicalizeNaN(value) : value; |
michael@0 | 2332 | } |
michael@0 | 2333 | |
michael@0 | 2334 | // Stop helper functions. |
michael@0 | 2335 | bool |
michael@0 | 2336 | Simulator::isStopInstruction(SimInstruction *instr) |
michael@0 | 2337 | { |
michael@0 | 2338 | return (instr->bits(27, 24) == 0xF) && (instr->svcValue() >= kStopCode); |
michael@0 | 2339 | } |
michael@0 | 2340 | |
michael@0 | 2341 | bool Simulator::isWatchedStop(uint32_t code) |
michael@0 | 2342 | { |
michael@0 | 2343 | MOZ_ASSERT(code <= kMaxStopCode); |
michael@0 | 2344 | return code < kNumOfWatchedStops; |
michael@0 | 2345 | } |
michael@0 | 2346 | |
michael@0 | 2347 | bool |
michael@0 | 2348 | Simulator::isEnabledStop(uint32_t code) |
michael@0 | 2349 | { |
michael@0 | 2350 | MOZ_ASSERT(code <= kMaxStopCode); |
michael@0 | 2351 | // Unwatched stops are always enabled. |
michael@0 | 2352 | return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); |
michael@0 | 2353 | } |
michael@0 | 2354 | |
michael@0 | 2355 | void |
michael@0 | 2356 | Simulator::enableStop(uint32_t code) |
michael@0 | 2357 | { |
michael@0 | 2358 | MOZ_ASSERT(isWatchedStop(code)); |
michael@0 | 2359 | if (!isEnabledStop(code)) |
michael@0 | 2360 | watched_stops_[code].count &= ~kStopDisabledBit; |
michael@0 | 2361 | } |
michael@0 | 2362 | |
michael@0 | 2363 | void |
michael@0 | 2364 | Simulator::disableStop(uint32_t code) |
michael@0 | 2365 | { |
michael@0 | 2366 | MOZ_ASSERT(isWatchedStop(code)); |
michael@0 | 2367 | if (isEnabledStop(code)) |
michael@0 | 2368 | watched_stops_[code].count |= kStopDisabledBit; |
michael@0 | 2369 | } |
michael@0 | 2370 | |
michael@0 | 2371 | void |
michael@0 | 2372 | Simulator::increaseStopCounter(uint32_t code) |
michael@0 | 2373 | { |
michael@0 | 2374 | MOZ_ASSERT(code <= kMaxStopCode); |
michael@0 | 2375 | MOZ_ASSERT(isWatchedStop(code)); |
michael@0 | 2376 | if ((watched_stops_[code].count & ~(1 << 31)) == 0x7fffffff) { |
michael@0 | 2377 | printf("Stop counter for code %i has overflowed.\n" |
michael@0 | 2378 | "Enabling this code and reseting the counter to 0.\n", code); |
michael@0 | 2379 | watched_stops_[code].count = 0; |
michael@0 | 2380 | enableStop(code); |
michael@0 | 2381 | } else { |
michael@0 | 2382 | watched_stops_[code].count++; |
michael@0 | 2383 | } |
michael@0 | 2384 | } |
michael@0 | 2385 | |
michael@0 | 2386 | // Print a stop status. |
michael@0 | 2387 | void |
michael@0 | 2388 | Simulator::printStopInfo(uint32_t code) |
michael@0 | 2389 | { |
michael@0 | 2390 | MOZ_ASSERT(code <= kMaxStopCode); |
michael@0 | 2391 | if (!isWatchedStop(code)) { |
michael@0 | 2392 | printf("Stop not watched."); |
michael@0 | 2393 | } else { |
michael@0 | 2394 | const char *state = isEnabledStop(code) ? "Enabled" : "Disabled"; |
michael@0 | 2395 | int32_t count = watched_stops_[code].count & ~kStopDisabledBit; |
michael@0 | 2396 | // Don't print the state of unused breakpoints. |
michael@0 | 2397 | if (count != 0) { |
michael@0 | 2398 | if (watched_stops_[code].desc) { |
michael@0 | 2399 | printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", |
michael@0 | 2400 | code, code, state, count, watched_stops_[code].desc); |
michael@0 | 2401 | } else { |
michael@0 | 2402 | printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", |
michael@0 | 2403 | code, code, state, count); |
michael@0 | 2404 | } |
michael@0 | 2405 | } |
michael@0 | 2406 | } |
michael@0 | 2407 | } |
michael@0 | 2408 | |
michael@0 | 2409 | // Instruction types 0 and 1 are both rolled into one function because they |
michael@0 | 2410 | // only differ in the handling of the shifter_operand. |
michael@0 | 2411 | void |
michael@0 | 2412 | Simulator::decodeType01(SimInstruction *instr) |
michael@0 | 2413 | { |
michael@0 | 2414 | int type = instr->typeValue(); |
michael@0 | 2415 | if (type == 0 && instr->isSpecialType0()) { |
michael@0 | 2416 | // Multiply instruction or extra loads and stores. |
michael@0 | 2417 | if (instr->bits(7, 4) == 9) { |
michael@0 | 2418 | if (instr->bit(24) == 0) { |
michael@0 | 2419 | // Raw field decoding here. Multiply instructions have their Rd |
michael@0 | 2420 | // in funny places. |
michael@0 | 2421 | int rn = instr->rnValue(); |
michael@0 | 2422 | int rm = instr->rmValue(); |
michael@0 | 2423 | int rs = instr->rsValue(); |
michael@0 | 2424 | int32_t rs_val = get_register(rs); |
michael@0 | 2425 | int32_t rm_val = get_register(rm); |
michael@0 | 2426 | if (instr->bit(23) == 0) { |
michael@0 | 2427 | if (instr->bit(21) == 0) { |
michael@0 | 2428 | // The MUL instruction description (A 4.1.33) refers to Rd as being |
michael@0 | 2429 | // the destination for the operation, but it confusingly uses the |
michael@0 | 2430 | // Rn field to encode it. |
michael@0 | 2431 | int rd = rn; // Remap the rn field to the Rd register. |
michael@0 | 2432 | int32_t alu_out = rm_val * rs_val; |
michael@0 | 2433 | set_register(rd, alu_out); |
michael@0 | 2434 | if (instr->hasS()) |
michael@0 | 2435 | setNZFlags(alu_out); |
michael@0 | 2436 | } else { |
michael@0 | 2437 | int rd = instr->rdValue(); |
michael@0 | 2438 | int32_t acc_value = get_register(rd); |
michael@0 | 2439 | if (instr->bit(22) == 0) { |
michael@0 | 2440 | // The MLA instruction description (A 4.1.28) refers to the order |
michael@0 | 2441 | // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the |
michael@0 | 2442 | // Rn field to encode the Rd register and the Rd field to encode |
michael@0 | 2443 | // the Rn register. |
michael@0 | 2444 | int32_t mul_out = rm_val * rs_val; |
michael@0 | 2445 | int32_t result = acc_value + mul_out; |
michael@0 | 2446 | set_register(rn, result); |
michael@0 | 2447 | } else { |
michael@0 | 2448 | int32_t mul_out = rm_val * rs_val; |
michael@0 | 2449 | int32_t result = acc_value - mul_out; |
michael@0 | 2450 | set_register(rn, result); |
michael@0 | 2451 | } |
michael@0 | 2452 | } |
michael@0 | 2453 | } else { |
michael@0 | 2454 | // The signed/long multiply instructions use the terms RdHi and RdLo |
michael@0 | 2455 | // when referring to the target registers. They are mapped to the Rn |
michael@0 | 2456 | // and Rd fields as follows: |
michael@0 | 2457 | // RdLo == Rd |
michael@0 | 2458 | // RdHi == Rn (This is confusingly stored in variable rd here |
michael@0 | 2459 | // because the mul instruction from above uses the |
michael@0 | 2460 | // Rn field to encode the Rd register. Good luck figuring |
michael@0 | 2461 | // this out without reading the ARM instruction manual |
michael@0 | 2462 | // at a very detailed level.) |
michael@0 | 2463 | int rd_hi = rn; // Remap the rn field to the RdHi register. |
michael@0 | 2464 | int rd_lo = instr->rdValue(); |
michael@0 | 2465 | int32_t hi_res = 0; |
michael@0 | 2466 | int32_t lo_res = 0; |
michael@0 | 2467 | if (instr->bit(22) == 1) { |
michael@0 | 2468 | int64_t left_op = static_cast<int32_t>(rm_val); |
michael@0 | 2469 | int64_t right_op = static_cast<int32_t>(rs_val); |
michael@0 | 2470 | uint64_t result = left_op * right_op; |
michael@0 | 2471 | hi_res = static_cast<int32_t>(result >> 32); |
michael@0 | 2472 | lo_res = static_cast<int32_t>(result & 0xffffffff); |
michael@0 | 2473 | } else { |
michael@0 | 2474 | // unsigned multiply |
michael@0 | 2475 | uint64_t left_op = static_cast<uint32_t>(rm_val); |
michael@0 | 2476 | uint64_t right_op = static_cast<uint32_t>(rs_val); |
michael@0 | 2477 | uint64_t result = left_op * right_op; |
michael@0 | 2478 | hi_res = static_cast<int32_t>(result >> 32); |
michael@0 | 2479 | lo_res = static_cast<int32_t>(result & 0xffffffff); |
michael@0 | 2480 | } |
michael@0 | 2481 | set_register(rd_lo, lo_res); |
michael@0 | 2482 | set_register(rd_hi, hi_res); |
michael@0 | 2483 | if (instr->hasS()) |
michael@0 | 2484 | MOZ_CRASH(); |
michael@0 | 2485 | } |
michael@0 | 2486 | } else { |
michael@0 | 2487 | MOZ_CRASH(); // Not used atm. |
michael@0 | 2488 | } |
michael@0 | 2489 | } else { |
michael@0 | 2490 | // extra load/store instructions |
michael@0 | 2491 | int rd = instr->rdValue(); |
michael@0 | 2492 | int rn = instr->rnValue(); |
michael@0 | 2493 | int32_t rn_val = get_register(rn); |
michael@0 | 2494 | int32_t addr = 0; |
michael@0 | 2495 | if (instr->bit(22) == 0) { |
michael@0 | 2496 | int rm = instr->rmValue(); |
michael@0 | 2497 | int32_t rm_val = get_register(rm); |
michael@0 | 2498 | switch (instr->PUField()) { |
michael@0 | 2499 | case da_x: |
michael@0 | 2500 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2501 | addr = rn_val; |
michael@0 | 2502 | rn_val -= rm_val; |
michael@0 | 2503 | set_register(rn, rn_val); |
michael@0 | 2504 | break; |
michael@0 | 2505 | case ia_x: |
michael@0 | 2506 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2507 | addr = rn_val; |
michael@0 | 2508 | rn_val += rm_val; |
michael@0 | 2509 | set_register(rn, rn_val); |
michael@0 | 2510 | break; |
michael@0 | 2511 | case db_x: |
michael@0 | 2512 | rn_val -= rm_val; |
michael@0 | 2513 | addr = rn_val; |
michael@0 | 2514 | if (instr->hasW()) |
michael@0 | 2515 | set_register(rn, rn_val); |
michael@0 | 2516 | break; |
michael@0 | 2517 | case ib_x: |
michael@0 | 2518 | rn_val += rm_val; |
michael@0 | 2519 | addr = rn_val; |
michael@0 | 2520 | if (instr->hasW()) |
michael@0 | 2521 | set_register(rn, rn_val); |
michael@0 | 2522 | break; |
michael@0 | 2523 | default: |
michael@0 | 2524 | // The PU field is a 2-bit field. |
michael@0 | 2525 | MOZ_CRASH(); |
michael@0 | 2526 | break; |
michael@0 | 2527 | } |
michael@0 | 2528 | } else { |
michael@0 | 2529 | int32_t imm_val = (instr->immedHValue() << 4) | instr->immedLValue(); |
michael@0 | 2530 | switch (instr->PUField()) { |
michael@0 | 2531 | case da_x: |
michael@0 | 2532 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2533 | addr = rn_val; |
michael@0 | 2534 | rn_val -= imm_val; |
michael@0 | 2535 | set_register(rn, rn_val); |
michael@0 | 2536 | break; |
michael@0 | 2537 | case ia_x: |
michael@0 | 2538 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2539 | addr = rn_val; |
michael@0 | 2540 | rn_val += imm_val; |
michael@0 | 2541 | set_register(rn, rn_val); |
michael@0 | 2542 | break; |
michael@0 | 2543 | case db_x: |
michael@0 | 2544 | rn_val -= imm_val; |
michael@0 | 2545 | addr = rn_val; |
michael@0 | 2546 | if (instr->hasW()) |
michael@0 | 2547 | set_register(rn, rn_val); |
michael@0 | 2548 | break; |
michael@0 | 2549 | case ib_x: |
michael@0 | 2550 | rn_val += imm_val; |
michael@0 | 2551 | addr = rn_val; |
michael@0 | 2552 | if (instr->hasW()) |
michael@0 | 2553 | set_register(rn, rn_val); |
michael@0 | 2554 | break; |
michael@0 | 2555 | default: |
michael@0 | 2556 | // The PU field is a 2-bit field. |
michael@0 | 2557 | MOZ_CRASH(); |
michael@0 | 2558 | break; |
michael@0 | 2559 | } |
michael@0 | 2560 | } |
michael@0 | 2561 | if ((instr->bits(7, 4) & 0xd) == 0xd && instr->bit(20) == 0) { |
michael@0 | 2562 | MOZ_ASSERT((rd % 2) == 0); |
michael@0 | 2563 | if (instr->hasH()) { |
michael@0 | 2564 | // The strd instruction. |
michael@0 | 2565 | int32_t value1 = get_register(rd); |
michael@0 | 2566 | int32_t value2 = get_register(rd+1); |
michael@0 | 2567 | writeDW(addr, value1, value2); |
michael@0 | 2568 | } else { |
michael@0 | 2569 | // The ldrd instruction. |
michael@0 | 2570 | int *rn_data = readDW(addr); |
michael@0 | 2571 | set_dw_register(rd, rn_data); |
michael@0 | 2572 | } |
michael@0 | 2573 | } else if (instr->hasH()) { |
michael@0 | 2574 | if (instr->hasSign()) { |
michael@0 | 2575 | if (instr->hasL()) { |
michael@0 | 2576 | int16_t val = readH(addr, instr); |
michael@0 | 2577 | set_register(rd, val); |
michael@0 | 2578 | } else { |
michael@0 | 2579 | int16_t val = get_register(rd); |
michael@0 | 2580 | writeH(addr, val, instr); |
michael@0 | 2581 | } |
michael@0 | 2582 | } else { |
michael@0 | 2583 | if (instr->hasL()) { |
michael@0 | 2584 | uint16_t val = readHU(addr, instr); |
michael@0 | 2585 | set_register(rd, val); |
michael@0 | 2586 | } else { |
michael@0 | 2587 | uint16_t val = get_register(rd); |
michael@0 | 2588 | writeH(addr, val, instr); |
michael@0 | 2589 | } |
michael@0 | 2590 | } |
michael@0 | 2591 | } else { |
michael@0 | 2592 | // signed byte loads |
michael@0 | 2593 | MOZ_ASSERT(instr->hasSign()); |
michael@0 | 2594 | MOZ_ASSERT(instr->hasL()); |
michael@0 | 2595 | int8_t val = readB(addr); |
michael@0 | 2596 | set_register(rd, val); |
michael@0 | 2597 | } |
michael@0 | 2598 | return; |
michael@0 | 2599 | } |
michael@0 | 2600 | } else if ((type == 0) && instr->isMiscType0()) { |
michael@0 | 2601 | if (instr->bits(7, 4) == 0) { |
michael@0 | 2602 | if (instr->bit(21) == 0) { |
michael@0 | 2603 | // mrs |
michael@0 | 2604 | int rd = instr->rdValue(); |
michael@0 | 2605 | uint32_t flags; |
michael@0 | 2606 | if (instr->bit(22) == 0) { |
michael@0 | 2607 | // CPSR. Note: The Q flag is not yet implemented! |
michael@0 | 2608 | flags = (n_flag_ << 31) | |
michael@0 | 2609 | (z_flag_ << 30) | |
michael@0 | 2610 | (c_flag_ << 29) | |
michael@0 | 2611 | (v_flag_ << 28); |
michael@0 | 2612 | } else { |
michael@0 | 2613 | // SPSR |
michael@0 | 2614 | MOZ_CRASH(); |
michael@0 | 2615 | } |
michael@0 | 2616 | set_register(rd, flags); |
michael@0 | 2617 | } else { |
michael@0 | 2618 | // msr |
michael@0 | 2619 | if (instr->bits(27, 23) == 2) { |
michael@0 | 2620 | // Register operand. For now we only emit mask 0b1100. |
michael@0 | 2621 | int rm = instr->rmValue(); |
michael@0 | 2622 | uint32_t mask = instr->bits(19, 16); |
michael@0 | 2623 | MOZ_ASSERT(mask == (3 << 2)); |
michael@0 | 2624 | |
michael@0 | 2625 | uint32_t flags = get_register(rm); |
michael@0 | 2626 | n_flag_ = (flags >> 31) & 1; |
michael@0 | 2627 | z_flag_ = (flags >> 30) & 1; |
michael@0 | 2628 | c_flag_ = (flags >> 29) & 1; |
michael@0 | 2629 | v_flag_ = (flags >> 28) & 1; |
michael@0 | 2630 | } else { |
michael@0 | 2631 | MOZ_CRASH(); |
michael@0 | 2632 | } |
michael@0 | 2633 | } |
michael@0 | 2634 | } else if (instr->bits(22, 21) == 1) { |
michael@0 | 2635 | int rm = instr->rmValue(); |
michael@0 | 2636 | switch (instr->bits(7, 4)) { |
michael@0 | 2637 | case 1: // BX |
michael@0 | 2638 | set_pc(get_register(rm)); |
michael@0 | 2639 | break; |
michael@0 | 2640 | case 3: { // BLX |
michael@0 | 2641 | uint32_t old_pc = get_pc(); |
michael@0 | 2642 | set_pc(get_register(rm)); |
michael@0 | 2643 | set_register(lr, old_pc + SimInstruction::kInstrSize); |
michael@0 | 2644 | break; |
michael@0 | 2645 | } |
michael@0 | 2646 | case 7: { // BKPT |
michael@0 | 2647 | ArmDebugger dbg(this); |
michael@0 | 2648 | printf("Simulator hit BKPT.\n"); |
michael@0 | 2649 | dbg.debug(); |
michael@0 | 2650 | break; |
michael@0 | 2651 | } |
michael@0 | 2652 | default: |
michael@0 | 2653 | MOZ_CRASH(); |
michael@0 | 2654 | } |
michael@0 | 2655 | } else if (instr->bits(22, 21) == 3) { |
michael@0 | 2656 | int rm = instr->rmValue(); |
michael@0 | 2657 | int rd = instr->rdValue(); |
michael@0 | 2658 | switch (instr->bits(7, 4)) { |
michael@0 | 2659 | case 1: { // CLZ |
michael@0 | 2660 | uint32_t bits = get_register(rm); |
michael@0 | 2661 | int leading_zeros = 0; |
michael@0 | 2662 | if (bits == 0) |
michael@0 | 2663 | leading_zeros = 32; |
michael@0 | 2664 | else |
michael@0 | 2665 | leading_zeros = mozilla::CountLeadingZeroes32(bits); |
michael@0 | 2666 | set_register(rd, leading_zeros); |
michael@0 | 2667 | break; |
michael@0 | 2668 | } |
michael@0 | 2669 | default: |
michael@0 | 2670 | MOZ_CRASH(); |
michael@0 | 2671 | break; |
michael@0 | 2672 | } |
michael@0 | 2673 | } else { |
michael@0 | 2674 | printf("%08x\n", instr->instructionBits()); |
michael@0 | 2675 | MOZ_CRASH(); |
michael@0 | 2676 | } |
michael@0 | 2677 | } else if ((type == 1) && instr->isNopType1()) { |
michael@0 | 2678 | // NOP. |
michael@0 | 2679 | } else { |
michael@0 | 2680 | int rd = instr->rdValue(); |
michael@0 | 2681 | int rn = instr->rnValue(); |
michael@0 | 2682 | int32_t rn_val = get_register(rn); |
michael@0 | 2683 | int32_t shifter_operand = 0; |
michael@0 | 2684 | bool shifter_carry_out = 0; |
michael@0 | 2685 | if (type == 0) { |
michael@0 | 2686 | shifter_operand = getShiftRm(instr, &shifter_carry_out); |
michael@0 | 2687 | } else { |
michael@0 | 2688 | MOZ_ASSERT(instr->typeValue() == 1); |
michael@0 | 2689 | shifter_operand = getImm(instr, &shifter_carry_out); |
michael@0 | 2690 | } |
michael@0 | 2691 | int32_t alu_out; |
michael@0 | 2692 | switch (instr->opcodeField()) { |
michael@0 | 2693 | case op_and: |
michael@0 | 2694 | alu_out = rn_val & shifter_operand; |
michael@0 | 2695 | set_register(rd, alu_out); |
michael@0 | 2696 | if (instr->hasS()) { |
michael@0 | 2697 | setNZFlags(alu_out); |
michael@0 | 2698 | setCFlag(shifter_carry_out); |
michael@0 | 2699 | } |
michael@0 | 2700 | break; |
michael@0 | 2701 | case op_eor: |
michael@0 | 2702 | alu_out = rn_val ^ shifter_operand; |
michael@0 | 2703 | set_register(rd, alu_out); |
michael@0 | 2704 | if (instr->hasS()) { |
michael@0 | 2705 | setNZFlags(alu_out); |
michael@0 | 2706 | setCFlag(shifter_carry_out); |
michael@0 | 2707 | } |
michael@0 | 2708 | break; |
michael@0 | 2709 | case op_sub: |
michael@0 | 2710 | alu_out = rn_val - shifter_operand; |
michael@0 | 2711 | set_register(rd, alu_out); |
michael@0 | 2712 | if (instr->hasS()) { |
michael@0 | 2713 | setNZFlags(alu_out); |
michael@0 | 2714 | setCFlag(!borrowFrom(rn_val, shifter_operand)); |
michael@0 | 2715 | setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, false)); |
michael@0 | 2716 | } |
michael@0 | 2717 | break; |
michael@0 | 2718 | case op_rsb: |
michael@0 | 2719 | alu_out = shifter_operand - rn_val; |
michael@0 | 2720 | set_register(rd, alu_out); |
michael@0 | 2721 | if (instr->hasS()) { |
michael@0 | 2722 | setNZFlags(alu_out); |
michael@0 | 2723 | setCFlag(!borrowFrom(shifter_operand, rn_val)); |
michael@0 | 2724 | setVFlag(overflowFrom(alu_out, shifter_operand, rn_val, false)); |
michael@0 | 2725 | } |
michael@0 | 2726 | break; |
michael@0 | 2727 | case op_add: |
michael@0 | 2728 | alu_out = rn_val + shifter_operand; |
michael@0 | 2729 | set_register(rd, alu_out); |
michael@0 | 2730 | if (instr->hasS()) { |
michael@0 | 2731 | setNZFlags(alu_out); |
michael@0 | 2732 | setCFlag(carryFrom(rn_val, shifter_operand)); |
michael@0 | 2733 | setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); |
michael@0 | 2734 | } |
michael@0 | 2735 | break; |
michael@0 | 2736 | case op_adc: |
michael@0 | 2737 | alu_out = rn_val + shifter_operand + getCarry(); |
michael@0 | 2738 | set_register(rd, alu_out); |
michael@0 | 2739 | if (instr->hasS()) { |
michael@0 | 2740 | setNZFlags(alu_out); |
michael@0 | 2741 | setCFlag(carryFrom(rn_val, shifter_operand, getCarry())); |
michael@0 | 2742 | setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); |
michael@0 | 2743 | } |
michael@0 | 2744 | break; |
michael@0 | 2745 | case op_sbc: |
michael@0 | 2746 | case op_rsc: |
michael@0 | 2747 | MOZ_CRASH(); |
michael@0 | 2748 | break; |
michael@0 | 2749 | case op_tst: |
michael@0 | 2750 | if (instr->hasS()) { |
michael@0 | 2751 | alu_out = rn_val & shifter_operand; |
michael@0 | 2752 | setNZFlags(alu_out); |
michael@0 | 2753 | setCFlag(shifter_carry_out); |
michael@0 | 2754 | } else { |
michael@0 | 2755 | alu_out = instr->immedMovwMovtValue(); |
michael@0 | 2756 | set_register(rd, alu_out); |
michael@0 | 2757 | } |
michael@0 | 2758 | break; |
michael@0 | 2759 | case op_teq: |
michael@0 | 2760 | if (instr->hasS()) { |
michael@0 | 2761 | alu_out = rn_val ^ shifter_operand; |
michael@0 | 2762 | setNZFlags(alu_out); |
michael@0 | 2763 | setCFlag(shifter_carry_out); |
michael@0 | 2764 | } else { |
michael@0 | 2765 | // Other instructions matching this pattern are handled in the |
michael@0 | 2766 | // miscellaneous instructions part above. |
michael@0 | 2767 | MOZ_CRASH(); |
michael@0 | 2768 | } |
michael@0 | 2769 | break; |
michael@0 | 2770 | case op_cmp: |
michael@0 | 2771 | if (instr->hasS()) { |
michael@0 | 2772 | alu_out = rn_val - shifter_operand; |
michael@0 | 2773 | setNZFlags(alu_out); |
michael@0 | 2774 | setCFlag(!borrowFrom(rn_val, shifter_operand)); |
michael@0 | 2775 | setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, false)); |
michael@0 | 2776 | } else { |
michael@0 | 2777 | alu_out = (get_register(rd) & 0xffff) | |
michael@0 | 2778 | (instr->immedMovwMovtValue() << 16); |
michael@0 | 2779 | set_register(rd, alu_out); |
michael@0 | 2780 | } |
michael@0 | 2781 | break; |
michael@0 | 2782 | case op_cmn: |
michael@0 | 2783 | if (instr->hasS()) { |
michael@0 | 2784 | alu_out = rn_val + shifter_operand; |
michael@0 | 2785 | setNZFlags(alu_out); |
michael@0 | 2786 | setCFlag(carryFrom(rn_val, shifter_operand)); |
michael@0 | 2787 | setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); |
michael@0 | 2788 | } else { |
michael@0 | 2789 | // Other instructions matching this pattern are handled in the |
michael@0 | 2790 | // miscellaneous instructions part above. |
michael@0 | 2791 | MOZ_CRASH(); |
michael@0 | 2792 | } |
michael@0 | 2793 | break; |
michael@0 | 2794 | case op_orr: |
michael@0 | 2795 | alu_out = rn_val | shifter_operand; |
michael@0 | 2796 | set_register(rd, alu_out); |
michael@0 | 2797 | if (instr->hasS()) { |
michael@0 | 2798 | setNZFlags(alu_out); |
michael@0 | 2799 | setCFlag(shifter_carry_out); |
michael@0 | 2800 | } |
michael@0 | 2801 | break; |
michael@0 | 2802 | case op_mov: |
michael@0 | 2803 | alu_out = shifter_operand; |
michael@0 | 2804 | set_register(rd, alu_out); |
michael@0 | 2805 | if (instr->hasS()) { |
michael@0 | 2806 | setNZFlags(alu_out); |
michael@0 | 2807 | setCFlag(shifter_carry_out); |
michael@0 | 2808 | } |
michael@0 | 2809 | break; |
michael@0 | 2810 | case op_bic: |
michael@0 | 2811 | alu_out = rn_val & ~shifter_operand; |
michael@0 | 2812 | set_register(rd, alu_out); |
michael@0 | 2813 | if (instr->hasS()) { |
michael@0 | 2814 | setNZFlags(alu_out); |
michael@0 | 2815 | setCFlag(shifter_carry_out); |
michael@0 | 2816 | } |
michael@0 | 2817 | break; |
michael@0 | 2818 | case op_mvn: |
michael@0 | 2819 | alu_out = ~shifter_operand; |
michael@0 | 2820 | set_register(rd, alu_out); |
michael@0 | 2821 | if (instr->hasS()) { |
michael@0 | 2822 | setNZFlags(alu_out); |
michael@0 | 2823 | setCFlag(shifter_carry_out); |
michael@0 | 2824 | } |
michael@0 | 2825 | break; |
michael@0 | 2826 | default: |
michael@0 | 2827 | MOZ_CRASH(); |
michael@0 | 2828 | break; |
michael@0 | 2829 | } |
michael@0 | 2830 | } |
michael@0 | 2831 | } |
michael@0 | 2832 | |
michael@0 | 2833 | void |
michael@0 | 2834 | Simulator::decodeType2(SimInstruction *instr) |
michael@0 | 2835 | { |
michael@0 | 2836 | int rd = instr->rdValue(); |
michael@0 | 2837 | int rn = instr->rnValue(); |
michael@0 | 2838 | int32_t rn_val = get_register(rn); |
michael@0 | 2839 | int32_t im_val = instr->offset12Value(); |
michael@0 | 2840 | int32_t addr = 0; |
michael@0 | 2841 | switch (instr->PUField()) { |
michael@0 | 2842 | case da_x: |
michael@0 | 2843 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2844 | addr = rn_val; |
michael@0 | 2845 | rn_val -= im_val; |
michael@0 | 2846 | set_register(rn, rn_val); |
michael@0 | 2847 | break; |
michael@0 | 2848 | case ia_x: |
michael@0 | 2849 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2850 | addr = rn_val; |
michael@0 | 2851 | rn_val += im_val; |
michael@0 | 2852 | set_register(rn, rn_val); |
michael@0 | 2853 | break; |
michael@0 | 2854 | case db_x: |
michael@0 | 2855 | rn_val -= im_val; |
michael@0 | 2856 | addr = rn_val; |
michael@0 | 2857 | if (instr->hasW()) |
michael@0 | 2858 | set_register(rn, rn_val); |
michael@0 | 2859 | break; |
michael@0 | 2860 | case ib_x: |
michael@0 | 2861 | rn_val += im_val; |
michael@0 | 2862 | addr = rn_val; |
michael@0 | 2863 | if (instr->hasW()) |
michael@0 | 2864 | set_register(rn, rn_val); |
michael@0 | 2865 | break; |
michael@0 | 2866 | default: |
michael@0 | 2867 | MOZ_CRASH(); |
michael@0 | 2868 | break; |
michael@0 | 2869 | } |
michael@0 | 2870 | if (instr->hasB()) { |
michael@0 | 2871 | if (instr->hasL()) { |
michael@0 | 2872 | uint8_t val = readBU(addr); |
michael@0 | 2873 | set_register(rd, val); |
michael@0 | 2874 | } else { |
michael@0 | 2875 | uint8_t val = get_register(rd); |
michael@0 | 2876 | writeB(addr, val); |
michael@0 | 2877 | } |
michael@0 | 2878 | } else { |
michael@0 | 2879 | if (instr->hasL()) |
michael@0 | 2880 | set_register(rd, readW(addr, instr)); |
michael@0 | 2881 | else |
michael@0 | 2882 | writeW(addr, get_register(rd), instr); |
michael@0 | 2883 | } |
michael@0 | 2884 | } |
michael@0 | 2885 | |
michael@0 | 2886 | void |
michael@0 | 2887 | Simulator::decodeType3(SimInstruction *instr) |
michael@0 | 2888 | { |
michael@0 | 2889 | int rd = instr->rdValue(); |
michael@0 | 2890 | int rn = instr->rnValue(); |
michael@0 | 2891 | int32_t rn_val = get_register(rn); |
michael@0 | 2892 | bool shifter_carry_out = 0; |
michael@0 | 2893 | int32_t shifter_operand = getShiftRm(instr, &shifter_carry_out); |
michael@0 | 2894 | int32_t addr = 0; |
michael@0 | 2895 | switch (instr->PUField()) { |
michael@0 | 2896 | case da_x: |
michael@0 | 2897 | MOZ_ASSERT(!instr->hasW()); |
michael@0 | 2898 | MOZ_CRASH(); |
michael@0 | 2899 | break; |
michael@0 | 2900 | case ia_x: { |
michael@0 | 2901 | if (instr->bit(4) == 0) { |
michael@0 | 2902 | // Memop. |
michael@0 | 2903 | } else { |
michael@0 | 2904 | if (instr->bit(5) == 0) { |
michael@0 | 2905 | switch (instr->bits(22, 21)) { |
michael@0 | 2906 | case 0: |
michael@0 | 2907 | if (instr->bit(20) == 0) { |
michael@0 | 2908 | if (instr->bit(6) == 0) { |
michael@0 | 2909 | // Pkhbt. |
michael@0 | 2910 | uint32_t rn_val = get_register(rn); |
michael@0 | 2911 | uint32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 2912 | int32_t shift = instr->bits(11, 7); |
michael@0 | 2913 | rm_val <<= shift; |
michael@0 | 2914 | set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); |
michael@0 | 2915 | } else { |
michael@0 | 2916 | // Pkhtb. |
michael@0 | 2917 | uint32_t rn_val = get_register(rn); |
michael@0 | 2918 | int32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 2919 | int32_t shift = instr->bits(11, 7); |
michael@0 | 2920 | if (shift == 0) |
michael@0 | 2921 | shift = 32; |
michael@0 | 2922 | rm_val >>= shift; |
michael@0 | 2923 | set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); |
michael@0 | 2924 | } |
michael@0 | 2925 | } else { |
michael@0 | 2926 | MOZ_CRASH(); |
michael@0 | 2927 | } |
michael@0 | 2928 | break; |
michael@0 | 2929 | case 1: |
michael@0 | 2930 | MOZ_CRASH(); |
michael@0 | 2931 | break; |
michael@0 | 2932 | case 2: |
michael@0 | 2933 | MOZ_CRASH(); |
michael@0 | 2934 | break; |
michael@0 | 2935 | case 3: { |
michael@0 | 2936 | // Usat. |
michael@0 | 2937 | int32_t sat_pos = instr->bits(20, 16); |
michael@0 | 2938 | int32_t sat_val = (1 << sat_pos) - 1; |
michael@0 | 2939 | int32_t shift = instr->bits(11, 7); |
michael@0 | 2940 | int32_t shift_type = instr->bit(6); |
michael@0 | 2941 | int32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 2942 | if (shift_type == 0) // LSL |
michael@0 | 2943 | rm_val <<= shift; |
michael@0 | 2944 | else // ASR |
michael@0 | 2945 | rm_val >>= shift; |
michael@0 | 2946 | |
michael@0 | 2947 | // If saturation occurs, the Q flag should be set in the CPSR. |
michael@0 | 2948 | // There is no Q flag yet, and no instruction (MRS) to read the |
michael@0 | 2949 | // CPSR directly. |
michael@0 | 2950 | if (rm_val > sat_val) |
michael@0 | 2951 | rm_val = sat_val; |
michael@0 | 2952 | else if (rm_val < 0) |
michael@0 | 2953 | rm_val = 0; |
michael@0 | 2954 | set_register(rd, rm_val); |
michael@0 | 2955 | break; |
michael@0 | 2956 | } |
michael@0 | 2957 | } |
michael@0 | 2958 | } else { |
michael@0 | 2959 | switch (instr->bits(22, 21)) { |
michael@0 | 2960 | case 0: |
michael@0 | 2961 | MOZ_CRASH(); |
michael@0 | 2962 | break; |
michael@0 | 2963 | case 1: |
michael@0 | 2964 | MOZ_CRASH(); |
michael@0 | 2965 | break; |
michael@0 | 2966 | case 2: |
michael@0 | 2967 | if ((instr->bit(20) == 0) && (instr->bits(9, 6) == 1)) { |
michael@0 | 2968 | if (instr->bits(19, 16) == 0xF) { |
michael@0 | 2969 | // Uxtb16. |
michael@0 | 2970 | uint32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 2971 | int32_t rotate = instr->bits(11, 10); |
michael@0 | 2972 | switch (rotate) { |
michael@0 | 2973 | case 0: |
michael@0 | 2974 | break; |
michael@0 | 2975 | case 1: |
michael@0 | 2976 | rm_val = (rm_val >> 8) | (rm_val << 24); |
michael@0 | 2977 | break; |
michael@0 | 2978 | case 2: |
michael@0 | 2979 | rm_val = (rm_val >> 16) | (rm_val << 16); |
michael@0 | 2980 | break; |
michael@0 | 2981 | case 3: |
michael@0 | 2982 | rm_val = (rm_val >> 24) | (rm_val << 8); |
michael@0 | 2983 | break; |
michael@0 | 2984 | } |
michael@0 | 2985 | set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); |
michael@0 | 2986 | } else { |
michael@0 | 2987 | MOZ_CRASH(); |
michael@0 | 2988 | } |
michael@0 | 2989 | } else { |
michael@0 | 2990 | MOZ_CRASH(); |
michael@0 | 2991 | } |
michael@0 | 2992 | break; |
michael@0 | 2993 | case 3: |
michael@0 | 2994 | if ((instr->bit(20) == 0) && (instr->bits(9, 6) == 1)) { |
michael@0 | 2995 | if (instr->bits(19, 16) == 0xF) { |
michael@0 | 2996 | // Uxtb. |
michael@0 | 2997 | uint32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 2998 | int32_t rotate = instr->bits(11, 10); |
michael@0 | 2999 | switch (rotate) { |
michael@0 | 3000 | case 0: |
michael@0 | 3001 | break; |
michael@0 | 3002 | case 1: |
michael@0 | 3003 | rm_val = (rm_val >> 8) | (rm_val << 24); |
michael@0 | 3004 | break; |
michael@0 | 3005 | case 2: |
michael@0 | 3006 | rm_val = (rm_val >> 16) | (rm_val << 16); |
michael@0 | 3007 | break; |
michael@0 | 3008 | case 3: |
michael@0 | 3009 | rm_val = (rm_val >> 24) | (rm_val << 8); |
michael@0 | 3010 | break; |
michael@0 | 3011 | } |
michael@0 | 3012 | set_register(rd, (rm_val & 0xFF)); |
michael@0 | 3013 | } else { |
michael@0 | 3014 | // Uxtab. |
michael@0 | 3015 | uint32_t rn_val = get_register(rn); |
michael@0 | 3016 | uint32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 3017 | int32_t rotate = instr->bits(11, 10); |
michael@0 | 3018 | switch (rotate) { |
michael@0 | 3019 | case 0: |
michael@0 | 3020 | break; |
michael@0 | 3021 | case 1: |
michael@0 | 3022 | rm_val = (rm_val >> 8) | (rm_val << 24); |
michael@0 | 3023 | break; |
michael@0 | 3024 | case 2: |
michael@0 | 3025 | rm_val = (rm_val >> 16) | (rm_val << 16); |
michael@0 | 3026 | break; |
michael@0 | 3027 | case 3: |
michael@0 | 3028 | rm_val = (rm_val >> 24) | (rm_val << 8); |
michael@0 | 3029 | break; |
michael@0 | 3030 | } |
michael@0 | 3031 | set_register(rd, rn_val + (rm_val & 0xFF)); |
michael@0 | 3032 | } |
michael@0 | 3033 | } else { |
michael@0 | 3034 | MOZ_CRASH(); |
michael@0 | 3035 | } |
michael@0 | 3036 | break; |
michael@0 | 3037 | } |
michael@0 | 3038 | } |
michael@0 | 3039 | return; |
michael@0 | 3040 | } |
michael@0 | 3041 | break; |
michael@0 | 3042 | } |
michael@0 | 3043 | case db_x: { // sudiv |
michael@0 | 3044 | if (instr->bit(22) == 0x0 && instr->bit(20) == 0x1 && |
michael@0 | 3045 | instr->bits(15,12) == 0x0f && instr->bits(7, 4) == 0x1) { |
michael@0 | 3046 | if (!instr->hasW()) { |
michael@0 | 3047 | // sdiv (in V8 notation matching ARM ISA format) rn = rm/rs |
michael@0 | 3048 | int rm = instr->rmValue(); |
michael@0 | 3049 | int32_t rm_val = get_register(rm); |
michael@0 | 3050 | int rs = instr->rsValue(); |
michael@0 | 3051 | int32_t rs_val = get_register(rs); |
michael@0 | 3052 | int32_t ret_val = 0; |
michael@0 | 3053 | MOZ_ASSERT(rs_val != 0); |
michael@0 | 3054 | if ((rm_val == INT32_MIN) && (rs_val == -1)) |
michael@0 | 3055 | ret_val = INT32_MIN; |
michael@0 | 3056 | else |
michael@0 | 3057 | ret_val = rm_val / rs_val; |
michael@0 | 3058 | set_register(rn, ret_val); |
michael@0 | 3059 | return; |
michael@0 | 3060 | } else { |
michael@0 | 3061 | // udiv (in V8 notation matching ARM ISA format) rn = rm/rs |
michael@0 | 3062 | int rm = instr->rmValue(); |
michael@0 | 3063 | uint32_t rm_val = get_register(rm); |
michael@0 | 3064 | int rs = instr->rsValue(); |
michael@0 | 3065 | uint32_t rs_val = get_register(rs); |
michael@0 | 3066 | uint32_t ret_val = 0; |
michael@0 | 3067 | MOZ_ASSERT(rs_val != 0); |
michael@0 | 3068 | ret_val = rm_val / rs_val; |
michael@0 | 3069 | set_register(rn, ret_val); |
michael@0 | 3070 | return; |
michael@0 | 3071 | } |
michael@0 | 3072 | } |
michael@0 | 3073 | |
michael@0 | 3074 | addr = rn_val - shifter_operand; |
michael@0 | 3075 | if (instr->hasW()) |
michael@0 | 3076 | set_register(rn, addr); |
michael@0 | 3077 | break; |
michael@0 | 3078 | } |
michael@0 | 3079 | case ib_x: { |
michael@0 | 3080 | if (instr->hasW() && (instr->bits(6, 4) == 0x5)) { |
michael@0 | 3081 | uint32_t widthminus1 = static_cast<uint32_t>(instr->bits(20, 16)); |
michael@0 | 3082 | uint32_t lsbit = static_cast<uint32_t>(instr->bits(11, 7)); |
michael@0 | 3083 | uint32_t msbit = widthminus1 + lsbit; |
michael@0 | 3084 | if (msbit <= 31) { |
michael@0 | 3085 | if (instr->bit(22)) { |
michael@0 | 3086 | // ubfx - unsigned bitfield extract. |
michael@0 | 3087 | uint32_t rm_val = static_cast<uint32_t>(get_register(instr->rmValue())); |
michael@0 | 3088 | uint32_t extr_val = rm_val << (31 - msbit); |
michael@0 | 3089 | extr_val = extr_val >> (31 - widthminus1); |
michael@0 | 3090 | set_register(instr->rdValue(), extr_val); |
michael@0 | 3091 | } else { |
michael@0 | 3092 | // sbfx - signed bitfield extract. |
michael@0 | 3093 | int32_t rm_val = get_register(instr->rmValue()); |
michael@0 | 3094 | int32_t extr_val = rm_val << (31 - msbit); |
michael@0 | 3095 | extr_val = extr_val >> (31 - widthminus1); |
michael@0 | 3096 | set_register(instr->rdValue(), extr_val); |
michael@0 | 3097 | } |
michael@0 | 3098 | } else { |
michael@0 | 3099 | MOZ_CRASH(); |
michael@0 | 3100 | } |
michael@0 | 3101 | return; |
michael@0 | 3102 | } else if (!instr->hasW() && (instr->bits(6, 4) == 0x1)) { |
michael@0 | 3103 | uint32_t lsbit = static_cast<uint32_t>(instr->bits(11, 7)); |
michael@0 | 3104 | uint32_t msbit = static_cast<uint32_t>(instr->bits(20, 16)); |
michael@0 | 3105 | if (msbit >= lsbit) { |
michael@0 | 3106 | // bfc or bfi - bitfield clear/insert. |
michael@0 | 3107 | uint32_t rd_val = |
michael@0 | 3108 | static_cast<uint32_t>(get_register(instr->rdValue())); |
michael@0 | 3109 | uint32_t bitcount = msbit - lsbit + 1; |
michael@0 | 3110 | uint32_t mask = (1 << bitcount) - 1; |
michael@0 | 3111 | rd_val &= ~(mask << lsbit); |
michael@0 | 3112 | if (instr->rmValue() != 15) { |
michael@0 | 3113 | // bfi - bitfield insert. |
michael@0 | 3114 | uint32_t rm_val = |
michael@0 | 3115 | static_cast<uint32_t>(get_register(instr->rmValue())); |
michael@0 | 3116 | rm_val &= mask; |
michael@0 | 3117 | rd_val |= rm_val << lsbit; |
michael@0 | 3118 | } |
michael@0 | 3119 | set_register(instr->rdValue(), rd_val); |
michael@0 | 3120 | } else { |
michael@0 | 3121 | MOZ_CRASH(); |
michael@0 | 3122 | } |
michael@0 | 3123 | return; |
michael@0 | 3124 | } else { |
michael@0 | 3125 | addr = rn_val + shifter_operand; |
michael@0 | 3126 | if (instr->hasW()) |
michael@0 | 3127 | set_register(rn, addr); |
michael@0 | 3128 | } |
michael@0 | 3129 | break; |
michael@0 | 3130 | } |
michael@0 | 3131 | default: |
michael@0 | 3132 | MOZ_CRASH(); |
michael@0 | 3133 | break; |
michael@0 | 3134 | } |
michael@0 | 3135 | if (instr->hasB()) { |
michael@0 | 3136 | if (instr->hasL()) { |
michael@0 | 3137 | uint8_t byte = readB(addr); |
michael@0 | 3138 | set_register(rd, byte); |
michael@0 | 3139 | } else { |
michael@0 | 3140 | uint8_t byte = get_register(rd); |
michael@0 | 3141 | writeB(addr, byte); |
michael@0 | 3142 | } |
michael@0 | 3143 | } else { |
michael@0 | 3144 | if (instr->hasL()) |
michael@0 | 3145 | set_register(rd, readW(addr, instr)); |
michael@0 | 3146 | else |
michael@0 | 3147 | writeW(addr, get_register(rd), instr); |
michael@0 | 3148 | } |
michael@0 | 3149 | } |
michael@0 | 3150 | |
michael@0 | 3151 | void |
michael@0 | 3152 | Simulator::decodeType4(SimInstruction *instr) |
michael@0 | 3153 | { |
michael@0 | 3154 | MOZ_ASSERT(instr->bit(22) == 0); // Only allowed to be set in privileged mode. |
michael@0 | 3155 | bool load = instr->hasL(); |
michael@0 | 3156 | handleRList(instr, load); |
michael@0 | 3157 | } |
michael@0 | 3158 | |
michael@0 | 3159 | void |
michael@0 | 3160 | Simulator::decodeType5(SimInstruction *instr) |
michael@0 | 3161 | { |
michael@0 | 3162 | int off = instr->sImmed24Value() << 2; |
michael@0 | 3163 | intptr_t pc_address = get_pc(); |
michael@0 | 3164 | if (instr->hasLink()) |
michael@0 | 3165 | set_register(lr, pc_address + SimInstruction::kInstrSize); |
michael@0 | 3166 | int pc_reg = get_register(pc); |
michael@0 | 3167 | set_pc(pc_reg + off); |
michael@0 | 3168 | } |
michael@0 | 3169 | |
michael@0 | 3170 | void |
michael@0 | 3171 | Simulator::decodeType6(SimInstruction *instr) |
michael@0 | 3172 | { |
michael@0 | 3173 | decodeType6CoprocessorIns(instr); |
michael@0 | 3174 | } |
michael@0 | 3175 | |
michael@0 | 3176 | void |
michael@0 | 3177 | Simulator::decodeType7(SimInstruction *instr) |
michael@0 | 3178 | { |
michael@0 | 3179 | if (instr->bit(24) == 1) |
michael@0 | 3180 | softwareInterrupt(instr); |
michael@0 | 3181 | else |
michael@0 | 3182 | decodeTypeVFP(instr); |
michael@0 | 3183 | } |
michael@0 | 3184 | |
michael@0 | 3185 | void |
michael@0 | 3186 | Simulator::decodeTypeVFP(SimInstruction *instr) |
michael@0 | 3187 | { |
michael@0 | 3188 | MOZ_ASSERT(instr->typeValue() == 7 && instr->bit(24) == 0); |
michael@0 | 3189 | MOZ_ASSERT(instr->bits(11, 9) == 0x5); |
michael@0 | 3190 | |
michael@0 | 3191 | // Obtain double precision register codes. |
michael@0 | 3192 | VFPRegPrecision precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; |
michael@0 | 3193 | int vm = instr->VFPMRegValue(precision); |
michael@0 | 3194 | int vd = instr->VFPDRegValue(precision); |
michael@0 | 3195 | int vn = instr->VFPNRegValue(precision); |
michael@0 | 3196 | |
michael@0 | 3197 | if (instr->bit(4) == 0) { |
michael@0 | 3198 | if (instr->opc1Value() == 0x7) { |
michael@0 | 3199 | // Other data processing instructions |
michael@0 | 3200 | if ((instr->opc2Value() == 0x0) && (instr->opc3Value() == 0x1)) { |
michael@0 | 3201 | // vmov register to register. |
michael@0 | 3202 | if (instr->szValue() == 0x1) { |
michael@0 | 3203 | int m = instr->VFPMRegValue(kDoublePrecision); |
michael@0 | 3204 | int d = instr->VFPDRegValue(kDoublePrecision); |
michael@0 | 3205 | set_d_register_from_double(d, get_double_from_d_register(m)); |
michael@0 | 3206 | } else { |
michael@0 | 3207 | int m = instr->VFPMRegValue(kSinglePrecision); |
michael@0 | 3208 | int d = instr->VFPDRegValue(kSinglePrecision); |
michael@0 | 3209 | set_s_register_from_float(d, get_float_from_s_register(m)); |
michael@0 | 3210 | } |
michael@0 | 3211 | } else if ((instr->opc2Value() == 0x0) && (instr->opc3Value() == 0x3)) { |
michael@0 | 3212 | // vabs |
michael@0 | 3213 | if (instr->szValue() == 0x1) { |
michael@0 | 3214 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3215 | double dd_value = std::fabs(dm_value); |
michael@0 | 3216 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3217 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3218 | } else { |
michael@0 | 3219 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3220 | float fd_value = std::fabs(fm_value); |
michael@0 | 3221 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3222 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3223 | } |
michael@0 | 3224 | } else if ((instr->opc2Value() == 0x1) && (instr->opc3Value() == 0x1)) { |
michael@0 | 3225 | // vneg |
michael@0 | 3226 | if (instr->szValue() == 0x1) { |
michael@0 | 3227 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3228 | double dd_value = -dm_value; |
michael@0 | 3229 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3230 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3231 | } else { |
michael@0 | 3232 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3233 | float fd_value = -fm_value; |
michael@0 | 3234 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3235 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3236 | } |
michael@0 | 3237 | } else if ((instr->opc2Value() == 0x7) && (instr->opc3Value() == 0x3)) { |
michael@0 | 3238 | decodeVCVTBetweenDoubleAndSingle(instr); |
michael@0 | 3239 | } else if ((instr->opc2Value() == 0x8) && (instr->opc3Value() & 0x1)) { |
michael@0 | 3240 | decodeVCVTBetweenFloatingPointAndInteger(instr); |
michael@0 | 3241 | } else if ((instr->opc2Value() == 0xA) && (instr->opc3Value() == 0x3) && |
michael@0 | 3242 | (instr->bit(8) == 1)) { |
michael@0 | 3243 | // vcvt.f64.s32 Dd, Dd, #<fbits> |
michael@0 | 3244 | int fraction_bits = 32 - ((instr->bits(3, 0) << 1) | instr->bit(5)); |
michael@0 | 3245 | int fixed_value = get_sinteger_from_s_register(vd * 2); |
michael@0 | 3246 | double divide = 1 << fraction_bits; |
michael@0 | 3247 | set_d_register_from_double(vd, fixed_value / divide); |
michael@0 | 3248 | } else if (((instr->opc2Value() >> 1) == 0x6) && |
michael@0 | 3249 | (instr->opc3Value() & 0x1)) { |
michael@0 | 3250 | decodeVCVTBetweenFloatingPointAndInteger(instr); |
michael@0 | 3251 | } else if (((instr->opc2Value() == 0x4) || (instr->opc2Value() == 0x5)) && |
michael@0 | 3252 | (instr->opc3Value() & 0x1)) { |
michael@0 | 3253 | decodeVCMP(instr); |
michael@0 | 3254 | } else if (((instr->opc2Value() == 0x1)) && (instr->opc3Value() == 0x3)) { |
michael@0 | 3255 | // vsqrt |
michael@0 | 3256 | if (instr->szValue() == 0x1) { |
michael@0 | 3257 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3258 | double dd_value = std::sqrt(dm_value); |
michael@0 | 3259 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3260 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3261 | } else { |
michael@0 | 3262 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3263 | float fd_value = std::sqrt(fm_value); |
michael@0 | 3264 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3265 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3266 | } |
michael@0 | 3267 | } else if (instr->opc3Value() == 0x0) { |
michael@0 | 3268 | // vmov immediate. |
michael@0 | 3269 | if (instr->szValue() == 0x1) { |
michael@0 | 3270 | set_d_register_from_double(vd, instr->doubleImmedVmov()); |
michael@0 | 3271 | } else { |
michael@0 | 3272 | // vmov.f32 immediate |
michael@0 | 3273 | set_s_register_from_float(vd, instr->float32ImmedVmov()); |
michael@0 | 3274 | } |
michael@0 | 3275 | } else { |
michael@0 | 3276 | decodeVCVTBetweenFloatingPointAndIntegerFrac(instr); |
michael@0 | 3277 | } |
michael@0 | 3278 | } else if (instr->opc1Value() == 0x3) { |
michael@0 | 3279 | if (instr->szValue() != 0x1) { |
michael@0 | 3280 | if (instr->opc3Value() & 0x1) { |
michael@0 | 3281 | // vsub |
michael@0 | 3282 | float fn_value = get_float_from_s_register(vn); |
michael@0 | 3283 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3284 | float fd_value = fn_value - fm_value; |
michael@0 | 3285 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3286 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3287 | } else { |
michael@0 | 3288 | // vadd |
michael@0 | 3289 | float fn_value = get_float_from_s_register(vn); |
michael@0 | 3290 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3291 | float fd_value = fn_value + fm_value; |
michael@0 | 3292 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3293 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3294 | } |
michael@0 | 3295 | } else { |
michael@0 | 3296 | if (instr->opc3Value() & 0x1) { |
michael@0 | 3297 | // vsub |
michael@0 | 3298 | double dn_value = get_double_from_d_register(vn); |
michael@0 | 3299 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3300 | double dd_value = dn_value - dm_value; |
michael@0 | 3301 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3302 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3303 | } else { |
michael@0 | 3304 | // vadd |
michael@0 | 3305 | double dn_value = get_double_from_d_register(vn); |
michael@0 | 3306 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3307 | double dd_value = dn_value + dm_value; |
michael@0 | 3308 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3309 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3310 | } |
michael@0 | 3311 | } |
michael@0 | 3312 | } else if ((instr->opc1Value() == 0x2) && !(instr->opc3Value() & 0x1)) { |
michael@0 | 3313 | // vmul |
michael@0 | 3314 | if (instr->szValue() != 0x1) { |
michael@0 | 3315 | float fn_value = get_float_from_s_register(vn); |
michael@0 | 3316 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3317 | float fd_value = fn_value * fm_value; |
michael@0 | 3318 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3319 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3320 | } else { |
michael@0 | 3321 | double dn_value = get_double_from_d_register(vn); |
michael@0 | 3322 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3323 | double dd_value = dn_value * dm_value; |
michael@0 | 3324 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3325 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3326 | } |
michael@0 | 3327 | } else if ((instr->opc1Value() == 0x0)) { |
michael@0 | 3328 | // vmla, vmls |
michael@0 | 3329 | const bool is_vmls = (instr->opc3Value() & 0x1); |
michael@0 | 3330 | |
michael@0 | 3331 | if (instr->szValue() != 0x1) |
michael@0 | 3332 | MOZ_ASSUME_UNREACHABLE(); // Not used by V8. |
michael@0 | 3333 | |
michael@0 | 3334 | const double dd_val = get_double_from_d_register(vd); |
michael@0 | 3335 | const double dn_val = get_double_from_d_register(vn); |
michael@0 | 3336 | const double dm_val = get_double_from_d_register(vm); |
michael@0 | 3337 | |
michael@0 | 3338 | // Note: we do the mul and add/sub in separate steps to avoid getting a |
michael@0 | 3339 | // result with too high precision. |
michael@0 | 3340 | set_d_register_from_double(vd, dn_val * dm_val); |
michael@0 | 3341 | if (is_vmls) { |
michael@0 | 3342 | set_d_register_from_double(vd, |
michael@0 | 3343 | canonicalizeNaN(dd_val - get_double_from_d_register(vd))); |
michael@0 | 3344 | } else { |
michael@0 | 3345 | set_d_register_from_double(vd, |
michael@0 | 3346 | canonicalizeNaN(dd_val + get_double_from_d_register(vd))); |
michael@0 | 3347 | } |
michael@0 | 3348 | } else if ((instr->opc1Value() == 0x4) && !(instr->opc3Value() & 0x1)) { |
michael@0 | 3349 | // vdiv |
michael@0 | 3350 | if (instr->szValue() != 0x1) { |
michael@0 | 3351 | float fn_value = get_float_from_s_register(vn); |
michael@0 | 3352 | float fm_value = get_float_from_s_register(vm); |
michael@0 | 3353 | float fd_value = fn_value / fm_value; |
michael@0 | 3354 | div_zero_vfp_flag_ = (fm_value == 0); |
michael@0 | 3355 | fd_value = canonicalizeNaN(fd_value); |
michael@0 | 3356 | set_s_register_from_float(vd, fd_value); |
michael@0 | 3357 | } else { |
michael@0 | 3358 | double dn_value = get_double_from_d_register(vn); |
michael@0 | 3359 | double dm_value = get_double_from_d_register(vm); |
michael@0 | 3360 | double dd_value = dn_value / dm_value; |
michael@0 | 3361 | div_zero_vfp_flag_ = (dm_value == 0); |
michael@0 | 3362 | dd_value = canonicalizeNaN(dd_value); |
michael@0 | 3363 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3364 | } |
michael@0 | 3365 | } else { |
michael@0 | 3366 | MOZ_CRASH(); |
michael@0 | 3367 | } |
michael@0 | 3368 | } else { |
michael@0 | 3369 | if (instr->VCValue() == 0x0 && instr->VAValue() == 0x0) { |
michael@0 | 3370 | decodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); |
michael@0 | 3371 | } else if ((instr->VLValue() == 0x0) && |
michael@0 | 3372 | (instr->VCValue() == 0x1) && |
michael@0 | 3373 | (instr->bit(23) == 0x0)) { |
michael@0 | 3374 | // vmov (ARM core register to scalar) |
michael@0 | 3375 | int vd = instr->bits(19, 16) | (instr->bit(7) << 4); |
michael@0 | 3376 | double dd_value = get_double_from_d_register(vd); |
michael@0 | 3377 | int32_t data[2]; |
michael@0 | 3378 | memcpy(data, &dd_value, 8); |
michael@0 | 3379 | data[instr->bit(21)] = get_register(instr->rtValue()); |
michael@0 | 3380 | memcpy(&dd_value, data, 8); |
michael@0 | 3381 | set_d_register_from_double(vd, dd_value); |
michael@0 | 3382 | } else if ((instr->VLValue() == 0x1) && |
michael@0 | 3383 | (instr->VCValue() == 0x1) && |
michael@0 | 3384 | (instr->bit(23) == 0x0)) { |
michael@0 | 3385 | // vmov (scalar to ARM core register) |
michael@0 | 3386 | int vn = instr->bits(19, 16) | (instr->bit(7) << 4); |
michael@0 | 3387 | double dn_value = get_double_from_d_register(vn); |
michael@0 | 3388 | int32_t data[2]; |
michael@0 | 3389 | memcpy(data, &dn_value, 8); |
michael@0 | 3390 | set_register(instr->rtValue(), data[instr->bit(21)]); |
michael@0 | 3391 | } else if ((instr->VLValue() == 0x1) && |
michael@0 | 3392 | (instr->VCValue() == 0x0) && |
michael@0 | 3393 | (instr->VAValue() == 0x7) && |
michael@0 | 3394 | (instr->bits(19, 16) == 0x1)) { |
michael@0 | 3395 | // vmrs |
michael@0 | 3396 | uint32_t rt = instr->rtValue(); |
michael@0 | 3397 | if (rt == 0xF) { |
michael@0 | 3398 | copy_FPSCR_to_APSR(); |
michael@0 | 3399 | } else { |
michael@0 | 3400 | // Emulate FPSCR from the Simulator flags. |
michael@0 | 3401 | uint32_t fpscr = (n_flag_FPSCR_ << 31) | |
michael@0 | 3402 | (z_flag_FPSCR_ << 30) | |
michael@0 | 3403 | (c_flag_FPSCR_ << 29) | |
michael@0 | 3404 | (v_flag_FPSCR_ << 28) | |
michael@0 | 3405 | (FPSCR_default_NaN_mode_ << 25) | |
michael@0 | 3406 | (inexact_vfp_flag_ << 4) | |
michael@0 | 3407 | (underflow_vfp_flag_ << 3) | |
michael@0 | 3408 | (overflow_vfp_flag_ << 2) | |
michael@0 | 3409 | (div_zero_vfp_flag_ << 1) | |
michael@0 | 3410 | (inv_op_vfp_flag_ << 0) | |
michael@0 | 3411 | (FPSCR_rounding_mode_); |
michael@0 | 3412 | set_register(rt, fpscr); |
michael@0 | 3413 | } |
michael@0 | 3414 | } else if ((instr->VLValue() == 0x0) && |
michael@0 | 3415 | (instr->VCValue() == 0x0) && |
michael@0 | 3416 | (instr->VAValue() == 0x7) && |
michael@0 | 3417 | (instr->bits(19, 16) == 0x1)) { |
michael@0 | 3418 | // vmsr |
michael@0 | 3419 | uint32_t rt = instr->rtValue(); |
michael@0 | 3420 | if (rt == pc) { |
michael@0 | 3421 | MOZ_CRASH(); |
michael@0 | 3422 | } else { |
michael@0 | 3423 | uint32_t rt_value = get_register(rt); |
michael@0 | 3424 | n_flag_FPSCR_ = (rt_value >> 31) & 1; |
michael@0 | 3425 | z_flag_FPSCR_ = (rt_value >> 30) & 1; |
michael@0 | 3426 | c_flag_FPSCR_ = (rt_value >> 29) & 1; |
michael@0 | 3427 | v_flag_FPSCR_ = (rt_value >> 28) & 1; |
michael@0 | 3428 | FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; |
michael@0 | 3429 | inexact_vfp_flag_ = (rt_value >> 4) & 1; |
michael@0 | 3430 | underflow_vfp_flag_ = (rt_value >> 3) & 1; |
michael@0 | 3431 | overflow_vfp_flag_ = (rt_value >> 2) & 1; |
michael@0 | 3432 | div_zero_vfp_flag_ = (rt_value >> 1) & 1; |
michael@0 | 3433 | inv_op_vfp_flag_ = (rt_value >> 0) & 1; |
michael@0 | 3434 | FPSCR_rounding_mode_ = |
michael@0 | 3435 | static_cast<VFPRoundingMode>((rt_value) & kVFPRoundingModeMask); |
michael@0 | 3436 | } |
michael@0 | 3437 | } else { |
michael@0 | 3438 | MOZ_CRASH(); |
michael@0 | 3439 | } |
michael@0 | 3440 | } |
michael@0 | 3441 | } |
michael@0 | 3442 | |
michael@0 | 3443 | void |
michael@0 | 3444 | Simulator::decodeVMOVBetweenCoreAndSinglePrecisionRegisters(SimInstruction *instr) |
michael@0 | 3445 | { |
michael@0 | 3446 | MOZ_ASSERT(instr->bit(4) == 1 && |
michael@0 | 3447 | instr->VCValue() == 0x0 && |
michael@0 | 3448 | instr->VAValue() == 0x0); |
michael@0 | 3449 | |
michael@0 | 3450 | int t = instr->rtValue(); |
michael@0 | 3451 | int n = instr->VFPNRegValue(kSinglePrecision); |
michael@0 | 3452 | bool to_arm_register = (instr->VLValue() == 0x1); |
michael@0 | 3453 | if (to_arm_register) { |
michael@0 | 3454 | int32_t int_value = get_sinteger_from_s_register(n); |
michael@0 | 3455 | set_register(t, int_value); |
michael@0 | 3456 | } else { |
michael@0 | 3457 | int32_t rs_val = get_register(t); |
michael@0 | 3458 | set_s_register_from_sinteger(n, rs_val); |
michael@0 | 3459 | } |
michael@0 | 3460 | } |
michael@0 | 3461 | |
michael@0 | 3462 | void |
michael@0 | 3463 | Simulator::decodeVCMP(SimInstruction *instr) |
michael@0 | 3464 | { |
michael@0 | 3465 | MOZ_ASSERT((instr->bit(4) == 0) && (instr->opc1Value() == 0x7)); |
michael@0 | 3466 | MOZ_ASSERT(((instr->opc2Value() == 0x4) || (instr->opc2Value() == 0x5)) && |
michael@0 | 3467 | (instr->opc3Value() & 0x1)); |
michael@0 | 3468 | // Comparison. |
michael@0 | 3469 | |
michael@0 | 3470 | VFPRegPrecision precision = kSinglePrecision; |
michael@0 | 3471 | if (instr->szValue() == 1) |
michael@0 | 3472 | precision = kDoublePrecision; |
michael@0 | 3473 | |
michael@0 | 3474 | int d = instr->VFPDRegValue(precision); |
michael@0 | 3475 | int m = 0; |
michael@0 | 3476 | if (instr->opc2Value() == 0x4) |
michael@0 | 3477 | m = instr->VFPMRegValue(precision); |
michael@0 | 3478 | |
michael@0 | 3479 | if (precision == kDoublePrecision) { |
michael@0 | 3480 | double dd_value = get_double_from_d_register(d); |
michael@0 | 3481 | double dm_value = 0.0; |
michael@0 | 3482 | if (instr->opc2Value() == 0x4) { |
michael@0 | 3483 | dm_value = get_double_from_d_register(m); |
michael@0 | 3484 | } |
michael@0 | 3485 | |
michael@0 | 3486 | // Raise exceptions for quiet NaNs if necessary. |
michael@0 | 3487 | if (instr->bit(7) == 1) { |
michael@0 | 3488 | if (mozilla::IsNaN(dd_value)) |
michael@0 | 3489 | inv_op_vfp_flag_ = true; |
michael@0 | 3490 | } |
michael@0 | 3491 | compute_FPSCR_Flags(dd_value, dm_value); |
michael@0 | 3492 | } else { |
michael@0 | 3493 | float fd_value = get_float_from_s_register(d); |
michael@0 | 3494 | float fm_value = 0.0; |
michael@0 | 3495 | if (instr->opc2Value() == 0x4) |
michael@0 | 3496 | fm_value = get_float_from_s_register(m); |
michael@0 | 3497 | |
michael@0 | 3498 | // Raise exceptions for quiet NaNs if necessary. |
michael@0 | 3499 | if (instr->bit(7) == 1) { |
michael@0 | 3500 | if (mozilla::IsNaN(fd_value)) |
michael@0 | 3501 | inv_op_vfp_flag_ = true; |
michael@0 | 3502 | } |
michael@0 | 3503 | compute_FPSCR_Flags(fd_value, fm_value); |
michael@0 | 3504 | } |
michael@0 | 3505 | } |
michael@0 | 3506 | |
michael@0 | 3507 | void |
michael@0 | 3508 | Simulator::decodeVCVTBetweenDoubleAndSingle(SimInstruction *instr) |
michael@0 | 3509 | { |
michael@0 | 3510 | MOZ_ASSERT(instr->bit(4) == 0 && instr->opc1Value() == 0x7); |
michael@0 | 3511 | MOZ_ASSERT(instr->opc2Value() == 0x7 && instr->opc3Value() == 0x3); |
michael@0 | 3512 | |
michael@0 | 3513 | VFPRegPrecision dst_precision = kDoublePrecision; |
michael@0 | 3514 | VFPRegPrecision src_precision = kSinglePrecision; |
michael@0 | 3515 | if (instr->szValue() == 1) { |
michael@0 | 3516 | dst_precision = kSinglePrecision; |
michael@0 | 3517 | src_precision = kDoublePrecision; |
michael@0 | 3518 | } |
michael@0 | 3519 | |
michael@0 | 3520 | int dst = instr->VFPDRegValue(dst_precision); |
michael@0 | 3521 | int src = instr->VFPMRegValue(src_precision); |
michael@0 | 3522 | |
michael@0 | 3523 | if (dst_precision == kSinglePrecision) { |
michael@0 | 3524 | double val = get_double_from_d_register(src); |
michael@0 | 3525 | set_s_register_from_float(dst, static_cast<float>(val)); |
michael@0 | 3526 | } else { |
michael@0 | 3527 | float val = get_float_from_s_register(src); |
michael@0 | 3528 | set_d_register_from_double(dst, static_cast<double>(val)); |
michael@0 | 3529 | } |
michael@0 | 3530 | } |
michael@0 | 3531 | |
michael@0 | 3532 | static bool |
michael@0 | 3533 | get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) |
michael@0 | 3534 | { |
michael@0 | 3535 | MOZ_ASSERT(mode == SimRN || mode == SimRM || mode == SimRZ); |
michael@0 | 3536 | double max_uint = static_cast<double>(0xffffffffu); |
michael@0 | 3537 | double max_int = static_cast<double>(INT32_MAX); |
michael@0 | 3538 | double min_int = static_cast<double>(INT32_MIN); |
michael@0 | 3539 | |
michael@0 | 3540 | // Check for NaN. |
michael@0 | 3541 | if (val != val) |
michael@0 | 3542 | return true; |
michael@0 | 3543 | |
michael@0 | 3544 | // Check for overflow. This code works because 32bit integers can be |
michael@0 | 3545 | // exactly represented by ieee-754 64bit floating-point values. |
michael@0 | 3546 | switch (mode) { |
michael@0 | 3547 | case SimRN: |
michael@0 | 3548 | return unsigned_ ? (val >= (max_uint + 0.5)) || |
michael@0 | 3549 | (val < -0.5) |
michael@0 | 3550 | : (val >= (max_int + 0.5)) || |
michael@0 | 3551 | (val < (min_int - 0.5)); |
michael@0 | 3552 | case SimRM: |
michael@0 | 3553 | return unsigned_ ? (val >= (max_uint + 1.0)) || |
michael@0 | 3554 | (val < 0) |
michael@0 | 3555 | : (val >= (max_int + 1.0)) || |
michael@0 | 3556 | (val < min_int); |
michael@0 | 3557 | case SimRZ: |
michael@0 | 3558 | return unsigned_ ? (val >= (max_uint + 1.0)) || |
michael@0 | 3559 | (val <= -1) |
michael@0 | 3560 | : (val >= (max_int + 1.0)) || |
michael@0 | 3561 | (val <= (min_int - 1.0)); |
michael@0 | 3562 | default: |
michael@0 | 3563 | MOZ_CRASH(); |
michael@0 | 3564 | return true; |
michael@0 | 3565 | } |
michael@0 | 3566 | } |
michael@0 | 3567 | |
michael@0 | 3568 | // We call this function only if we had a vfp invalid exception. |
michael@0 | 3569 | // It returns the correct saturated value. |
michael@0 | 3570 | static int |
michael@0 | 3571 | VFPConversionSaturate(double val, bool unsigned_res) |
michael@0 | 3572 | { |
michael@0 | 3573 | if (val != val) // NaN. |
michael@0 | 3574 | return 0; |
michael@0 | 3575 | if (unsigned_res) |
michael@0 | 3576 | return (val < 0) ? 0 : 0xffffffffu; |
michael@0 | 3577 | return (val < 0) ? INT32_MIN : INT32_MAX; |
michael@0 | 3578 | } |
michael@0 | 3579 | |
michael@0 | 3580 | void |
michael@0 | 3581 | Simulator::decodeVCVTBetweenFloatingPointAndInteger(SimInstruction *instr) |
michael@0 | 3582 | { |
michael@0 | 3583 | MOZ_ASSERT((instr->bit(4) == 0) && (instr->opc1Value() == 0x7) && |
michael@0 | 3584 | (instr->bits(27, 23) == 0x1D)); |
michael@0 | 3585 | MOZ_ASSERT(((instr->opc2Value() == 0x8) && (instr->opc3Value() & 0x1)) || |
michael@0 | 3586 | (((instr->opc2Value() >> 1) == 0x6) && (instr->opc3Value() & 0x1))); |
michael@0 | 3587 | |
michael@0 | 3588 | // Conversion between floating-point and integer. |
michael@0 | 3589 | bool to_integer = (instr->bit(18) == 1); |
michael@0 | 3590 | |
michael@0 | 3591 | VFPRegPrecision src_precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; |
michael@0 | 3592 | |
michael@0 | 3593 | if (to_integer) { |
michael@0 | 3594 | // We are playing with code close to the C++ standard's limits below, |
michael@0 | 3595 | // hence the very simple code and heavy checks. |
michael@0 | 3596 | // |
michael@0 | 3597 | // Note: |
michael@0 | 3598 | // C++ defines default type casting from floating point to integer as |
michael@0 | 3599 | // (close to) rounding toward zero ("fractional part discarded"). |
michael@0 | 3600 | |
michael@0 | 3601 | int dst = instr->VFPDRegValue(kSinglePrecision); |
michael@0 | 3602 | int src = instr->VFPMRegValue(src_precision); |
michael@0 | 3603 | |
michael@0 | 3604 | // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding |
michael@0 | 3605 | // mode or the default Round to Zero mode. |
michael@0 | 3606 | VFPRoundingMode mode = (instr->bit(7) != 1) ? FPSCR_rounding_mode_ : SimRZ; |
michael@0 | 3607 | MOZ_ASSERT(mode == SimRM || mode == SimRZ || mode == SimRN); |
michael@0 | 3608 | |
michael@0 | 3609 | bool unsigned_integer = (instr->bit(16) == 0); |
michael@0 | 3610 | bool double_precision = (src_precision == kDoublePrecision); |
michael@0 | 3611 | |
michael@0 | 3612 | double val = double_precision |
michael@0 | 3613 | ? get_double_from_d_register(src) |
michael@0 | 3614 | : get_float_from_s_register(src); |
michael@0 | 3615 | |
michael@0 | 3616 | int temp = unsigned_integer ? static_cast<uint32_t>(val) : static_cast<int32_t>(val); |
michael@0 | 3617 | |
michael@0 | 3618 | inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); |
michael@0 | 3619 | |
michael@0 | 3620 | double abs_diff = unsigned_integer |
michael@0 | 3621 | ? std::fabs(val - static_cast<uint32_t>(temp)) |
michael@0 | 3622 | : std::fabs(val - temp); |
michael@0 | 3623 | |
michael@0 | 3624 | inexact_vfp_flag_ = (abs_diff != 0); |
michael@0 | 3625 | |
michael@0 | 3626 | if (inv_op_vfp_flag_) { |
michael@0 | 3627 | temp = VFPConversionSaturate(val, unsigned_integer); |
michael@0 | 3628 | } else { |
michael@0 | 3629 | switch (mode) { |
michael@0 | 3630 | case SimRN: { |
michael@0 | 3631 | int val_sign = (val > 0) ? 1 : -1; |
michael@0 | 3632 | if (abs_diff > 0.5) { |
michael@0 | 3633 | temp += val_sign; |
michael@0 | 3634 | } else if (abs_diff == 0.5) { |
michael@0 | 3635 | // Round to even if exactly halfway. |
michael@0 | 3636 | temp = ((temp % 2) == 0) ? temp : temp + val_sign; |
michael@0 | 3637 | } |
michael@0 | 3638 | break; |
michael@0 | 3639 | } |
michael@0 | 3640 | |
michael@0 | 3641 | case SimRM: |
michael@0 | 3642 | temp = temp > val ? temp - 1 : temp; |
michael@0 | 3643 | break; |
michael@0 | 3644 | |
michael@0 | 3645 | case SimRZ: |
michael@0 | 3646 | // Nothing to do. |
michael@0 | 3647 | break; |
michael@0 | 3648 | |
michael@0 | 3649 | default: |
michael@0 | 3650 | MOZ_CRASH(); |
michael@0 | 3651 | } |
michael@0 | 3652 | } |
michael@0 | 3653 | |
michael@0 | 3654 | // Update the destination register. |
michael@0 | 3655 | set_s_register_from_sinteger(dst, temp); |
michael@0 | 3656 | } else { |
michael@0 | 3657 | bool unsigned_integer = (instr->bit(7) == 0); |
michael@0 | 3658 | int dst = instr->VFPDRegValue(src_precision); |
michael@0 | 3659 | int src = instr->VFPMRegValue(kSinglePrecision); |
michael@0 | 3660 | |
michael@0 | 3661 | int val = get_sinteger_from_s_register(src); |
michael@0 | 3662 | |
michael@0 | 3663 | if (src_precision == kDoublePrecision) { |
michael@0 | 3664 | if (unsigned_integer) |
michael@0 | 3665 | set_d_register_from_double(dst, static_cast<double>(static_cast<uint32_t>(val))); |
michael@0 | 3666 | else |
michael@0 | 3667 | set_d_register_from_double(dst, static_cast<double>(val)); |
michael@0 | 3668 | } else { |
michael@0 | 3669 | if (unsigned_integer) |
michael@0 | 3670 | set_s_register_from_float(dst, static_cast<float>(static_cast<uint32_t>(val))); |
michael@0 | 3671 | else |
michael@0 | 3672 | set_s_register_from_float(dst, static_cast<float>(val)); |
michael@0 | 3673 | } |
michael@0 | 3674 | } |
michael@0 | 3675 | } |
michael@0 | 3676 | |
michael@0 | 3677 | // A VFPv3 specific instruction. |
michael@0 | 3678 | void |
michael@0 | 3679 | Simulator::decodeVCVTBetweenFloatingPointAndIntegerFrac(SimInstruction *instr) |
michael@0 | 3680 | { |
michael@0 | 3681 | MOZ_ASSERT(instr->bits(27, 24) == 0xE && instr->opc1Value() == 0x7 && instr->bit(19) == 1 && |
michael@0 | 3682 | instr->bit(17) == 1 && instr->bits(11,9) == 0x5 && instr->bit(6) == 1 && |
michael@0 | 3683 | instr->bit(4) == 0); |
michael@0 | 3684 | |
michael@0 | 3685 | int size = (instr->bit(7) == 1) ? 32 : 16; |
michael@0 | 3686 | |
michael@0 | 3687 | int fraction_bits = size - ((instr->bits(3, 0) << 1) | instr->bit(5)); |
michael@0 | 3688 | double mult = 1 << fraction_bits; |
michael@0 | 3689 | |
michael@0 | 3690 | MOZ_ASSERT(size == 32); // Only handling size == 32 for now. |
michael@0 | 3691 | |
michael@0 | 3692 | // Conversion between floating-point and integer. |
michael@0 | 3693 | bool to_fixed = (instr->bit(18) == 1); |
michael@0 | 3694 | |
michael@0 | 3695 | VFPRegPrecision precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; |
michael@0 | 3696 | |
michael@0 | 3697 | if (to_fixed) { |
michael@0 | 3698 | // We are playing with code close to the C++ standard's limits below, |
michael@0 | 3699 | // hence the very simple code and heavy checks. |
michael@0 | 3700 | // |
michael@0 | 3701 | // Note: C++ defines default type casting from floating point to integer as |
michael@0 | 3702 | // (close to) rounding toward zero ("fractional part discarded"). |
michael@0 | 3703 | |
michael@0 | 3704 | int dst = instr->VFPDRegValue(precision); |
michael@0 | 3705 | |
michael@0 | 3706 | bool unsigned_integer = (instr->bit(16) == 1); |
michael@0 | 3707 | bool double_precision = (precision == kDoublePrecision); |
michael@0 | 3708 | |
michael@0 | 3709 | double val = double_precision |
michael@0 | 3710 | ? get_double_from_d_register(dst) |
michael@0 | 3711 | : get_float_from_s_register(dst); |
michael@0 | 3712 | |
michael@0 | 3713 | // Scale value by specified number of fraction bits. |
michael@0 | 3714 | val *= mult; |
michael@0 | 3715 | |
michael@0 | 3716 | // Rounding down towards zero. No need to account for the rounding error as this |
michael@0 | 3717 | // instruction always rounds down towards zero. See SimRZ below. |
michael@0 | 3718 | int temp = unsigned_integer ? static_cast<uint32_t>(val) : static_cast<int32_t>(val); |
michael@0 | 3719 | |
michael@0 | 3720 | inv_op_vfp_flag_ = get_inv_op_vfp_flag(SimRZ, val, unsigned_integer); |
michael@0 | 3721 | |
michael@0 | 3722 | double abs_diff = unsigned_integer |
michael@0 | 3723 | ? std::fabs(val - static_cast<uint32_t>(temp)) |
michael@0 | 3724 | : std::fabs(val - temp); |
michael@0 | 3725 | |
michael@0 | 3726 | inexact_vfp_flag_ = (abs_diff != 0); |
michael@0 | 3727 | |
michael@0 | 3728 | if (inv_op_vfp_flag_) |
michael@0 | 3729 | temp = VFPConversionSaturate(val, unsigned_integer); |
michael@0 | 3730 | |
michael@0 | 3731 | // Update the destination register. |
michael@0 | 3732 | if (double_precision) { |
michael@0 | 3733 | uint32_t dbl[2]; |
michael@0 | 3734 | dbl[0] = temp; dbl[1] = 0; |
michael@0 | 3735 | set_d_register(dst, dbl); |
michael@0 | 3736 | } else { |
michael@0 | 3737 | set_s_register_from_sinteger(dst, temp); |
michael@0 | 3738 | } |
michael@0 | 3739 | } else { |
michael@0 | 3740 | MOZ_ASSUME_UNREACHABLE(); // Not implemented, fixed to float. |
michael@0 | 3741 | } |
michael@0 | 3742 | } |
michael@0 | 3743 | |
michael@0 | 3744 | void |
michael@0 | 3745 | Simulator::decodeType6CoprocessorIns(SimInstruction *instr) |
michael@0 | 3746 | { |
michael@0 | 3747 | MOZ_ASSERT(instr->typeValue() == 6); |
michael@0 | 3748 | |
michael@0 | 3749 | if (instr->coprocessorValue() == 0xA) { |
michael@0 | 3750 | switch (instr->opcodeValue()) { |
michael@0 | 3751 | case 0x8: |
michael@0 | 3752 | case 0xA: |
michael@0 | 3753 | case 0xC: |
michael@0 | 3754 | case 0xE: { // Load and store single precision float to memory. |
michael@0 | 3755 | int rn = instr->rnValue(); |
michael@0 | 3756 | int vd = instr->VFPDRegValue(kSinglePrecision); |
michael@0 | 3757 | int offset = instr->immed8Value(); |
michael@0 | 3758 | if (!instr->hasU()) |
michael@0 | 3759 | offset = -offset; |
michael@0 | 3760 | |
michael@0 | 3761 | int32_t address = get_register(rn) + 4 * offset; |
michael@0 | 3762 | if (instr->hasL()) { |
michael@0 | 3763 | // Load double from memory: vldr. |
michael@0 | 3764 | set_s_register_from_sinteger(vd, readW(address, instr)); |
michael@0 | 3765 | } else { |
michael@0 | 3766 | // Store double to memory: vstr. |
michael@0 | 3767 | writeW(address, get_sinteger_from_s_register(vd), instr); |
michael@0 | 3768 | } |
michael@0 | 3769 | break; |
michael@0 | 3770 | } |
michael@0 | 3771 | case 0x4: |
michael@0 | 3772 | case 0x5: |
michael@0 | 3773 | case 0x6: |
michael@0 | 3774 | case 0x7: |
michael@0 | 3775 | case 0x9: |
michael@0 | 3776 | case 0xB: |
michael@0 | 3777 | // Load/store multiple single from memory: vldm/vstm. |
michael@0 | 3778 | handleVList(instr); |
michael@0 | 3779 | break; |
michael@0 | 3780 | default: |
michael@0 | 3781 | MOZ_CRASH(); |
michael@0 | 3782 | } |
michael@0 | 3783 | } else if (instr->coprocessorValue() == 0xB) { |
michael@0 | 3784 | switch (instr->opcodeValue()) { |
michael@0 | 3785 | case 0x2: |
michael@0 | 3786 | // Load and store double to two GP registers |
michael@0 | 3787 | if (instr->bits(7, 6) != 0 || instr->bit(4) != 1) { |
michael@0 | 3788 | MOZ_CRASH(); // Not used atm. |
michael@0 | 3789 | } else { |
michael@0 | 3790 | int rt = instr->rtValue(); |
michael@0 | 3791 | int rn = instr->rnValue(); |
michael@0 | 3792 | int vm = instr->VFPMRegValue(kDoublePrecision); |
michael@0 | 3793 | if (instr->hasL()) { |
michael@0 | 3794 | int32_t data[2]; |
michael@0 | 3795 | double d = get_double_from_d_register(vm); |
michael@0 | 3796 | memcpy(data, &d, 8); |
michael@0 | 3797 | set_register(rt, data[0]); |
michael@0 | 3798 | set_register(rn, data[1]); |
michael@0 | 3799 | } else { |
michael@0 | 3800 | int32_t data[] = { get_register(rt), get_register(rn) }; |
michael@0 | 3801 | double d; |
michael@0 | 3802 | memcpy(&d, data, 8); |
michael@0 | 3803 | set_d_register_from_double(vm, d); |
michael@0 | 3804 | } |
michael@0 | 3805 | } |
michael@0 | 3806 | break; |
michael@0 | 3807 | case 0x8: |
michael@0 | 3808 | case 0xA: |
michael@0 | 3809 | case 0xC: |
michael@0 | 3810 | case 0xE: { // Load and store double to memory. |
michael@0 | 3811 | int rn = instr->rnValue(); |
michael@0 | 3812 | int vd = instr->VFPDRegValue(kDoublePrecision); |
michael@0 | 3813 | int offset = instr->immed8Value(); |
michael@0 | 3814 | if (!instr->hasU()) |
michael@0 | 3815 | offset = -offset; |
michael@0 | 3816 | int32_t address = get_register(rn) + 4 * offset; |
michael@0 | 3817 | if (instr->hasL()) { |
michael@0 | 3818 | // Load double from memory: vldr. |
michael@0 | 3819 | int32_t data[] = { |
michael@0 | 3820 | readW(address, instr), |
michael@0 | 3821 | readW(address + 4, instr) |
michael@0 | 3822 | }; |
michael@0 | 3823 | double val; |
michael@0 | 3824 | memcpy(&val, data, 8); |
michael@0 | 3825 | set_d_register_from_double(vd, val); |
michael@0 | 3826 | } else { |
michael@0 | 3827 | // Store double to memory: vstr. |
michael@0 | 3828 | int32_t data[2]; |
michael@0 | 3829 | double val = get_double_from_d_register(vd); |
michael@0 | 3830 | memcpy(data, &val, 8); |
michael@0 | 3831 | writeW(address, data[0], instr); |
michael@0 | 3832 | writeW(address + 4, data[1], instr); |
michael@0 | 3833 | } |
michael@0 | 3834 | break; |
michael@0 | 3835 | } |
michael@0 | 3836 | case 0x4: |
michael@0 | 3837 | case 0x5: |
michael@0 | 3838 | case 0x6: |
michael@0 | 3839 | case 0x7: |
michael@0 | 3840 | case 0x9: |
michael@0 | 3841 | case 0xB: |
michael@0 | 3842 | // Load/store multiple double from memory: vldm/vstm. |
michael@0 | 3843 | handleVList(instr); |
michael@0 | 3844 | break; |
michael@0 | 3845 | default: |
michael@0 | 3846 | MOZ_CRASH(); |
michael@0 | 3847 | } |
michael@0 | 3848 | } else { |
michael@0 | 3849 | MOZ_CRASH(); |
michael@0 | 3850 | } |
michael@0 | 3851 | } |
michael@0 | 3852 | |
michael@0 | 3853 | void |
michael@0 | 3854 | Simulator::decodeSpecialCondition(SimInstruction *instr) |
michael@0 | 3855 | { |
michael@0 | 3856 | switch (instr->specialValue()) { |
michael@0 | 3857 | case 5: |
michael@0 | 3858 | if (instr->bits(18, 16) == 0 && instr->bits(11, 6) == 0x28 && instr->bit(4) == 1) { |
michael@0 | 3859 | // vmovl signed |
michael@0 | 3860 | int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
michael@0 | 3861 | int Vm = (instr->bit(5) << 4) | instr->vmValue(); |
michael@0 | 3862 | int imm3 = instr->bits(21, 19); |
michael@0 | 3863 | if (imm3 != 1 && imm3 != 2 && imm3 != 4) |
michael@0 | 3864 | MOZ_CRASH(); |
michael@0 | 3865 | int esize = 8 * imm3; |
michael@0 | 3866 | int elements = 64 / esize; |
michael@0 | 3867 | int8_t from[8]; |
michael@0 | 3868 | get_d_register(Vm, reinterpret_cast<uint64_t*>(from)); |
michael@0 | 3869 | int16_t to[8]; |
michael@0 | 3870 | int e = 0; |
michael@0 | 3871 | while (e < elements) { |
michael@0 | 3872 | to[e] = from[e]; |
michael@0 | 3873 | e++; |
michael@0 | 3874 | } |
michael@0 | 3875 | set_q_register(Vd, reinterpret_cast<uint64_t*>(to)); |
michael@0 | 3876 | } else { |
michael@0 | 3877 | MOZ_CRASH(); |
michael@0 | 3878 | } |
michael@0 | 3879 | break; |
michael@0 | 3880 | case 7: |
michael@0 | 3881 | if (instr->bits(18, 16) == 0 && instr->bits(11, 6) == 0x28 && instr->bit(4) == 1) { |
michael@0 | 3882 | // vmovl unsigned |
michael@0 | 3883 | int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
michael@0 | 3884 | int Vm = (instr->bit(5) << 4) | instr->vmValue(); |
michael@0 | 3885 | int imm3 = instr->bits(21, 19); |
michael@0 | 3886 | if (imm3 != 1 && imm3 != 2 && imm3 != 4) |
michael@0 | 3887 | MOZ_CRASH(); |
michael@0 | 3888 | int esize = 8 * imm3; |
michael@0 | 3889 | int elements = 64 / esize; |
michael@0 | 3890 | uint8_t from[8]; |
michael@0 | 3891 | get_d_register(Vm, reinterpret_cast<uint64_t*>(from)); |
michael@0 | 3892 | uint16_t to[8]; |
michael@0 | 3893 | int e = 0; |
michael@0 | 3894 | while (e < elements) { |
michael@0 | 3895 | to[e] = from[e]; |
michael@0 | 3896 | e++; |
michael@0 | 3897 | } |
michael@0 | 3898 | set_q_register(Vd, reinterpret_cast<uint64_t*>(to)); |
michael@0 | 3899 | } else { |
michael@0 | 3900 | MOZ_CRASH(); |
michael@0 | 3901 | } |
michael@0 | 3902 | break; |
michael@0 | 3903 | case 8: |
michael@0 | 3904 | if (instr->bits(21, 20) == 0) { |
michael@0 | 3905 | // vst1 |
michael@0 | 3906 | int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
michael@0 | 3907 | int Rn = instr->vnValue(); |
michael@0 | 3908 | int type = instr->bits(11, 8); |
michael@0 | 3909 | int Rm = instr->vmValue(); |
michael@0 | 3910 | int32_t address = get_register(Rn); |
michael@0 | 3911 | int regs = 0; |
michael@0 | 3912 | switch (type) { |
michael@0 | 3913 | case nlt_1: |
michael@0 | 3914 | regs = 1; |
michael@0 | 3915 | break; |
michael@0 | 3916 | case nlt_2: |
michael@0 | 3917 | regs = 2; |
michael@0 | 3918 | break; |
michael@0 | 3919 | case nlt_3: |
michael@0 | 3920 | regs = 3; |
michael@0 | 3921 | break; |
michael@0 | 3922 | case nlt_4: |
michael@0 | 3923 | regs = 4; |
michael@0 | 3924 | break; |
michael@0 | 3925 | default: |
michael@0 | 3926 | MOZ_CRASH(); |
michael@0 | 3927 | break; |
michael@0 | 3928 | } |
michael@0 | 3929 | int r = 0; |
michael@0 | 3930 | while (r < regs) { |
michael@0 | 3931 | uint32_t data[2]; |
michael@0 | 3932 | get_d_register(Vd + r, data); |
michael@0 | 3933 | writeW(address, data[0], instr); |
michael@0 | 3934 | writeW(address + 4, data[1], instr); |
michael@0 | 3935 | address += 8; |
michael@0 | 3936 | r++; |
michael@0 | 3937 | } |
michael@0 | 3938 | if (Rm != 15) { |
michael@0 | 3939 | if (Rm == 13) |
michael@0 | 3940 | set_register(Rn, address); |
michael@0 | 3941 | else |
michael@0 | 3942 | set_register(Rn, get_register(Rn) + get_register(Rm)); |
michael@0 | 3943 | } |
michael@0 | 3944 | } else if (instr->bits(21, 20) == 2) { |
michael@0 | 3945 | // vld1 |
michael@0 | 3946 | int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
michael@0 | 3947 | int Rn = instr->vnValue(); |
michael@0 | 3948 | int type = instr->bits(11, 8); |
michael@0 | 3949 | int Rm = instr->vmValue(); |
michael@0 | 3950 | int32_t address = get_register(Rn); |
michael@0 | 3951 | int regs = 0; |
michael@0 | 3952 | switch (type) { |
michael@0 | 3953 | case nlt_1: |
michael@0 | 3954 | regs = 1; |
michael@0 | 3955 | break; |
michael@0 | 3956 | case nlt_2: |
michael@0 | 3957 | regs = 2; |
michael@0 | 3958 | break; |
michael@0 | 3959 | case nlt_3: |
michael@0 | 3960 | regs = 3; |
michael@0 | 3961 | break; |
michael@0 | 3962 | case nlt_4: |
michael@0 | 3963 | regs = 4; |
michael@0 | 3964 | break; |
michael@0 | 3965 | default: |
michael@0 | 3966 | MOZ_CRASH(); |
michael@0 | 3967 | break; |
michael@0 | 3968 | } |
michael@0 | 3969 | int r = 0; |
michael@0 | 3970 | while (r < regs) { |
michael@0 | 3971 | uint32_t data[2]; |
michael@0 | 3972 | data[0] = readW(address, instr); |
michael@0 | 3973 | data[1] = readW(address + 4, instr); |
michael@0 | 3974 | set_d_register(Vd + r, data); |
michael@0 | 3975 | address += 8; |
michael@0 | 3976 | r++; |
michael@0 | 3977 | } |
michael@0 | 3978 | if (Rm != 15) { |
michael@0 | 3979 | if (Rm == 13) |
michael@0 | 3980 | set_register(Rn, address); |
michael@0 | 3981 | else |
michael@0 | 3982 | set_register(Rn, get_register(Rn) + get_register(Rm)); |
michael@0 | 3983 | } |
michael@0 | 3984 | } else { |
michael@0 | 3985 | MOZ_CRASH(); |
michael@0 | 3986 | } |
michael@0 | 3987 | break; |
michael@0 | 3988 | case 0xA: |
michael@0 | 3989 | case 0xB: |
michael@0 | 3990 | if (instr->bits(22, 20) == 5 && instr->bits(15, 12) == 0xf) { |
michael@0 | 3991 | // pld: ignore instruction. |
michael@0 | 3992 | } else { |
michael@0 | 3993 | MOZ_CRASH(); |
michael@0 | 3994 | } |
michael@0 | 3995 | break; |
michael@0 | 3996 | default: |
michael@0 | 3997 | MOZ_CRASH(); |
michael@0 | 3998 | } |
michael@0 | 3999 | } |
michael@0 | 4000 | |
michael@0 | 4001 | // Executes the current instruction. |
michael@0 | 4002 | void |
michael@0 | 4003 | Simulator::instructionDecode(SimInstruction *instr) |
michael@0 | 4004 | { |
michael@0 | 4005 | if (Simulator::ICacheCheckingEnabled) { |
michael@0 | 4006 | AutoLockSimulatorRuntime alsr(srt_); |
michael@0 | 4007 | CheckICache(srt_->icache(), instr); |
michael@0 | 4008 | } |
michael@0 | 4009 | |
michael@0 | 4010 | pc_modified_ = false; |
michael@0 | 4011 | |
michael@0 | 4012 | static const uint32_t kSpecialCondition = 15 << 28; |
michael@0 | 4013 | if (instr->conditionField() == kSpecialCondition) { |
michael@0 | 4014 | decodeSpecialCondition(instr); |
michael@0 | 4015 | } else if (conditionallyExecute(instr)) { |
michael@0 | 4016 | switch (instr->typeValue()) { |
michael@0 | 4017 | case 0: |
michael@0 | 4018 | case 1: |
michael@0 | 4019 | decodeType01(instr); |
michael@0 | 4020 | break; |
michael@0 | 4021 | case 2: |
michael@0 | 4022 | decodeType2(instr); |
michael@0 | 4023 | break; |
michael@0 | 4024 | case 3: |
michael@0 | 4025 | decodeType3(instr); |
michael@0 | 4026 | break; |
michael@0 | 4027 | case 4: |
michael@0 | 4028 | decodeType4(instr); |
michael@0 | 4029 | break; |
michael@0 | 4030 | case 5: |
michael@0 | 4031 | decodeType5(instr); |
michael@0 | 4032 | break; |
michael@0 | 4033 | case 6: |
michael@0 | 4034 | decodeType6(instr); |
michael@0 | 4035 | break; |
michael@0 | 4036 | case 7: |
michael@0 | 4037 | decodeType7(instr); |
michael@0 | 4038 | break; |
michael@0 | 4039 | default: |
michael@0 | 4040 | MOZ_CRASH(); |
michael@0 | 4041 | break; |
michael@0 | 4042 | } |
michael@0 | 4043 | // If the instruction is a non taken conditional stop, we need to skip the |
michael@0 | 4044 | // inlined message address. |
michael@0 | 4045 | } else if (instr->isStop()) { |
michael@0 | 4046 | set_pc(get_pc() + 2 * SimInstruction::kInstrSize); |
michael@0 | 4047 | } |
michael@0 | 4048 | if (!pc_modified_) |
michael@0 | 4049 | set_register(pc, reinterpret_cast<int32_t>(instr) + SimInstruction::kInstrSize); |
michael@0 | 4050 | } |
michael@0 | 4051 | |
michael@0 | 4052 | |
michael@0 | 4053 | template<bool EnableStopSimAt> |
michael@0 | 4054 | void |
michael@0 | 4055 | Simulator::execute() |
michael@0 | 4056 | { |
michael@0 | 4057 | // Get the PC to simulate. Cannot use the accessor here as we need the |
michael@0 | 4058 | // raw PC value and not the one used as input to arithmetic instructions. |
michael@0 | 4059 | int program_counter = get_pc(); |
michael@0 | 4060 | AsmJSActivation *activation = TlsPerThreadData.get()->asmJSActivationStackFromOwnerThread(); |
michael@0 | 4061 | |
michael@0 | 4062 | while (program_counter != end_sim_pc) { |
michael@0 | 4063 | if (EnableStopSimAt && (icount_ == Simulator::StopSimAt)) { |
michael@0 | 4064 | fprintf(stderr, "\nStopped simulation at icount %lld\n", icount_); |
michael@0 | 4065 | ArmDebugger dbg(this); |
michael@0 | 4066 | dbg.debug(); |
michael@0 | 4067 | } else { |
michael@0 | 4068 | SimInstruction *instr = reinterpret_cast<SimInstruction *>(program_counter); |
michael@0 | 4069 | instructionDecode(instr); |
michael@0 | 4070 | icount_++; |
michael@0 | 4071 | |
michael@0 | 4072 | int32_t rpc = resume_pc_; |
michael@0 | 4073 | if (MOZ_UNLIKELY(rpc != 0)) { |
michael@0 | 4074 | // AsmJS signal handler ran and we have to adjust the pc. |
michael@0 | 4075 | activation->setInterrupted((void *)get_pc()); |
michael@0 | 4076 | set_pc(rpc); |
michael@0 | 4077 | resume_pc_ = 0; |
michael@0 | 4078 | } |
michael@0 | 4079 | } |
michael@0 | 4080 | program_counter = get_pc(); |
michael@0 | 4081 | } |
michael@0 | 4082 | } |
michael@0 | 4083 | |
michael@0 | 4084 | void |
michael@0 | 4085 | Simulator::callInternal(uint8_t *entry) |
michael@0 | 4086 | { |
michael@0 | 4087 | // Prepare to execute the code at entry. |
michael@0 | 4088 | set_register(pc, reinterpret_cast<int32_t>(entry)); |
michael@0 | 4089 | |
michael@0 | 4090 | // Put down marker for end of simulation. The simulator will stop simulation |
michael@0 | 4091 | // when the PC reaches this value. By saving the "end simulation" value into |
michael@0 | 4092 | // the LR the simulation stops when returning to this call point. |
michael@0 | 4093 | set_register(lr, end_sim_pc); |
michael@0 | 4094 | |
michael@0 | 4095 | // Remember the values of callee-saved registers. |
michael@0 | 4096 | // The code below assumes that r9 is not used as sb (static base) in |
michael@0 | 4097 | // simulator code and therefore is regarded as a callee-saved register. |
michael@0 | 4098 | int32_t r4_val = get_register(r4); |
michael@0 | 4099 | int32_t r5_val = get_register(r5); |
michael@0 | 4100 | int32_t r6_val = get_register(r6); |
michael@0 | 4101 | int32_t r7_val = get_register(r7); |
michael@0 | 4102 | int32_t r8_val = get_register(r8); |
michael@0 | 4103 | int32_t r9_val = get_register(r9); |
michael@0 | 4104 | int32_t r10_val = get_register(r10); |
michael@0 | 4105 | int32_t r11_val = get_register(r11); |
michael@0 | 4106 | |
michael@0 | 4107 | // Remember d8 to d15 which are callee-saved. |
michael@0 | 4108 | uint64_t d8_val; |
michael@0 | 4109 | get_d_register(d8, &d8_val); |
michael@0 | 4110 | uint64_t d9_val; |
michael@0 | 4111 | get_d_register(d9, &d9_val); |
michael@0 | 4112 | uint64_t d10_val; |
michael@0 | 4113 | get_d_register(d10, &d10_val); |
michael@0 | 4114 | uint64_t d11_val; |
michael@0 | 4115 | get_d_register(d11, &d11_val); |
michael@0 | 4116 | uint64_t d12_val; |
michael@0 | 4117 | get_d_register(d12, &d12_val); |
michael@0 | 4118 | uint64_t d13_val; |
michael@0 | 4119 | get_d_register(d13, &d13_val); |
michael@0 | 4120 | uint64_t d14_val; |
michael@0 | 4121 | get_d_register(d14, &d14_val); |
michael@0 | 4122 | uint64_t d15_val; |
michael@0 | 4123 | get_d_register(d15, &d15_val); |
michael@0 | 4124 | |
michael@0 | 4125 | // Set up the callee-saved registers with a known value. To be able to check |
michael@0 | 4126 | // that they are preserved properly across JS execution. |
michael@0 | 4127 | int32_t callee_saved_value = uint32_t(icount_); |
michael@0 | 4128 | set_register(r4, callee_saved_value); |
michael@0 | 4129 | set_register(r5, callee_saved_value); |
michael@0 | 4130 | set_register(r6, callee_saved_value); |
michael@0 | 4131 | set_register(r7, callee_saved_value); |
michael@0 | 4132 | set_register(r8, callee_saved_value); |
michael@0 | 4133 | set_register(r9, callee_saved_value); |
michael@0 | 4134 | set_register(r10, callee_saved_value); |
michael@0 | 4135 | set_register(r11, callee_saved_value); |
michael@0 | 4136 | |
michael@0 | 4137 | uint64_t callee_saved_value_d = uint64_t(icount_); |
michael@0 | 4138 | set_d_register(d8, &callee_saved_value_d); |
michael@0 | 4139 | set_d_register(d9, &callee_saved_value_d); |
michael@0 | 4140 | set_d_register(d10, &callee_saved_value_d); |
michael@0 | 4141 | set_d_register(d11, &callee_saved_value_d); |
michael@0 | 4142 | set_d_register(d12, &callee_saved_value_d); |
michael@0 | 4143 | set_d_register(d13, &callee_saved_value_d); |
michael@0 | 4144 | set_d_register(d14, &callee_saved_value_d); |
michael@0 | 4145 | set_d_register(d15, &callee_saved_value_d); |
michael@0 | 4146 | |
michael@0 | 4147 | // Start the simulation |
michael@0 | 4148 | if (Simulator::StopSimAt != -1L) |
michael@0 | 4149 | execute<true>(); |
michael@0 | 4150 | else |
michael@0 | 4151 | execute<false>(); |
michael@0 | 4152 | |
michael@0 | 4153 | // Check that the callee-saved registers have been preserved. |
michael@0 | 4154 | MOZ_ASSERT(callee_saved_value == get_register(r4)); |
michael@0 | 4155 | MOZ_ASSERT(callee_saved_value == get_register(r5)); |
michael@0 | 4156 | MOZ_ASSERT(callee_saved_value == get_register(r6)); |
michael@0 | 4157 | MOZ_ASSERT(callee_saved_value == get_register(r7)); |
michael@0 | 4158 | MOZ_ASSERT(callee_saved_value == get_register(r8)); |
michael@0 | 4159 | MOZ_ASSERT(callee_saved_value == get_register(r9)); |
michael@0 | 4160 | MOZ_ASSERT(callee_saved_value == get_register(r10)); |
michael@0 | 4161 | MOZ_ASSERT(callee_saved_value == get_register(r11)); |
michael@0 | 4162 | |
michael@0 | 4163 | uint64_t value; |
michael@0 | 4164 | get_d_register(d8, &value); |
michael@0 | 4165 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4166 | get_d_register(d9, &value); |
michael@0 | 4167 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4168 | get_d_register(d10, &value); |
michael@0 | 4169 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4170 | get_d_register(d11, &value); |
michael@0 | 4171 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4172 | get_d_register(d12, &value); |
michael@0 | 4173 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4174 | get_d_register(d13, &value); |
michael@0 | 4175 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4176 | get_d_register(d14, &value); |
michael@0 | 4177 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4178 | get_d_register(d15, &value); |
michael@0 | 4179 | MOZ_ASSERT(callee_saved_value_d == value); |
michael@0 | 4180 | |
michael@0 | 4181 | // Restore callee-saved registers with the original value. |
michael@0 | 4182 | set_register(r4, r4_val); |
michael@0 | 4183 | set_register(r5, r5_val); |
michael@0 | 4184 | set_register(r6, r6_val); |
michael@0 | 4185 | set_register(r7, r7_val); |
michael@0 | 4186 | set_register(r8, r8_val); |
michael@0 | 4187 | set_register(r9, r9_val); |
michael@0 | 4188 | set_register(r10, r10_val); |
michael@0 | 4189 | set_register(r11, r11_val); |
michael@0 | 4190 | |
michael@0 | 4191 | set_d_register(d8, &d8_val); |
michael@0 | 4192 | set_d_register(d9, &d9_val); |
michael@0 | 4193 | set_d_register(d10, &d10_val); |
michael@0 | 4194 | set_d_register(d11, &d11_val); |
michael@0 | 4195 | set_d_register(d12, &d12_val); |
michael@0 | 4196 | set_d_register(d13, &d13_val); |
michael@0 | 4197 | set_d_register(d14, &d14_val); |
michael@0 | 4198 | set_d_register(d15, &d15_val); |
michael@0 | 4199 | } |
michael@0 | 4200 | |
michael@0 | 4201 | int64_t |
michael@0 | 4202 | Simulator::call(uint8_t* entry, int argument_count, ...) |
michael@0 | 4203 | { |
michael@0 | 4204 | va_list parameters; |
michael@0 | 4205 | va_start(parameters, argument_count); |
michael@0 | 4206 | |
michael@0 | 4207 | // First four arguments passed in registers. |
michael@0 | 4208 | MOZ_ASSERT(argument_count >= 2); |
michael@0 | 4209 | set_register(r0, va_arg(parameters, int32_t)); |
michael@0 | 4210 | set_register(r1, va_arg(parameters, int32_t)); |
michael@0 | 4211 | if (argument_count >= 3) |
michael@0 | 4212 | set_register(r2, va_arg(parameters, int32_t)); |
michael@0 | 4213 | if (argument_count >= 4) |
michael@0 | 4214 | set_register(r3, va_arg(parameters, int32_t)); |
michael@0 | 4215 | |
michael@0 | 4216 | // Remaining arguments passed on stack. |
michael@0 | 4217 | int original_stack = get_register(sp); |
michael@0 | 4218 | int entry_stack = original_stack; |
michael@0 | 4219 | if (argument_count >= 4) |
michael@0 | 4220 | entry_stack -= (argument_count - 4) * sizeof(int32_t); |
michael@0 | 4221 | |
michael@0 | 4222 | entry_stack &= ~StackAlignment; |
michael@0 | 4223 | |
michael@0 | 4224 | // Store remaining arguments on stack, from low to high memory. |
michael@0 | 4225 | intptr_t *stack_argument = reinterpret_cast<intptr_t*>(entry_stack); |
michael@0 | 4226 | for (int i = 4; i < argument_count; i++) |
michael@0 | 4227 | stack_argument[i - 4] = va_arg(parameters, int32_t); |
michael@0 | 4228 | va_end(parameters); |
michael@0 | 4229 | set_register(sp, entry_stack); |
michael@0 | 4230 | |
michael@0 | 4231 | callInternal(entry); |
michael@0 | 4232 | |
michael@0 | 4233 | // Pop stack passed arguments. |
michael@0 | 4234 | MOZ_ASSERT(entry_stack == get_register(sp)); |
michael@0 | 4235 | set_register(sp, original_stack); |
michael@0 | 4236 | |
michael@0 | 4237 | int64_t result = (int64_t(get_register(r1)) << 32) | get_register(r0); |
michael@0 | 4238 | return result; |
michael@0 | 4239 | } |
michael@0 | 4240 | |
michael@0 | 4241 | Simulator * |
michael@0 | 4242 | Simulator::Current() |
michael@0 | 4243 | { |
michael@0 | 4244 | PerThreadData *pt = TlsPerThreadData.get(); |
michael@0 | 4245 | Simulator *sim = pt->simulator(); |
michael@0 | 4246 | if (!sim) { |
michael@0 | 4247 | sim = js_new<Simulator>(pt->simulatorRuntime()); |
michael@0 | 4248 | pt->setSimulator(sim); |
michael@0 | 4249 | } |
michael@0 | 4250 | |
michael@0 | 4251 | return sim; |
michael@0 | 4252 | } |
michael@0 | 4253 | |
michael@0 | 4254 | } // namespace jit |
michael@0 | 4255 | } // namespace js |
michael@0 | 4256 | |
michael@0 | 4257 | js::jit::Simulator * |
michael@0 | 4258 | js::PerThreadData::simulator() const |
michael@0 | 4259 | { |
michael@0 | 4260 | return simulator_; |
michael@0 | 4261 | } |
michael@0 | 4262 | |
michael@0 | 4263 | void |
michael@0 | 4264 | js::PerThreadData::setSimulator(js::jit::Simulator *sim) |
michael@0 | 4265 | { |
michael@0 | 4266 | simulator_ = sim; |
michael@0 | 4267 | simulatorStackLimit_ = sim->stackLimit(); |
michael@0 | 4268 | } |
michael@0 | 4269 | |
michael@0 | 4270 | js::jit::SimulatorRuntime * |
michael@0 | 4271 | js::PerThreadData::simulatorRuntime() const |
michael@0 | 4272 | { |
michael@0 | 4273 | return runtime_->simulatorRuntime(); |
michael@0 | 4274 | } |
michael@0 | 4275 | |
michael@0 | 4276 | uintptr_t * |
michael@0 | 4277 | js::PerThreadData::addressOfSimulatorStackLimit() |
michael@0 | 4278 | { |
michael@0 | 4279 | return &simulatorStackLimit_; |
michael@0 | 4280 | } |
michael@0 | 4281 | |
michael@0 | 4282 | js::jit::SimulatorRuntime * |
michael@0 | 4283 | JSRuntime::simulatorRuntime() const |
michael@0 | 4284 | { |
michael@0 | 4285 | return simulatorRuntime_; |
michael@0 | 4286 | } |
michael@0 | 4287 | |
michael@0 | 4288 | void |
michael@0 | 4289 | JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt) |
michael@0 | 4290 | { |
michael@0 | 4291 | MOZ_ASSERT(!simulatorRuntime_); |
michael@0 | 4292 | simulatorRuntime_ = srt; |
michael@0 | 4293 | } |