michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: // Copyright 2012 the V8 project authors. All rights reserved. michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following michael@0: // disclaimer in the documentation and/or other materials provided michael@0: // with the distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived michael@0: // from this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: #include "jit/arm/Simulator-arm.h" michael@0: michael@0: #include "mozilla/Casting.h" michael@0: #include "mozilla/FloatingPoint.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #include "jit/arm/Assembler-arm.h" michael@0: #include "jit/AsmJS.h" michael@0: #include "vm/Runtime.h" michael@0: michael@0: extern "C" { michael@0: michael@0: int64_t michael@0: __aeabi_idivmod(int x, int y) michael@0: { michael@0: uint32_t lo = uint32_t(x / y); michael@0: uint32_t hi = uint32_t(x % y); michael@0: return (int64_t(hi) << 32) | lo; michael@0: } michael@0: michael@0: int64_t michael@0: __aeabi_uidivmod(int x, int y) michael@0: { michael@0: uint32_t lo = uint32_t(x) / uint32_t(y); michael@0: uint32_t hi = uint32_t(x) % uint32_t(y); michael@0: return (int64_t(hi) << 32) | lo; michael@0: } michael@0: } michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: // Load/store multiple addressing mode. michael@0: enum BlockAddrMode { michael@0: // Alias modes for comparison when writeback does not matter. michael@0: da_x = (0|0|0) << 21, // Decrement after. michael@0: ia_x = (0|4|0) << 21, // Increment after. michael@0: db_x = (8|0|0) << 21, // Decrement before. michael@0: ib_x = (8|4|0) << 21, // Increment before. michael@0: }; michael@0: michael@0: // Type of VFP register. Determines register encoding. michael@0: enum VFPRegPrecision { michael@0: kSinglePrecision = 0, michael@0: kDoublePrecision = 1 michael@0: }; michael@0: michael@0: enum NeonListType { michael@0: nlt_1 = 0x7, michael@0: nlt_2 = 0xA, michael@0: nlt_3 = 0x6, michael@0: nlt_4 = 0x2 michael@0: }; michael@0: michael@0: // Supervisor Call (svc) specific support. michael@0: michael@0: // Special Software Interrupt codes when used in the presence of the ARM michael@0: // simulator. michael@0: // svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for michael@0: // standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature. michael@0: enum SoftwareInterruptCodes { michael@0: kCallRtRedirected = 0x10, // Transition to C code. michael@0: kBreakpoint= 0x20, // Breakpoint. michael@0: kStopCode = 1 << 23 // Stop. michael@0: }; michael@0: michael@0: const uint32_t kStopCodeMask = kStopCode - 1; michael@0: const uint32_t kMaxStopCode = kStopCode - 1; michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // Instruction abstraction. michael@0: michael@0: // The class Instruction enables access to individual fields defined in the ARM michael@0: // architecture instruction set encoding as described in figure A3-1. michael@0: // Note that the Assembler uses typedef int32_t Instr. michael@0: // michael@0: // Example: Test whether the instruction at ptr does set the condition code michael@0: // bits. michael@0: // michael@0: // bool InstructionSetsConditionCodes(byte* ptr) { michael@0: // Instruction* instr = Instruction::At(ptr); michael@0: // int type = instr->TypeValue(); michael@0: // return ((type == 0) || (type == 1)) && instr->hasS(); michael@0: // } michael@0: // michael@0: class SimInstruction { michael@0: public: michael@0: enum { michael@0: kInstrSize = 4, michael@0: kPCReadOffset = 8 michael@0: }; michael@0: michael@0: // Get the raw instruction bits. michael@0: inline Instr instructionBits() const { michael@0: return *reinterpret_cast(this); michael@0: } michael@0: michael@0: // Set the raw instruction bits to value. michael@0: inline void setInstructionBits(Instr value) { michael@0: *reinterpret_cast(this) = value; michael@0: } michael@0: michael@0: // Read one particular bit out of the instruction bits. michael@0: inline int bit(int nr) const { michael@0: return (instructionBits() >> nr) & 1; michael@0: } michael@0: michael@0: // Read a bit field's value out of the instruction bits. michael@0: inline int bits(int hi, int lo) const { michael@0: return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1); michael@0: } michael@0: michael@0: // Read a bit field out of the instruction bits. michael@0: inline int bitField(int hi, int lo) const { michael@0: return instructionBits() & (((2 << (hi - lo)) - 1) << lo); michael@0: } michael@0: michael@0: // Accessors for the different named fields used in the ARM encoding. michael@0: // The naming of these accessor corresponds to figure A3-1. michael@0: // michael@0: // Two kind of accessors are declared: michael@0: // - Field() will return the raw field, i.e. the field's bits at their michael@0: // original place in the instruction encoding. michael@0: // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as michael@0: // 0xC0810002 conditionField(instr) will return 0xC0000000. michael@0: // - Value() will return the field value, shifted back to bit 0. michael@0: // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as michael@0: // 0xC0810002 conditionField(instr) will return 0xC. michael@0: michael@0: // Generally applicable fields michael@0: inline Assembler::ARMCondition conditionField() const { michael@0: return static_cast(bitField(31, 28)); michael@0: } michael@0: inline int typeValue() const { return bits(27, 25); } michael@0: inline int specialValue() const { return bits(27, 23); } michael@0: michael@0: inline int rnValue() const { return bits(19, 16); } michael@0: inline int rdValue() const { return bits(15, 12); } michael@0: michael@0: inline int coprocessorValue() const { return bits(11, 8); } michael@0: michael@0: // Support for VFP. michael@0: // Vn(19-16) | Vd(15-12) | Vm(3-0) michael@0: inline int vnValue() const { return bits(19, 16); } michael@0: inline int vmValue() const { return bits(3, 0); } michael@0: inline int vdValue() const { return bits(15, 12); } michael@0: inline int nValue() const { return bit(7); } michael@0: inline int mValue() const { return bit(5); } michael@0: inline int dValue() const { return bit(22); } michael@0: inline int rtValue() const { return bits(15, 12); } michael@0: inline int pValue() const { return bit(24); } michael@0: inline int uValue() const { return bit(23); } michael@0: inline int opc1Value() const { return (bit(23) << 2) | bits(21, 20); } michael@0: inline int opc2Value() const { return bits(19, 16); } michael@0: inline int opc3Value() const { return bits(7, 6); } michael@0: inline int szValue() const { return bit(8); } michael@0: inline int VLValue() const { return bit(20); } michael@0: inline int VCValue() const { return bit(8); } michael@0: inline int VAValue() const { return bits(23, 21); } michael@0: inline int VBValue() const { return bits(6, 5); } michael@0: inline int VFPNRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 16, 7); } michael@0: inline int VFPMRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 0, 5); } michael@0: inline int VFPDRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 12, 22); } michael@0: michael@0: // Fields used in Data processing instructions michael@0: inline int opcodeValue() const { return static_cast(bits(24, 21)); } michael@0: inline ALUOp opcodeField() const { return static_cast(bitField(24, 21)); } michael@0: inline int sValue() const { return bit(20); } michael@0: michael@0: // with register michael@0: inline int rmValue() const { return bits(3, 0); } michael@0: inline ShiftType shifttypeValue() const { return static_cast(bits(6, 5)); } michael@0: inline int rsValue() const { return bits(11, 8); } michael@0: inline int shiftAmountValue() const { return bits(11, 7); } michael@0: michael@0: // with immediate michael@0: inline int rotateValue() const { return bits(11, 8); } michael@0: inline int immed8Value() const { return bits(7, 0); } michael@0: inline int immed4Value() const { return bits(19, 16); } michael@0: inline int immedMovwMovtValue() const { return immed4Value() << 12 | offset12Value(); } michael@0: michael@0: // Fields used in Load/Store instructions michael@0: inline int PUValue() const { return bits(24, 23); } michael@0: inline int PUField() const { return bitField(24, 23); } michael@0: inline int bValue() const { return bit(22); } michael@0: inline int wValue() const { return bit(21); } michael@0: inline int lValue() const { return bit(20); } michael@0: michael@0: // with register uses same fields as Data processing instructions above michael@0: // with immediate michael@0: inline int offset12Value() const { return bits(11, 0); } michael@0: michael@0: // multiple michael@0: inline int rlistValue() const { return bits(15, 0); } michael@0: michael@0: // extra loads and stores michael@0: inline int signValue() const { return bit(6); } michael@0: inline int hValue() const { return bit(5); } michael@0: inline int immedHValue() const { return bits(11, 8); } michael@0: inline int immedLValue() const { return bits(3, 0); } michael@0: michael@0: // Fields used in Branch instructions michael@0: inline int linkValue() const { return bit(24); } michael@0: inline int sImmed24Value() const { return ((instructionBits() << 8) >> 8); } michael@0: michael@0: // Fields used in Software interrupt instructions michael@0: inline SoftwareInterruptCodes svcValue() const { michael@0: return static_cast(bits(23, 0)); michael@0: } michael@0: michael@0: // Test for special encodings of type 0 instructions (extra loads and stores, michael@0: // as well as multiplications). michael@0: inline bool isSpecialType0() const { return (bit(7) == 1) && (bit(4) == 1); } michael@0: michael@0: // Test for miscellaneous instructions encodings of type 0 instructions. michael@0: inline bool isMiscType0() const { michael@0: return bit(24) == 1 && bit(23) == 0 && bit(20) == 0 && (bit(7) == 0); michael@0: } michael@0: michael@0: // Test for a nop instruction, which falls under type 1. michael@0: inline bool isNopType1() const { return bits(24, 0) == 0x0120F000; } michael@0: michael@0: // Test for a stop instruction. michael@0: inline bool isStop() const { michael@0: return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode; michael@0: } michael@0: michael@0: // Special accessors that test for existence of a value. michael@0: inline bool hasS() const { return sValue() == 1; } michael@0: inline bool hasB() const { return bValue() == 1; } michael@0: inline bool hasW() const { return wValue() == 1; } michael@0: inline bool hasL() const { return lValue() == 1; } michael@0: inline bool hasU() const { return uValue() == 1; } michael@0: inline bool hasSign() const { return signValue() == 1; } michael@0: inline bool hasH() const { return hValue() == 1; } michael@0: inline bool hasLink() const { return linkValue() == 1; } michael@0: michael@0: // Decoding the double immediate in the vmov instruction. michael@0: double doubleImmedVmov() const; michael@0: // Decoding the float32 immediate in the vmov.f32 instruction. michael@0: float float32ImmedVmov() const; michael@0: michael@0: private: michael@0: // Join split register codes, depending on single or double precision. michael@0: // four_bit is the position of the least-significant bit of the four michael@0: // bit specifier. one_bit is the position of the additional single bit michael@0: // specifier. michael@0: inline int VFPGlueRegValue(VFPRegPrecision pre, int four_bit, int one_bit) { michael@0: if (pre == kSinglePrecision) michael@0: return (bits(four_bit + 3, four_bit) << 1) | bit(one_bit); michael@0: return (bit(one_bit) << 4) | bits(four_bit + 3, four_bit); michael@0: } michael@0: michael@0: SimInstruction() MOZ_DELETE; michael@0: SimInstruction(const SimInstruction &other) MOZ_DELETE; michael@0: void operator=(const SimInstruction &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: double michael@0: SimInstruction::doubleImmedVmov() const michael@0: { michael@0: // Reconstruct a double from the immediate encoded in the vmov instruction. michael@0: // michael@0: // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh] michael@0: // double: [aBbbbbbb,bbcdefgh,00000000,00000000, michael@0: // 00000000,00000000,00000000,00000000] michael@0: // michael@0: // where B = ~b. Only the high 16 bits are affected. michael@0: uint64_t high16; michael@0: high16 = (bits(17, 16) << 4) | bits(3, 0); // xxxxxxxx,xxcdefgh. michael@0: high16 |= (0xff * bit(18)) << 6; // xxbbbbbb,bbxxxxxx. michael@0: high16 |= (bit(18) ^ 1) << 14; // xBxxxxxx,xxxxxxxx. michael@0: high16 |= bit(19) << 15; // axxxxxxx,xxxxxxxx. michael@0: michael@0: uint64_t imm = high16 << 48; michael@0: return mozilla::BitwiseCast(imm); michael@0: } michael@0: michael@0: float michael@0: SimInstruction::float32ImmedVmov() const michael@0: { michael@0: // Reconstruct a float32 from the immediate encoded in the vmov instruction. michael@0: // michael@0: // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh] michael@0: // float32: [aBbbbbbc, defgh000, 00000000, 00000000] michael@0: // michael@0: // where B = ~b. Only the high 16 bits are affected. michael@0: uint32_t imm; michael@0: imm = (bits(17, 16) << 23) | (bits(3, 0) << 19); // xxxxxxxc,defgh000.0.0 michael@0: imm |= (0x1f * bit(18)) << 25; // xxbbbbbx,xxxxxxxx.0.0 michael@0: imm |= (bit(18) ^ 1) << 30; // xBxxxxxx,xxxxxxxx.0.0 michael@0: imm |= bit(19) << 31; // axxxxxxx,xxxxxxxx.0.0 michael@0: michael@0: return mozilla::BitwiseCast(imm); michael@0: } michael@0: michael@0: class CachePage michael@0: { michael@0: public: michael@0: static const int LINE_VALID = 0; michael@0: static const int LINE_INVALID = 1; michael@0: static const int kPageShift = 12; michael@0: static const int kPageSize = 1 << kPageShift; michael@0: static const int kPageMask = kPageSize - 1; michael@0: static const int kLineShift = 2; // The cache line is only 4 bytes right now. michael@0: static const int kLineLength = 1 << kLineShift; michael@0: static const int kLineMask = kLineLength - 1; michael@0: michael@0: CachePage() { michael@0: memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); michael@0: } michael@0: char *validityByte(int offset) { michael@0: return &validity_map_[offset >> kLineShift]; michael@0: } michael@0: char *cachedData(int offset) { michael@0: return &data_[offset]; michael@0: } michael@0: michael@0: private: michael@0: char data_[kPageSize]; // The cached data. michael@0: static const int kValidityMapSize = kPageSize >> kLineShift; michael@0: char validity_map_[kValidityMapSize]; // One byte per line. michael@0: }; michael@0: michael@0: class Redirection; michael@0: michael@0: class SimulatorRuntime michael@0: { michael@0: friend class AutoLockSimulatorRuntime; michael@0: michael@0: Redirection *redirection_; michael@0: michael@0: // ICache checking. michael@0: struct ICacheHasher { michael@0: typedef void *Key; michael@0: typedef void *Lookup; michael@0: static HashNumber hash(const Lookup &l); michael@0: static bool match(const Key &k, const Lookup &l); michael@0: }; michael@0: michael@0: public: michael@0: typedef HashMap ICacheMap; michael@0: michael@0: protected: michael@0: ICacheMap icache_; michael@0: michael@0: // Synchronize access between main thread and compilation/PJS threads. michael@0: PRLock *lock_; michael@0: mozilla::DebugOnly lockOwner_; michael@0: michael@0: public: michael@0: SimulatorRuntime() michael@0: : redirection_(nullptr), michael@0: lock_(nullptr), michael@0: lockOwner_(nullptr) michael@0: {} michael@0: ~SimulatorRuntime(); michael@0: bool init() { michael@0: #ifdef JS_THREADSAFE michael@0: lock_ = PR_NewLock(); michael@0: if (!lock_) michael@0: return false; michael@0: #endif michael@0: if (!icache_.init()) michael@0: return false; michael@0: return true; michael@0: } michael@0: ICacheMap &icache() { michael@0: #ifdef JS_THREADSAFE michael@0: MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); michael@0: #endif michael@0: return icache_; michael@0: } michael@0: Redirection *redirection() const { michael@0: #ifdef JS_THREADSAFE michael@0: MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); michael@0: #endif michael@0: return redirection_; michael@0: } michael@0: void setRedirection(js::jit::Redirection *redirection) { michael@0: #ifdef JS_THREADSAFE michael@0: MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); michael@0: #endif michael@0: redirection_ = redirection; michael@0: } michael@0: }; michael@0: michael@0: class AutoLockSimulatorRuntime michael@0: { michael@0: protected: michael@0: SimulatorRuntime *srt_; michael@0: michael@0: public: michael@0: AutoLockSimulatorRuntime(SimulatorRuntime *srt) michael@0: : srt_(srt) michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: PR_Lock(srt_->lock_); michael@0: MOZ_ASSERT(!srt_->lockOwner_); michael@0: #ifdef DEBUG michael@0: srt_->lockOwner_ = PR_GetCurrentThread(); michael@0: #endif michael@0: #endif michael@0: } michael@0: michael@0: ~AutoLockSimulatorRuntime() { michael@0: #ifdef JS_THREADSAFE michael@0: MOZ_ASSERT(srt_->lockOwner_ == PR_GetCurrentThread()); michael@0: srt_->lockOwner_ = nullptr; michael@0: PR_Unlock(srt_->lock_); michael@0: #endif michael@0: } michael@0: }; michael@0: michael@0: bool Simulator::ICacheCheckingEnabled = false; michael@0: michael@0: int64_t Simulator::StopSimAt = -1L; michael@0: michael@0: SimulatorRuntime * michael@0: CreateSimulatorRuntime() michael@0: { michael@0: SimulatorRuntime *srt = js_new(); michael@0: if (!srt) michael@0: return nullptr; michael@0: michael@0: if (!srt->init()) { michael@0: js_delete(srt); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (getenv("ARM_SIM_ICACHE_CHECKS")) michael@0: Simulator::ICacheCheckingEnabled = true; michael@0: michael@0: char *stopAtStr = getenv("ARM_SIM_STOP_AT"); michael@0: int64_t stopAt; michael@0: if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) { michael@0: fprintf(stderr, "\nStopping simulation at icount %lld\n", stopAt); michael@0: Simulator::StopSimAt = stopAt; michael@0: } michael@0: michael@0: return srt; michael@0: } michael@0: michael@0: void michael@0: DestroySimulatorRuntime(SimulatorRuntime *srt) michael@0: { michael@0: js_delete(srt); michael@0: } michael@0: michael@0: // The ArmDebugger class is used by the simulator while debugging simulated ARM michael@0: // code. michael@0: class ArmDebugger { michael@0: public: michael@0: explicit ArmDebugger(Simulator *sim) : sim_(sim) { } michael@0: michael@0: void stop(SimInstruction *instr); michael@0: void debug(); michael@0: michael@0: private: michael@0: static const Instr kBreakpointInstr = (Assembler::AL | (7 * (1 << 25)) | (1* (1 << 24)) | kBreakpoint); michael@0: static const Instr kNopInstr = (Assembler::AL | (13 * (1 << 21))); michael@0: michael@0: Simulator* sim_; michael@0: michael@0: int32_t getRegisterValue(int regnum); michael@0: double getRegisterPairDoubleValue(int regnum); michael@0: double getVFPDoubleRegisterValue(int regnum); michael@0: bool getValue(const char *desc, int32_t *value); michael@0: bool getVFPDoubleValue(const char *desc, double *value); michael@0: michael@0: // Set or delete a breakpoint. Returns true if successful. michael@0: bool setBreakpoint(SimInstruction *breakpc); michael@0: bool deleteBreakpoint(SimInstruction *breakpc); michael@0: michael@0: // Undo and redo all breakpoints. This is needed to bracket disassembly and michael@0: // execution to skip past breakpoints when run from the debugger. michael@0: void undoBreakpoints(); michael@0: void redoBreakpoints(); michael@0: }; michael@0: michael@0: void michael@0: ArmDebugger::stop(SimInstruction * instr) michael@0: { michael@0: // Get the stop code. michael@0: uint32_t code = instr->svcValue() & kStopCodeMask; michael@0: // Retrieve the encoded address, which comes just after this stop. michael@0: char* msg = *reinterpret_cast(sim_->get_pc() michael@0: + SimInstruction::kInstrSize); michael@0: // Update this stop description. michael@0: if (sim_->isWatchedStop(code) && !sim_->watched_stops_[code].desc) { michael@0: sim_->watched_stops_[code].desc = msg; michael@0: } michael@0: // Print the stop message and code if it is not the default code. michael@0: if (code != kMaxStopCode) { michael@0: printf("Simulator hit stop %u: %s\n", code, msg); michael@0: } else { michael@0: printf("Simulator hit %s\n", msg); michael@0: } michael@0: sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize); michael@0: debug(); michael@0: } michael@0: michael@0: int32_t michael@0: ArmDebugger::getRegisterValue(int regnum) michael@0: { michael@0: if (regnum == Registers::pc) michael@0: return sim_->get_pc(); michael@0: return sim_->get_register(regnum); michael@0: } michael@0: michael@0: double michael@0: ArmDebugger::getRegisterPairDoubleValue(int regnum) michael@0: { michael@0: return sim_->get_double_from_register_pair(regnum); michael@0: } michael@0: michael@0: double michael@0: ArmDebugger::getVFPDoubleRegisterValue(int regnum) michael@0: { michael@0: return sim_->get_double_from_d_register(regnum); michael@0: } michael@0: michael@0: bool michael@0: ArmDebugger::getValue(const char *desc, int32_t *value) michael@0: { michael@0: Register reg = Register::FromName(desc); michael@0: if (reg != InvalidReg) { michael@0: *value = getRegisterValue(reg.code()); michael@0: return true; michael@0: } michael@0: if (strncmp(desc, "0x", 2) == 0) michael@0: return sscanf(desc + 2, "%x", reinterpret_cast(value)) == 1; michael@0: return sscanf(desc, "%u", reinterpret_cast(value)) == 1; michael@0: } michael@0: michael@0: bool michael@0: ArmDebugger::getVFPDoubleValue(const char *desc, double *value) michael@0: { michael@0: FloatRegister reg = FloatRegister::FromName(desc); michael@0: if (reg != InvalidFloatReg) { michael@0: *value = sim_->get_double_from_d_register(reg.code()); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: ArmDebugger::setBreakpoint(SimInstruction *breakpc) michael@0: { michael@0: // Check if a breakpoint can be set. If not return without any side-effects. michael@0: if (sim_->break_pc_) michael@0: return false; michael@0: michael@0: // Set the breakpoint. michael@0: sim_->break_pc_ = breakpc; michael@0: sim_->break_instr_ = breakpc->instructionBits(); michael@0: // Not setting the breakpoint instruction in the code itself. It will be set michael@0: // when the debugger shell continues. michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArmDebugger::deleteBreakpoint(SimInstruction *breakpc) michael@0: { michael@0: if (sim_->break_pc_ != nullptr) michael@0: sim_->break_pc_->setInstructionBits(sim_->break_instr_); michael@0: michael@0: sim_->break_pc_ = nullptr; michael@0: sim_->break_instr_ = 0; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ArmDebugger::undoBreakpoints() michael@0: { michael@0: if (sim_->break_pc_) michael@0: sim_->break_pc_->setInstructionBits(sim_->break_instr_); michael@0: } michael@0: michael@0: void michael@0: ArmDebugger::redoBreakpoints() michael@0: { michael@0: if (sim_->break_pc_) michael@0: sim_->break_pc_->setInstructionBits(kBreakpointInstr); michael@0: } michael@0: michael@0: static char * michael@0: ReadLine(const char *prompt) michael@0: { michael@0: char *result = nullptr; michael@0: char line_buf[256]; michael@0: int offset = 0; michael@0: bool keep_going = true; michael@0: fprintf(stdout, "%s", prompt); michael@0: fflush(stdout); michael@0: while (keep_going) { michael@0: if (fgets(line_buf, sizeof(line_buf), stdin) == nullptr) { michael@0: // fgets got an error. Just give up. michael@0: if (result) michael@0: js_delete(result); michael@0: return nullptr; michael@0: } michael@0: int len = strlen(line_buf); michael@0: if (len > 0 && line_buf[len - 1] == '\n') { michael@0: // Since we read a new line we are done reading the line. This michael@0: // will exit the loop after copying this buffer into the result. michael@0: keep_going = false; michael@0: } michael@0: if (!result) { michael@0: // Allocate the initial result and make room for the terminating '\0' michael@0: result = (char *)js_malloc(len + 1); michael@0: if (!result) michael@0: return nullptr; michael@0: } else { michael@0: // Allocate a new result with enough room for the new addition. michael@0: int new_len = offset + len + 1; michael@0: char *new_result = (char *)js_malloc(new_len); michael@0: if (!new_result) michael@0: return nullptr; michael@0: // Copy the existing input into the new array and set the new michael@0: // array as the result. michael@0: memcpy(new_result, result, offset * sizeof(char)); michael@0: js_free(result); michael@0: result = new_result; michael@0: } michael@0: // Copy the newly read line into the result. michael@0: memcpy(result + offset, line_buf, len * sizeof(char)); michael@0: offset += len; michael@0: } michael@0: michael@0: MOZ_ASSERT(result); michael@0: result[offset] = '\0'; michael@0: return result; michael@0: } michael@0: michael@0: static void michael@0: DisassembleInstruction(uint32_t pc) michael@0: { michael@0: uint8_t *bytes = reinterpret_cast(pc); michael@0: char hexbytes[256]; michael@0: sprintf(hexbytes, "0x%x 0x%x 0x%x 0x%x", bytes[0], bytes[1], bytes[2], bytes[3]); michael@0: char llvmcmd[1024]; michael@0: sprintf(llvmcmd, "bash -c \"echo -n '%p'; echo '%s' | " michael@0: "llvm-mc -disassemble -arch=arm -mcpu=cortex-a9 | " michael@0: "grep -v pure_instructions | grep -v .text\"", michael@0: reinterpret_cast(pc), hexbytes); michael@0: system(llvmcmd); michael@0: } michael@0: michael@0: void michael@0: ArmDebugger::debug() michael@0: { michael@0: intptr_t last_pc = -1; michael@0: bool done = false; michael@0: michael@0: #define COMMAND_SIZE 63 michael@0: #define ARG_SIZE 255 michael@0: michael@0: #define STR(a) #a michael@0: #define XSTR(a) STR(a) michael@0: michael@0: char cmd[COMMAND_SIZE + 1]; michael@0: char arg1[ARG_SIZE + 1]; michael@0: char arg2[ARG_SIZE + 1]; michael@0: char *argv[3] = { cmd, arg1, arg2 }; michael@0: michael@0: // make sure to have a proper terminating character if reaching the limit michael@0: cmd[COMMAND_SIZE] = 0; michael@0: arg1[ARG_SIZE] = 0; michael@0: arg2[ARG_SIZE] = 0; michael@0: michael@0: // Undo all set breakpoints while running in the debugger shell. This will michael@0: // make them invisible to all commands. michael@0: undoBreakpoints(); michael@0: michael@0: while (!done && !sim_->has_bad_pc()) { michael@0: if (last_pc != sim_->get_pc()) { michael@0: DisassembleInstruction(sim_->get_pc()); michael@0: last_pc = sim_->get_pc(); michael@0: } michael@0: char *line = ReadLine("sim> "); michael@0: if (line == nullptr) { michael@0: break; michael@0: } else { michael@0: char *last_input = sim_->lastDebuggerInput(); michael@0: if (strcmp(line, "\n") == 0 && last_input != nullptr) { michael@0: line = last_input; michael@0: } else { michael@0: // Ownership is transferred to sim_; michael@0: sim_->setLastDebuggerInput(line); michael@0: } michael@0: michael@0: // Use sscanf to parse the individual parts of the command line. At the michael@0: // moment no command expects more than two parameters. michael@0: int argc = sscanf(line, michael@0: "%" XSTR(COMMAND_SIZE) "s " michael@0: "%" XSTR(ARG_SIZE) "s " michael@0: "%" XSTR(ARG_SIZE) "s", michael@0: cmd, arg1, arg2); michael@0: if (argc < 0) { michael@0: continue; michael@0: } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { michael@0: sim_->instructionDecode(reinterpret_cast(sim_->get_pc())); michael@0: sim_->icount_++; michael@0: } else if ((strcmp(cmd, "skip") == 0)) { michael@0: sim_->set_pc(sim_->get_pc() + 4); michael@0: sim_->icount_++; michael@0: } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { michael@0: // Execute the one instruction we broke at with breakpoints disabled. michael@0: sim_->instructionDecode(reinterpret_cast(sim_->get_pc())); michael@0: sim_->icount_++; michael@0: // Leave the debugger shell. michael@0: done = true; michael@0: } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { michael@0: if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) { michael@0: int32_t value; michael@0: double dvalue; michael@0: if (strcmp(arg1, "all") == 0) { michael@0: for (uint32_t i = 0; i < Registers::Total; i++) { michael@0: value = getRegisterValue(i); michael@0: printf("%3s: 0x%08x %10d", Registers::GetName(i), value, value); michael@0: if ((argc == 3 && strcmp(arg2, "fp") == 0) && michael@0: i < 8 && michael@0: (i % 2) == 0) { michael@0: dvalue = getRegisterPairDoubleValue(i); michael@0: printf(" (%f)\n", dvalue); michael@0: } else { michael@0: printf("\n"); michael@0: } michael@0: } michael@0: for (uint32_t i = 0; i < FloatRegisters::Total; i++) { michael@0: dvalue = getVFPDoubleRegisterValue(i); michael@0: uint64_t as_words = mozilla::BitwiseCast(dvalue); michael@0: printf("%3s: %f 0x%08x %08x\n", michael@0: FloatRegister::FromCode(i).name(), michael@0: dvalue, michael@0: static_cast(as_words >> 32), michael@0: static_cast(as_words & 0xffffffff)); michael@0: } michael@0: } else { michael@0: if (getValue(arg1, &value)) { michael@0: printf("%s: 0x%08x %d \n", arg1, value, value); michael@0: } else if (getVFPDoubleValue(arg1, &dvalue)) { michael@0: uint64_t as_words = mozilla::BitwiseCast(dvalue); michael@0: printf("%s: %f 0x%08x %08x\n", michael@0: arg1, michael@0: dvalue, michael@0: static_cast(as_words >> 32), michael@0: static_cast(as_words & 0xffffffff)); michael@0: } else { michael@0: printf("%s unrecognized\n", arg1); michael@0: } michael@0: } michael@0: } else { michael@0: printf("print \n"); michael@0: } michael@0: } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { michael@0: int32_t *cur = nullptr; michael@0: int32_t *end = nullptr; michael@0: int next_arg = 1; michael@0: michael@0: if (strcmp(cmd, "stack") == 0) { michael@0: cur = reinterpret_cast(sim_->get_register(Simulator::sp)); michael@0: } else { // "mem" michael@0: int32_t value; michael@0: if (!getValue(arg1, &value)) { michael@0: printf("%s unrecognized\n", arg1); michael@0: continue; michael@0: } michael@0: cur = reinterpret_cast(value); michael@0: next_arg++; michael@0: } michael@0: michael@0: int32_t words; michael@0: if (argc == next_arg) { michael@0: words = 10; michael@0: } else { michael@0: if (!getValue(argv[next_arg], &words)) { michael@0: words = 10; michael@0: } michael@0: } michael@0: end = cur + words; michael@0: michael@0: while (cur < end) { michael@0: printf(" %p: 0x%08x %10d", cur, *cur, *cur); michael@0: printf("\n"); michael@0: cur++; michael@0: } michael@0: } else if (strcmp(cmd, "disasm") == 0 || strcmp(cmd, "di") == 0) { michael@0: uint8_t *cur = nullptr; michael@0: uint8_t *end = nullptr; michael@0: if (argc == 1) { michael@0: cur = reinterpret_cast(sim_->get_pc()); michael@0: end = cur + (10 * SimInstruction::kInstrSize); michael@0: } else if (argc == 2) { michael@0: Register reg = Register::FromName(arg1); michael@0: if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) { michael@0: // The argument is an address or a register name. michael@0: int32_t value; michael@0: if (getValue(arg1, &value)) { michael@0: cur = reinterpret_cast(value); michael@0: // Disassemble 10 instructions at . michael@0: end = cur + (10 * SimInstruction::kInstrSize); michael@0: } michael@0: } else { michael@0: // The argument is the number of instructions. michael@0: int32_t value; michael@0: if (getValue(arg1, &value)) { michael@0: cur = reinterpret_cast(sim_->get_pc()); michael@0: // Disassemble instructions. michael@0: end = cur + (value * SimInstruction::kInstrSize); michael@0: } michael@0: } michael@0: } else { michael@0: int32_t value1; michael@0: int32_t value2; michael@0: if (getValue(arg1, &value1) && getValue(arg2, &value2)) { michael@0: cur = reinterpret_cast(value1); michael@0: end = cur + (value2 * SimInstruction::kInstrSize); michael@0: } michael@0: } michael@0: while (cur < end) { michael@0: DisassembleInstruction(uint32_t(cur)); michael@0: cur += SimInstruction::kInstrSize; michael@0: } michael@0: } else if (strcmp(cmd, "gdb") == 0) { michael@0: printf("relinquishing control to gdb\n"); michael@0: asm("int $3"); michael@0: printf("regaining control from gdb\n"); michael@0: } else if (strcmp(cmd, "break") == 0) { michael@0: if (argc == 2) { michael@0: int32_t value; michael@0: if (getValue(arg1, &value)) { michael@0: if (!setBreakpoint(reinterpret_cast(value))) michael@0: printf("setting breakpoint failed\n"); michael@0: } else { michael@0: printf("%s unrecognized\n", arg1); michael@0: } michael@0: } else { michael@0: printf("break
\n"); michael@0: } michael@0: } else if (strcmp(cmd, "del") == 0) { michael@0: if (!deleteBreakpoint(nullptr)) { michael@0: printf("deleting breakpoint failed\n"); michael@0: } michael@0: } else if (strcmp(cmd, "flags") == 0) { michael@0: printf("N flag: %d; ", sim_->n_flag_); michael@0: printf("Z flag: %d; ", sim_->z_flag_); michael@0: printf("C flag: %d; ", sim_->c_flag_); michael@0: printf("V flag: %d\n", sim_->v_flag_); michael@0: printf("INVALID OP flag: %d; ", sim_->inv_op_vfp_flag_); michael@0: printf("DIV BY ZERO flag: %d; ", sim_->div_zero_vfp_flag_); michael@0: printf("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_); michael@0: printf("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_); michael@0: printf("INEXACT flag: %d;\n", sim_->inexact_vfp_flag_); michael@0: } else if (strcmp(cmd, "stop") == 0) { michael@0: int32_t value; michael@0: intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize; michael@0: SimInstruction *stop_instr = reinterpret_cast(stop_pc); michael@0: SimInstruction *msg_address = michael@0: reinterpret_cast(stop_pc + SimInstruction::kInstrSize); michael@0: if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { michael@0: // Remove the current stop. michael@0: if (sim_->isStopInstruction(stop_instr)) { michael@0: stop_instr->setInstructionBits(kNopInstr); michael@0: msg_address->setInstructionBits(kNopInstr); michael@0: } else { michael@0: printf("Not at debugger stop.\n"); michael@0: } michael@0: } else if (argc == 3) { michael@0: // Print information about all/the specified breakpoint(s). michael@0: if (strcmp(arg1, "info") == 0) { michael@0: if (strcmp(arg2, "all") == 0) { michael@0: printf("Stop information:\n"); michael@0: for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) michael@0: sim_->printStopInfo(i); michael@0: } else if (getValue(arg2, &value)) { michael@0: sim_->printStopInfo(value); michael@0: } else { michael@0: printf("Unrecognized argument.\n"); michael@0: } michael@0: } else if (strcmp(arg1, "enable") == 0) { michael@0: // Enable all/the specified breakpoint(s). michael@0: if (strcmp(arg2, "all") == 0) { michael@0: for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) michael@0: sim_->enableStop(i); michael@0: } else if (getValue(arg2, &value)) { michael@0: sim_->enableStop(value); michael@0: } else { michael@0: printf("Unrecognized argument.\n"); michael@0: } michael@0: } else if (strcmp(arg1, "disable") == 0) { michael@0: // Disable all/the specified breakpoint(s). michael@0: if (strcmp(arg2, "all") == 0) { michael@0: for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) { michael@0: sim_->disableStop(i); michael@0: } michael@0: } else if (getValue(arg2, &value)) { michael@0: sim_->disableStop(value); michael@0: } else { michael@0: printf("Unrecognized argument.\n"); michael@0: } michael@0: } michael@0: } else { michael@0: printf("Wrong usage. Use help command for more information.\n"); michael@0: } michael@0: } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { michael@0: printf("cont\n"); michael@0: printf(" continue execution (alias 'c')\n"); michael@0: printf("skip\n"); michael@0: printf(" skip one instruction (set pc to next instruction)\n"); michael@0: printf("stepi\n"); michael@0: printf(" step one instruction (alias 'si')\n"); michael@0: printf("print \n"); michael@0: printf(" print register content (alias 'p')\n"); michael@0: printf(" use register name 'all' to print all registers\n"); michael@0: printf(" add argument 'fp' to print register pair double values\n"); michael@0: printf("flags\n"); michael@0: printf(" print flags\n"); michael@0: printf("stack []\n"); michael@0: printf(" dump stack content, default dump 10 words)\n"); michael@0: printf("mem
[]\n"); michael@0: printf(" dump memory content, default dump 10 words)\n"); michael@0: printf("disasm []\n"); michael@0: printf("disasm [
]\n"); michael@0: printf("disasm [[
] ]\n"); michael@0: printf(" disassemble code, default is 10 instructions\n"); michael@0: printf(" from pc (alias 'di')\n"); michael@0: printf("gdb\n"); michael@0: printf(" enter gdb\n"); michael@0: printf("break
\n"); michael@0: printf(" set a break point on the address\n"); michael@0: printf("del\n"); michael@0: printf(" delete the breakpoint\n"); michael@0: printf("stop feature:\n"); michael@0: printf(" Description:\n"); michael@0: printf(" Stops are debug instructions inserted by\n"); michael@0: printf(" the Assembler::stop() function.\n"); michael@0: printf(" When hitting a stop, the Simulator will\n"); michael@0: printf(" stop and and give control to the ArmDebugger.\n"); michael@0: printf(" The first %d stop codes are watched:\n", michael@0: Simulator::kNumOfWatchedStops); michael@0: printf(" - They can be enabled / disabled: the Simulator\n"); michael@0: printf(" will / won't stop when hitting them.\n"); michael@0: printf(" - The Simulator keeps track of how many times they \n"); michael@0: printf(" are met. (See the info command.) Going over a\n"); michael@0: printf(" disabled stop still increases its counter. \n"); michael@0: printf(" Commands:\n"); michael@0: printf(" stop info all/ : print infos about number \n"); michael@0: printf(" or all stop(s).\n"); michael@0: printf(" stop enable/disable all/ : enables / disables\n"); michael@0: printf(" all or number stop(s)\n"); michael@0: printf(" stop unstop\n"); michael@0: printf(" ignore the stop instruction at the current location\n"); michael@0: printf(" from now on\n"); michael@0: } else { michael@0: printf("Unknown command: %s\n", cmd); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Add all the breakpoints back to stop execution and enter the debugger michael@0: // shell when hit. michael@0: redoBreakpoints(); michael@0: michael@0: #undef COMMAND_SIZE michael@0: #undef ARG_SIZE michael@0: michael@0: #undef STR michael@0: #undef XSTR michael@0: } michael@0: michael@0: static bool michael@0: AllOnOnePage(uintptr_t start, int size) michael@0: { michael@0: intptr_t start_page = (start & ~CachePage::kPageMask); michael@0: intptr_t end_page = ((start + size) & ~CachePage::kPageMask); michael@0: return start_page == end_page; michael@0: } michael@0: michael@0: static CachePage * michael@0: GetCachePage(SimulatorRuntime::ICacheMap &i_cache, void *page) michael@0: { michael@0: MOZ_ASSERT(Simulator::ICacheCheckingEnabled); michael@0: michael@0: SimulatorRuntime::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: CachePage *new_page = js_new(); michael@0: if (!i_cache.add(p, page, new_page)) michael@0: return nullptr; michael@0: return new_page; michael@0: } michael@0: michael@0: // Flush from start up to and not including start + size. michael@0: static void michael@0: FlushOnePage(SimulatorRuntime::ICacheMap &i_cache, intptr_t start, int size) michael@0: { michael@0: MOZ_ASSERT(size <= CachePage::kPageSize); michael@0: MOZ_ASSERT(AllOnOnePage(start, size - 1)); michael@0: MOZ_ASSERT((start & CachePage::kLineMask) == 0); michael@0: MOZ_ASSERT((size & CachePage::kLineMask) == 0); michael@0: michael@0: void *page = reinterpret_cast(start & (~CachePage::kPageMask)); michael@0: int offset = (start & CachePage::kPageMask); michael@0: CachePage *cache_page = GetCachePage(i_cache, page); michael@0: char *valid_bytemap = cache_page->validityByte(offset); michael@0: memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); michael@0: } michael@0: michael@0: static void michael@0: FlushICache(SimulatorRuntime::ICacheMap &i_cache, void *start_addr, size_t size) michael@0: { michael@0: intptr_t start = reinterpret_cast(start_addr); michael@0: int intra_line = (start & CachePage::kLineMask); michael@0: start -= intra_line; michael@0: size += intra_line; michael@0: size = ((size - 1) | CachePage::kLineMask) + 1; michael@0: int offset = (start & CachePage::kPageMask); michael@0: while (!AllOnOnePage(start, size - 1)) { michael@0: int bytes_to_flush = CachePage::kPageSize - offset; michael@0: FlushOnePage(i_cache, start, bytes_to_flush); michael@0: start += bytes_to_flush; michael@0: size -= bytes_to_flush; michael@0: MOZ_ASSERT((start & CachePage::kPageMask) == 0); michael@0: offset = 0; michael@0: } michael@0: if (size != 0) michael@0: FlushOnePage(i_cache, start, size); michael@0: } michael@0: michael@0: static void michael@0: CheckICache(SimulatorRuntime::ICacheMap &i_cache, SimInstruction *instr) michael@0: { michael@0: intptr_t address = reinterpret_cast(instr); michael@0: void *page = reinterpret_cast(address & (~CachePage::kPageMask)); michael@0: void *line = reinterpret_cast(address & (~CachePage::kLineMask)); michael@0: int offset = (address & CachePage::kPageMask); michael@0: CachePage* cache_page = GetCachePage(i_cache, page); michael@0: char *cache_valid_byte = cache_page->validityByte(offset); michael@0: bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); michael@0: char *cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask); michael@0: if (cache_hit) { michael@0: // Check that the data in memory matches the contents of the I-cache. michael@0: MOZ_ASSERT(memcmp(reinterpret_cast(instr), michael@0: cache_page->cachedData(offset), michael@0: SimInstruction::kInstrSize) == 0); michael@0: } else { michael@0: // Cache miss. Load memory into the cache. michael@0: memcpy(cached_line, line, CachePage::kLineLength); michael@0: *cache_valid_byte = CachePage::LINE_VALID; michael@0: } michael@0: } michael@0: michael@0: HashNumber michael@0: SimulatorRuntime::ICacheHasher::hash(const Lookup &l) michael@0: { michael@0: return static_cast(reinterpret_cast(l)) >> 2; michael@0: } michael@0: michael@0: bool michael@0: SimulatorRuntime::ICacheHasher::match(const Key &k, const Lookup &l) michael@0: { michael@0: MOZ_ASSERT((reinterpret_cast(k) & CachePage::kPageMask) == 0); michael@0: MOZ_ASSERT((reinterpret_cast(l) & CachePage::kPageMask) == 0); michael@0: return k == l; michael@0: } michael@0: michael@0: void michael@0: Simulator::setLastDebuggerInput(char *input) michael@0: { michael@0: js_free(lastDebuggerInput_); michael@0: lastDebuggerInput_ = input; michael@0: } michael@0: michael@0: void michael@0: Simulator::FlushICache(void *start_addr, size_t size) michael@0: { michael@0: IonSpewCont(IonSpew_CacheFlush, "[%p %zx]", start_addr, size); michael@0: if (!Simulator::ICacheCheckingEnabled) michael@0: return; michael@0: SimulatorRuntime *srt = Simulator::Current()->srt_; michael@0: AutoLockSimulatorRuntime alsr(srt); michael@0: js::jit::FlushICache(srt->icache(), start_addr, size); michael@0: } michael@0: michael@0: Simulator::~Simulator() michael@0: { michael@0: js_free(stack_); michael@0: } michael@0: michael@0: Simulator::Simulator(SimulatorRuntime *srt) michael@0: : srt_(srt) michael@0: { michael@0: // Set up simulator support first. Some of this information is needed to michael@0: // setup the architecture state. michael@0: michael@0: // Allocate 2MB for the stack. Note that we will only use 1MB, see also michael@0: // Simulator::stackLimit(). michael@0: static const size_t stackSize = 2 * 1024*1024; michael@0: stack_ = reinterpret_cast(js_malloc(stackSize)); michael@0: if (!stack_) { michael@0: MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__); michael@0: MOZ_CRASH(); michael@0: } michael@0: pc_modified_ = false; michael@0: icount_ = 0L; michael@0: resume_pc_ = 0; michael@0: break_pc_ = nullptr; michael@0: break_instr_ = 0; michael@0: michael@0: // Set up architecture state. michael@0: // All registers are initialized to zero to start with. michael@0: for (int i = 0; i < num_registers; i++) michael@0: registers_[i] = 0; michael@0: michael@0: n_flag_ = false; michael@0: z_flag_ = false; michael@0: c_flag_ = false; michael@0: v_flag_ = false; michael@0: michael@0: for (int i = 0; i < num_d_registers * 2; i++) michael@0: vfp_registers_[i] = 0; michael@0: michael@0: n_flag_FPSCR_ = false; michael@0: z_flag_FPSCR_ = false; michael@0: c_flag_FPSCR_ = false; michael@0: v_flag_FPSCR_ = false; michael@0: FPSCR_rounding_mode_ = SimRZ; michael@0: FPSCR_default_NaN_mode_ = true; michael@0: michael@0: inv_op_vfp_flag_ = false; michael@0: div_zero_vfp_flag_ = false; michael@0: overflow_vfp_flag_ = false; michael@0: underflow_vfp_flag_ = false; michael@0: inexact_vfp_flag_ = false; michael@0: michael@0: // The sp is initialized to point to the bottom (high address) of the michael@0: // allocated stack area. To be safe in potential stack underflows we leave michael@0: // some buffer below. michael@0: registers_[sp] = reinterpret_cast(stack_) + stackSize - 64; michael@0: michael@0: // The lr and pc are initialized to a known bad value that will cause an michael@0: // access violation if the simulator ever tries to execute it. michael@0: registers_[pc] = bad_lr; michael@0: registers_[lr] = bad_lr; michael@0: michael@0: lastDebuggerInput_ = nullptr; michael@0: } michael@0: michael@0: // When the generated code calls a VM function (masm.callWithABI) we need to michael@0: // call that function instead of trying to execute it with the simulator michael@0: // (because it's x86 code instead of arm code). We do that by redirecting the michael@0: // VM call to a svc (Supervisor Call) instruction that is handled by the michael@0: // simulator. We write the original destination of the jump just at a known michael@0: // offset from the svc instruction so the simulator knows what to call. michael@0: class Redirection michael@0: { michael@0: friend class SimulatorRuntime; michael@0: michael@0: Redirection(void *nativeFunction, ABIFunctionType type) michael@0: : nativeFunction_(nativeFunction), michael@0: swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected), michael@0: type_(type), michael@0: next_(nullptr) michael@0: { michael@0: Simulator *sim = Simulator::Current(); michael@0: SimulatorRuntime *srt = sim->srt_; michael@0: next_ = srt->redirection(); michael@0: if (Simulator::ICacheCheckingEnabled) michael@0: FlushICache(srt->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); michael@0: srt->setRedirection(this); michael@0: } michael@0: michael@0: public: michael@0: void *addressOfSwiInstruction() { return &swiInstruction_; } michael@0: void *nativeFunction() const { return nativeFunction_; } michael@0: ABIFunctionType type() const { return type_; } michael@0: michael@0: static Redirection *Get(void *nativeFunction, ABIFunctionType type) { michael@0: Simulator *sim = Simulator::Current(); michael@0: AutoLockSimulatorRuntime alsr(sim->srt_); michael@0: michael@0: Redirection *current = sim->srt_->redirection(); michael@0: for (; current != nullptr; current = current->next_) { michael@0: if (current->nativeFunction_ == nativeFunction) { michael@0: MOZ_ASSERT(current->type() == type); michael@0: return current; michael@0: } michael@0: } michael@0: michael@0: Redirection *redir = (Redirection *)js_malloc(sizeof(Redirection)); michael@0: if (!redir) { michael@0: MOZ_ReportAssertionFailure("[unhandlable oom] Simulator redirection", michael@0: __FILE__, __LINE__); michael@0: MOZ_CRASH(); michael@0: } michael@0: new(redir) Redirection(nativeFunction, type); michael@0: return redir; michael@0: } michael@0: michael@0: static Redirection *FromSwiInstruction(SimInstruction *swiInstruction) { michael@0: uint8_t *addrOfSwi = reinterpret_cast(swiInstruction); michael@0: uint8_t *addrOfRedirection = addrOfSwi - offsetof(Redirection, swiInstruction_); michael@0: return reinterpret_cast(addrOfRedirection); michael@0: } michael@0: michael@0: private: michael@0: void *nativeFunction_; michael@0: uint32_t swiInstruction_; michael@0: ABIFunctionType type_; michael@0: Redirection *next_; michael@0: }; michael@0: michael@0: /* static */ void * michael@0: Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) michael@0: { michael@0: Redirection *redirection = Redirection::Get(nativeFunction, type); michael@0: return redirection->addressOfSwiInstruction(); michael@0: } michael@0: michael@0: SimulatorRuntime::~SimulatorRuntime() michael@0: { michael@0: Redirection *r = redirection_; michael@0: while (r) { michael@0: Redirection *next = r->next_; michael@0: js_delete(r); michael@0: r = next; michael@0: } michael@0: #ifdef JS_THREADSAFE michael@0: if (lock_) michael@0: PR_DestroyLock(lock_); michael@0: #endif michael@0: } michael@0: michael@0: // Sets the register in the architecture state. It will also deal with updating michael@0: // Simulator internal state for special registers such as PC. michael@0: void michael@0: Simulator::set_register(int reg, int32_t value) michael@0: { michael@0: MOZ_ASSERT(reg >= 0 && reg < num_registers); michael@0: if (reg == pc) michael@0: pc_modified_ = true; michael@0: registers_[reg] = value; michael@0: } michael@0: michael@0: // Get the register from the architecture state. This function does handle michael@0: // the special case of accessing the PC register. michael@0: int32_t michael@0: Simulator::get_register(int reg) const michael@0: { michael@0: MOZ_ASSERT(reg >= 0 && reg < num_registers); michael@0: // Work around GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 michael@0: if (reg >= num_registers) return 0; michael@0: return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0); michael@0: } michael@0: michael@0: double michael@0: Simulator::get_double_from_register_pair(int reg) michael@0: { michael@0: MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0); michael@0: michael@0: // Read the bits from the unsigned integer register_[] array michael@0: // into the double precision floating point value and return it. michael@0: double dm_val = 0.0; michael@0: char buffer[2 * sizeof(vfp_registers_[0])]; michael@0: memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); michael@0: memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); michael@0: return dm_val; michael@0: } michael@0: michael@0: void michael@0: Simulator::set_register_pair_from_double(int reg, double *value) michael@0: { michael@0: MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0); michael@0: memcpy(registers_ + reg, value, sizeof(*value)); michael@0: } michael@0: michael@0: void michael@0: Simulator::set_dw_register(int dreg, const int *dbl) michael@0: { michael@0: MOZ_ASSERT(dreg >= 0 && dreg < num_d_registers); michael@0: registers_[dreg] = dbl[0]; michael@0: registers_[dreg + 1] = dbl[1]; michael@0: } michael@0: michael@0: void michael@0: Simulator::get_d_register(int dreg, uint64_t *value) michael@0: { michael@0: MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); michael@0: memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); michael@0: } michael@0: michael@0: void michael@0: Simulator::set_d_register(int dreg, const uint64_t *value) michael@0: { michael@0: MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); michael@0: memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); michael@0: } michael@0: michael@0: void michael@0: Simulator::get_d_register(int dreg, uint32_t *value) michael@0: { michael@0: MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); michael@0: memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); michael@0: } michael@0: michael@0: void michael@0: Simulator::set_d_register(int dreg, const uint32_t *value) michael@0: { michael@0: MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); michael@0: memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); michael@0: } michael@0: michael@0: void michael@0: Simulator::get_q_register(int qreg, uint64_t *value) michael@0: { michael@0: MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); michael@0: memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 2); michael@0: } michael@0: michael@0: void michael@0: Simulator::set_q_register(int qreg, const uint64_t *value) michael@0: { michael@0: MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); michael@0: memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 2); michael@0: } michael@0: michael@0: void michael@0: Simulator::get_q_register(int qreg, uint32_t *value) michael@0: { michael@0: MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); michael@0: memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 4); michael@0: } michael@0: michael@0: void michael@0: Simulator::set_q_register(int qreg, const uint32_t *value) michael@0: { michael@0: MOZ_ASSERT((qreg >= 0) && (qreg < num_q_registers)); michael@0: memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 4); michael@0: } michael@0: michael@0: void michael@0: Simulator::set_pc(int32_t value) michael@0: { michael@0: pc_modified_ = true; michael@0: registers_[pc] = value; michael@0: } michael@0: michael@0: bool michael@0: Simulator::has_bad_pc() const michael@0: { michael@0: return registers_[pc] == bad_lr || registers_[pc] == end_sim_pc; michael@0: } michael@0: michael@0: // Raw access to the PC register without the special adjustment when reading. michael@0: int32_t michael@0: Simulator::get_pc() const michael@0: { michael@0: return registers_[pc]; michael@0: } michael@0: michael@0: void michael@0: Simulator::set_s_register(int sreg, unsigned int value) michael@0: { michael@0: MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers); michael@0: vfp_registers_[sreg] = value; michael@0: } michael@0: michael@0: unsigned michael@0: Simulator::get_s_register(int sreg) const michael@0: { michael@0: MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers); michael@0: return vfp_registers_[sreg]; michael@0: } michael@0: michael@0: template michael@0: void michael@0: Simulator::setVFPRegister(int reg_index, const InputType &value) michael@0: { michael@0: MOZ_ASSERT(reg_index >= 0); michael@0: MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers); michael@0: MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::Total)); michael@0: michael@0: char buffer[register_size * sizeof(vfp_registers_[0])]; michael@0: memcpy(buffer, &value, register_size * sizeof(vfp_registers_[0])); michael@0: memcpy(&vfp_registers_[reg_index * register_size], buffer, michael@0: register_size * sizeof(vfp_registers_[0])); michael@0: } michael@0: michael@0: template michael@0: ReturnType Simulator::getFromVFPRegister(int reg_index) michael@0: { michael@0: MOZ_ASSERT(reg_index >= 0); michael@0: MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers); michael@0: MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::Total)); michael@0: michael@0: ReturnType value = 0; michael@0: char buffer[register_size * sizeof(vfp_registers_[0])]; michael@0: memcpy(buffer, &vfp_registers_[register_size * reg_index], michael@0: register_size * sizeof(vfp_registers_[0])); michael@0: memcpy(&value, buffer, register_size * sizeof(vfp_registers_[0])); michael@0: return value; michael@0: } michael@0: michael@0: void michael@0: Simulator::getFpArgs(double *x, double *y, int32_t *z) michael@0: { michael@0: if (useHardFpABI()) { michael@0: *x = get_double_from_d_register(0); michael@0: *y = get_double_from_d_register(1); michael@0: *z = get_register(0); michael@0: } else { michael@0: *x = get_double_from_register_pair(0); michael@0: *y = get_double_from_register_pair(2); michael@0: *z = get_register(2); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::setCallResultDouble(double result) michael@0: { michael@0: // The return value is either in r0/r1 or d0. michael@0: if (useHardFpABI()) { michael@0: char buffer[2 * sizeof(vfp_registers_[0])]; michael@0: memcpy(buffer, &result, sizeof(buffer)); michael@0: // Copy result to d0. michael@0: memcpy(vfp_registers_, buffer, sizeof(buffer)); michael@0: } else { michael@0: char buffer[2 * sizeof(registers_[0])]; michael@0: memcpy(buffer, &result, sizeof(buffer)); michael@0: // Copy result to r0 and r1. michael@0: memcpy(registers_, buffer, sizeof(buffer)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::setCallResultFloat(float result) michael@0: { michael@0: if (useHardFpABI()) { michael@0: char buffer[sizeof(registers_[0])]; michael@0: memcpy(buffer, &result, sizeof(buffer)); michael@0: // Copy result to s0. michael@0: memcpy(vfp_registers_, buffer, sizeof(buffer)); michael@0: } else { michael@0: char buffer[sizeof(registers_[0])]; michael@0: memcpy(buffer, &result, sizeof(buffer)); michael@0: // Copy result to r0. michael@0: memcpy(registers_, buffer, sizeof(buffer)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::setCallResult(int64_t res) michael@0: { michael@0: set_register(r0, static_cast(res)); michael@0: set_register(r1, static_cast(res >> 32)); michael@0: } michael@0: michael@0: int michael@0: Simulator::readW(int32_t addr, SimInstruction *instr) michael@0: { michael@0: // YARR emits unaligned loads, so we don't check for them here like the michael@0: // other methods below. michael@0: intptr_t *ptr = reinterpret_cast(addr); michael@0: return *ptr; michael@0: } michael@0: michael@0: void michael@0: Simulator::writeW(int32_t addr, int value, SimInstruction *instr) michael@0: { michael@0: if ((addr & 3) == 0) { michael@0: intptr_t *ptr = reinterpret_cast(addr); michael@0: *ptr = value; michael@0: } else { michael@0: printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr); michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: uint16_t michael@0: Simulator::readHU(int32_t addr, SimInstruction *instr) michael@0: { michael@0: if ((addr & 1) == 0) { michael@0: uint16_t *ptr = reinterpret_cast(addr); michael@0: return *ptr; michael@0: } michael@0: printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr); michael@0: MOZ_CRASH(); michael@0: return 0; michael@0: } michael@0: michael@0: int16_t michael@0: Simulator::readH(int32_t addr, SimInstruction *instr) michael@0: { michael@0: if ((addr & 1) == 0) { michael@0: int16_t *ptr = reinterpret_cast(addr); michael@0: return *ptr; michael@0: } michael@0: printf("Unaligned signed halfword read at 0x%08x\n", addr); michael@0: MOZ_CRASH(); michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: Simulator::writeH(int32_t addr, uint16_t value, SimInstruction *instr) michael@0: { michael@0: if ((addr & 1) == 0) { michael@0: uint16_t *ptr = reinterpret_cast(addr); michael@0: *ptr = value; michael@0: } else { michael@0: printf("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr); michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::writeH(int32_t addr, int16_t value, SimInstruction *instr) michael@0: { michael@0: if ((addr & 1) == 0) { michael@0: int16_t *ptr = reinterpret_cast(addr); michael@0: *ptr = value; michael@0: } else { michael@0: printf("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr); michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: uint8_t michael@0: Simulator::readBU(int32_t addr) michael@0: { michael@0: uint8_t *ptr = reinterpret_cast(addr); michael@0: return *ptr; michael@0: } michael@0: michael@0: int8_t michael@0: Simulator::readB(int32_t addr) michael@0: { michael@0: int8_t *ptr = reinterpret_cast(addr); michael@0: return *ptr; michael@0: } michael@0: michael@0: void michael@0: Simulator::writeB(int32_t addr, uint8_t value) michael@0: { michael@0: uint8_t *ptr = reinterpret_cast(addr); michael@0: *ptr = value; michael@0: } michael@0: michael@0: void michael@0: Simulator::writeB(int32_t addr, int8_t value) michael@0: { michael@0: int8_t *ptr = reinterpret_cast(addr); michael@0: *ptr = value; michael@0: } michael@0: michael@0: int32_t * michael@0: Simulator::readDW(int32_t addr) michael@0: { michael@0: if ((addr & 3) == 0) { michael@0: int32_t *ptr = reinterpret_cast(addr); michael@0: return ptr; michael@0: } michael@0: printf("Unaligned read at 0x%08x\n", addr); michael@0: MOZ_CRASH(); michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2) michael@0: { michael@0: if ((addr & 3) == 0) { michael@0: int32_t *ptr = reinterpret_cast(addr); michael@0: *ptr++ = value1; michael@0: *ptr = value2; michael@0: } else { michael@0: printf("Unaligned write at 0x%08x\n", addr); michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: uintptr_t michael@0: Simulator::stackLimit() const michael@0: { michael@0: // Leave a safety margin of 1MB to prevent overrunning the stack when michael@0: // pushing values (total stack size is 2MB). michael@0: return reinterpret_cast(stack_) + 1024 * 1024; michael@0: } michael@0: michael@0: bool michael@0: Simulator::overRecursed(uintptr_t newsp) const michael@0: { michael@0: if (newsp == 0) michael@0: newsp = get_register(sp); michael@0: return newsp <= stackLimit(); michael@0: } michael@0: michael@0: bool michael@0: Simulator::overRecursedWithExtra(uint32_t extra) const michael@0: { michael@0: uintptr_t newsp = get_register(sp) - extra; michael@0: return newsp <= stackLimit(); michael@0: } michael@0: michael@0: // Checks if the current instruction should be executed based on its michael@0: // condition bits. michael@0: bool michael@0: Simulator::conditionallyExecute(SimInstruction *instr) michael@0: { michael@0: switch (instr->conditionField()) { michael@0: case Assembler::EQ: return z_flag_; michael@0: case Assembler::NE: return !z_flag_; michael@0: case Assembler::CS: return c_flag_; michael@0: case Assembler::CC: return !c_flag_; michael@0: case Assembler::MI: return n_flag_; michael@0: case Assembler::PL: return !n_flag_; michael@0: case Assembler::VS: return v_flag_; michael@0: case Assembler::VC: return !v_flag_; michael@0: case Assembler::HI: return c_flag_ && !z_flag_; michael@0: case Assembler::LS: return !c_flag_ || z_flag_; michael@0: case Assembler::GE: return n_flag_ == v_flag_; michael@0: case Assembler::LT: return n_flag_ != v_flag_; michael@0: case Assembler::GT: return !z_flag_ && (n_flag_ == v_flag_); michael@0: case Assembler::LE: return z_flag_ || (n_flag_ != v_flag_); michael@0: case Assembler::AL: return true; michael@0: default: MOZ_ASSUME_UNREACHABLE(); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Calculate and set the Negative and Zero flags. michael@0: void michael@0: Simulator::setNZFlags(int32_t val) michael@0: { michael@0: n_flag_ = (val < 0); michael@0: z_flag_ = (val == 0); michael@0: } michael@0: michael@0: // Set the Carry flag. michael@0: void michael@0: Simulator::setCFlag(bool val) michael@0: { michael@0: c_flag_ = val; michael@0: } michael@0: michael@0: // Set the oVerflow flag. michael@0: void michael@0: Simulator::setVFlag(bool val) michael@0: { michael@0: v_flag_ = val; michael@0: } michael@0: michael@0: // Calculate C flag value for additions. michael@0: bool michael@0: Simulator::carryFrom(int32_t left, int32_t right, int32_t carry) michael@0: { michael@0: uint32_t uleft = static_cast(left); michael@0: uint32_t uright = static_cast(right); michael@0: uint32_t urest = 0xffffffffU - uleft; michael@0: return (uright > urest) || michael@0: (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); michael@0: } michael@0: michael@0: // Calculate C flag value for subtractions. michael@0: bool michael@0: Simulator::borrowFrom(int32_t left, int32_t right) michael@0: { michael@0: uint32_t uleft = static_cast(left); michael@0: uint32_t uright = static_cast(right); michael@0: return (uright > uleft); michael@0: } michael@0: michael@0: // Calculate V flag value for additions and subtractions. michael@0: bool michael@0: Simulator::overflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) michael@0: { michael@0: bool overflow; michael@0: if (addition) { michael@0: // operands have the same sign michael@0: overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) michael@0: // and operands and result have different sign michael@0: && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); michael@0: } else { michael@0: // operands have different signs michael@0: overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) michael@0: // and first operand and result have different signs michael@0: && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); michael@0: } michael@0: return overflow; michael@0: } michael@0: michael@0: // Support for VFP comparisons. michael@0: void michael@0: Simulator::compute_FPSCR_Flags(double val1, double val2) michael@0: { michael@0: if (mozilla::IsNaN(val1) || mozilla::IsNaN(val2)) { michael@0: n_flag_FPSCR_ = false; michael@0: z_flag_FPSCR_ = false; michael@0: c_flag_FPSCR_ = true; michael@0: v_flag_FPSCR_ = true; michael@0: // All non-NaN cases. michael@0: } else if (val1 == val2) { michael@0: n_flag_FPSCR_ = false; michael@0: z_flag_FPSCR_ = true; michael@0: c_flag_FPSCR_ = true; michael@0: v_flag_FPSCR_ = false; michael@0: } else if (val1 < val2) { michael@0: n_flag_FPSCR_ = true; michael@0: z_flag_FPSCR_ = false; michael@0: c_flag_FPSCR_ = false; michael@0: v_flag_FPSCR_ = false; michael@0: } else { michael@0: // Case when (val1 > val2). michael@0: n_flag_FPSCR_ = false; michael@0: z_flag_FPSCR_ = false; michael@0: c_flag_FPSCR_ = true; michael@0: v_flag_FPSCR_ = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::copy_FPSCR_to_APSR() michael@0: { michael@0: n_flag_ = n_flag_FPSCR_; michael@0: z_flag_ = z_flag_FPSCR_; michael@0: c_flag_ = c_flag_FPSCR_; michael@0: v_flag_ = v_flag_FPSCR_; michael@0: } michael@0: michael@0: // Addressing Mode 1 - Data-processing operands: michael@0: // Get the value based on the shifter_operand with register. michael@0: int32_t michael@0: Simulator::getShiftRm(SimInstruction *instr, bool *carry_out) michael@0: { michael@0: ShiftType shift = instr->shifttypeValue(); michael@0: int shift_amount = instr->shiftAmountValue(); michael@0: int32_t result = get_register(instr->rmValue()); michael@0: if (instr->bit(4) == 0) { michael@0: // By immediate. michael@0: if (shift == ROR && shift_amount == 0) { michael@0: MOZ_ASSUME_UNREACHABLE("NYI"); michael@0: return result; michael@0: } michael@0: if ((shift == LSR || shift == ASR) && shift_amount == 0) michael@0: shift_amount = 32; michael@0: switch (shift) { michael@0: case ASR: { michael@0: if (shift_amount == 0) { michael@0: if (result < 0) { michael@0: result = 0xffffffff; michael@0: *carry_out = true; michael@0: } else { michael@0: result = 0; michael@0: *carry_out = false; michael@0: } michael@0: } else { michael@0: result >>= (shift_amount - 1); michael@0: *carry_out = (result & 1) == 1; michael@0: result >>= 1; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case LSL: { michael@0: if (shift_amount == 0) { michael@0: *carry_out = c_flag_; michael@0: } else { michael@0: result <<= (shift_amount - 1); michael@0: *carry_out = (result < 0); michael@0: result <<= 1; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case LSR: { michael@0: if (shift_amount == 0) { michael@0: result = 0; michael@0: *carry_out = c_flag_; michael@0: } else { michael@0: uint32_t uresult = static_cast(result); michael@0: uresult >>= (shift_amount - 1); michael@0: *carry_out = (uresult & 1) == 1; michael@0: uresult >>= 1; michael@0: result = static_cast(uresult); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case ROR: { michael@0: if (shift_amount == 0) { michael@0: *carry_out = c_flag_; michael@0: } else { michael@0: uint32_t left = static_cast(result) >> shift_amount; michael@0: uint32_t right = static_cast(result) << (32 - shift_amount); michael@0: result = right | left; michael@0: *carry_out = (static_cast(result) >> 31) != 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE(); michael@0: break; michael@0: } michael@0: } else { michael@0: // By register. michael@0: int rs = instr->rsValue(); michael@0: shift_amount = get_register(rs) &0xff; michael@0: switch (shift) { michael@0: case ASR: { michael@0: if (shift_amount == 0) { michael@0: *carry_out = c_flag_; michael@0: } else if (shift_amount < 32) { michael@0: result >>= (shift_amount - 1); michael@0: *carry_out = (result & 1) == 1; michael@0: result >>= 1; michael@0: } else { michael@0: MOZ_ASSERT(shift_amount >= 32); michael@0: if (result < 0) { michael@0: *carry_out = true; michael@0: result = 0xffffffff; michael@0: } else { michael@0: *carry_out = false; michael@0: result = 0; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case LSL: { michael@0: if (shift_amount == 0) { michael@0: *carry_out = c_flag_; michael@0: } else if (shift_amount < 32) { michael@0: result <<= (shift_amount - 1); michael@0: *carry_out = (result < 0); michael@0: result <<= 1; michael@0: } else if (shift_amount == 32) { michael@0: *carry_out = (result & 1) == 1; michael@0: result = 0; michael@0: } else { michael@0: MOZ_ASSERT(shift_amount > 32); michael@0: *carry_out = false; michael@0: result = 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case LSR: { michael@0: if (shift_amount == 0) { michael@0: *carry_out = c_flag_; michael@0: } else if (shift_amount < 32) { michael@0: uint32_t uresult = static_cast(result); michael@0: uresult >>= (shift_amount - 1); michael@0: *carry_out = (uresult & 1) == 1; michael@0: uresult >>= 1; michael@0: result = static_cast(uresult); michael@0: } else if (shift_amount == 32) { michael@0: *carry_out = (result < 0); michael@0: result = 0; michael@0: } else { michael@0: *carry_out = false; michael@0: result = 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case ROR: { michael@0: if (shift_amount == 0) { michael@0: *carry_out = c_flag_; michael@0: } else { michael@0: uint32_t left = static_cast(result) >> shift_amount; michael@0: uint32_t right = static_cast(result) << (32 - shift_amount); michael@0: result = right | left; michael@0: *carry_out = (static_cast(result) >> 31) != 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE(); michael@0: break; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // Addressing Mode 1 - Data-processing operands: michael@0: // Get the value based on the shifter_operand with immediate. michael@0: int32_t michael@0: Simulator::getImm(SimInstruction *instr, bool *carry_out) michael@0: { michael@0: int rotate = instr->rotateValue() * 2; michael@0: int immed8 = instr->immed8Value(); michael@0: int imm = (immed8 >> rotate) | (immed8 << (32 - rotate)); michael@0: *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); michael@0: return imm; michael@0: } michael@0: michael@0: int32_t michael@0: Simulator::processPU(SimInstruction *instr, int num_regs, int reg_size, michael@0: intptr_t *start_address, intptr_t *end_address) michael@0: { michael@0: int rn = instr->rnValue(); michael@0: int32_t rn_val = get_register(rn); michael@0: switch (instr->PUField()) { michael@0: case da_x: michael@0: MOZ_CRASH(); michael@0: break; michael@0: case ia_x: michael@0: *start_address = rn_val; michael@0: *end_address = rn_val + (num_regs * reg_size) - reg_size; michael@0: rn_val = rn_val + (num_regs * reg_size); michael@0: break; michael@0: case db_x: michael@0: *start_address = rn_val - (num_regs * reg_size); michael@0: *end_address = rn_val - reg_size; michael@0: rn_val = *start_address; michael@0: break; michael@0: case ib_x: michael@0: *start_address = rn_val + reg_size; michael@0: *end_address = rn_val + (num_regs * reg_size); michael@0: rn_val = *end_address; michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE(); michael@0: break; michael@0: } michael@0: return rn_val; michael@0: } michael@0: michael@0: // Addressing Mode 4 - Load and Store Multiple michael@0: void michael@0: Simulator::handleRList(SimInstruction *instr, bool load) michael@0: { michael@0: int rlist = instr->rlistValue(); michael@0: int num_regs = mozilla::CountPopulation32(rlist); michael@0: michael@0: intptr_t start_address = 0; michael@0: intptr_t end_address = 0; michael@0: int32_t rn_val = processPU(instr, num_regs, sizeof(void *), &start_address, &end_address); michael@0: intptr_t *address = reinterpret_cast(start_address); michael@0: michael@0: // Catch null pointers a little earlier. michael@0: MOZ_ASSERT(start_address > 8191 || start_address < 0); michael@0: michael@0: int reg = 0; michael@0: while (rlist != 0) { michael@0: if ((rlist & 1) != 0) { michael@0: if (load) { michael@0: set_register(reg, *address); michael@0: } else { michael@0: *address = get_register(reg); michael@0: } michael@0: address += 1; michael@0: } michael@0: reg++; michael@0: rlist >>= 1; michael@0: } michael@0: MOZ_ASSERT(end_address == ((intptr_t)address) - 4); michael@0: if (instr->hasW()) michael@0: set_register(instr->rnValue(), rn_val); michael@0: } michael@0: michael@0: // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. michael@0: void michael@0: Simulator::handleVList(SimInstruction *instr) michael@0: { michael@0: VFPRegPrecision precision = (instr->szValue() == 0) ? kSinglePrecision : kDoublePrecision; michael@0: int operand_size = (precision == kSinglePrecision) ? 4 : 8; michael@0: bool load = (instr->VLValue() == 0x1); michael@0: michael@0: int vd; michael@0: int num_regs; michael@0: vd = instr->VFPDRegValue(precision); michael@0: if (precision == kSinglePrecision) michael@0: num_regs = instr->immed8Value(); michael@0: else michael@0: num_regs = instr->immed8Value() / 2; michael@0: michael@0: intptr_t start_address = 0; michael@0: intptr_t end_address = 0; michael@0: int32_t rn_val = processPU(instr, num_regs, operand_size, &start_address, &end_address); michael@0: michael@0: intptr_t *address = reinterpret_cast(start_address); michael@0: for (int reg = vd; reg < vd + num_regs; reg++) { michael@0: if (precision == kSinglePrecision) { michael@0: if (load) michael@0: set_s_register_from_sinteger(reg, readW(reinterpret_cast(address), instr)); michael@0: else michael@0: writeW(reinterpret_cast(address), get_sinteger_from_s_register(reg), instr); michael@0: address += 1; michael@0: } else { michael@0: if (load) { michael@0: int32_t data[] = { michael@0: readW(reinterpret_cast(address), instr), michael@0: readW(reinterpret_cast(address + 1), instr) michael@0: }; michael@0: double d; michael@0: memcpy(&d, data, 8); michael@0: set_d_register_from_double(reg, d); michael@0: } else { michael@0: int32_t data[2]; michael@0: double d = get_double_from_d_register(reg); michael@0: memcpy(data, &d, 8); michael@0: writeW(reinterpret_cast(address), data[0], instr); michael@0: writeW(reinterpret_cast(address + 1), data[1], instr); michael@0: } michael@0: address += 2; michael@0: } michael@0: } michael@0: MOZ_ASSERT(reinterpret_cast(address) - operand_size == end_address); michael@0: if (instr->hasW()) michael@0: set_register(instr->rnValue(), rn_val); michael@0: } michael@0: michael@0: michael@0: // Note: With the code below we assume that all runtime calls return a 64 bits michael@0: // result. If they don't, the r1 result register contains a bogus value, which michael@0: // is fine because it is caller-saved. michael@0: typedef int64_t (*Prototype_General0)(); michael@0: typedef int64_t (*Prototype_General1)(int32_t arg0); michael@0: typedef int64_t (*Prototype_General2)(int32_t arg0, int32_t arg1); michael@0: typedef int64_t (*Prototype_General3)(int32_t arg0, int32_t arg1, int32_t arg2); michael@0: typedef int64_t (*Prototype_General4)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); michael@0: typedef int64_t (*Prototype_General5)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, michael@0: int32_t arg4); michael@0: typedef int64_t (*Prototype_General6)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, michael@0: int32_t arg4, int32_t arg5); michael@0: typedef int64_t (*Prototype_General7)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, michael@0: int32_t arg4, int32_t arg5, int32_t arg6); michael@0: typedef int64_t (*Prototype_General8)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, michael@0: int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7); michael@0: michael@0: typedef double (*Prototype_Double_None)(); michael@0: typedef double (*Prototype_Double_Double)(double arg0); michael@0: typedef double (*Prototype_Double_Int)(int32_t arg0); michael@0: typedef int32_t (*Prototype_Int_Double)(double arg0); michael@0: typedef float (*Prototype_Float32_Float32)(float arg0); michael@0: michael@0: typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1); michael@0: typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1); michael@0: typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); michael@0: typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1); michael@0: michael@0: michael@0: // Fill the volatile registers with scratch values. michael@0: // michael@0: // Some of the ABI calls assume that the float registers are not scratched, even michael@0: // though the ABI defines them as volatile - a performance optimization. These are michael@0: // all calls passing operands in integer registers, so for now the simulator does not michael@0: // scratch any float registers for these calls. Should try to narrow it further in michael@0: // future. michael@0: // michael@0: void michael@0: Simulator::scratchVolatileRegisters(bool scratchFloat) michael@0: { michael@0: int32_t scratch_value = 0xa5a5a5a5 ^ uint32_t(icount_); michael@0: set_register(r0, scratch_value); michael@0: set_register(r1, scratch_value); michael@0: set_register(r2, scratch_value); michael@0: set_register(r3, scratch_value); michael@0: set_register(r12, scratch_value); // Intra-Procedure-call scratch register michael@0: set_register(r14, scratch_value); // Link register michael@0: michael@0: if (scratchFloat) { michael@0: uint64_t scratch_value_d = 0x5a5a5a5a5a5a5a5aLU ^ uint64_t(icount_) ^ (uint64_t(icount_) << 30); michael@0: for (uint32_t i = d0; i < d8; i++) michael@0: set_d_register(i, &scratch_value_d); michael@0: for (uint32_t i = d16; i < FloatRegisters::Total; i++) michael@0: set_d_register(i, &scratch_value_d); michael@0: } michael@0: } michael@0: michael@0: // Software interrupt instructions are used by the simulator to call into C++. michael@0: void michael@0: Simulator::softwareInterrupt(SimInstruction *instr) michael@0: { michael@0: int svc = instr->svcValue(); michael@0: switch (svc) { michael@0: case kCallRtRedirected: { michael@0: Redirection *redirection = Redirection::FromSwiInstruction(instr); michael@0: int32_t arg0 = get_register(r0); michael@0: int32_t arg1 = get_register(r1); michael@0: int32_t arg2 = get_register(r2); michael@0: int32_t arg3 = get_register(r3); michael@0: int32_t *stack_pointer = reinterpret_cast(get_register(sp)); michael@0: int32_t arg4 = stack_pointer[0]; michael@0: int32_t arg5 = stack_pointer[1]; michael@0: michael@0: int32_t saved_lr = get_register(lr); michael@0: intptr_t external = reinterpret_cast(redirection->nativeFunction()); michael@0: michael@0: bool stack_aligned = (get_register(sp) & (StackAlignment - 1)) == 0; michael@0: if (!stack_aligned) { michael@0: fprintf(stderr, "Runtime call with unaligned stack!\n"); michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: switch (redirection->type()) { michael@0: case Args_General0: { michael@0: Prototype_General0 target = reinterpret_cast(external); michael@0: int64_t result = target(); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General1: { michael@0: Prototype_General1 target = reinterpret_cast(external); michael@0: int64_t result = target(arg0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General2: { michael@0: Prototype_General2 target = reinterpret_cast(external); michael@0: int64_t result = target(arg0, arg1); michael@0: // The ARM backend makes calls to __aeabi_idivmod and __aeabi_uidivmod assuming michael@0: // that the float registers are non-volatile as a performance optimization, so the michael@0: // float registers must not be scratch when calling these. michael@0: bool scratchFloat = target != __aeabi_idivmod && target != __aeabi_uidivmod; michael@0: scratchVolatileRegisters(/* scratchFloat = */ scratchFloat); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General3: { michael@0: Prototype_General3 target = reinterpret_cast(external); michael@0: int64_t result = target(arg0, arg1, arg2); michael@0: scratchVolatileRegisters(/* scratchFloat = true*/); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General4: { michael@0: Prototype_General4 target = reinterpret_cast(external); michael@0: int64_t result = target(arg0, arg1, arg2, arg3); michael@0: scratchVolatileRegisters(/* scratchFloat = true*/); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General5: { michael@0: Prototype_General5 target = reinterpret_cast(external); michael@0: int64_t result = target(arg0, arg1, arg2, arg3, arg4); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General6: { michael@0: Prototype_General6 target = reinterpret_cast(external); michael@0: int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General7: { michael@0: Prototype_General7 target = reinterpret_cast(external); michael@0: int32_t arg6 = stack_pointer[2]; michael@0: int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_General8: { michael@0: Prototype_General8 target = reinterpret_cast(external); michael@0: int32_t arg6 = stack_pointer[2]; michael@0: int32_t arg7 = stack_pointer[3]; michael@0: int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResult(result); michael@0: break; michael@0: } michael@0: case Args_Double_None: { michael@0: Prototype_Double_None target = reinterpret_cast(external); michael@0: double dresult = target(); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultDouble(dresult); michael@0: break; michael@0: } michael@0: case Args_Int_Double: { michael@0: double dval0, dval1; michael@0: int32_t ival; michael@0: getFpArgs(&dval0, &dval1, &ival); michael@0: Prototype_Int_Double target = reinterpret_cast(external); michael@0: int32_t res = target(dval0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: set_register(r0, res); michael@0: break; michael@0: } michael@0: case Args_Double_Double: { michael@0: double dval0, dval1; michael@0: int32_t ival; michael@0: getFpArgs(&dval0, &dval1, &ival); michael@0: Prototype_Double_Double target = reinterpret_cast(external); michael@0: double dresult = target(dval0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultDouble(dresult); michael@0: break; michael@0: } michael@0: case Args_Float32_Float32: { michael@0: float fval0; michael@0: if (useHardFpABI()) michael@0: fval0 = get_float_from_s_register(0); michael@0: else michael@0: fval0 = mozilla::BitwiseCast(arg0); michael@0: Prototype_Float32_Float32 target = reinterpret_cast(external); michael@0: float fresult = target(fval0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultFloat(fresult); michael@0: break; michael@0: } michael@0: case Args_Double_Int: { michael@0: Prototype_Double_Int target = reinterpret_cast(external); michael@0: double dresult = target(arg0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultDouble(dresult); michael@0: break; michael@0: } michael@0: case Args_Double_DoubleInt: { michael@0: double dval0, dval1; michael@0: int32_t ival; michael@0: getFpArgs(&dval0, &dval1, &ival); michael@0: Prototype_DoubleInt target = reinterpret_cast(external); michael@0: double dresult = target(dval0, ival); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultDouble(dresult); michael@0: break; michael@0: } michael@0: case Args_Double_DoubleDouble: { michael@0: double dval0, dval1; michael@0: int32_t ival; michael@0: getFpArgs(&dval0, &dval1, &ival); michael@0: Prototype_Double_DoubleDouble target = reinterpret_cast(external); michael@0: double dresult = target(dval0, dval1); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultDouble(dresult); michael@0: break; michael@0: } michael@0: case Args_Double_IntDouble: { michael@0: int32_t ival = get_register(0); michael@0: double dval0; michael@0: if (useHardFpABI()) michael@0: dval0 = get_double_from_d_register(0); michael@0: else michael@0: dval0 = get_double_from_register_pair(2); michael@0: Prototype_Double_IntDouble target = reinterpret_cast(external); michael@0: double dresult = target(ival, dval0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: setCallResultDouble(dresult); michael@0: break; michael@0: } michael@0: case Args_Int_IntDouble: { michael@0: int32_t ival = get_register(0); michael@0: double dval0; michael@0: if (useHardFpABI()) michael@0: dval0 = get_double_from_d_register(0); michael@0: else michael@0: dval0 = get_double_from_register_pair(2); michael@0: Prototype_Int_IntDouble target = reinterpret_cast(external); michael@0: int32_t result = target(ival, dval0); michael@0: scratchVolatileRegisters(/* scratchFloat = true */); michael@0: set_register(r0, result); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("call"); michael@0: } michael@0: michael@0: set_register(lr, saved_lr); michael@0: set_pc(get_register(lr)); michael@0: break; michael@0: } michael@0: case kBreakpoint: { michael@0: ArmDebugger dbg(this); michael@0: dbg.debug(); michael@0: break; michael@0: } michael@0: default: { // Stop uses all codes greater than 1 << 23. michael@0: if (svc >= (1 << 23)) { michael@0: uint32_t code = svc & kStopCodeMask; michael@0: if (isWatchedStop(code)) michael@0: increaseStopCounter(code); michael@0: michael@0: // Stop if it is enabled, otherwise go on jumping over the stop michael@0: // and the message address. michael@0: if (isEnabledStop(code)) { michael@0: ArmDebugger dbg(this); michael@0: dbg.stop(instr); michael@0: } else { michael@0: set_pc(get_pc() + 2 * SimInstruction::kInstrSize); michael@0: } michael@0: } else { michael@0: // This is not a valid svc code. michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: double michael@0: Simulator::canonicalizeNaN(double value) michael@0: { michael@0: return FPSCR_default_NaN_mode_ ? JS::CanonicalizeNaN(value) : value; michael@0: } michael@0: michael@0: // Stop helper functions. michael@0: bool michael@0: Simulator::isStopInstruction(SimInstruction *instr) michael@0: { michael@0: return (instr->bits(27, 24) == 0xF) && (instr->svcValue() >= kStopCode); michael@0: } michael@0: michael@0: bool Simulator::isWatchedStop(uint32_t code) michael@0: { michael@0: MOZ_ASSERT(code <= kMaxStopCode); michael@0: return code < kNumOfWatchedStops; michael@0: } michael@0: michael@0: bool michael@0: Simulator::isEnabledStop(uint32_t code) michael@0: { michael@0: MOZ_ASSERT(code <= kMaxStopCode); michael@0: // Unwatched stops are always enabled. michael@0: return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); michael@0: } michael@0: michael@0: void michael@0: Simulator::enableStop(uint32_t code) michael@0: { michael@0: MOZ_ASSERT(isWatchedStop(code)); michael@0: if (!isEnabledStop(code)) michael@0: watched_stops_[code].count &= ~kStopDisabledBit; michael@0: } michael@0: michael@0: void michael@0: Simulator::disableStop(uint32_t code) michael@0: { michael@0: MOZ_ASSERT(isWatchedStop(code)); michael@0: if (isEnabledStop(code)) michael@0: watched_stops_[code].count |= kStopDisabledBit; michael@0: } michael@0: michael@0: void michael@0: Simulator::increaseStopCounter(uint32_t code) michael@0: { michael@0: MOZ_ASSERT(code <= kMaxStopCode); michael@0: MOZ_ASSERT(isWatchedStop(code)); michael@0: if ((watched_stops_[code].count & ~(1 << 31)) == 0x7fffffff) { michael@0: printf("Stop counter for code %i has overflowed.\n" michael@0: "Enabling this code and reseting the counter to 0.\n", code); michael@0: watched_stops_[code].count = 0; michael@0: enableStop(code); michael@0: } else { michael@0: watched_stops_[code].count++; michael@0: } michael@0: } michael@0: michael@0: // Print a stop status. michael@0: void michael@0: Simulator::printStopInfo(uint32_t code) michael@0: { michael@0: MOZ_ASSERT(code <= kMaxStopCode); michael@0: if (!isWatchedStop(code)) { michael@0: printf("Stop not watched."); michael@0: } else { michael@0: const char *state = isEnabledStop(code) ? "Enabled" : "Disabled"; michael@0: int32_t count = watched_stops_[code].count & ~kStopDisabledBit; michael@0: // Don't print the state of unused breakpoints. michael@0: if (count != 0) { michael@0: if (watched_stops_[code].desc) { michael@0: printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", michael@0: code, code, state, count, watched_stops_[code].desc); michael@0: } else { michael@0: printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", michael@0: code, code, state, count); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Instruction types 0 and 1 are both rolled into one function because they michael@0: // only differ in the handling of the shifter_operand. michael@0: void michael@0: Simulator::decodeType01(SimInstruction *instr) michael@0: { michael@0: int type = instr->typeValue(); michael@0: if (type == 0 && instr->isSpecialType0()) { michael@0: // Multiply instruction or extra loads and stores. michael@0: if (instr->bits(7, 4) == 9) { michael@0: if (instr->bit(24) == 0) { michael@0: // Raw field decoding here. Multiply instructions have their Rd michael@0: // in funny places. michael@0: int rn = instr->rnValue(); michael@0: int rm = instr->rmValue(); michael@0: int rs = instr->rsValue(); michael@0: int32_t rs_val = get_register(rs); michael@0: int32_t rm_val = get_register(rm); michael@0: if (instr->bit(23) == 0) { michael@0: if (instr->bit(21) == 0) { michael@0: // The MUL instruction description (A 4.1.33) refers to Rd as being michael@0: // the destination for the operation, but it confusingly uses the michael@0: // Rn field to encode it. michael@0: int rd = rn; // Remap the rn field to the Rd register. michael@0: int32_t alu_out = rm_val * rs_val; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) michael@0: setNZFlags(alu_out); michael@0: } else { michael@0: int rd = instr->rdValue(); michael@0: int32_t acc_value = get_register(rd); michael@0: if (instr->bit(22) == 0) { michael@0: // The MLA instruction description (A 4.1.28) refers to the order michael@0: // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the michael@0: // Rn field to encode the Rd register and the Rd field to encode michael@0: // the Rn register. michael@0: int32_t mul_out = rm_val * rs_val; michael@0: int32_t result = acc_value + mul_out; michael@0: set_register(rn, result); michael@0: } else { michael@0: int32_t mul_out = rm_val * rs_val; michael@0: int32_t result = acc_value - mul_out; michael@0: set_register(rn, result); michael@0: } michael@0: } michael@0: } else { michael@0: // The signed/long multiply instructions use the terms RdHi and RdLo michael@0: // when referring to the target registers. They are mapped to the Rn michael@0: // and Rd fields as follows: michael@0: // RdLo == Rd michael@0: // RdHi == Rn (This is confusingly stored in variable rd here michael@0: // because the mul instruction from above uses the michael@0: // Rn field to encode the Rd register. Good luck figuring michael@0: // this out without reading the ARM instruction manual michael@0: // at a very detailed level.) michael@0: int rd_hi = rn; // Remap the rn field to the RdHi register. michael@0: int rd_lo = instr->rdValue(); michael@0: int32_t hi_res = 0; michael@0: int32_t lo_res = 0; michael@0: if (instr->bit(22) == 1) { michael@0: int64_t left_op = static_cast(rm_val); michael@0: int64_t right_op = static_cast(rs_val); michael@0: uint64_t result = left_op * right_op; michael@0: hi_res = static_cast(result >> 32); michael@0: lo_res = static_cast(result & 0xffffffff); michael@0: } else { michael@0: // unsigned multiply michael@0: uint64_t left_op = static_cast(rm_val); michael@0: uint64_t right_op = static_cast(rs_val); michael@0: uint64_t result = left_op * right_op; michael@0: hi_res = static_cast(result >> 32); michael@0: lo_res = static_cast(result & 0xffffffff); michael@0: } michael@0: set_register(rd_lo, lo_res); michael@0: set_register(rd_hi, hi_res); michael@0: if (instr->hasS()) michael@0: MOZ_CRASH(); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); // Not used atm. michael@0: } michael@0: } else { michael@0: // extra load/store instructions michael@0: int rd = instr->rdValue(); michael@0: int rn = instr->rnValue(); michael@0: int32_t rn_val = get_register(rn); michael@0: int32_t addr = 0; michael@0: if (instr->bit(22) == 0) { michael@0: int rm = instr->rmValue(); michael@0: int32_t rm_val = get_register(rm); michael@0: switch (instr->PUField()) { michael@0: case da_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: addr = rn_val; michael@0: rn_val -= rm_val; michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case ia_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: addr = rn_val; michael@0: rn_val += rm_val; michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case db_x: michael@0: rn_val -= rm_val; michael@0: addr = rn_val; michael@0: if (instr->hasW()) michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case ib_x: michael@0: rn_val += rm_val; michael@0: addr = rn_val; michael@0: if (instr->hasW()) michael@0: set_register(rn, rn_val); michael@0: break; michael@0: default: michael@0: // The PU field is a 2-bit field. michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: } else { michael@0: int32_t imm_val = (instr->immedHValue() << 4) | instr->immedLValue(); michael@0: switch (instr->PUField()) { michael@0: case da_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: addr = rn_val; michael@0: rn_val -= imm_val; michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case ia_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: addr = rn_val; michael@0: rn_val += imm_val; michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case db_x: michael@0: rn_val -= imm_val; michael@0: addr = rn_val; michael@0: if (instr->hasW()) michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case ib_x: michael@0: rn_val += imm_val; michael@0: addr = rn_val; michael@0: if (instr->hasW()) michael@0: set_register(rn, rn_val); michael@0: break; michael@0: default: michael@0: // The PU field is a 2-bit field. michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: } michael@0: if ((instr->bits(7, 4) & 0xd) == 0xd && instr->bit(20) == 0) { michael@0: MOZ_ASSERT((rd % 2) == 0); michael@0: if (instr->hasH()) { michael@0: // The strd instruction. michael@0: int32_t value1 = get_register(rd); michael@0: int32_t value2 = get_register(rd+1); michael@0: writeDW(addr, value1, value2); michael@0: } else { michael@0: // The ldrd instruction. michael@0: int *rn_data = readDW(addr); michael@0: set_dw_register(rd, rn_data); michael@0: } michael@0: } else if (instr->hasH()) { michael@0: if (instr->hasSign()) { michael@0: if (instr->hasL()) { michael@0: int16_t val = readH(addr, instr); michael@0: set_register(rd, val); michael@0: } else { michael@0: int16_t val = get_register(rd); michael@0: writeH(addr, val, instr); michael@0: } michael@0: } else { michael@0: if (instr->hasL()) { michael@0: uint16_t val = readHU(addr, instr); michael@0: set_register(rd, val); michael@0: } else { michael@0: uint16_t val = get_register(rd); michael@0: writeH(addr, val, instr); michael@0: } michael@0: } michael@0: } else { michael@0: // signed byte loads michael@0: MOZ_ASSERT(instr->hasSign()); michael@0: MOZ_ASSERT(instr->hasL()); michael@0: int8_t val = readB(addr); michael@0: set_register(rd, val); michael@0: } michael@0: return; michael@0: } michael@0: } else if ((type == 0) && instr->isMiscType0()) { michael@0: if (instr->bits(7, 4) == 0) { michael@0: if (instr->bit(21) == 0) { michael@0: // mrs michael@0: int rd = instr->rdValue(); michael@0: uint32_t flags; michael@0: if (instr->bit(22) == 0) { michael@0: // CPSR. Note: The Q flag is not yet implemented! michael@0: flags = (n_flag_ << 31) | michael@0: (z_flag_ << 30) | michael@0: (c_flag_ << 29) | michael@0: (v_flag_ << 28); michael@0: } else { michael@0: // SPSR michael@0: MOZ_CRASH(); michael@0: } michael@0: set_register(rd, flags); michael@0: } else { michael@0: // msr michael@0: if (instr->bits(27, 23) == 2) { michael@0: // Register operand. For now we only emit mask 0b1100. michael@0: int rm = instr->rmValue(); michael@0: uint32_t mask = instr->bits(19, 16); michael@0: MOZ_ASSERT(mask == (3 << 2)); michael@0: michael@0: uint32_t flags = get_register(rm); michael@0: n_flag_ = (flags >> 31) & 1; michael@0: z_flag_ = (flags >> 30) & 1; michael@0: c_flag_ = (flags >> 29) & 1; michael@0: v_flag_ = (flags >> 28) & 1; michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: } else if (instr->bits(22, 21) == 1) { michael@0: int rm = instr->rmValue(); michael@0: switch (instr->bits(7, 4)) { michael@0: case 1: // BX michael@0: set_pc(get_register(rm)); michael@0: break; michael@0: case 3: { // BLX michael@0: uint32_t old_pc = get_pc(); michael@0: set_pc(get_register(rm)); michael@0: set_register(lr, old_pc + SimInstruction::kInstrSize); michael@0: break; michael@0: } michael@0: case 7: { // BKPT michael@0: ArmDebugger dbg(this); michael@0: printf("Simulator hit BKPT.\n"); michael@0: dbg.debug(); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: } else if (instr->bits(22, 21) == 3) { michael@0: int rm = instr->rmValue(); michael@0: int rd = instr->rdValue(); michael@0: switch (instr->bits(7, 4)) { michael@0: case 1: { // CLZ michael@0: uint32_t bits = get_register(rm); michael@0: int leading_zeros = 0; michael@0: if (bits == 0) michael@0: leading_zeros = 32; michael@0: else michael@0: leading_zeros = mozilla::CountLeadingZeroes32(bits); michael@0: set_register(rd, leading_zeros); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: } else { michael@0: printf("%08x\n", instr->instructionBits()); michael@0: MOZ_CRASH(); michael@0: } michael@0: } else if ((type == 1) && instr->isNopType1()) { michael@0: // NOP. michael@0: } else { michael@0: int rd = instr->rdValue(); michael@0: int rn = instr->rnValue(); michael@0: int32_t rn_val = get_register(rn); michael@0: int32_t shifter_operand = 0; michael@0: bool shifter_carry_out = 0; michael@0: if (type == 0) { michael@0: shifter_operand = getShiftRm(instr, &shifter_carry_out); michael@0: } else { michael@0: MOZ_ASSERT(instr->typeValue() == 1); michael@0: shifter_operand = getImm(instr, &shifter_carry_out); michael@0: } michael@0: int32_t alu_out; michael@0: switch (instr->opcodeField()) { michael@0: case op_and: michael@0: alu_out = rn_val & shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } michael@0: break; michael@0: case op_eor: michael@0: alu_out = rn_val ^ shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } michael@0: break; michael@0: case op_sub: michael@0: alu_out = rn_val - shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(!borrowFrom(rn_val, shifter_operand)); michael@0: setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, false)); michael@0: } michael@0: break; michael@0: case op_rsb: michael@0: alu_out = shifter_operand - rn_val; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(!borrowFrom(shifter_operand, rn_val)); michael@0: setVFlag(overflowFrom(alu_out, shifter_operand, rn_val, false)); michael@0: } michael@0: break; michael@0: case op_add: michael@0: alu_out = rn_val + shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(carryFrom(rn_val, shifter_operand)); michael@0: setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); michael@0: } michael@0: break; michael@0: case op_adc: michael@0: alu_out = rn_val + shifter_operand + getCarry(); michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(carryFrom(rn_val, shifter_operand, getCarry())); michael@0: setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); michael@0: } michael@0: break; michael@0: case op_sbc: michael@0: case op_rsc: michael@0: MOZ_CRASH(); michael@0: break; michael@0: case op_tst: michael@0: if (instr->hasS()) { michael@0: alu_out = rn_val & shifter_operand; michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } else { michael@0: alu_out = instr->immedMovwMovtValue(); michael@0: set_register(rd, alu_out); michael@0: } michael@0: break; michael@0: case op_teq: michael@0: if (instr->hasS()) { michael@0: alu_out = rn_val ^ shifter_operand; michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } else { michael@0: // Other instructions matching this pattern are handled in the michael@0: // miscellaneous instructions part above. michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case op_cmp: michael@0: if (instr->hasS()) { michael@0: alu_out = rn_val - shifter_operand; michael@0: setNZFlags(alu_out); michael@0: setCFlag(!borrowFrom(rn_val, shifter_operand)); michael@0: setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, false)); michael@0: } else { michael@0: alu_out = (get_register(rd) & 0xffff) | michael@0: (instr->immedMovwMovtValue() << 16); michael@0: set_register(rd, alu_out); michael@0: } michael@0: break; michael@0: case op_cmn: michael@0: if (instr->hasS()) { michael@0: alu_out = rn_val + shifter_operand; michael@0: setNZFlags(alu_out); michael@0: setCFlag(carryFrom(rn_val, shifter_operand)); michael@0: setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); michael@0: } else { michael@0: // Other instructions matching this pattern are handled in the michael@0: // miscellaneous instructions part above. michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case op_orr: michael@0: alu_out = rn_val | shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } michael@0: break; michael@0: case op_mov: michael@0: alu_out = shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } michael@0: break; michael@0: case op_bic: michael@0: alu_out = rn_val & ~shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } michael@0: break; michael@0: case op_mvn: michael@0: alu_out = ~shifter_operand; michael@0: set_register(rd, alu_out); michael@0: if (instr->hasS()) { michael@0: setNZFlags(alu_out); michael@0: setCFlag(shifter_carry_out); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType2(SimInstruction *instr) michael@0: { michael@0: int rd = instr->rdValue(); michael@0: int rn = instr->rnValue(); michael@0: int32_t rn_val = get_register(rn); michael@0: int32_t im_val = instr->offset12Value(); michael@0: int32_t addr = 0; michael@0: switch (instr->PUField()) { michael@0: case da_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: addr = rn_val; michael@0: rn_val -= im_val; michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case ia_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: addr = rn_val; michael@0: rn_val += im_val; michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case db_x: michael@0: rn_val -= im_val; michael@0: addr = rn_val; michael@0: if (instr->hasW()) michael@0: set_register(rn, rn_val); michael@0: break; michael@0: case ib_x: michael@0: rn_val += im_val; michael@0: addr = rn_val; michael@0: if (instr->hasW()) michael@0: set_register(rn, rn_val); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: if (instr->hasB()) { michael@0: if (instr->hasL()) { michael@0: uint8_t val = readBU(addr); michael@0: set_register(rd, val); michael@0: } else { michael@0: uint8_t val = get_register(rd); michael@0: writeB(addr, val); michael@0: } michael@0: } else { michael@0: if (instr->hasL()) michael@0: set_register(rd, readW(addr, instr)); michael@0: else michael@0: writeW(addr, get_register(rd), instr); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType3(SimInstruction *instr) michael@0: { michael@0: int rd = instr->rdValue(); michael@0: int rn = instr->rnValue(); michael@0: int32_t rn_val = get_register(rn); michael@0: bool shifter_carry_out = 0; michael@0: int32_t shifter_operand = getShiftRm(instr, &shifter_carry_out); michael@0: int32_t addr = 0; michael@0: switch (instr->PUField()) { michael@0: case da_x: michael@0: MOZ_ASSERT(!instr->hasW()); michael@0: MOZ_CRASH(); michael@0: break; michael@0: case ia_x: { michael@0: if (instr->bit(4) == 0) { michael@0: // Memop. michael@0: } else { michael@0: if (instr->bit(5) == 0) { michael@0: switch (instr->bits(22, 21)) { michael@0: case 0: michael@0: if (instr->bit(20) == 0) { michael@0: if (instr->bit(6) == 0) { michael@0: // Pkhbt. michael@0: uint32_t rn_val = get_register(rn); michael@0: uint32_t rm_val = get_register(instr->rmValue()); michael@0: int32_t shift = instr->bits(11, 7); michael@0: rm_val <<= shift; michael@0: set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); michael@0: } else { michael@0: // Pkhtb. michael@0: uint32_t rn_val = get_register(rn); michael@0: int32_t rm_val = get_register(instr->rmValue()); michael@0: int32_t shift = instr->bits(11, 7); michael@0: if (shift == 0) michael@0: shift = 32; michael@0: rm_val >>= shift; michael@0: set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case 1: michael@0: MOZ_CRASH(); michael@0: break; michael@0: case 2: michael@0: MOZ_CRASH(); michael@0: break; michael@0: case 3: { michael@0: // Usat. michael@0: int32_t sat_pos = instr->bits(20, 16); michael@0: int32_t sat_val = (1 << sat_pos) - 1; michael@0: int32_t shift = instr->bits(11, 7); michael@0: int32_t shift_type = instr->bit(6); michael@0: int32_t rm_val = get_register(instr->rmValue()); michael@0: if (shift_type == 0) // LSL michael@0: rm_val <<= shift; michael@0: else // ASR michael@0: rm_val >>= shift; michael@0: michael@0: // If saturation occurs, the Q flag should be set in the CPSR. michael@0: // There is no Q flag yet, and no instruction (MRS) to read the michael@0: // CPSR directly. michael@0: if (rm_val > sat_val) michael@0: rm_val = sat_val; michael@0: else if (rm_val < 0) michael@0: rm_val = 0; michael@0: set_register(rd, rm_val); michael@0: break; michael@0: } michael@0: } michael@0: } else { michael@0: switch (instr->bits(22, 21)) { michael@0: case 0: michael@0: MOZ_CRASH(); michael@0: break; michael@0: case 1: michael@0: MOZ_CRASH(); michael@0: break; michael@0: case 2: michael@0: if ((instr->bit(20) == 0) && (instr->bits(9, 6) == 1)) { michael@0: if (instr->bits(19, 16) == 0xF) { michael@0: // Uxtb16. michael@0: uint32_t rm_val = get_register(instr->rmValue()); michael@0: int32_t rotate = instr->bits(11, 10); michael@0: switch (rotate) { michael@0: case 0: michael@0: break; michael@0: case 1: michael@0: rm_val = (rm_val >> 8) | (rm_val << 24); michael@0: break; michael@0: case 2: michael@0: rm_val = (rm_val >> 16) | (rm_val << 16); michael@0: break; michael@0: case 3: michael@0: rm_val = (rm_val >> 24) | (rm_val << 8); michael@0: break; michael@0: } michael@0: set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case 3: michael@0: if ((instr->bit(20) == 0) && (instr->bits(9, 6) == 1)) { michael@0: if (instr->bits(19, 16) == 0xF) { michael@0: // Uxtb. michael@0: uint32_t rm_val = get_register(instr->rmValue()); michael@0: int32_t rotate = instr->bits(11, 10); michael@0: switch (rotate) { michael@0: case 0: michael@0: break; michael@0: case 1: michael@0: rm_val = (rm_val >> 8) | (rm_val << 24); michael@0: break; michael@0: case 2: michael@0: rm_val = (rm_val >> 16) | (rm_val << 16); michael@0: break; michael@0: case 3: michael@0: rm_val = (rm_val >> 24) | (rm_val << 8); michael@0: break; michael@0: } michael@0: set_register(rd, (rm_val & 0xFF)); michael@0: } else { michael@0: // Uxtab. michael@0: uint32_t rn_val = get_register(rn); michael@0: uint32_t rm_val = get_register(instr->rmValue()); michael@0: int32_t rotate = instr->bits(11, 10); michael@0: switch (rotate) { michael@0: case 0: michael@0: break; michael@0: case 1: michael@0: rm_val = (rm_val >> 8) | (rm_val << 24); michael@0: break; michael@0: case 2: michael@0: rm_val = (rm_val >> 16) | (rm_val << 16); michael@0: break; michael@0: case 3: michael@0: rm_val = (rm_val >> 24) | (rm_val << 8); michael@0: break; michael@0: } michael@0: set_register(rd, rn_val + (rm_val & 0xFF)); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: break; michael@0: } michael@0: case db_x: { // sudiv michael@0: if (instr->bit(22) == 0x0 && instr->bit(20) == 0x1 && michael@0: instr->bits(15,12) == 0x0f && instr->bits(7, 4) == 0x1) { michael@0: if (!instr->hasW()) { michael@0: // sdiv (in V8 notation matching ARM ISA format) rn = rm/rs michael@0: int rm = instr->rmValue(); michael@0: int32_t rm_val = get_register(rm); michael@0: int rs = instr->rsValue(); michael@0: int32_t rs_val = get_register(rs); michael@0: int32_t ret_val = 0; michael@0: MOZ_ASSERT(rs_val != 0); michael@0: if ((rm_val == INT32_MIN) && (rs_val == -1)) michael@0: ret_val = INT32_MIN; michael@0: else michael@0: ret_val = rm_val / rs_val; michael@0: set_register(rn, ret_val); michael@0: return; michael@0: } else { michael@0: // udiv (in V8 notation matching ARM ISA format) rn = rm/rs michael@0: int rm = instr->rmValue(); michael@0: uint32_t rm_val = get_register(rm); michael@0: int rs = instr->rsValue(); michael@0: uint32_t rs_val = get_register(rs); michael@0: uint32_t ret_val = 0; michael@0: MOZ_ASSERT(rs_val != 0); michael@0: ret_val = rm_val / rs_val; michael@0: set_register(rn, ret_val); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: addr = rn_val - shifter_operand; michael@0: if (instr->hasW()) michael@0: set_register(rn, addr); michael@0: break; michael@0: } michael@0: case ib_x: { michael@0: if (instr->hasW() && (instr->bits(6, 4) == 0x5)) { michael@0: uint32_t widthminus1 = static_cast(instr->bits(20, 16)); michael@0: uint32_t lsbit = static_cast(instr->bits(11, 7)); michael@0: uint32_t msbit = widthminus1 + lsbit; michael@0: if (msbit <= 31) { michael@0: if (instr->bit(22)) { michael@0: // ubfx - unsigned bitfield extract. michael@0: uint32_t rm_val = static_cast(get_register(instr->rmValue())); michael@0: uint32_t extr_val = rm_val << (31 - msbit); michael@0: extr_val = extr_val >> (31 - widthminus1); michael@0: set_register(instr->rdValue(), extr_val); michael@0: } else { michael@0: // sbfx - signed bitfield extract. michael@0: int32_t rm_val = get_register(instr->rmValue()); michael@0: int32_t extr_val = rm_val << (31 - msbit); michael@0: extr_val = extr_val >> (31 - widthminus1); michael@0: set_register(instr->rdValue(), extr_val); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: return; michael@0: } else if (!instr->hasW() && (instr->bits(6, 4) == 0x1)) { michael@0: uint32_t lsbit = static_cast(instr->bits(11, 7)); michael@0: uint32_t msbit = static_cast(instr->bits(20, 16)); michael@0: if (msbit >= lsbit) { michael@0: // bfc or bfi - bitfield clear/insert. michael@0: uint32_t rd_val = michael@0: static_cast(get_register(instr->rdValue())); michael@0: uint32_t bitcount = msbit - lsbit + 1; michael@0: uint32_t mask = (1 << bitcount) - 1; michael@0: rd_val &= ~(mask << lsbit); michael@0: if (instr->rmValue() != 15) { michael@0: // bfi - bitfield insert. michael@0: uint32_t rm_val = michael@0: static_cast(get_register(instr->rmValue())); michael@0: rm_val &= mask; michael@0: rd_val |= rm_val << lsbit; michael@0: } michael@0: set_register(instr->rdValue(), rd_val); michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: return; michael@0: } else { michael@0: addr = rn_val + shifter_operand; michael@0: if (instr->hasW()) michael@0: set_register(rn, addr); michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: if (instr->hasB()) { michael@0: if (instr->hasL()) { michael@0: uint8_t byte = readB(addr); michael@0: set_register(rd, byte); michael@0: } else { michael@0: uint8_t byte = get_register(rd); michael@0: writeB(addr, byte); michael@0: } michael@0: } else { michael@0: if (instr->hasL()) michael@0: set_register(rd, readW(addr, instr)); michael@0: else michael@0: writeW(addr, get_register(rd), instr); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType4(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT(instr->bit(22) == 0); // Only allowed to be set in privileged mode. michael@0: bool load = instr->hasL(); michael@0: handleRList(instr, load); michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType5(SimInstruction *instr) michael@0: { michael@0: int off = instr->sImmed24Value() << 2; michael@0: intptr_t pc_address = get_pc(); michael@0: if (instr->hasLink()) michael@0: set_register(lr, pc_address + SimInstruction::kInstrSize); michael@0: int pc_reg = get_register(pc); michael@0: set_pc(pc_reg + off); michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType6(SimInstruction *instr) michael@0: { michael@0: decodeType6CoprocessorIns(instr); michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType7(SimInstruction *instr) michael@0: { michael@0: if (instr->bit(24) == 1) michael@0: softwareInterrupt(instr); michael@0: else michael@0: decodeTypeVFP(instr); michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeTypeVFP(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT(instr->typeValue() == 7 && instr->bit(24) == 0); michael@0: MOZ_ASSERT(instr->bits(11, 9) == 0x5); michael@0: michael@0: // Obtain double precision register codes. michael@0: VFPRegPrecision precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; michael@0: int vm = instr->VFPMRegValue(precision); michael@0: int vd = instr->VFPDRegValue(precision); michael@0: int vn = instr->VFPNRegValue(precision); michael@0: michael@0: if (instr->bit(4) == 0) { michael@0: if (instr->opc1Value() == 0x7) { michael@0: // Other data processing instructions michael@0: if ((instr->opc2Value() == 0x0) && (instr->opc3Value() == 0x1)) { michael@0: // vmov register to register. michael@0: if (instr->szValue() == 0x1) { michael@0: int m = instr->VFPMRegValue(kDoublePrecision); michael@0: int d = instr->VFPDRegValue(kDoublePrecision); michael@0: set_d_register_from_double(d, get_double_from_d_register(m)); michael@0: } else { michael@0: int m = instr->VFPMRegValue(kSinglePrecision); michael@0: int d = instr->VFPDRegValue(kSinglePrecision); michael@0: set_s_register_from_float(d, get_float_from_s_register(m)); michael@0: } michael@0: } else if ((instr->opc2Value() == 0x0) && (instr->opc3Value() == 0x3)) { michael@0: // vabs michael@0: if (instr->szValue() == 0x1) { michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = std::fabs(dm_value); michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } else { michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = std::fabs(fm_value); michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } michael@0: } else if ((instr->opc2Value() == 0x1) && (instr->opc3Value() == 0x1)) { michael@0: // vneg michael@0: if (instr->szValue() == 0x1) { michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = -dm_value; michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } else { michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = -fm_value; michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } michael@0: } else if ((instr->opc2Value() == 0x7) && (instr->opc3Value() == 0x3)) { michael@0: decodeVCVTBetweenDoubleAndSingle(instr); michael@0: } else if ((instr->opc2Value() == 0x8) && (instr->opc3Value() & 0x1)) { michael@0: decodeVCVTBetweenFloatingPointAndInteger(instr); michael@0: } else if ((instr->opc2Value() == 0xA) && (instr->opc3Value() == 0x3) && michael@0: (instr->bit(8) == 1)) { michael@0: // vcvt.f64.s32 Dd, Dd, # michael@0: int fraction_bits = 32 - ((instr->bits(3, 0) << 1) | instr->bit(5)); michael@0: int fixed_value = get_sinteger_from_s_register(vd * 2); michael@0: double divide = 1 << fraction_bits; michael@0: set_d_register_from_double(vd, fixed_value / divide); michael@0: } else if (((instr->opc2Value() >> 1) == 0x6) && michael@0: (instr->opc3Value() & 0x1)) { michael@0: decodeVCVTBetweenFloatingPointAndInteger(instr); michael@0: } else if (((instr->opc2Value() == 0x4) || (instr->opc2Value() == 0x5)) && michael@0: (instr->opc3Value() & 0x1)) { michael@0: decodeVCMP(instr); michael@0: } else if (((instr->opc2Value() == 0x1)) && (instr->opc3Value() == 0x3)) { michael@0: // vsqrt michael@0: if (instr->szValue() == 0x1) { michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = std::sqrt(dm_value); michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } else { michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = std::sqrt(fm_value); michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } michael@0: } else if (instr->opc3Value() == 0x0) { michael@0: // vmov immediate. michael@0: if (instr->szValue() == 0x1) { michael@0: set_d_register_from_double(vd, instr->doubleImmedVmov()); michael@0: } else { michael@0: // vmov.f32 immediate michael@0: set_s_register_from_float(vd, instr->float32ImmedVmov()); michael@0: } michael@0: } else { michael@0: decodeVCVTBetweenFloatingPointAndIntegerFrac(instr); michael@0: } michael@0: } else if (instr->opc1Value() == 0x3) { michael@0: if (instr->szValue() != 0x1) { michael@0: if (instr->opc3Value() & 0x1) { michael@0: // vsub michael@0: float fn_value = get_float_from_s_register(vn); michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = fn_value - fm_value; michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } else { michael@0: // vadd michael@0: float fn_value = get_float_from_s_register(vn); michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = fn_value + fm_value; michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } michael@0: } else { michael@0: if (instr->opc3Value() & 0x1) { michael@0: // vsub michael@0: double dn_value = get_double_from_d_register(vn); michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = dn_value - dm_value; michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } else { michael@0: // vadd michael@0: double dn_value = get_double_from_d_register(vn); michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = dn_value + dm_value; michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } michael@0: } michael@0: } else if ((instr->opc1Value() == 0x2) && !(instr->opc3Value() & 0x1)) { michael@0: // vmul michael@0: if (instr->szValue() != 0x1) { michael@0: float fn_value = get_float_from_s_register(vn); michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = fn_value * fm_value; michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } else { michael@0: double dn_value = get_double_from_d_register(vn); michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = dn_value * dm_value; michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } michael@0: } else if ((instr->opc1Value() == 0x0)) { michael@0: // vmla, vmls michael@0: const bool is_vmls = (instr->opc3Value() & 0x1); michael@0: michael@0: if (instr->szValue() != 0x1) michael@0: MOZ_ASSUME_UNREACHABLE(); // Not used by V8. michael@0: michael@0: const double dd_val = get_double_from_d_register(vd); michael@0: const double dn_val = get_double_from_d_register(vn); michael@0: const double dm_val = get_double_from_d_register(vm); michael@0: michael@0: // Note: we do the mul and add/sub in separate steps to avoid getting a michael@0: // result with too high precision. michael@0: set_d_register_from_double(vd, dn_val * dm_val); michael@0: if (is_vmls) { michael@0: set_d_register_from_double(vd, michael@0: canonicalizeNaN(dd_val - get_double_from_d_register(vd))); michael@0: } else { michael@0: set_d_register_from_double(vd, michael@0: canonicalizeNaN(dd_val + get_double_from_d_register(vd))); michael@0: } michael@0: } else if ((instr->opc1Value() == 0x4) && !(instr->opc3Value() & 0x1)) { michael@0: // vdiv michael@0: if (instr->szValue() != 0x1) { michael@0: float fn_value = get_float_from_s_register(vn); michael@0: float fm_value = get_float_from_s_register(vm); michael@0: float fd_value = fn_value / fm_value; michael@0: div_zero_vfp_flag_ = (fm_value == 0); michael@0: fd_value = canonicalizeNaN(fd_value); michael@0: set_s_register_from_float(vd, fd_value); michael@0: } else { michael@0: double dn_value = get_double_from_d_register(vn); michael@0: double dm_value = get_double_from_d_register(vm); michael@0: double dd_value = dn_value / dm_value; michael@0: div_zero_vfp_flag_ = (dm_value == 0); michael@0: dd_value = canonicalizeNaN(dd_value); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: } else { michael@0: if (instr->VCValue() == 0x0 && instr->VAValue() == 0x0) { michael@0: decodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); michael@0: } else if ((instr->VLValue() == 0x0) && michael@0: (instr->VCValue() == 0x1) && michael@0: (instr->bit(23) == 0x0)) { michael@0: // vmov (ARM core register to scalar) michael@0: int vd = instr->bits(19, 16) | (instr->bit(7) << 4); michael@0: double dd_value = get_double_from_d_register(vd); michael@0: int32_t data[2]; michael@0: memcpy(data, &dd_value, 8); michael@0: data[instr->bit(21)] = get_register(instr->rtValue()); michael@0: memcpy(&dd_value, data, 8); michael@0: set_d_register_from_double(vd, dd_value); michael@0: } else if ((instr->VLValue() == 0x1) && michael@0: (instr->VCValue() == 0x1) && michael@0: (instr->bit(23) == 0x0)) { michael@0: // vmov (scalar to ARM core register) michael@0: int vn = instr->bits(19, 16) | (instr->bit(7) << 4); michael@0: double dn_value = get_double_from_d_register(vn); michael@0: int32_t data[2]; michael@0: memcpy(data, &dn_value, 8); michael@0: set_register(instr->rtValue(), data[instr->bit(21)]); michael@0: } else if ((instr->VLValue() == 0x1) && michael@0: (instr->VCValue() == 0x0) && michael@0: (instr->VAValue() == 0x7) && michael@0: (instr->bits(19, 16) == 0x1)) { michael@0: // vmrs michael@0: uint32_t rt = instr->rtValue(); michael@0: if (rt == 0xF) { michael@0: copy_FPSCR_to_APSR(); michael@0: } else { michael@0: // Emulate FPSCR from the Simulator flags. michael@0: uint32_t fpscr = (n_flag_FPSCR_ << 31) | michael@0: (z_flag_FPSCR_ << 30) | michael@0: (c_flag_FPSCR_ << 29) | michael@0: (v_flag_FPSCR_ << 28) | michael@0: (FPSCR_default_NaN_mode_ << 25) | michael@0: (inexact_vfp_flag_ << 4) | michael@0: (underflow_vfp_flag_ << 3) | michael@0: (overflow_vfp_flag_ << 2) | michael@0: (div_zero_vfp_flag_ << 1) | michael@0: (inv_op_vfp_flag_ << 0) | michael@0: (FPSCR_rounding_mode_); michael@0: set_register(rt, fpscr); michael@0: } michael@0: } else if ((instr->VLValue() == 0x0) && michael@0: (instr->VCValue() == 0x0) && michael@0: (instr->VAValue() == 0x7) && michael@0: (instr->bits(19, 16) == 0x1)) { michael@0: // vmsr michael@0: uint32_t rt = instr->rtValue(); michael@0: if (rt == pc) { michael@0: MOZ_CRASH(); michael@0: } else { michael@0: uint32_t rt_value = get_register(rt); michael@0: n_flag_FPSCR_ = (rt_value >> 31) & 1; michael@0: z_flag_FPSCR_ = (rt_value >> 30) & 1; michael@0: c_flag_FPSCR_ = (rt_value >> 29) & 1; michael@0: v_flag_FPSCR_ = (rt_value >> 28) & 1; michael@0: FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; michael@0: inexact_vfp_flag_ = (rt_value >> 4) & 1; michael@0: underflow_vfp_flag_ = (rt_value >> 3) & 1; michael@0: overflow_vfp_flag_ = (rt_value >> 2) & 1; michael@0: div_zero_vfp_flag_ = (rt_value >> 1) & 1; michael@0: inv_op_vfp_flag_ = (rt_value >> 0) & 1; michael@0: FPSCR_rounding_mode_ = michael@0: static_cast((rt_value) & kVFPRoundingModeMask); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeVMOVBetweenCoreAndSinglePrecisionRegisters(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT(instr->bit(4) == 1 && michael@0: instr->VCValue() == 0x0 && michael@0: instr->VAValue() == 0x0); michael@0: michael@0: int t = instr->rtValue(); michael@0: int n = instr->VFPNRegValue(kSinglePrecision); michael@0: bool to_arm_register = (instr->VLValue() == 0x1); michael@0: if (to_arm_register) { michael@0: int32_t int_value = get_sinteger_from_s_register(n); michael@0: set_register(t, int_value); michael@0: } else { michael@0: int32_t rs_val = get_register(t); michael@0: set_s_register_from_sinteger(n, rs_val); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeVCMP(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT((instr->bit(4) == 0) && (instr->opc1Value() == 0x7)); michael@0: MOZ_ASSERT(((instr->opc2Value() == 0x4) || (instr->opc2Value() == 0x5)) && michael@0: (instr->opc3Value() & 0x1)); michael@0: // Comparison. michael@0: michael@0: VFPRegPrecision precision = kSinglePrecision; michael@0: if (instr->szValue() == 1) michael@0: precision = kDoublePrecision; michael@0: michael@0: int d = instr->VFPDRegValue(precision); michael@0: int m = 0; michael@0: if (instr->opc2Value() == 0x4) michael@0: m = instr->VFPMRegValue(precision); michael@0: michael@0: if (precision == kDoublePrecision) { michael@0: double dd_value = get_double_from_d_register(d); michael@0: double dm_value = 0.0; michael@0: if (instr->opc2Value() == 0x4) { michael@0: dm_value = get_double_from_d_register(m); michael@0: } michael@0: michael@0: // Raise exceptions for quiet NaNs if necessary. michael@0: if (instr->bit(7) == 1) { michael@0: if (mozilla::IsNaN(dd_value)) michael@0: inv_op_vfp_flag_ = true; michael@0: } michael@0: compute_FPSCR_Flags(dd_value, dm_value); michael@0: } else { michael@0: float fd_value = get_float_from_s_register(d); michael@0: float fm_value = 0.0; michael@0: if (instr->opc2Value() == 0x4) michael@0: fm_value = get_float_from_s_register(m); michael@0: michael@0: // Raise exceptions for quiet NaNs if necessary. michael@0: if (instr->bit(7) == 1) { michael@0: if (mozilla::IsNaN(fd_value)) michael@0: inv_op_vfp_flag_ = true; michael@0: } michael@0: compute_FPSCR_Flags(fd_value, fm_value); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeVCVTBetweenDoubleAndSingle(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT(instr->bit(4) == 0 && instr->opc1Value() == 0x7); michael@0: MOZ_ASSERT(instr->opc2Value() == 0x7 && instr->opc3Value() == 0x3); michael@0: michael@0: VFPRegPrecision dst_precision = kDoublePrecision; michael@0: VFPRegPrecision src_precision = kSinglePrecision; michael@0: if (instr->szValue() == 1) { michael@0: dst_precision = kSinglePrecision; michael@0: src_precision = kDoublePrecision; michael@0: } michael@0: michael@0: int dst = instr->VFPDRegValue(dst_precision); michael@0: int src = instr->VFPMRegValue(src_precision); michael@0: michael@0: if (dst_precision == kSinglePrecision) { michael@0: double val = get_double_from_d_register(src); michael@0: set_s_register_from_float(dst, static_cast(val)); michael@0: } else { michael@0: float val = get_float_from_s_register(src); michael@0: set_d_register_from_double(dst, static_cast(val)); michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) michael@0: { michael@0: MOZ_ASSERT(mode == SimRN || mode == SimRM || mode == SimRZ); michael@0: double max_uint = static_cast(0xffffffffu); michael@0: double max_int = static_cast(INT32_MAX); michael@0: double min_int = static_cast(INT32_MIN); michael@0: michael@0: // Check for NaN. michael@0: if (val != val) michael@0: return true; michael@0: michael@0: // Check for overflow. This code works because 32bit integers can be michael@0: // exactly represented by ieee-754 64bit floating-point values. michael@0: switch (mode) { michael@0: case SimRN: michael@0: return unsigned_ ? (val >= (max_uint + 0.5)) || michael@0: (val < -0.5) michael@0: : (val >= (max_int + 0.5)) || michael@0: (val < (min_int - 0.5)); michael@0: case SimRM: michael@0: return unsigned_ ? (val >= (max_uint + 1.0)) || michael@0: (val < 0) michael@0: : (val >= (max_int + 1.0)) || michael@0: (val < min_int); michael@0: case SimRZ: michael@0: return unsigned_ ? (val >= (max_uint + 1.0)) || michael@0: (val <= -1) michael@0: : (val >= (max_int + 1.0)) || michael@0: (val <= (min_int - 1.0)); michael@0: default: michael@0: MOZ_CRASH(); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // We call this function only if we had a vfp invalid exception. michael@0: // It returns the correct saturated value. michael@0: static int michael@0: VFPConversionSaturate(double val, bool unsigned_res) michael@0: { michael@0: if (val != val) // NaN. michael@0: return 0; michael@0: if (unsigned_res) michael@0: return (val < 0) ? 0 : 0xffffffffu; michael@0: return (val < 0) ? INT32_MIN : INT32_MAX; michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeVCVTBetweenFloatingPointAndInteger(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT((instr->bit(4) == 0) && (instr->opc1Value() == 0x7) && michael@0: (instr->bits(27, 23) == 0x1D)); michael@0: MOZ_ASSERT(((instr->opc2Value() == 0x8) && (instr->opc3Value() & 0x1)) || michael@0: (((instr->opc2Value() >> 1) == 0x6) && (instr->opc3Value() & 0x1))); michael@0: michael@0: // Conversion between floating-point and integer. michael@0: bool to_integer = (instr->bit(18) == 1); michael@0: michael@0: VFPRegPrecision src_precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; michael@0: michael@0: if (to_integer) { michael@0: // We are playing with code close to the C++ standard's limits below, michael@0: // hence the very simple code and heavy checks. michael@0: // michael@0: // Note: michael@0: // C++ defines default type casting from floating point to integer as michael@0: // (close to) rounding toward zero ("fractional part discarded"). michael@0: michael@0: int dst = instr->VFPDRegValue(kSinglePrecision); michael@0: int src = instr->VFPMRegValue(src_precision); michael@0: michael@0: // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding michael@0: // mode or the default Round to Zero mode. michael@0: VFPRoundingMode mode = (instr->bit(7) != 1) ? FPSCR_rounding_mode_ : SimRZ; michael@0: MOZ_ASSERT(mode == SimRM || mode == SimRZ || mode == SimRN); michael@0: michael@0: bool unsigned_integer = (instr->bit(16) == 0); michael@0: bool double_precision = (src_precision == kDoublePrecision); michael@0: michael@0: double val = double_precision michael@0: ? get_double_from_d_register(src) michael@0: : get_float_from_s_register(src); michael@0: michael@0: int temp = unsigned_integer ? static_cast(val) : static_cast(val); michael@0: michael@0: inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); michael@0: michael@0: double abs_diff = unsigned_integer michael@0: ? std::fabs(val - static_cast(temp)) michael@0: : std::fabs(val - temp); michael@0: michael@0: inexact_vfp_flag_ = (abs_diff != 0); michael@0: michael@0: if (inv_op_vfp_flag_) { michael@0: temp = VFPConversionSaturate(val, unsigned_integer); michael@0: } else { michael@0: switch (mode) { michael@0: case SimRN: { michael@0: int val_sign = (val > 0) ? 1 : -1; michael@0: if (abs_diff > 0.5) { michael@0: temp += val_sign; michael@0: } else if (abs_diff == 0.5) { michael@0: // Round to even if exactly halfway. michael@0: temp = ((temp % 2) == 0) ? temp : temp + val_sign; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case SimRM: michael@0: temp = temp > val ? temp - 1 : temp; michael@0: break; michael@0: michael@0: case SimRZ: michael@0: // Nothing to do. michael@0: break; michael@0: michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: // Update the destination register. michael@0: set_s_register_from_sinteger(dst, temp); michael@0: } else { michael@0: bool unsigned_integer = (instr->bit(7) == 0); michael@0: int dst = instr->VFPDRegValue(src_precision); michael@0: int src = instr->VFPMRegValue(kSinglePrecision); michael@0: michael@0: int val = get_sinteger_from_s_register(src); michael@0: michael@0: if (src_precision == kDoublePrecision) { michael@0: if (unsigned_integer) michael@0: set_d_register_from_double(dst, static_cast(static_cast(val))); michael@0: else michael@0: set_d_register_from_double(dst, static_cast(val)); michael@0: } else { michael@0: if (unsigned_integer) michael@0: set_s_register_from_float(dst, static_cast(static_cast(val))); michael@0: else michael@0: set_s_register_from_float(dst, static_cast(val)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // A VFPv3 specific instruction. michael@0: void michael@0: Simulator::decodeVCVTBetweenFloatingPointAndIntegerFrac(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT(instr->bits(27, 24) == 0xE && instr->opc1Value() == 0x7 && instr->bit(19) == 1 && michael@0: instr->bit(17) == 1 && instr->bits(11,9) == 0x5 && instr->bit(6) == 1 && michael@0: instr->bit(4) == 0); michael@0: michael@0: int size = (instr->bit(7) == 1) ? 32 : 16; michael@0: michael@0: int fraction_bits = size - ((instr->bits(3, 0) << 1) | instr->bit(5)); michael@0: double mult = 1 << fraction_bits; michael@0: michael@0: MOZ_ASSERT(size == 32); // Only handling size == 32 for now. michael@0: michael@0: // Conversion between floating-point and integer. michael@0: bool to_fixed = (instr->bit(18) == 1); michael@0: michael@0: VFPRegPrecision precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; michael@0: michael@0: if (to_fixed) { michael@0: // We are playing with code close to the C++ standard's limits below, michael@0: // hence the very simple code and heavy checks. michael@0: // michael@0: // Note: C++ defines default type casting from floating point to integer as michael@0: // (close to) rounding toward zero ("fractional part discarded"). michael@0: michael@0: int dst = instr->VFPDRegValue(precision); michael@0: michael@0: bool unsigned_integer = (instr->bit(16) == 1); michael@0: bool double_precision = (precision == kDoublePrecision); michael@0: michael@0: double val = double_precision michael@0: ? get_double_from_d_register(dst) michael@0: : get_float_from_s_register(dst); michael@0: michael@0: // Scale value by specified number of fraction bits. michael@0: val *= mult; michael@0: michael@0: // Rounding down towards zero. No need to account for the rounding error as this michael@0: // instruction always rounds down towards zero. See SimRZ below. michael@0: int temp = unsigned_integer ? static_cast(val) : static_cast(val); michael@0: michael@0: inv_op_vfp_flag_ = get_inv_op_vfp_flag(SimRZ, val, unsigned_integer); michael@0: michael@0: double abs_diff = unsigned_integer michael@0: ? std::fabs(val - static_cast(temp)) michael@0: : std::fabs(val - temp); michael@0: michael@0: inexact_vfp_flag_ = (abs_diff != 0); michael@0: michael@0: if (inv_op_vfp_flag_) michael@0: temp = VFPConversionSaturate(val, unsigned_integer); michael@0: michael@0: // Update the destination register. michael@0: if (double_precision) { michael@0: uint32_t dbl[2]; michael@0: dbl[0] = temp; dbl[1] = 0; michael@0: set_d_register(dst, dbl); michael@0: } else { michael@0: set_s_register_from_sinteger(dst, temp); michael@0: } michael@0: } else { michael@0: MOZ_ASSUME_UNREACHABLE(); // Not implemented, fixed to float. michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeType6CoprocessorIns(SimInstruction *instr) michael@0: { michael@0: MOZ_ASSERT(instr->typeValue() == 6); michael@0: michael@0: if (instr->coprocessorValue() == 0xA) { michael@0: switch (instr->opcodeValue()) { michael@0: case 0x8: michael@0: case 0xA: michael@0: case 0xC: michael@0: case 0xE: { // Load and store single precision float to memory. michael@0: int rn = instr->rnValue(); michael@0: int vd = instr->VFPDRegValue(kSinglePrecision); michael@0: int offset = instr->immed8Value(); michael@0: if (!instr->hasU()) michael@0: offset = -offset; michael@0: michael@0: int32_t address = get_register(rn) + 4 * offset; michael@0: if (instr->hasL()) { michael@0: // Load double from memory: vldr. michael@0: set_s_register_from_sinteger(vd, readW(address, instr)); michael@0: } else { michael@0: // Store double to memory: vstr. michael@0: writeW(address, get_sinteger_from_s_register(vd), instr); michael@0: } michael@0: break; michael@0: } michael@0: case 0x4: michael@0: case 0x5: michael@0: case 0x6: michael@0: case 0x7: michael@0: case 0x9: michael@0: case 0xB: michael@0: // Load/store multiple single from memory: vldm/vstm. michael@0: handleVList(instr); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: } else if (instr->coprocessorValue() == 0xB) { michael@0: switch (instr->opcodeValue()) { michael@0: case 0x2: michael@0: // Load and store double to two GP registers michael@0: if (instr->bits(7, 6) != 0 || instr->bit(4) != 1) { michael@0: MOZ_CRASH(); // Not used atm. michael@0: } else { michael@0: int rt = instr->rtValue(); michael@0: int rn = instr->rnValue(); michael@0: int vm = instr->VFPMRegValue(kDoublePrecision); michael@0: if (instr->hasL()) { michael@0: int32_t data[2]; michael@0: double d = get_double_from_d_register(vm); michael@0: memcpy(data, &d, 8); michael@0: set_register(rt, data[0]); michael@0: set_register(rn, data[1]); michael@0: } else { michael@0: int32_t data[] = { get_register(rt), get_register(rn) }; michael@0: double d; michael@0: memcpy(&d, data, 8); michael@0: set_d_register_from_double(vm, d); michael@0: } michael@0: } michael@0: break; michael@0: case 0x8: michael@0: case 0xA: michael@0: case 0xC: michael@0: case 0xE: { // Load and store double to memory. michael@0: int rn = instr->rnValue(); michael@0: int vd = instr->VFPDRegValue(kDoublePrecision); michael@0: int offset = instr->immed8Value(); michael@0: if (!instr->hasU()) michael@0: offset = -offset; michael@0: int32_t address = get_register(rn) + 4 * offset; michael@0: if (instr->hasL()) { michael@0: // Load double from memory: vldr. michael@0: int32_t data[] = { michael@0: readW(address, instr), michael@0: readW(address + 4, instr) michael@0: }; michael@0: double val; michael@0: memcpy(&val, data, 8); michael@0: set_d_register_from_double(vd, val); michael@0: } else { michael@0: // Store double to memory: vstr. michael@0: int32_t data[2]; michael@0: double val = get_double_from_d_register(vd); michael@0: memcpy(data, &val, 8); michael@0: writeW(address, data[0], instr); michael@0: writeW(address + 4, data[1], instr); michael@0: } michael@0: break; michael@0: } michael@0: case 0x4: michael@0: case 0x5: michael@0: case 0x6: michael@0: case 0x7: michael@0: case 0x9: michael@0: case 0xB: michael@0: // Load/store multiple double from memory: vldm/vstm. michael@0: handleVList(instr); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::decodeSpecialCondition(SimInstruction *instr) michael@0: { michael@0: switch (instr->specialValue()) { michael@0: case 5: michael@0: if (instr->bits(18, 16) == 0 && instr->bits(11, 6) == 0x28 && instr->bit(4) == 1) { michael@0: // vmovl signed michael@0: int Vd = (instr->bit(22) << 4) | instr->vdValue(); michael@0: int Vm = (instr->bit(5) << 4) | instr->vmValue(); michael@0: int imm3 = instr->bits(21, 19); michael@0: if (imm3 != 1 && imm3 != 2 && imm3 != 4) michael@0: MOZ_CRASH(); michael@0: int esize = 8 * imm3; michael@0: int elements = 64 / esize; michael@0: int8_t from[8]; michael@0: get_d_register(Vm, reinterpret_cast(from)); michael@0: int16_t to[8]; michael@0: int e = 0; michael@0: while (e < elements) { michael@0: to[e] = from[e]; michael@0: e++; michael@0: } michael@0: set_q_register(Vd, reinterpret_cast(to)); michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case 7: michael@0: if (instr->bits(18, 16) == 0 && instr->bits(11, 6) == 0x28 && instr->bit(4) == 1) { michael@0: // vmovl unsigned michael@0: int Vd = (instr->bit(22) << 4) | instr->vdValue(); michael@0: int Vm = (instr->bit(5) << 4) | instr->vmValue(); michael@0: int imm3 = instr->bits(21, 19); michael@0: if (imm3 != 1 && imm3 != 2 && imm3 != 4) michael@0: MOZ_CRASH(); michael@0: int esize = 8 * imm3; michael@0: int elements = 64 / esize; michael@0: uint8_t from[8]; michael@0: get_d_register(Vm, reinterpret_cast(from)); michael@0: uint16_t to[8]; michael@0: int e = 0; michael@0: while (e < elements) { michael@0: to[e] = from[e]; michael@0: e++; michael@0: } michael@0: set_q_register(Vd, reinterpret_cast(to)); michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case 8: michael@0: if (instr->bits(21, 20) == 0) { michael@0: // vst1 michael@0: int Vd = (instr->bit(22) << 4) | instr->vdValue(); michael@0: int Rn = instr->vnValue(); michael@0: int type = instr->bits(11, 8); michael@0: int Rm = instr->vmValue(); michael@0: int32_t address = get_register(Rn); michael@0: int regs = 0; michael@0: switch (type) { michael@0: case nlt_1: michael@0: regs = 1; michael@0: break; michael@0: case nlt_2: michael@0: regs = 2; michael@0: break; michael@0: case nlt_3: michael@0: regs = 3; michael@0: break; michael@0: case nlt_4: michael@0: regs = 4; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: int r = 0; michael@0: while (r < regs) { michael@0: uint32_t data[2]; michael@0: get_d_register(Vd + r, data); michael@0: writeW(address, data[0], instr); michael@0: writeW(address + 4, data[1], instr); michael@0: address += 8; michael@0: r++; michael@0: } michael@0: if (Rm != 15) { michael@0: if (Rm == 13) michael@0: set_register(Rn, address); michael@0: else michael@0: set_register(Rn, get_register(Rn) + get_register(Rm)); michael@0: } michael@0: } else if (instr->bits(21, 20) == 2) { michael@0: // vld1 michael@0: int Vd = (instr->bit(22) << 4) | instr->vdValue(); michael@0: int Rn = instr->vnValue(); michael@0: int type = instr->bits(11, 8); michael@0: int Rm = instr->vmValue(); michael@0: int32_t address = get_register(Rn); michael@0: int regs = 0; michael@0: switch (type) { michael@0: case nlt_1: michael@0: regs = 1; michael@0: break; michael@0: case nlt_2: michael@0: regs = 2; michael@0: break; michael@0: case nlt_3: michael@0: regs = 3; michael@0: break; michael@0: case nlt_4: michael@0: regs = 4; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: int r = 0; michael@0: while (r < regs) { michael@0: uint32_t data[2]; michael@0: data[0] = readW(address, instr); michael@0: data[1] = readW(address + 4, instr); michael@0: set_d_register(Vd + r, data); michael@0: address += 8; michael@0: r++; michael@0: } michael@0: if (Rm != 15) { michael@0: if (Rm == 13) michael@0: set_register(Rn, address); michael@0: else michael@0: set_register(Rn, get_register(Rn) + get_register(Rm)); michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: case 0xA: michael@0: case 0xB: michael@0: if (instr->bits(22, 20) == 5 && instr->bits(15, 12) == 0xf) { michael@0: // pld: ignore instruction. michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: // Executes the current instruction. michael@0: void michael@0: Simulator::instructionDecode(SimInstruction *instr) michael@0: { michael@0: if (Simulator::ICacheCheckingEnabled) { michael@0: AutoLockSimulatorRuntime alsr(srt_); michael@0: CheckICache(srt_->icache(), instr); michael@0: } michael@0: michael@0: pc_modified_ = false; michael@0: michael@0: static const uint32_t kSpecialCondition = 15 << 28; michael@0: if (instr->conditionField() == kSpecialCondition) { michael@0: decodeSpecialCondition(instr); michael@0: } else if (conditionallyExecute(instr)) { michael@0: switch (instr->typeValue()) { michael@0: case 0: michael@0: case 1: michael@0: decodeType01(instr); michael@0: break; michael@0: case 2: michael@0: decodeType2(instr); michael@0: break; michael@0: case 3: michael@0: decodeType3(instr); michael@0: break; michael@0: case 4: michael@0: decodeType4(instr); michael@0: break; michael@0: case 5: michael@0: decodeType5(instr); michael@0: break; michael@0: case 6: michael@0: decodeType6(instr); michael@0: break; michael@0: case 7: michael@0: decodeType7(instr); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: // If the instruction is a non taken conditional stop, we need to skip the michael@0: // inlined message address. michael@0: } else if (instr->isStop()) { michael@0: set_pc(get_pc() + 2 * SimInstruction::kInstrSize); michael@0: } michael@0: if (!pc_modified_) michael@0: set_register(pc, reinterpret_cast(instr) + SimInstruction::kInstrSize); michael@0: } michael@0: michael@0: michael@0: template michael@0: void michael@0: Simulator::execute() michael@0: { michael@0: // Get the PC to simulate. Cannot use the accessor here as we need the michael@0: // raw PC value and not the one used as input to arithmetic instructions. michael@0: int program_counter = get_pc(); michael@0: AsmJSActivation *activation = TlsPerThreadData.get()->asmJSActivationStackFromOwnerThread(); michael@0: michael@0: while (program_counter != end_sim_pc) { michael@0: if (EnableStopSimAt && (icount_ == Simulator::StopSimAt)) { michael@0: fprintf(stderr, "\nStopped simulation at icount %lld\n", icount_); michael@0: ArmDebugger dbg(this); michael@0: dbg.debug(); michael@0: } else { michael@0: SimInstruction *instr = reinterpret_cast(program_counter); michael@0: instructionDecode(instr); michael@0: icount_++; michael@0: michael@0: int32_t rpc = resume_pc_; michael@0: if (MOZ_UNLIKELY(rpc != 0)) { michael@0: // AsmJS signal handler ran and we have to adjust the pc. michael@0: activation->setInterrupted((void *)get_pc()); michael@0: set_pc(rpc); michael@0: resume_pc_ = 0; michael@0: } michael@0: } michael@0: program_counter = get_pc(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Simulator::callInternal(uint8_t *entry) michael@0: { michael@0: // Prepare to execute the code at entry. michael@0: set_register(pc, reinterpret_cast(entry)); michael@0: michael@0: // Put down marker for end of simulation. The simulator will stop simulation michael@0: // when the PC reaches this value. By saving the "end simulation" value into michael@0: // the LR the simulation stops when returning to this call point. michael@0: set_register(lr, end_sim_pc); michael@0: michael@0: // Remember the values of callee-saved registers. michael@0: // The code below assumes that r9 is not used as sb (static base) in michael@0: // simulator code and therefore is regarded as a callee-saved register. michael@0: int32_t r4_val = get_register(r4); michael@0: int32_t r5_val = get_register(r5); michael@0: int32_t r6_val = get_register(r6); michael@0: int32_t r7_val = get_register(r7); michael@0: int32_t r8_val = get_register(r8); michael@0: int32_t r9_val = get_register(r9); michael@0: int32_t r10_val = get_register(r10); michael@0: int32_t r11_val = get_register(r11); michael@0: michael@0: // Remember d8 to d15 which are callee-saved. michael@0: uint64_t d8_val; michael@0: get_d_register(d8, &d8_val); michael@0: uint64_t d9_val; michael@0: get_d_register(d9, &d9_val); michael@0: uint64_t d10_val; michael@0: get_d_register(d10, &d10_val); michael@0: uint64_t d11_val; michael@0: get_d_register(d11, &d11_val); michael@0: uint64_t d12_val; michael@0: get_d_register(d12, &d12_val); michael@0: uint64_t d13_val; michael@0: get_d_register(d13, &d13_val); michael@0: uint64_t d14_val; michael@0: get_d_register(d14, &d14_val); michael@0: uint64_t d15_val; michael@0: get_d_register(d15, &d15_val); michael@0: michael@0: // Set up the callee-saved registers with a known value. To be able to check michael@0: // that they are preserved properly across JS execution. michael@0: int32_t callee_saved_value = uint32_t(icount_); michael@0: set_register(r4, callee_saved_value); michael@0: set_register(r5, callee_saved_value); michael@0: set_register(r6, callee_saved_value); michael@0: set_register(r7, callee_saved_value); michael@0: set_register(r8, callee_saved_value); michael@0: set_register(r9, callee_saved_value); michael@0: set_register(r10, callee_saved_value); michael@0: set_register(r11, callee_saved_value); michael@0: michael@0: uint64_t callee_saved_value_d = uint64_t(icount_); michael@0: set_d_register(d8, &callee_saved_value_d); michael@0: set_d_register(d9, &callee_saved_value_d); michael@0: set_d_register(d10, &callee_saved_value_d); michael@0: set_d_register(d11, &callee_saved_value_d); michael@0: set_d_register(d12, &callee_saved_value_d); michael@0: set_d_register(d13, &callee_saved_value_d); michael@0: set_d_register(d14, &callee_saved_value_d); michael@0: set_d_register(d15, &callee_saved_value_d); michael@0: michael@0: // Start the simulation michael@0: if (Simulator::StopSimAt != -1L) michael@0: execute(); michael@0: else michael@0: execute(); michael@0: michael@0: // Check that the callee-saved registers have been preserved. michael@0: MOZ_ASSERT(callee_saved_value == get_register(r4)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r5)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r6)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r7)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r8)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r9)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r10)); michael@0: MOZ_ASSERT(callee_saved_value == get_register(r11)); michael@0: michael@0: uint64_t value; michael@0: get_d_register(d8, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d9, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d10, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d11, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d12, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d13, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d14, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: get_d_register(d15, &value); michael@0: MOZ_ASSERT(callee_saved_value_d == value); michael@0: michael@0: // Restore callee-saved registers with the original value. michael@0: set_register(r4, r4_val); michael@0: set_register(r5, r5_val); michael@0: set_register(r6, r6_val); michael@0: set_register(r7, r7_val); michael@0: set_register(r8, r8_val); michael@0: set_register(r9, r9_val); michael@0: set_register(r10, r10_val); michael@0: set_register(r11, r11_val); michael@0: michael@0: set_d_register(d8, &d8_val); michael@0: set_d_register(d9, &d9_val); michael@0: set_d_register(d10, &d10_val); michael@0: set_d_register(d11, &d11_val); michael@0: set_d_register(d12, &d12_val); michael@0: set_d_register(d13, &d13_val); michael@0: set_d_register(d14, &d14_val); michael@0: set_d_register(d15, &d15_val); michael@0: } michael@0: michael@0: int64_t michael@0: Simulator::call(uint8_t* entry, int argument_count, ...) michael@0: { michael@0: va_list parameters; michael@0: va_start(parameters, argument_count); michael@0: michael@0: // First four arguments passed in registers. michael@0: MOZ_ASSERT(argument_count >= 2); michael@0: set_register(r0, va_arg(parameters, int32_t)); michael@0: set_register(r1, va_arg(parameters, int32_t)); michael@0: if (argument_count >= 3) michael@0: set_register(r2, va_arg(parameters, int32_t)); michael@0: if (argument_count >= 4) michael@0: set_register(r3, va_arg(parameters, int32_t)); michael@0: michael@0: // Remaining arguments passed on stack. michael@0: int original_stack = get_register(sp); michael@0: int entry_stack = original_stack; michael@0: if (argument_count >= 4) michael@0: entry_stack -= (argument_count - 4) * sizeof(int32_t); michael@0: michael@0: entry_stack &= ~StackAlignment; michael@0: michael@0: // Store remaining arguments on stack, from low to high memory. michael@0: intptr_t *stack_argument = reinterpret_cast(entry_stack); michael@0: for (int i = 4; i < argument_count; i++) michael@0: stack_argument[i - 4] = va_arg(parameters, int32_t); michael@0: va_end(parameters); michael@0: set_register(sp, entry_stack); michael@0: michael@0: callInternal(entry); michael@0: michael@0: // Pop stack passed arguments. michael@0: MOZ_ASSERT(entry_stack == get_register(sp)); michael@0: set_register(sp, original_stack); michael@0: michael@0: int64_t result = (int64_t(get_register(r1)) << 32) | get_register(r0); michael@0: return result; michael@0: } michael@0: michael@0: Simulator * michael@0: Simulator::Current() michael@0: { michael@0: PerThreadData *pt = TlsPerThreadData.get(); michael@0: Simulator *sim = pt->simulator(); michael@0: if (!sim) { michael@0: sim = js_new(pt->simulatorRuntime()); michael@0: pt->setSimulator(sim); michael@0: } michael@0: michael@0: return sim; michael@0: } michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: js::jit::Simulator * michael@0: js::PerThreadData::simulator() const michael@0: { michael@0: return simulator_; michael@0: } michael@0: michael@0: void michael@0: js::PerThreadData::setSimulator(js::jit::Simulator *sim) michael@0: { michael@0: simulator_ = sim; michael@0: simulatorStackLimit_ = sim->stackLimit(); michael@0: } michael@0: michael@0: js::jit::SimulatorRuntime * michael@0: js::PerThreadData::simulatorRuntime() const michael@0: { michael@0: return runtime_->simulatorRuntime(); michael@0: } michael@0: michael@0: uintptr_t * michael@0: js::PerThreadData::addressOfSimulatorStackLimit() michael@0: { michael@0: return &simulatorStackLimit_; michael@0: } michael@0: michael@0: js::jit::SimulatorRuntime * michael@0: JSRuntime::simulatorRuntime() const michael@0: { michael@0: return simulatorRuntime_; michael@0: } michael@0: michael@0: void michael@0: JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt) michael@0: { michael@0: MOZ_ASSERT(!simulatorRuntime_); michael@0: simulatorRuntime_ = srt; michael@0: }