// Copyright 2015, VIXL 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 ARM Limited 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 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.
// Representation of memory, with typed getters and setters for access. class Memory { public: template <typename T> static T AddressUntag(T address) { // Cast the address using a C-style cast. A reinterpret_cast would be // appropriate, but it can't cast one integral type to another.
uint64_t bits = (uint64_t)address; return (T)(bits & ~kAddressTagMask);
}
// Represent a register (r0-r31, v0-v31). template<int kSizeInBytes> class SimRegisterBase { public:
SimRegisterBase() : written_since_last_log_(false) {}
// Write the specified value. The value is zero-extended if necessary. template<typename T> void Set(T new_value) {
VIXL_STATIC_ASSERT(sizeof(new_value) <= kSizeInBytes); if (sizeof(new_value) < kSizeInBytes) { // All AArch64 registers are zero-extending.
memset(value_ + sizeof(new_value), 0, kSizeInBytes - sizeof(new_value));
}
memcpy(value_, &new_value, sizeof(new_value));
NotifyRegisterWrite();
}
// Insert a typed value into a register, leaving the rest of the register // unchanged. The lane parameter indicates where in the register the value // should be inserted, in the range [ 0, sizeof(value_) / sizeof(T) ), where // 0 represents the least significant bits. template<typename T> void Insert(int lane, T new_value) {
VIXL_ASSERT(lane >= 0);
VIXL_ASSERT((sizeof(new_value) +
(lane * sizeof(new_value))) <= kSizeInBytes);
memcpy(&value_[lane * sizeof(new_value)], &new_value, sizeof(new_value));
NotifyRegisterWrite();
}
// Read the value as the specified type. The value is truncated if necessary. template<typename T>
T Get(int lane = 0) const {
T result;
VIXL_ASSERT(lane >= 0);
VIXL_ASSERT((sizeof(result) + (lane * sizeof(result))) <= kSizeInBytes);
memcpy(&result, &value_[lane * sizeof(result)], sizeof(result)); return result;
}
// TODO: Make this return a map of updated bytes, so that we can highlight // updated lanes for load-and-insert. (That never happens for scalar code, but // NEON has some instructions that can update individual lanes.) bool WrittenSinceLastLog() const { return written_since_last_log_;
}
// Representation of a vector register, with typed getters and setters for lanes // and additional information to represent lane state. class LogicVRegister { public: inline LogicVRegister(SimVRegister& other) // NOLINT
: register_(other) { for (unsigned i = 0; i < sizeof(saturated_) / sizeof(saturated_[0]); i++) {
saturated_[i] = kNotSaturated;
} for (unsigned i = 0; i < sizeof(round_) / sizeof(round_[0]); i++) {
round_[i] = 0;
}
}
int64_t Int(VectorFormat vform, int index) const {
int64_t element; switch (LaneSizeInBitsFromFormat(vform)) { case 8: element = register_.Get<int8_t>(index); break; case 16: element = register_.Get<int16_t>(index); break; case 32: element = register_.Get<int32_t>(index); break; case 64: element = register_.Get<int64_t>(index); break; default: VIXL_UNREACHABLE(); return 0;
} return element;
}
uint64_t Uint(VectorFormat vform, int index) const {
uint64_t element; switch (LaneSizeInBitsFromFormat(vform)) { case 8: element = register_.Get<uint8_t>(index); break; case 16: element = register_.Get<uint16_t>(index); break; case 32: element = register_.Get<uint32_t>(index); break; case 64: element = register_.Get<uint64_t>(index); break; default: VIXL_UNREACHABLE(); return 0;
} return element;
}
// When setting a result in a register of size less than Q, the top bits of // the Q register must be cleared. void ClearForWrite(VectorFormat vform) const { unsigned size = RegisterSizeInBytesFromFormat(vform); for (unsigned i = size; i < kQRegSizeInBytes; i++) {
SetUint(kFormat16B, i, 0);
}
}
// Saturation state for each lane of a vector. enum Saturation {
kNotSaturated = 0,
kSignedSatPositive = 1 << 0,
kSignedSatNegative = 1 << 1,
kSignedSatMask = kSignedSatPositive | kSignedSatNegative,
kSignedSatUndefined = kSignedSatMask,
kUnsignedSatPositive = 1 << 2,
kUnsignedSatNegative = 1 << 3,
kUnsignedSatMask = kUnsignedSatPositive | kUnsignedSatNegative,
kUnsignedSatUndefined = kUnsignedSatMask
};
// Saturate lanes of a vector based on saturation state.
LogicVRegister& SignedSaturate(VectorFormat vform) { for (int i = 0; i < LaneCountFromFormat(vform); i++) {
Saturation sat = GetSignedSaturation(i); if (sat == kSignedSatPositive) {
SetInt(vform, i, MaxIntFromFormat(vform));
} elseif (sat == kSignedSatNegative) {
SetInt(vform, i, MinIntFromFormat(vform));
}
} return *this;
}
LogicVRegister& UnsignedSaturate(VectorFormat vform) { for (int i = 0; i < LaneCountFromFormat(vform); i++) {
Saturation sat = GetUnsignedSaturation(i); if (sat == kUnsignedSatPositive) {
SetUint(vform, i, MaxUintFromFormat(vform));
} elseif (sat == kUnsignedSatNegative) {
SetUint(vform, i, 0);
}
} return *this;
}
// Round lanes of a vector based on rounding state.
LogicVRegister& Round(VectorFormat vform) { for (int i = 0; i < LaneCountFromFormat(vform); i++) {
SetInt(vform, i, Int(vform, i) + (GetRounding(i) ? 1 : 0));
} return *this;
}
// Unsigned halve lanes of a vector, and use the saturation state to set the // top bit.
LogicVRegister& Uhalve(VectorFormat vform) { for (int i = 0; i < LaneCountFromFormat(vform); i++) {
uint64_t val = Uint(vform, i);
SetRounding(i, (val & 1) == 1);
val >>= 1; if (GetUnsignedSaturation(i) != kNotSaturated) { // If the operation causes unsigned saturation, the bit shifted into the // most significant bit must be set.
val |= (MaxUintFromFormat(vform) >> 1) + 1;
}
SetInt(vform, i, val);
} return *this;
}
// Signed halve lanes of a vector, and use the carry state to set the top bit.
LogicVRegister& Halve(VectorFormat vform) { for (int i = 0; i < LaneCountFromFormat(vform); i++) {
int64_t val = Int(vform, i);
SetRounding(i, (val & 1) == 1);
val >>= 1; if (GetSignedSaturation(i) != kNotSaturated) { // If the operation causes signed saturation, the sign bit must be // inverted.
val ^= (MaxUintFromFormat(vform) >> 1) + 1;
}
SetInt(vform, i, val);
} return *this;
}
private:
SimVRegister& register_;
// Allocate one saturation state entry per lane; largest register is type Q, // and lanes can be a minimum of one byte wide.
Saturation saturated_[kQRegSizeInBytes];
// Allocate one rounding state entry per lane. bool round_[kQRegSizeInBytes];
};
// The proper way to initialize a simulated system register (such as NZCV) is as // follows: // SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV); class SimSystemRegister { public: // The default constructor represents a register which has no writable bits. // It is not possible to set its value to anything other than 0.
SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { }
protected: // Most system registers only implement a few of the bits in the word. Other // bits are "read-as-zero, write-ignored". The write_ignore_mask argument // describes the bits which are not modifiable.
SimSystemRegister(uint32_t value, uint32_t write_ignore_mask)
: value_(value), write_ignore_mask_(write_ignore_mask) { }
// Clear the exclusive monitor most of the time. void MaybeClear() { if ((seed_ % kSkipClearProbability) != 0) {
Clear();
}
// Advance seed_ using a simple linear congruential generator.
seed_ = (seed_ * 48271) % 2147483647;
}
// Mark the address range for exclusive access (like load-exclusive). void MarkExclusive(uint64_t address, size_t size) {
address_ = address;
size_ = size;
}
// Return true if the address range is marked (like store-exclusive). // This helper doesn't implicitly clear the monitor. bool IsExclusive(uint64_t address, size_t size) {
VIXL_ASSERT(size > 0); // Be pedantic: Require both the address and the size to match. return (size == size_) && (address == address_);
}
// We can't accurate simulate the global monitor since it depends on external // influences. Instead, this implementation occasionally causes accesses to // fail, according to kPassProbability. class SimExclusiveGlobalMonitor { public:
SimExclusiveGlobalMonitor() : kPassProbability(8), seed_(0x87654321) {}
// When the SingleStepCallback is called, the simulator is about to execute // sim->get_pc() and the current machine state represents the completed // execution of the previous pc. typedefvoid (*SingleStepCallback)(void* arg, Simulator* sim, void* pc);
class Simulator : public DecoderVisitor { public: #ifdef JS_CACHE_SIMULATOR_ARM64 using Decoder = CachingDecoder;
mozilla::Atomic<bool> pendingCacheRequests = mozilla::Atomic<bool>{ false }; #endif explicit Simulator(Decoder* decoder, FILE* stream = stdout);
~Simulator();
// Handle any wasm faults, returning true if the fault was handled. // This method is rather hot so inline the normal (no-wasm) case. bool MOZ_ALWAYS_INLINE handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes) { if (MOZ_LIKELY(!js::wasm::CodeExists)) { returnfalse;
}
uint8_t* newPC; if (!js::wasm::MemoryAccessTraps(registerState(), (uint8_t*)addr, numBytes, &newPC)) { returnfalse;
}
set_pc((Instruction*)newPC); returntrue;
}
void increment_pc() { if (!pc_modified_) {
pc_ = pc_->NextInstruction();
}
// As above, with parameterized size and return type. The value is // either zero-extended or truncated to fit, as required. template<typename T>
T reg(unsigned size, unsigned code,
Reg31Mode r31mode = Reg31IsZeroRegister) const {
uint64_t raw; switch (size) { case kWRegSize: raw = reg<uint32_t>(code, r31mode); break; case kXRegSize: raw = reg<uint64_t>(code, r31mode); break; default:
VIXL_UNREACHABLE(); return 0;
}
T result;
VIXL_STATIC_ASSERT(sizeof(result) <= sizeof(raw)); // Copy the result and truncate to fit. This assumes a little-endian host.
memcpy(&result, &raw, sizeof(result)); return result;
}
// Use int64_t by default if T is not specified.
int64_t reg(unsigned size, unsigned code,
Reg31Mode r31mode = Reg31IsZeroRegister) const { return reg<int64_t>(size, code, r31mode);
}
enum RegLogMode {
LogRegWrites,
NoRegLog
};
// Write 'value' into an integer register. The value is zero-extended. This // behaviour matches AArch64 register writes. template<typename T> void set_reg(unsigned code, T value,
RegLogMode log_mode = LogRegWrites,
Reg31Mode r31mode = Reg31IsZeroRegister) { if (sizeof(T) < kWRegSizeInBytes) { // We use a C-style cast on purpose here. // Since we do not have access to 'constepxr if', the casts in this `if` // must be valid even if we know the code will never be executed, in // particular when `T` is a pointer type.
int64_t tmp_64bit = (int64_t)value;
int32_t tmp_32bit = static_cast<int32_t>(tmp_64bit);
set_reg<int32_t>(code, tmp_32bit, log_mode, r31mode); return;
}
// As above, with parameterized size and type. The value is either // zero-extended or truncated to fit, as required. template<typename T> void set_reg(unsigned size, unsigned code, T value,
RegLogMode log_mode = LogRegWrites,
Reg31Mode r31mode = Reg31IsZeroRegister) { // Zero-extend the input.
uint64_t raw = 0;
VIXL_STATIC_ASSERT(sizeof(value) <= sizeof(raw));
memcpy(&raw, &value, sizeof(value));
// Write (and possibly truncate) the value. switch (size) { case kWRegSize:
set_reg(code, static_cast<uint32_t>(raw), log_mode, r31mode); break; case kXRegSize:
set_reg(code, raw, log_mode, r31mode); break; default:
VIXL_UNREACHABLE(); return;
}
}
// Common specialized accessors for the set_reg() template.
// As above, with parameterized size and return type. The value is // either zero-extended or truncated to fit, as required. template<typename T>
T vreg(unsigned size, unsigned code) const {
uint64_t raw = 0;
T result;
switch (size) { case kSRegSize: raw = vreg<uint32_t>(code); break; case kDRegSize: raw = vreg<uint64_t>(code); break; default:
VIXL_UNREACHABLE(); break;
}
VIXL_STATIC_ASSERT(sizeof(result) <= sizeof(raw)); // Copy the result and truncate to fit. This assumes a little-endian host.
memcpy(&result, &raw, sizeof(result)); return result;
}
// TODO: Find a way to make the fpcr_ members return the proper types, so // these accessors are not necessary.
FPRounding RMode() { returnstatic_cast<FPRounding>(fpcr_.RMode()); } bool DN() { return fpcr_.DN() != 0; }
SimSystemRegister& fpcr() { return fpcr_; }
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.