js/src/jit/arm/Simulator-arm.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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, &registers_[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 }

mercurial