Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/jit/arm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 151 kB image not shown  

Quelle  Simulator-arm.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "jit/arm/Simulator-arm.h"

#include "mozilla/Casting.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h"

#include "jit/arm/Assembler-arm.h"
#include "jit/arm/disasm/Constants-arm.h"
#include "jit/AtomicOperations.h"
#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "threading/LockGuard.h"
#include "vm/Float16.h"
#include "vm/JSContext.h"
#include "vm/Runtime.h"
#include "vm/SharedMem.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmSignalHandlers.h"

extern "C" {

MOZ_EXPORT int64_t __aeabi_idivmod(int x, int y) {
  // Run-time ABI for the ARM architecture specifies that for |INT_MIN / -1|
  // "an implementation is (sic) may return any convenient value, possibly the
  // original numerator."
  //
  // |INT_MIN / -1| traps on x86, which isn't listed as an allowed behavior in
  // the ARM docs, so instead follow LLVM and return the numerator. (And zero
  // for the remainder.)

  if (x == INT32_MIN && y == -1) {
    return uint32_t(x);
  }

  uint32_t lo = uint32_t(x / y);
  uint32_t hi = uint32_t(x % y);
  return (int64_t(hi) << 32) | lo;
}

MOZ_EXPORT int64_t __aeabi_uidivmod(int x, int y) {
  uint32_t lo = uint32_t(x) / uint32_t(y);
  uint32_t hi = uint32_t(x) % uint32_t(y);
  return (int64_t(hi) << 32) | lo;
}
}

namespace js {
namespace jit {

// For decoding load-exclusive and store-exclusive instructions.
namespace excl {

// Bit positions.
enum {
  ExclusiveOpHi = 24,    // Hi bit of opcode field
  ExclusiveOpLo = 23,    // Lo bit of opcode field
  ExclusiveSizeHi = 22,  // Hi bit of operand size field
  ExclusiveSizeLo = 21,  // Lo bit of operand size field
  ExclusiveLoad = 20     // Bit indicating load
};

// Opcode bits for exclusive instructions.
enum { ExclusiveOpcode = 3 };

// Operand size, Bits(ExclusiveSizeHi,ExclusiveSizeLo).
enum {
  ExclusiveWord = 0,
  ExclusiveDouble = 1,
  ExclusiveByte = 2,
  ExclusiveHalf = 3
};

}  // namespace excl

// Load/store multiple addressing mode.
enum BlockAddrMode {
  // Alias modes for comparison when writeback does not matter.
  da_x = (0 | 0 | 0) << 21,  // Decrement after.
  ia_x = (0 | 4 | 0) << 21,  // Increment after.
  db_x = (8 | 0 | 0) << 21,  // Decrement before.
  ib_x = (8 | 4 | 0) << 21,  // Increment before.
};

// Type of VFP register. Determines register encoding.
enum VFPRegPrecision { kSinglePrecision = 0, kDoublePrecision = 1 };

enum NeonListType { nlt_1 = 0x7, nlt_2 = 0xA, nlt_3 = 0x6, nlt_4 = 0x2 };

// Supervisor Call (svc) specific support.

// Special Software Interrupt codes when used in the presence of the ARM
// simulator.
// svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for
// standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature.
enum SoftwareInterruptCodes {
  kCallRtRedirected = 0x10,  // Transition to C code.
  kBreakpoint = 0x20,        // Breakpoint.
  kStopCode = 1 << 23        // Stop.
};

const uint32_t kStopCodeMask = kStopCode - 1;
const uint32_t kMaxStopCode = kStopCode - 1;

// -----------------------------------------------------------------------------
// Instruction abstraction.

// The class Instruction enables access to individual fields defined in the ARM
// architecture instruction set encoding as described in figure A3-1.
// Note that the Assembler uses using Instr = int32_t.
//
// Example: Test whether the instruction at ptr does set the condition code
// bits.
//
// bool InstructionSetsConditionCodes(byte* ptr) {
//   Instruction* instr = Instruction::At(ptr);
//   int type = instr->TypeValue();
//   return ((type == 0) || (type == 1)) && instr->hasS();
// }
//
class SimInstruction {
 public:
  enum { kInstrSize = 4, kPCReadOffset = 8 };

  // Get the raw instruction bits.
  inline Instr instructionBits() const {
    return *reinterpret_cast<const Instr*>(this);
  }

  // Set the raw instruction bits to value.
  inline void setInstructionBits(Instr value) {
    *reinterpret_cast<Instr*>(this) = value;
  }

  // Read one particular bit out of the instruction bits.
  inline int bit(int nr) const { return (instructionBits() >> nr) & 1; }

  // Read a bit field's value out of the instruction bits.
  inline int bits(int hi, int lo) const {
    return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1);
  }

  // Read a bit field out of the instruction bits.
  inline int bitField(int hi, int lo) const {
    return instructionBits() & (((2 << (hi - lo)) - 1) << lo);
  }

  // Accessors for the different named fields used in the ARM encoding.
  // The naming of these accessor corresponds to figure A3-1.
  //
  // Two kind of accessors are declared:
  // - <Name>Field() will return the raw field, i.e. the field's bits at their
  //   original place in the instruction encoding.
  //   e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as
  //   0xC0810002 conditionField(instr) will return 0xC0000000.
  // - <Name>Value() will return the field value, shifted back to bit 0.
  //   e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as
  //   0xC0810002 conditionField(instr) will return 0xC.

  // Generally applicable fields
  inline Assembler::ARMCondition conditionField() const {
    return static_cast<Assembler::ARMCondition>(bitField(31, 28));
  }
  inline int typeValue() const { return bits(27, 25); }
  inline int specialValue() const { return bits(27, 23); }

  inline int rnValue() const { return bits(19, 16); }
  inline int rdValue() const { return bits(15, 12); }

  inline int coprocessorValue() const { return bits(11, 8); }

  // Support for VFP.
  // Vn(19-16) | Vd(15-12) |  Vm(3-0)
  inline int vnValue() const { return bits(19, 16); }
  inline int vmValue() const { return bits(3, 0); }
  inline int vdValue() const { return bits(15, 12); }
  inline int nValue() const { return bit(7); }
  inline int mValue() const { return bit(5); }
  inline int dValue() const { return bit(22); }
  inline int rtValue() const { return bits(15, 12); }
  inline int pValue() const { return bit(24); }
  inline int uValue() const { return bit(23); }
  inline int opc1Value() const { return (bit(23) << 2) | bits(21, 20); }
  inline int opc2Value() const { return bits(19, 16); }
  inline int opc3Value() const { return bits(7, 6); }
  inline int szValue() const { return bit(8); }
  inline int VLValue() const { return bit(20); }
  inline int VCValue() const { return bit(8); }
  inline int VAValue() const { return bits(23, 21); }
  inline int VBValue() const { return bits(6, 5); }
  inline int VFPNRegValue(VFPRegPrecision pre) {
    return VFPGlueRegValue(pre, 16, 7);
  }
  inline int VFPMRegValue(VFPRegPrecision pre) {
    return VFPGlueRegValue(pre, 0, 5);
  }
  inline int VFPDRegValue(VFPRegPrecision pre) {
    return VFPGlueRegValue(pre, 12, 22);
  }

  // Fields used in Data processing instructions.
  inline int opcodeValue() const { return static_cast<ALUOp>(bits(24, 21)); }
  inline ALUOp opcodeField() const {
    return static_cast<ALUOp>(bitField(24, 21));
  }
  inline int sValue() const { return bit(20); }

  // With register.
  inline int rmValue() const { return bits(3, 0); }
  inline ShiftType shifttypeValue() const {
    return static_cast<ShiftType>(bits(6, 5));
  }
  inline int rsValue() const { return bits(11, 8); }
  inline int shiftAmountValue() const { return bits(11, 7); }

  // With immediate.
  inline int rotateValue() const { return bits(11, 8); }
  inline int immed8Value() const { return bits(7, 0); }
  inline int immed4Value() const { return bits(19, 16); }
  inline int immedMovwMovtValue() const {
    return immed4Value() << 12 | offset12Value();
  }

  // Fields used in Load/Store instructions.
  inline int PUValue() const { return bits(24, 23); }
  inline int PUField() const { return bitField(24, 23); }
  inline int bValue() const { return bit(22); }
  inline int wValue() const { return bit(21); }
  inline int lValue() const { return bit(20); }

  // With register uses same fields as Data processing instructions above with
  // immediate.
  inline int offset12Value() const { return bits(11, 0); }

  // Multiple.
  inline int rlistValue() const { return bits(15, 0); }

  // Extra loads and stores.
  inline int signValue() const { return bit(6); }
  inline int hValue() const { return bit(5); }
  inline int immedHValue() const { return bits(11, 8); }
  inline int immedLValue() const { return bits(3, 0); }

  // Fields used in Branch instructions.
  inline int linkValue() const { return bit(24); }
  inline int sImmed24Value() const { return ((instructionBits() << 8) >> 8); }

  // Fields used in Software interrupt instructions.
  inline SoftwareInterruptCodes svcValue() const {
    return static_cast<SoftwareInterruptCodes>(bits(23, 0));
  }

  // Test for special encodings of type 0 instructions (extra loads and
  // stores, as well as multiplications).
  inline bool isSpecialType0() const { return (bit(7) == 1) && (bit(4) == 1); }

  // Test for miscellaneous instructions encodings of type 0 instructions.
  inline bool isMiscType0() const {
    return bit(24) == 1 && bit(23) == 0 && bit(20) == 0 && (bit(7) == 0);
  }

  // Test for a nop instruction, which falls under type 1.
  inline bool isNopType1() const { return bits(24, 0) == 0x0120F000; }

  // Test for a yield instruction, which falls under type 1.
  inline bool isYieldType1() const { return bits(24, 0) == 0x0120F001; }

  // Test for a nop instruction, which falls under type 1.
  inline bool isCsdbType1() const { return bits(24, 0) == 0x0120F014; }

  // Test for a stop instruction.
  inline bool isStop() const {
    return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode;
  }

  // Test for a udf instruction, which falls under type 3.
  inline bool isUDF() const {
    return (instructionBits() & 0xfff000f0) == 0xe7f000f0;
  }

  // Special accessors that test for existence of a value.
  inline bool hasS() const { return sValue() == 1; }
  inline bool hasB() const { return bValue() == 1; }
  inline bool hasW() const { return wValue() == 1; }
  inline bool hasL() const { return lValue() == 1; }
  inline bool hasU() const { return uValue() == 1; }
  inline bool hasSign() const { return signValue() == 1; }
  inline bool hasH() const { return hValue() == 1; }
  inline bool hasLink() const { return linkValue() == 1; }

  // Decoding the double immediate in the vmov instruction.
  double doubleImmedVmov() const;
  // Decoding the float32 immediate in the vmov.f32 instruction.
  float float32ImmedVmov() const;

 private:
  // Join split register codes, depending on single or double precision.
  // four_bit is the position of the least-significant bit of the four
  // bit specifier. one_bit is the position of the additional single bit
  // specifier.
  inline int VFPGlueRegValue(VFPRegPrecision pre, int four_bit, int one_bit) {
    if (pre == kSinglePrecision) {
      return (bits(four_bit + 3, four_bit) << 1) | bit(one_bit);
    }
    return (bit(one_bit) << 4) | bits(four_bit + 3, four_bit);
  }

  SimInstruction() = delete;
  SimInstruction(const SimInstruction& other) = delete;
  void operator=(const SimInstruction& other) = delete;
};

double SimInstruction::doubleImmedVmov() const {
  // Reconstruct a double from the immediate encoded in the vmov instruction.
  //
  //   instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh]
  //   double: [aBbbbbbb,bbcdefgh,00000000,00000000,
  //            00000000,00000000,00000000,00000000]
  //
  // where B = ~b. Only the high 16 bits are affected.
  uint64_t high16;
  high16 = (bits(17, 16) << 4) | bits(3, 0);  // xxxxxxxx,xxcdefgh.
  high16 |= (0xff * bit(18)) << 6;            // xxbbbbbb,bbxxxxxx.
  high16 |= (bit(18) ^ 1) << 14;              // xBxxxxxx,xxxxxxxx.
  high16 |= bit(19) << 15;                    // axxxxxxx,xxxxxxxx.

  uint64_t imm = high16 << 48;
  return mozilla::BitwiseCast<double>(imm);
}

float SimInstruction::float32ImmedVmov() const {
  // Reconstruct a float32 from the immediate encoded in the vmov instruction.
  //
  //   instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh]
  //   float32: [aBbbbbbc, defgh000, 00000000, 00000000]
  //
  // where B = ~b. Only the high 16 bits are affected.
  uint32_t imm;
  imm = (bits(17, 16) << 23) | (bits(3, 0) << 19);  // xxxxxxxc,defgh000.0.0
  imm |= (0x1f * bit(18)) << 25;                    // xxbbbbbx,xxxxxxxx.0.0
  imm |= (bit(18) ^ 1) << 30;                       // xBxxxxxx,xxxxxxxx.0.0
  imm |= bit(19) << 31;                             // axxxxxxx,xxxxxxxx.0.0

  return mozilla::BitwiseCast<float>(imm);
}

class CachePage {
 public:
  static const int LINE_VALID = 0;
  static const int LINE_INVALID = 1;
  static const int kPageShift = 12;
  static const int kPageSize = 1 << kPageShift;
  static const int kPageMask = kPageSize - 1;
  static const int kLineShift = 2;  // The cache line is only 4 bytes right now.
  static const int kLineLength = 1 << kLineShift;
  static const int kLineMask = kLineLength - 1;

  CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); }
  char* validityByte(int offset) {
    return &validity_map_[offset >> kLineShift];
  }
  char* cachedData(int offset) { return &data_[offset]; }

 private:
  char data_[kPageSize];  // The cached data.
  static const int kValidityMapSize = kPageSize >> kLineShift;
  char validity_map_[kValidityMapSize];  // One byte per line.
};

// Protects the icache() and redirection() properties of the
// Simulator.
class AutoLockSimulatorCache : public LockGuard<Mutex> {
  using Base = LockGuard<Mutex>;

 public:
  explicit AutoLockSimulatorCache()
      : Base(SimulatorProcess::singleton_->cacheLock_) {}
};

mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
    SimulatorProcess::ICacheCheckingDisableCount(
        1);  // Checking is disabled by default.
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;

int64_t Simulator::StopSimAt = -1L;

Simulator* Simulator::Create() {
  auto sim = MakeUnique<Simulator>();
  if (!sim) {
    return nullptr;
  }

  if (!sim->init()) {
    return nullptr;
  }

  char* stopAtStr = getenv("ARM_SIM_STOP_AT");
  int64_t stopAt;
  if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
    fprintf(stderr, "\nStopping simulation at icount %lld\n", stopAt);
    Simulator::StopSimAt = stopAt;
  }

  return sim.release();
}

void Simulator::Destroy(Simulator* sim) { js_delete(sim); }

void Simulator::disassemble(SimInstruction* instr, size_t n) {
#ifdef JS_DISASM_ARM
  disasm::NameConverter converter;
  disasm::Disassembler dasm(converter);
  disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
  while (n-- > 0) {
    dasm.InstructionDecode(buffer, reinterpret_cast<uint8_t*>(instr));
    fprintf(stderr, " 0x%08x %s\n", uint32_t(instr), buffer.start());
    instr = reinterpret_cast<SimInstruction*>(
        reinterpret_cast<uint8_t*>(instr) + 4);
  }
#endif
}

void Simulator::disasm(SimInstruction* instr) { disassemble(instr, 1); }

void Simulator::disasm(SimInstruction* instr, size_t n) {
  disassemble(instr, n);
}

void Simulator::disasm(SimInstruction* instr, size_t m, size_t n) {
  disassemble(reinterpret_cast<SimInstruction*>(
                  reinterpret_cast<uint8_t*>(instr) - m * 4),
              n);
}

// The ArmDebugger class is used by the simulator while debugging simulated ARM
// code.
class ArmDebugger {
 public:
  explicit ArmDebugger(Simulator* sim) : sim_(sim) {}

  void stop(SimInstruction* instr);
  void debug();

 private:
  static const Instr kBreakpointInstr =
      (Assembler::AL | (7 * (1 << 25)) | (1 * (1 << 24)) | kBreakpoint);
  static const Instr kNopInstr = (Assembler::AL | (13 * (1 << 21)));

  Simulator* sim_;

  int32_t getRegisterValue(int regnum);
  double getRegisterPairDoubleValue(int regnum);
  void getVFPDoubleRegisterValue(int regnum, double* value);
  bool getValue(const char* desc, int32_t* value);
  bool getVFPDoubleValue(const char* desc, double* value);

  // Set or delete a breakpoint. Returns true if successful.
  bool setBreakpoint(SimInstruction* breakpc);
  bool deleteBreakpoint(SimInstruction* breakpc);

  // Undo and redo all breakpoints. This is needed to bracket disassembly and
  // execution to skip past breakpoints when run from the debugger.
  void undoBreakpoints();
  void redoBreakpoints();
};

void ArmDebugger::stop(SimInstruction* instr) {
  // Get the stop code.
  uint32_t code = instr->svcValue() & kStopCodeMask;
  // Retrieve the encoded address, which comes just after this stop.
  char* msg =
      *reinterpret_cast<char**>(sim_->get_pc() + SimInstruction::kInstrSize);
  // Update this stop description.
  if (sim_->isWatchedStop(code) && !sim_->watched_stops_[code].desc) {
    sim_->watched_stops_[code].desc = msg;
  }
  // Print the stop message and code if it is not the default code.
  if (code != kMaxStopCode) {
    printf("Simulator hit stop %u: %s\n", code, msg);
  } else {
    printf("Simulator hit %s\n", msg);
  }
  sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize);
  debug();
}

int32_t ArmDebugger::getRegisterValue(int regnum) {
  if (regnum == Registers::pc) {
    return sim_->get_pc();
  }
  return sim_->get_register(regnum);
}

double ArmDebugger::getRegisterPairDoubleValue(int regnum) {
  return sim_->get_double_from_register_pair(regnum);
}

void ArmDebugger::getVFPDoubleRegisterValue(int regnum, double* out) {
  sim_->get_double_from_d_register(regnum, out);
}

bool ArmDebugger::getValue(const char* desc, int32_t* value) {
  Register reg = Register::FromName(desc);
  if (reg != InvalidReg) {
    *value = getRegisterValue(reg.code());
    return true;
  }
  if (strncmp(desc, "0x", 2) == 0) {
    return sscanf(desc + 2, "%x"reinterpret_cast<uint32_t*>(value)) == 1;
  }
  return sscanf(desc, "%u"reinterpret_cast<uint32_t*>(value)) == 1;
}

bool ArmDebugger::getVFPDoubleValue(const char* desc, double* value) {
  FloatRegister reg = FloatRegister::FromCode(FloatRegister::FromName(desc));
  if (reg.isInvalid()) {
    return false;
  }

  if (reg.isSingle()) {
    float fval;
    sim_->get_float_from_s_register(reg.id(), &fval);
    *value = fval;
    return true;
  }

  sim_->get_double_from_d_register(reg.id(), value);
  return true;
}

bool ArmDebugger::setBreakpoint(SimInstruction* breakpc) {
  // Check if a breakpoint can be set. If not return without any side-effects.
  if (sim_->break_pc_) {
    return false;
  }

  // Set the breakpoint.
  sim_->break_pc_ = breakpc;
  sim_->break_instr_ = breakpc->instructionBits();
  // Not setting the breakpoint instruction in the code itself. It will be set
  // when the debugger shell continues.
  return true;
}

bool ArmDebugger::deleteBreakpoint(SimInstruction* breakpc) {
  if (sim_->break_pc_ != nullptr) {
    sim_->break_pc_->setInstructionBits(sim_->break_instr_);
  }

  sim_->break_pc_ = nullptr;
  sim_->break_instr_ = 0;
  return true;
}

void ArmDebugger::undoBreakpoints() {
  if (sim_->break_pc_) {
    sim_->break_pc_->setInstructionBits(sim_->break_instr_);
  }
}

void ArmDebugger::redoBreakpoints() {
  if (sim_->break_pc_) {
    sim_->break_pc_->setInstructionBits(kBreakpointInstr);
  }
}

static char* ReadLine(const char* prompt) {
  UniqueChars result;
  char line_buf[256];
  int offset = 0;
  bool keep_going = true;
  fprintf(stdout, "%s", prompt);
  fflush(stdout);
  while (keep_going) {
    if (fgets(line_buf, sizeof(line_buf), stdin) == nullptr) {
      // fgets got an error. Just give up.
      return nullptr;
    }
    int len = strlen(line_buf);
    if (len > 0 && line_buf[len - 1] == '\n') {
      // Since we read a new line we are done reading the line. This will
      // exit the loop after copying this buffer into the result.
      keep_going = false;
    }
    if (!result) {
      // Allocate the initial result and make room for the terminating
      // '\0'.
      result.reset(js_pod_malloc<char>(len + 1));
      if (!result) {
        return nullptr;
      }
    } else {
      // Allocate a new result with enough room for the new addition.
      int new_len = offset + len + 1;
      char* new_result = js_pod_malloc<char>(new_len);
      if (!new_result) {
        return nullptr;
      }
      // Copy the existing input into the new array and set the new
      // array as the result.
      memcpy(new_result, result.get(), offset * sizeof(char));
      result.reset(new_result);
    }
    // Copy the newly read line into the result.
    memcpy(result.get() + offset, line_buf, len * sizeof(char));
    offset += len;
  }

  MOZ_ASSERT(result);
  result[offset] = '\0';
  return result.release();
}

void ArmDebugger::debug() {
  intptr_t last_pc = -1;
  bool done = false;

#define COMMAND_SIZE 63
#define ARG_SIZE 255

#define STR(a) #a
#define XSTR(a) STR(a)

  char cmd[COMMAND_SIZE + 1];
  char arg1[ARG_SIZE + 1];
  char arg2[ARG_SIZE + 1];
  char* argv[3] = {cmd, arg1, arg2};

  // Make sure to have a proper terminating character if reaching the limit.
  cmd[COMMAND_SIZE] = 0;
  arg1[ARG_SIZE] = 0;
  arg2[ARG_SIZE] = 0;

  // Undo all set breakpoints while running in the debugger shell. This will
  // make them invisible to all commands.
  undoBreakpoints();

#ifndef JS_DISASM_ARM
  static bool disasm_warning_printed = false;
  if (!disasm_warning_printed) {
    printf(
        " No ARM disassembler present. Enable JS_DISASM_ARM in "
        "configure.in.");
    disasm_warning_printed = true;
  }
#endif

  while (!done && !sim_->has_bad_pc()) {
    if (last_pc != sim_->get_pc()) {
#ifdef JS_DISASM_ARM
      disasm::NameConverter converter;
      disasm::Disassembler dasm(converter);
      disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
      dasm.InstructionDecode(buffer,
                             reinterpret_cast<uint8_t*>(sim_->get_pc()));
      printf(" 0x%08x %s\n", sim_->get_pc(), buffer.start());
#endif
      last_pc = sim_->get_pc();
    }
    char* line = ReadLine("sim> ");
    if (line == nullptr) {
      break;
    } else {
      char* last_input = sim_->lastDebuggerInput();
      if (strcmp(line, "\n") == 0 && last_input != nullptr) {
        line = last_input;
      } else {
        // Ownership is transferred to sim_;
        sim_->setLastDebuggerInput(line);
      }

      // Use sscanf to parse the individual parts of the command line. At the
      // moment no command expects more than two parameters.
      int argc = sscanf(line,
                              "%" XSTR(COMMAND_SIZE) "s "
                              "%" XSTR(ARG_SIZE) "s "
                              "%" XSTR(ARG_SIZE) "s",
                              cmd, arg1, arg2);
      if (argc < 0) {
        continue;
      } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
        sim_->instructionDecode(
            reinterpret_cast<SimInstruction*>(sim_->get_pc()));
        sim_->icount_++;
      } else if ((strcmp(cmd, "skip") == 0)) {
        sim_->set_pc(sim_->get_pc() + 4);
        sim_->icount_++;
      } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
        // Execute the one instruction we broke at with breakpoints
        // disabled.
        sim_->instructionDecode(
            reinterpret_cast<SimInstruction*>(sim_->get_pc()));
        sim_->icount_++;
        // Leave the debugger shell.
        done = true;
      } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
        if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) {
          int32_t value;
          double dvalue;
          if (strcmp(arg1, "all") == 0) {
            for (uint32_t i = 0; i < Registers::Total; i++) {
              value = getRegisterValue(i);
              printf("%3s: 0x%08x %10d", Registers::GetName(i), value, value);
              if ((argc == 3 && strcmp(arg2, "fp") == 0) && i < 8 &&
                  (i % 2) == 0) {
                dvalue = getRegisterPairDoubleValue(i);
                printf(" (%.16g)\n", dvalue);
              } else {
                printf("\n");
              }
            }
            for (uint32_t i = 0; i < FloatRegisters::TotalPhys; i++) {
              getVFPDoubleRegisterValue(i, &dvalue);
              uint64_t as_words = mozilla::BitwiseCast<uint64_t>(dvalue);
              printf("%3s: %.16g 0x%08x %08x\n",
                     FloatRegister::FromCode(i).name(), dvalue,
                     static_cast<uint32_t>(as_words >> 32),
                     static_cast<uint32_t>(as_words & 0xffffffff));
            }
          } else {
            if (getValue(arg1, &value)) {
              printf("%s: 0x%08x %d \n", arg1, value, value);
            } else if (getVFPDoubleValue(arg1, &dvalue)) {
              uint64_t as_words = mozilla::BitwiseCast<uint64_t>(dvalue);
              printf("%s: %.16g 0x%08x %08x\n", arg1, dvalue,
                     static_cast<uint32_t>(as_words >> 32),
                     static_cast<uint32_t>(as_words & 0xffffffff));
            } else {
              printf("%s unrecognized\n", arg1);
            }
          }
        } else {
          printf("print \n");
        }
      } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
        int32_t* cur = nullptr;
        int32_t* end = nullptr;
        int next_arg = 1;

        if (strcmp(cmd, "stack") == 0) {
          cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp));
        } else {  // "mem"
          int32_t value;
          if (!getValue(arg1, &value)) {
            printf("%s unrecognized\n", arg1);
            continue;
          }
          cur = reinterpret_cast<int32_t*>(value);
          next_arg++;
        }

        int32_t words;
        if (argc == next_arg) {
          words = 10;
        } else {
          if (!getValue(argv[next_arg], &words)) {
            words = 10;
          }
        }
        end = cur + words;

        while (cur < end) {
          printf(" %p: 0x%08x %10d", cur, *cur, *cur);
          printf("\n");
          cur++;
        }
      } else if (strcmp(cmd, "disasm") == 0 || strcmp(cmd, "di") == 0) {
#ifdef JS_DISASM_ARM
        uint8_t* prev = nullptr;
        uint8_t* cur = nullptr;
        uint8_t* end = nullptr;

        if (argc == 1) {
          cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
          end = cur + (10 * SimInstruction::kInstrSize);
        } else if (argc == 2) {
          Register reg = Register::FromName(arg1);
          if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) {
            // The argument is an address or a register name.
            int32_t value;
            if (getValue(arg1, &value)) {
              cur = reinterpret_cast<uint8_t*>(value);
              // Disassemble 10 instructions at <arg1>.
              end = cur + (10 * SimInstruction::kInstrSize);
            }
          } else {
            // The argument is the number of instructions.
            int32_t value;
            if (getValue(arg1, &value)) {
              cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
              // Disassemble <arg1> instructions.
              end = cur + (value * SimInstruction::kInstrSize);
            }
          }
        } else {
          int32_t value1;
          int32_t value2;
          if (getValue(arg1, &value1) && getValue(arg2, &value2)) {
            cur = reinterpret_cast<uint8_t*>(value1);
            end = cur + (value2 * SimInstruction::kInstrSize);
          }
        }
        while (cur < end) {
          disasm::NameConverter converter;
          disasm::Disassembler dasm(converter);
          disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;

          prev = cur;
          cur += dasm.InstructionDecode(buffer, cur);
          printf(" 0x%08x %s\n"reinterpret_cast<uint32_t>(prev),
                 buffer.start());
        }
#endif
      } else if (strcmp(cmd, "gdb") == 0) {
        printf("relinquishing control to gdb\n");
#ifdef _MSC_VER
        __debugbreak();
#else
        asm("int $3");
#endif
        printf("regaining control from gdb\n");
      } else if (strcmp(cmd, "break") == 0) {
        if (argc == 2) {
          int32_t value;
          if (getValue(arg1, &value)) {
            if (!setBreakpoint(reinterpret_cast<SimInstruction*>(value))) {
              printf("setting breakpoint failed\n");
            }
          } else {
            printf("%s unrecognized\n", arg1);
          }
        } else {
          printf("break
\n");
        }
      } else if (strcmp(cmd, "del") == 0) {
        if (!deleteBreakpoint(nullptr)) {
          printf("deleting breakpoint failed\n");
        }
      } else if (strcmp(cmd, "flags") == 0) {
        printf("N flag: %d; ", sim_->n_flag_);
        printf("Z flag: %d; ", sim_->z_flag_);
        printf("C flag: %d; ", sim_->c_flag_);
        printf("V flag: %d\n", sim_->v_flag_);
        printf("INVALID OP flag: %d; ", sim_->inv_op_vfp_flag_);
        printf("DIV BY ZERO flag: %d; ", sim_->div_zero_vfp_flag_);
        printf("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_);
        printf("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_);
        printf("INEXACT flag: %d;\n", sim_->inexact_vfp_flag_);
      } else if (strcmp(cmd, "stop") == 0) {
        int32_t value;
        intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize;
        SimInstruction* stop_instr = reinterpret_cast<SimInstruction*>(stop_pc);
        SimInstruction* msg_address = reinterpret_cast<SimInstruction*>(
            stop_pc + SimInstruction::kInstrSize);
        if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
          // Remove the current stop.
          if (sim_->isStopInstruction(stop_instr)) {
            stop_instr->setInstructionBits(kNopInstr);
            msg_address->setInstructionBits(kNopInstr);
          } else {
            printf("Not at debugger stop.\n");
          }
        } else if (argc == 3) {
          // Print information about all/the specified breakpoint(s).
          if (strcmp(arg1, "info") == 0) {
            if (strcmp(arg2, "all") == 0) {
              printf("Stop information:\n");
              for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
                sim_->printStopInfo(i);
              }
            } else if (getValue(arg2, &value)) {
              sim_->printStopInfo(value);
            } else {
              printf("Unrecognized argument.\n");
            }
          } else if (strcmp(arg1, "enable") == 0) {
            // Enable all/the specified breakpoint(s).
            if (strcmp(arg2, "all") == 0) {
              for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
                sim_->enableStop(i);
              }
            } else if (getValue(arg2, &value)) {
              sim_->enableStop(value);
            } else {
              printf("Unrecognized argument.\n");
            }
          } else if (strcmp(arg1, "disable") == 0) {
            // Disable all/the specified breakpoint(s).
            if (strcmp(arg2, "all") == 0) {
              for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
                sim_->disableStop(i);
              }
            } else if (getValue(arg2, &value)) {
              sim_->disableStop(value);
            } else {
              printf("Unrecognized argument.\n");
            }
          }
        } else {
          printf("Wrong usage. Use help command for more information.\n");
        }
      } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
        printf("cont\n");
        printf(" continue execution (alias 'c')\n");
        printf("skip\n");
        printf(" skip one instruction (set pc to next instruction)\n");
        printf("stepi\n");
        printf(" step one instruction (alias 'si')\n");
        printf("print \n");
        printf(" print register content (alias 'p')\n");
        printf(" use register name 'all' to print all registers\n");
        printf(" add argument 'fp' to print register pair double values\n");
        printf("flags\n");
        printf(" print flags\n");
        printf("stack []\n");
        printf(" dump stack content, default dump 10 words)\n");
        printf("mem
[]\n");
        printf(" dump memory content, default dump 10 words)\n");
        printf("disasm []\n");
        printf("disasm [
]\n");
        printf("disasm [[
] ]\n");
        printf(" disassemble code, default is 10 instructions\n");
        printf(" from pc (alias 'di')\n");
        printf("gdb\n");
        printf(" enter gdb\n");
        printf("break
\n");
        printf(" set a break point on the address\n");
        printf("del\n");
        printf(" delete the breakpoint\n");
        printf("stop feature:\n");
        printf(" Description:\n");
        printf(" Stops are debug instructions inserted by\n");
        printf(" the Assembler::stop() function.\n");
        printf(" When hitting a stop, the Simulator will\n");
        printf(" stop and and give control to the ArmDebugger.\n");
        printf(" The first %d stop codes are watched:\n",
               Simulator::kNumOfWatchedStops);
        printf(" - They can be enabled / disabled: the Simulator\n");
        printf(" will / won't stop when hitting them.\n");
        printf(" - The Simulator keeps track of how many times they \n");
        printf(" are met. (See the info command.) Going over a\n");
        printf(" disabled stop still increases its counter. \n");
        printf(" Commands:\n");
        printf(" stop info all/ : print infos about number \n");
        printf(" or all stop(s).\n");
        printf(" stop enable/disable all/ : enables / disables\n");
        printf(" all or number stop(s)\n");
        printf(" stop unstop\n");
        printf(" ignore the stop instruction at the current location\n");
        printf(" from now on\n");
      } else {
        printf("Unknown command: %s\n", cmd);
      }
    }
  }

  // Add all the breakpoints back to stop execution and enter the debugger
  // shell when hit.
  redoBreakpoints();

#undef COMMAND_SIZE
#undef ARG_SIZE

#undef STR
#undef XSTR
}

static bool AllOnOnePage(uintptr_t start, int size) {
  intptr_t start_page = (start & ~CachePage::kPageMask);
  intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
  return start_page == end_page;
}

static CachePage* GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache,
                                     void* page) {
  SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
  if (p) {
    return p->value();
  }

  AutoEnterOOMUnsafeRegion oomUnsafe;
  CachePage* new_page = js_new<CachePage>();
  if (!new_page || !i_cache.add(p, page, new_page)) {
    oomUnsafe.crash("Simulator CachePage");
  }

  return new_page;
}

// Flush from start up to and not including start + size.
static void FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache,
                               intptr_t start, int size) {
  MOZ_ASSERT(size <= CachePage::kPageSize);
  MOZ_ASSERT(AllOnOnePage(start, size - 1));
  MOZ_ASSERT((start & CachePage::kLineMask) == 0);
  MOZ_ASSERT((size & CachePage::kLineMask) == 0);

  void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
  int offset = (start & CachePage::kPageMask);
  CachePage* cache_page = GetCachePageLocked(i_cache, page);
  char* valid_bytemap = cache_page->validityByte(offset);
  memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
}

static void FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache,
                              void* start_addr, size_t size) {
  intptr_t start = reinterpret_cast<intptr_t>(start_addr);
  int intra_line = (start & CachePage::kLineMask);
  start -= intra_line;
  size += intra_line;
  size = ((size - 1) | CachePage::kLineMask) + 1;
  int offset = (start & CachePage::kPageMask);
  while (!AllOnOnePage(start, size - 1)) {
    int bytes_to_flush = CachePage::kPageSize - offset;
    FlushOnePageLocked(i_cache, start, bytes_to_flush);
    start += bytes_to_flush;
    size -= bytes_to_flush;
    MOZ_ASSERT((start & CachePage::kPageMask) == 0);
    offset = 0;
  }
  if (size != 0) {
    FlushOnePageLocked(i_cache, start, size);
  }
}

/* static */
void SimulatorProcess::checkICacheLocked(SimInstruction* instr) {
  intptr_t address = reinterpret_cast<intptr_t>(instr);
  void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
  void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
  int offset = (address & CachePage::kPageMask);
  CachePage* cache_page = GetCachePageLocked(icache(), page);
  char* cache_valid_byte = cache_page->validityByte(offset);
  bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
  char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);

  if (cache_hit) {
    // Check that the data in memory matches the contents of the I-cache.
    mozilla::DebugOnly<int> cmpret =
        memcmp(reinterpret_cast<void*>(instr), cache_page->cachedData(offset),
               SimInstruction::kInstrSize);
    MOZ_ASSERT(cmpret == 0);
  } else {
    // Cache miss. Load memory into the cache.
    memcpy(cached_line, line, CachePage::kLineLength);
    *cache_valid_byte = CachePage::LINE_VALID;
  }
}

HashNumber SimulatorProcess::ICacheHasher::hash(const Lookup& l) {
  return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2;
}

bool SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l) {
  MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
  MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
  return k == l;
}

void Simulator::setLastDebuggerInput(char* input) {
  js_free(lastDebuggerInput_);
  lastDebuggerInput_ = input;
}

/* static */
void SimulatorProcess::FlushICache(void* start_addr, size_t size) {
  JitSpewCont(JitSpew_CacheFlush, "[%p %zx]", start_addr, size);
  if (!ICacheCheckingDisableCount) {
    AutoLockSimulatorCache als;
    js::jit::FlushICacheLocked(icache(), start_addr, size);
  }
}

Simulator::Simulator() {
  // Set up simulator support first. Some of this information is needed to
  // setup the architecture state.

  // Note, allocation and anything that depends on allocated memory is
  // deferred until init(), in order to handle OOM properly.

  stack_ = nullptr;
  stackLimit_ = 0;
  pc_modified_ = false;
  icount_ = 0L;
  break_pc_ = nullptr;
  break_instr_ = 0;
  single_stepping_ = false;
  single_step_callback_ = nullptr;
  single_step_callback_arg_ = nullptr;
  skipCalleeSavedRegsCheck = false;

  // Set up architecture state.
  // All registers are initialized to zero to start with.
  for (int i = 0; i < num_registers; i++) {
    registers_[i] = 0;
  }

  n_flag_ = false;
  z_flag_ = false;
  c_flag_ = false;
  v_flag_ = false;

  for (int i = 0; i < num_d_registers * 2; i++) {
    vfp_registers_[i] = 0;
  }

  n_flag_FPSCR_ = false;
  z_flag_FPSCR_ = false;
  c_flag_FPSCR_ = false;
  v_flag_FPSCR_ = false;
  FPSCR_rounding_mode_ = SimRZ;
  FPSCR_default_NaN_mode_ = true;

  inv_op_vfp_flag_ = false;
  div_zero_vfp_flag_ = false;
  overflow_vfp_flag_ = false;
  underflow_vfp_flag_ = false;
  inexact_vfp_flag_ = false;

  // The lr and pc are initialized to a known bad value that will cause an
  // access violation if the simulator ever tries to execute it.
  registers_[pc] = bad_lr;
  registers_[lr] = bad_lr;

  lastDebuggerInput_ = nullptr;

  exclusiveMonitorHeld_ = false;
  exclusiveMonitor_ = 0;
}

bool Simulator::init() {
  // Allocate 2MB for the stack. Note that we will only use 1MB, see below.
  static const size_t stackSize = 2 * 1024 * 1024;
  stack_ = js_pod_malloc<char>(stackSize);
  if (!stack_) {
    return false;
  }

  // Leave a safety margin of 1MB to prevent overrunning the stack when
  // pushing values (total stack size is 2MB).
  stackLimit_ = reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024;

  // The sp is initialized to point to the bottom (high address) of the
  // allocated stack area. To be safe in potential stack underflows we leave
  // some buffer below.
  registers_[sp] = reinterpret_cast<int32_t>(stack_) + stackSize - 64;

  return true;
}

// When the generated code calls a VM function (masm.callWithABI) we need to
// call that function instead of trying to execute it with the simulator
// (because it's x86 code instead of arm code). We do that by redirecting the VM
// call to a svc (Supervisor Call) instruction that is handled by the
// simulator. We write the original destination of the jump just at a known
// offset from the svc instruction so the simulator knows what to call.
class Redirection {
  friend class SimulatorProcess;

  // sim's lock must already be held.
  Redirection(void* nativeFunction, ABIFunctionType type)
      : nativeFunction_(nativeFunction),
        swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected),
        type_(type),
        next_(nullptr) {
    next_ = SimulatorProcess::redirection();
    if (!SimulatorProcess::ICacheCheckingDisableCount) {
      FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
                        SimInstruction::kInstrSize);
    }
    SimulatorProcess::setRedirection(this);
  }

 public:
  void* addressOfSwiInstruction() { return &swiInstruction_; }
  void* nativeFunction() const { return nativeFunction_; }
  ABIFunctionType type() const { return type_; }

  static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
    AutoLockSimulatorCache als;

    Redirection* current = SimulatorProcess::redirection();
    for (; current != nullptr; current = current->next_) {
      if (current->nativeFunction_ == nativeFunction) {
        MOZ_ASSERT(current->type() == type);
        return current;
      }
    }

    // Note: we can't use js_new here because the constructor is private.
    AutoEnterOOMUnsafeRegion oomUnsafe;
    Redirection* redir = js_pod_malloc<Redirection>(1);
    if (!redir) {
      oomUnsafe.crash("Simulator redirection");
    }
    new (redir) Redirection(nativeFunction, type);
    return redir;
  }

  static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
    uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
    uint8_t* addrOfRedirection =
        addrOfSwi - offsetof(Redirection, swiInstruction_);
    return reinterpret_cast<Redirection*>(addrOfRedirection);
  }

 private:
  void* nativeFunction_;
  uint32_t swiInstruction_;
  ABIFunctionType type_;
  Redirection* next_;
};

Simulator::~Simulator() { js_free(stack_); }

SimulatorProcess::SimulatorProcess()
    : cacheLock_(mutexid::SimulatorCacheLock), redirection_(nullptr) {
  if (getenv("ARM_SIM_ICACHE_CHECKS")) {
    ICacheCheckingDisableCount = 0;
  }
}

SimulatorProcess::~SimulatorProcess() {
  Redirection* r = redirection_;
  while (r) {
    Redirection* next = r->next_;
    js_delete(r);
    r = next;
  }
}

/* static */
void* Simulator::RedirectNativeFunction(void* nativeFunction,
                                        ABIFunctionType type) {
  Redirection* redirection = Redirection::Get(nativeFunction, type);
  return redirection->addressOfSwiInstruction();
}

// Sets the register in the architecture state. It will also deal with updating
// Simulator internal state for special registers such as PC.
void Simulator::set_register(int reg, int32_t value) {
  MOZ_ASSERT(reg >= 0 && reg < num_registers);
  if (reg == pc) {
    pc_modified_ = true;
  }
  registers_[reg] = value;
}

// Get the register from the architecture state. This function does handle the
// special case of accessing the PC register.
int32_t Simulator::get_register(int reg) const {
  MOZ_ASSERT(reg >= 0 && reg < num_registers);
  // Work around GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
  if (reg >= num_registers) return 0;
  return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0);
}

double Simulator::get_double_from_register_pair(int reg) {
  MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0);

  // Read the bits from the unsigned integer register_[] array into the double
  // precision floating point value and return it.
  double dm_val = 0.0;
  char buffer[2 * sizeof(vfp_registers_[0])];
  memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0]));
  memcpy(&dm_val, buffer, 2 * sizeof(registers_[0]));
  return dm_val;
}

void Simulator::set_register_pair_from_double(int reg, double* value) {
  MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0);
  memcpy(registers_ + reg, value, sizeof(*value));
}

void Simulator::set_dw_register(int dreg, const int* dbl) {
  MOZ_ASSERT(dreg >= 0 && dreg < num_d_registers);
  registers_[dreg] = dbl[0];
  registers_[dreg + 1] = dbl[1];
}

void Simulator::get_d_register(int dreg, uint64_t* value) {
  MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::TotalPhys));
  memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value));
}

void Simulator::set_d_register(int dreg, const uint64_t* value) {
  MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::TotalPhys));
  memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value));
}

void Simulator::get_d_register(int dreg, uint32_t* value) {
  MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::TotalPhys));
  memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2);
}

void Simulator::set_d_register(int dreg, const uint32_t* value) {
  MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::TotalPhys));
  memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2);
}

void Simulator::get_q_register(int qreg, uint64_t* value) {
  MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers);
  memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 2);
}

void Simulator::set_q_register(int qreg, const uint64_t* value) {
  MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers);
  memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 2);
}

void Simulator::get_q_register(int qreg, uint32_t* value) {
  MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers);
  memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 4);
}

void Simulator::set_q_register(int qreg, const uint32_t* value) {
  MOZ_ASSERT((qreg >= 0) && (qreg < num_q_registers));
  memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 4);
}

void Simulator::set_pc(int32_t value) {
  pc_modified_ = true;
  registers_[pc] = value;
}

bool Simulator::has_bad_pc() const {
  return registers_[pc] == bad_lr || registers_[pc] == end_sim_pc;
}

// Raw access to the PC register without the special adjustment when reading.
int32_t Simulator::get_pc() const { return registers_[pc]; }

void Simulator::set_s_register(int sreg, unsigned int value) {
  MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers);
  vfp_registers_[sreg] = value;
}

unsigned Simulator::get_s_register(int sreg) const {
  MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers);
  return vfp_registers_[sreg];
}

template <class InputType, int register_size>
void Simulator::setVFPRegister(int reg_index, const InputType& value) {
  MOZ_ASSERT(reg_index >= 0);
  MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers);
  MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::TotalPhys));

  char buffer[register_size * sizeof(vfp_registers_[0])];
  memcpy(buffer, &value, register_size * sizeof(vfp_registers_[0]));
  memcpy(&vfp_registers_[reg_index * register_size], buffer,
         register_size * sizeof(vfp_registers_[0]));
}

template <class ReturnType, int register_size>
void Simulator::getFromVFPRegister(int reg_index, ReturnType* out) {
  MOZ_ASSERT(reg_index >= 0);
  MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers);
  MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::TotalPhys));

  char buffer[register_size * sizeof(vfp_registers_[0])];
  memcpy(buffer, &vfp_registers_[register_size * reg_index],
         register_size * sizeof(vfp_registers_[0]));
  memcpy(out, buffer, register_size * sizeof(vfp_registers_[0]));
}

// These forced-instantiations are for jsapi-tests. Evidently, nothing
// requires these to be instantiated.
template void Simulator::getFromVFPRegister<double, 2>(int reg_index,
                                                       double* out);
template void Simulator::getFromVFPRegister<float, 1>(int reg_index,
                                                      float* out);
template void Simulator::setVFPRegister<double, 2>(int reg_index,
                                                   const double& value);
template void Simulator::setVFPRegister<float, 1>(int reg_index,
                                                  const float& value);

void Simulator::getFpArgs(double* x, double* y, int32_t* z) {
  if (ARMFlags::UseHardFpABI()) {
    get_double_from_d_register(0, x);
    get_double_from_d_register(1, y);
    *z = get_register(0);
  } else {
    *x = get_double_from_register_pair(0);
    *y = get_double_from_register_pair(2);
    *z = get_register(2);
  }
}

void Simulator::getFpFromStack(int32_t* stack, double* x) {
  MOZ_ASSERT(stack && x);
  char buffer[2 * sizeof(stack[0])];
  memcpy(buffer, stack, 2 * sizeof(stack[0]));
  memcpy(x, buffer, 2 * sizeof(stack[0]));
}

void Simulator::setCallResultDouble(double result) {
  // The return value is either in r0/r1 or d0.
  if (ARMFlags::UseHardFpABI()) {
    char buffer[2 * sizeof(vfp_registers_[0])];
    memcpy(buffer, &result, sizeof(buffer));
    // Copy result to d0.
    memcpy(vfp_registers_, buffer, sizeof(buffer));
  } else {
    char buffer[2 * sizeof(registers_[0])];
    memcpy(buffer, &result, sizeof(buffer));
    // Copy result to r0 and r1.
    memcpy(registers_, buffer, sizeof(buffer));
  }
}

void Simulator::setCallResultFloat(float result) {
  if (ARMFlags::UseHardFpABI()) {
    char buffer[sizeof(registers_[0])];
    memcpy(buffer, &result, sizeof(buffer));
    // Copy result to s0.
    memcpy(vfp_registers_, buffer, sizeof(buffer));
  } else {
    char buffer[sizeof(registers_[0])];
    memcpy(buffer, &result, sizeof(buffer));
    // Copy result to r0.
    memcpy(registers_, buffer, sizeof(buffer));
  }
}

void Simulator::setCallResult(int64_t res) {
  set_register(r0, static_cast<int32_t>(res));
  set_register(r1, static_cast<int32_t>(res >> 32));
}

void Simulator::exclusiveMonitorSet(uint64_t value) {
  exclusiveMonitor_ = value;
  exclusiveMonitorHeld_ = true;
}

uint64_t Simulator::exclusiveMonitorGetAndClear(bool* held) {
  *held = exclusiveMonitorHeld_;
  exclusiveMonitorHeld_ = false;
  return *held ? exclusiveMonitor_ : 0;
}

void Simulator::exclusiveMonitorClear() { exclusiveMonitorHeld_ = false; }

JS::ProfilingFrameIterator::RegisterState Simulator::registerState() {
  wasm::RegisterState state;
  state.pc = (void*)get_pc();
  state.fp = (void*)get_register(fp);
  state.sp = (void*)get_register(sp);
  state.lr = (void*)get_register(lr);
  return state;
}

uint64_t Simulator::readQ(int32_t addr, SimInstruction* instr,
                          UnalignedPolicy f) {
  if (handleWasmSegFault(addr, 8)) {
    return UINT64_MAX;
  }

  if ((addr & 3) == 0 ||
      (f == AllowUnaligned && !ARMFlags::HasAlignmentFault())) {
    uint64_t* ptr = reinterpret_cast<uint64_t*>(addr);
    return *ptr;
  }

  // See the comments below in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    uint64_t value;
    memcpy(&value, ptr, sizeof(value));
    return value;
  }

  printf("Unaligned read at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
}

void Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr,
                       UnalignedPolicy f) {
  if (handleWasmSegFault(addr, 8)) {
    return;
  }

  if ((addr & 3) == 0 ||
      (f == AllowUnaligned && !ARMFlags::HasAlignmentFault())) {
    uint64_t* ptr = reinterpret_cast<uint64_t*>(addr);
    *ptr = value;
    return;
  }

  // See the comments below in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    memcpy(ptr, &value, sizeof(value));
    return;
  }

  printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
}

int Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f) {
  if (handleWasmSegFault(addr, 4)) {
    return -1;
  }

  if ((addr & 3) == 0 ||
      (f == AllowUnaligned && !ARMFlags::HasAlignmentFault())) {
    intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
    return *ptr;
  }

  // In WebAssembly, we want unaligned accesses to either raise a signal or
  // do the right thing. Making this simulator properly emulate the behavior
  // of raising a signal is complex, so as a special-case, when in wasm code,
  // we just do the right thing.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    int value;
    memcpy(&value, ptr, sizeof(value));
    return value;
  }

  printf("Unaligned read at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
}

void Simulator::writeW(int32_t addr, int value, SimInstruction* instr,
                       UnalignedPolicy f) {
  if (handleWasmSegFault(addr, 4)) {
    return;
  }

  if ((addr & 3) == 0 ||
      (f == AllowUnaligned && !ARMFlags::HasAlignmentFault())) {
    intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
    *ptr = value;
    return;
  }

  // See the comments above in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    memcpy(ptr, &value, sizeof(value));
    return;
  }

  printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
}

// For the time being, define Relaxed operations in terms of SeqCst
// operations - we don't yet need Relaxed operations anywhere else in
// the system, and the distinction is not important to the simulation
// at the level where we're operating.

template <typename T>
static T loadRelaxed(SharedMem<T*> addr) {
  return AtomicOperations::loadSeqCst(addr);
}

template <typename T>
static T compareExchangeRelaxed(SharedMem<T*> addr, T oldval, T newval) {
  return AtomicOperations::compareExchangeSeqCst(addr, oldval, newval);
}

int Simulator::readExW(int32_t addr, SimInstruction* instr) {
  if (addr & 3) {
    MOZ_CRASH("Unaligned exclusive read");
  }

  if (handleWasmSegFault(addr, 4)) {
    return -1;
  }

  SharedMem<int32_t*> ptr =
      SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
  int32_t value = loadRelaxed(ptr);
  exclusiveMonitorSet(value);
  return value;
}

int32_t Simulator::writeExW(int32_t addr, int value, SimInstruction* instr) {
  if (addr & 3) {
    MOZ_CRASH("Unaligned exclusive write");
  }

  if (handleWasmSegFault(addr, 4)) {
    return -1;
  }

  SharedMem<int32_t*> ptr =
      SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
  bool held;
  int32_t expected = int32_t(exclusiveMonitorGetAndClear(&held));
  if (!held) {
    return 1;
  }
  int32_t old = compareExchangeRelaxed(ptr, expected, int32_t(value));
  return old != expected;
}

uint16_t Simulator::readHU(int32_t addr, SimInstruction* instr) {
  if (handleWasmSegFault(addr, 2)) {
    return UINT16_MAX;
  }

  // The regexp engine emits unaligned loads, so we don't check for them here
  // like most of the other methods do.
  if ((addr & 1) == 0 || !ARMFlags::HasAlignmentFault()) {
    uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
    return *ptr;
  }

  // See comments above in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    uint16_t value;
    memcpy(&value, ptr, sizeof(value));
    return value;
  }

  printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
  return 0;
}

int16_t Simulator::readH(int32_t addr, SimInstruction* instr) {
  if (handleWasmSegFault(addr, 2)) {
    return -1;
  }

  if ((addr & 1) == 0 || !ARMFlags::HasAlignmentFault()) {
    int16_t* ptr = reinterpret_cast<int16_t*>(addr);
    return *ptr;
  }

  // See comments above in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    int16_t value;
    memcpy(&value, ptr, sizeof(value));
    return value;
  }

  printf("Unaligned signed halfword read at 0x%08x\n", addr);
  MOZ_CRASH();
  return 0;
}

void Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr) {
  if (handleWasmSegFault(addr, 2)) {
    return;
  }

  if ((addr & 1) == 0 || !ARMFlags::HasAlignmentFault()) {
    uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
    *ptr = value;
    return;
  }

  // See the comments above in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    memcpy(ptr, &value, sizeof(value));
    return;
  }

  printf("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
}

void Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr) {
  if (handleWasmSegFault(addr, 2)) {
    return;
  }

  if ((addr & 1) == 0 || !ARMFlags::HasAlignmentFault()) {
    int16_t* ptr = reinterpret_cast<int16_t*>(addr);
    *ptr = value;
    return;
  }

  // See the comments above in readW.
  if (ARMFlags::FixupFault() &&
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    char* ptr = reinterpret_cast<char*>(addr);
    memcpy(ptr, &value, sizeof(value));
    return;
  }

  printf("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr);
  MOZ_CRASH();
}

uint16_t Simulator::readExHU(int32_t addr, SimInstruction* instr) {
  if (addr & 1) {
    MOZ_CRASH("Unaligned exclusive read");
  }

  if (handleWasmSegFault(addr, 2)) {
    return UINT16_MAX;
  }

  SharedMem<uint16_t*> ptr =
      SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
  uint16_t value = loadRelaxed(ptr);
  exclusiveMonitorSet(value);
  return value;
}

int32_t Simulator::writeExH(int32_t addr, uint16_t value,
                            SimInstruction* instr) {
  if (addr & 1) {
    MOZ_CRASH("Unaligned exclusive write");
  }

  if (handleWasmSegFault(addr, 2)) {
    return -1;
  }

  SharedMem<uint16_t*> ptr =
      SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
  bool held;
  uint16_t expected = uint16_t(exclusiveMonitorGetAndClear(&held));
  if (!held) {
    return 1;
  }
  uint16_t old = compareExchangeRelaxed(ptr, expected, value);
  return old != expected;
}

uint8_t Simulator::readBU(int32_t addr) {
  if (handleWasmSegFault(addr, 1)) {
    return UINT8_MAX;
  }

  uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
  return *ptr;
}

uint8_t Simulator::readExBU(int32_t addr) {
  if (handleWasmSegFault(addr, 1)) {
    return UINT8_MAX;
  }

  SharedMem<uint8_t*> ptr =
      SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
  uint8_t value = loadRelaxed(ptr);
  exclusiveMonitorSet(value);
  return value;
}

int32_t Simulator::writeExB(int32_t addr, uint8_t value) {
  if (handleWasmSegFault(addr, 1)) {
    return -1;
  }

  SharedMem<uint8_t*> ptr =
      SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
  bool held;
  uint8_t expected = uint8_t(exclusiveMonitorGetAndClear(&held));
  if (!held) {
    return 1;
  }
  uint8_t old = compareExchangeRelaxed(ptr, expected, value);
  return old != expected;
}

int8_t Simulator::readB(int32_t addr) {
  if (handleWasmSegFault(addr, 1)) {
    return -1;
  }

  int8_t* ptr = reinterpret_cast<int8_t*>(addr);
  return *ptr;
}

void Simulator::writeB(int32_t addr, uint8_t value) {
  if (handleWasmSegFault(addr, 1)) {
    return;
  }

  uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
  *ptr = value;
}

void Simulator::writeB(int32_t addr, int8_t value) {
  if (handleWasmSegFault(addr, 1)) {
    return;
  }

  int8_t* ptr = reinterpret_cast<int8_t*>(addr);
  *ptr = value;
}

int32_t* Simulator::readDW(int32_t addr) {
  if (handleWasmSegFault(addr, 8)) {
    return nullptr;
  }

  if ((addr & 3) == 0) {
    int32_t* ptr = reinterpret_cast<int32_t*>(addr);
    return ptr;
  }

  printf("Unaligned read at 0x%08x\n", addr);
  MOZ_CRASH();
}

void Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2) {
  if (handleWasmSegFault(addr, 8)) {
    return;
  }

  if ((addr & 3) == 0) {
    int32_t* ptr = reinterpret_cast<int32_t*>(addr);
    *ptr++ = value1;
    *ptr = value2;
    return;
  }

  printf("Unaligned write at 0x%08x\n", addr);
  MOZ_CRASH();
}

int32_t Simulator::readExDW(int32_t addr, int32_t* hibits) {
  if (addr & 3) {
    MOZ_CRASH("Unaligned exclusive read");
  }

  if (handleWasmSegFault(addr, 8)) {
    return -1;
  }

  SharedMem<uint64_t*> ptr =
      SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
  // The spec says that the low part of value shall be read from addr and
  // the high part shall be read from addr+4.  On a little-endian system
  // where we read a 64-bit quadword the low part of the value will be in
  // the low part of the quadword, and the high part of the value in the
  // high part of the quadword.
  uint64_t value = loadRelaxed(ptr);
  exclusiveMonitorSet(value);
  *hibits = int32_t(value >> 32);
  return int32_t(value);
}

int32_t Simulator::writeExDW(int32_t addr, int32_t value1, int32_t value2) {
  if (addr & 3) {
    MOZ_CRASH("Unaligned exclusive write");
  }

  if (handleWasmSegFault(addr, 8)) {
    return -1;
  }

  SharedMem<uint64_t*> ptr =
      SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
  // The spec says that value1 shall be stored at addr and value2 at
  // addr+4.  On a little-endian system that means constructing a 64-bit
  // value where value1 is in the low half of a 64-bit quadword and value2
  // is in the high half of the quadword.
  uint64_t value = (uint64_t(value2) << 32) | uint32_t(value1);
  bool held;
  uint64_t expected = exclusiveMonitorGetAndClear(&held);
  if (!held) {
    return 1;
  }
  uint64_t old = compareExchangeRelaxed(ptr, expected, value);
  return old != expected;
}

uintptr_t Simulator::stackLimit() const { return stackLimit_; }

uintptr_t* Simulator::addressOfStackLimit() { return &stackLimit_; }

bool Simulator::overRecursed(uintptr_t newsp) const {
  if (newsp == 0) {
    newsp = get_register(sp);
  }
  return newsp <= stackLimit();
}

bool Simulator::overRecursedWithExtra(uint32_t extra) const {
  uintptr_t newsp = get_register(sp) - extra;
  return newsp <= stackLimit();
}

// Checks if the current instruction should be executed based on its condition
// bits.
bool Simulator::conditionallyExecute(SimInstruction* instr) {
  switch (instr->conditionField()) {
    case Assembler::EQ:
      return z_flag_;
    case Assembler::NE:
      return !z_flag_;
    case Assembler::CS:
      return c_flag_;
    case Assembler::CC:
      return !c_flag_;
    case Assembler::MI:
      return n_flag_;
    case Assembler::PL:
      return !n_flag_;
    case Assembler::VS:
      return v_flag_;
    case Assembler::VC:
      return !v_flag_;
    case Assembler::HI:
      return c_flag_ && !z_flag_;
    case Assembler::LS:
      return !c_flag_ || z_flag_;
    case Assembler::GE:
      return n_flag_ == v_flag_;
    case Assembler::LT:
      return n_flag_ != v_flag_;
    case Assembler::GT:
      return !z_flag_ && (n_flag_ == v_flag_);
    case Assembler::LE:
      return z_flag_ || (n_flag_ != v_flag_);
    case Assembler::AL:
      return true;
    default:
      MOZ_CRASH();
  }
  return false;
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=89 H=98 G=93

¤ Dauer der Verarbeitung: 0.70 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.